Chromium Code Reviews| Index: src/builtins/builtins-regexp.cc |
| diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc |
| index b907a1bf45dd0559bbf92279e69cb6704121e94f..709ccf4c24d822862005f1cf04f164e996441421 100644 |
| --- a/src/builtins/builtins-regexp.cc |
| +++ b/src/builtins/builtins-regexp.cc |
| @@ -422,9 +422,10 @@ Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* map) { |
| // We use a fairly coarse granularity for this and simply check whether both |
| // the regexp itself is unmodified (i.e. its map has not changed) and its |
| // prototype is unmodified. |
| -void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map, |
| - Label* if_isunmodified, |
| - Label* if_ismodified) { |
| +Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* context, Node* map) { |
| + Label out(this); |
| + Variable var_result(this, MachineRepresentation::kWord32, Int32Constant(0)); |
|
Igor Sheludko
2017/02/03 09:17:54
Although the use cases look nicer with this change
jgruber
2017/02/03 10:29:28
Done.
|
| + |
| Node* const native_context = LoadNativeContext(context); |
| Node* const regexp_fun = |
| LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
| @@ -432,7 +433,7 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map, |
| LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| Node* const has_initialmap = WordEqual(map, initial_map); |
| - GotoUnless(has_initialmap, if_ismodified); |
| + GotoUnless(has_initialmap, &out); |
| Node* const initial_proto_initial_map = |
| LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); |
| @@ -440,10 +441,14 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map, |
| Node* const proto_has_initialmap = |
| WordEqual(proto_map, initial_proto_initial_map); |
| + var_result.Bind(proto_has_initialmap); |
| + Goto(&out); |
| + |
| // TODO(ishell): Update this check once map changes for constant field |
| // tracking are landing. |
| - Branch(proto_has_initialmap, if_isunmodified, if_ismodified); |
| + Bind(&out); |
| + return var_result.value(); |
| } |
| void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map, |
| @@ -1200,7 +1205,7 @@ Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, |
| Label out(this), if_isfastpath(this), if_isslowpath(this); |
| Node* const map = LoadMap(regexp); |
| - BranchIfFastRegExp(context, map, &if_isfastpath, &if_isslowpath); |
| + Branch(IsFastRegExpMap(context, map), &if_isfastpath, &if_isslowpath); |
| Bind(&if_isfastpath); |
| { |
| @@ -1274,7 +1279,7 @@ TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) { |
| Node* const string = ToString(context, maybe_string); |
| Label fast_path(this), slow_path(this); |
| - BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
| + Branch(IsFastRegExpMap(context, map), &fast_path, &slow_path); |
| Bind(&fast_path); |
| { |
| @@ -1649,7 +1654,7 @@ TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) { |
| Node* const string = ToString(context, maybe_string); |
| Label fast_path(this), slow_path(this); |
| - BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
| + Branch(IsFastRegExpMap(context, map), &fast_path, &slow_path); |
| Bind(&fast_path); |
| RegExpPrototypeMatchBody(context, receiver, string, true); |
| @@ -1774,7 +1779,7 @@ TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) { |
| Node* const string = ToString(context, maybe_string); |
| Label fast_path(this), slow_path(this); |
| - BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
| + Branch(IsFastRegExpMap(context, map), &fast_path, &slow_path); |
| Bind(&fast_path); |
| RegExpPrototypeSearchBodyFast(context, receiver, string); |
| @@ -2029,6 +2034,51 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, |
| } |
| } |
| +// Helper that skips a few initial checks. |
| +TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) { |
| + typedef RegExpSplitDescriptor Descriptor; |
| + |
| + Node* const regexp = Parameter(Descriptor::kReceiver); |
| + Node* const string = Parameter(Descriptor::kString); |
| + Node* const maybe_limit = Parameter(Descriptor::kLimit); |
| + Node* const context = Parameter(Descriptor::kContext); |
| + |
| + CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp))); |
| + CSA_ASSERT(this, IsString(string)); |
| + |
| + // TODO(jgruber): Even if map checks send us to the fast path, we still need |
| + // to verify the constructor property and jump to the slow path if it has |
| + // been changed. |
| + |
| + // Convert {maybe_limit} to a uint32, capping at the maximal smi value. |
| + Variable var_limit(this, MachineRepresentation::kTagged); |
| + Label if_limitissmimax(this), limit_done(this); |
| + |
| + GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); |
| + |
| + { |
| + Node* const limit = ToUint32(context, maybe_limit); |
| + GotoUnless(TaggedIsSmi(limit), &if_limitissmimax); |
| + |
| + var_limit.Bind(limit); |
| + Goto(&limit_done); |
| + } |
| + |
| + Bind(&if_limitissmimax); |
| + { |
| + // TODO(jgruber): In this case, we can probably avoid generation of limit |
| + // checks in Generate_RegExpPrototypeSplitBody. |
| + var_limit.Bind(SmiConstant(Smi::kMaxValue)); |
| + Goto(&limit_done); |
| + } |
| + |
| + Bind(&limit_done); |
| + { |
| + Node* const limit = var_limit.value(); |
| + RegExpPrototypeSplitBody(context, regexp, string, limit); |
| + } |
| +} |
| + |
| // ES#sec-regexp.prototype-@@split |
| // RegExp.prototype [ @@split ] ( string, limit ) |
| TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { |
| @@ -2046,51 +2096,16 @@ TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { |
| // Convert {maybe_string} to a String. |
| Node* const string = ToString(context, maybe_string); |
| - Label fast_path(this), slow_path(this); |
| - BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
| + Label stub(this), runtime(this, Label::kDeferred); |
| + Branch(IsFastRegExpMap(context, map), &stub, &runtime); |
| - Bind(&fast_path); |
| - { |
| - // TODO(jgruber): Even if map checks send us to the fast path, we still need |
| - // to verify the constructor property and jump to the slow path if it has |
| - // been changed. |
| - |
| - // Convert {maybe_limit} to a uint32, capping at the maximal smi value. |
| - Variable var_limit(this, MachineRepresentation::kTagged); |
| - Label if_limitissmimax(this), limit_done(this); |
| - |
| - GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); |
| - |
| - { |
| - Node* const limit = ToUint32(context, maybe_limit); |
| - GotoUnless(TaggedIsSmi(limit), &if_limitissmimax); |
| - |
| - var_limit.Bind(limit); |
| - Goto(&limit_done); |
| - } |
| - |
| - Bind(&if_limitissmimax); |
| - { |
| - // TODO(jgruber): In this case, we can probably generation of limit checks |
| - // in Generate_RegExpPrototypeSplitBody. |
| - Node* const smi_max = SmiConstant(Smi::kMaxValue); |
| - var_limit.Bind(smi_max); |
| - Goto(&limit_done); |
| - } |
| - |
| - Bind(&limit_done); |
| - { |
| - Node* const limit = var_limit.value(); |
| - RegExpPrototypeSplitBody(context, receiver, string, limit); |
| - } |
| - } |
| + Bind(&stub); |
| + Callable split_callable = CodeFactory::RegExpSplit(isolate()); |
| + Return(CallStub(split_callable, context, receiver, string, maybe_limit)); |
| - Bind(&slow_path); |
| - { |
| - Node* const result = CallRuntime(Runtime::kRegExpSplit, context, receiver, |
| - string, maybe_limit); |
| - Return(result); |
| - } |
| + Bind(&runtime); |
| + Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string, |
| + maybe_limit)); |
| } |
| Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( |
| @@ -2406,50 +2421,37 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath( |
| return var_result.value(); |
| } |
| -// ES#sec-regexp.prototype-@@replace |
| -// RegExp.prototype [ @@replace ] ( string, replaceValue ) |
| -TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { |
| - Node* const maybe_receiver = Parameter(0); |
| - Node* const maybe_string = Parameter(1); |
| - Node* const replace_value = Parameter(2); |
| - Node* const context = Parameter(5); |
| - |
| - // Ensure {maybe_receiver} is a JSReceiver. |
| - Node* const map = ThrowIfNotJSReceiver( |
| - context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| - "RegExp.prototype.@@replace"); |
| - Node* const receiver = maybe_receiver; |
| +// Helper that skips a few initial checks. |
| +TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) { |
| + typedef RegExpReplaceDescriptor Descriptor; |
| - // Convert {maybe_string} to a String. |
| - Callable tostring_callable = CodeFactory::ToString(isolate()); |
| - Node* const string = CallStub(tostring_callable, context, maybe_string); |
| + Node* const regexp = Parameter(Descriptor::kReceiver); |
| + Node* const string = Parameter(Descriptor::kString); |
| + Node* const replace_value = Parameter(Descriptor::kReplaceValue); |
| + Node* const context = Parameter(Descriptor::kContext); |
| - // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? |
| - Label checkreplacecallable(this), runtime(this, Label::kDeferred), |
| - fastpath(this); |
| - BranchIfFastRegExp(context, map, &checkreplacecallable, &runtime); |
| + CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp))); |
| + CSA_ASSERT(this, IsString(string)); |
| - Bind(&checkreplacecallable); |
| - Node* const regexp = receiver; |
| + Label checkreplacestring(this), if_iscallable(this), |
| + runtime(this, Label::kDeferred); |
| // 2. Is {replace_value} callable? |
| - Label checkreplacestring(this), if_iscallable(this); |
| GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); |
| - |
| - Node* const replace_value_map = LoadMap(replace_value); |
| - Branch(IsCallableMap(replace_value_map), &if_iscallable, &checkreplacestring); |
| + Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable, |
| + &checkreplacestring); |
| // 3. Does ToString({replace_value}) contain '$'? |
| Bind(&checkreplacestring); |
| { |
| + Callable tostring_callable = CodeFactory::ToString(isolate()); |
| Node* const replace_string = |
| CallStub(tostring_callable, context, replace_value); |
| Node* const dollar_char = Int32Constant('$'); |
| - Node* const smi_minusone = SmiConstant(Smi::FromInt(-1)); |
| GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char, |
| SmiConstant(0)), |
| - smi_minusone), |
| + SmiConstant(-1)), |
| &runtime); |
| Return( |
| @@ -2459,35 +2461,56 @@ TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { |
| // {regexp} is unmodified and {replace_value} is callable. |
| Bind(&if_iscallable); |
| { |
| - Node* const replace_callable = replace_value; |
| + Node* const replace_fn = replace_value; |
| // Check if the {regexp} is global. |
| Label if_isglobal(this), if_isnotglobal(this); |
| + |
| Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal); |
| Branch(is_global, &if_isglobal, &if_isnotglobal); |
| Bind(&if_isglobal); |
| - { |
| - Node* const result = ReplaceGlobalCallableFastPath( |
| - context, regexp, string, replace_callable); |
| - Return(result); |
| - } |
| + Return(ReplaceGlobalCallableFastPath(context, regexp, string, replace_fn)); |
| Bind(&if_isnotglobal); |
| - { |
| - Node* const result = |
| - CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, |
| - context, string, regexp, replace_callable); |
| - Return(result); |
| - } |
| + Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, |
| + context, string, regexp, replace_fn)); |
| } |
| Bind(&runtime); |
| - { |
| - Node* const result = CallRuntime(Runtime::kRegExpReplace, context, receiver, |
| - string, replace_value); |
| - Return(result); |
| - } |
| + Return(CallRuntime(Runtime::kRegExpReplace, context, regexp, string, |
| + replace_value)); |
| +} |
| + |
| +// ES#sec-regexp.prototype-@@replace |
| +// RegExp.prototype [ @@replace ] ( string, replaceValue ) |
| +TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { |
| + Node* const maybe_receiver = Parameter(0); |
| + Node* const maybe_string = Parameter(1); |
| + Node* const replace_value = Parameter(2); |
| + Node* const context = Parameter(5); |
| + |
| + // Ensure {maybe_receiver} is a JSReceiver. |
| + Node* const map = ThrowIfNotJSReceiver( |
| + context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| + "RegExp.prototype.@@replace"); |
| + Node* const receiver = maybe_receiver; |
| + |
| + // Convert {maybe_string} to a String. |
| + Callable tostring_callable = CodeFactory::ToString(isolate()); |
| + Node* const string = CallStub(tostring_callable, context, maybe_string); |
| + |
| + // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? |
| + Label stub(this), runtime(this, Label::kDeferred); |
| + Branch(IsFastRegExpMap(context, map), &stub, &runtime); |
| + |
| + Bind(&stub); |
| + Callable replace_callable = CodeFactory::RegExpReplace(isolate()); |
| + Return(CallStub(replace_callable, context, receiver, string, replace_value)); |
| + |
| + Bind(&runtime); |
| + Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string, |
| + replace_value)); |
| } |
| // Simple string matching functionality for internal use which does not modify |