Index: src/builtins/builtins-regexp.cc |
diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc |
index b907a1bf45dd0559bbf92279e69cb6704121e94f..95979c91e1908023617c155bc685afa9e0b5934f 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) { |
+void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context, |
+ Node* const map, |
+ Label* const if_isunmodified, |
+ Label* const if_ismodified) { |
Node* const native_context = LoadNativeContext(context); |
Node* const regexp_fun = |
LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
@@ -446,6 +447,25 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map, |
Branch(proto_has_initialmap, if_isunmodified, if_ismodified); |
} |
+Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context, |
+ Node* const map) { |
+ Label yup(this), nope(this), out(this); |
+ Variable var_result(this, MachineRepresentation::kWord32); |
+ |
+ BranchIfFastRegExp(context, map, &yup, &nope); |
+ |
+ Bind(&yup); |
+ var_result.Bind(Int32Constant(1)); |
+ Goto(&out); |
+ |
+ Bind(&nope); |
+ var_result.Bind(Int32Constant(0)); |
+ Goto(&out); |
+ |
+ Bind(&out); |
+ return var_result.value(); |
+} |
+ |
void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map, |
Label* if_isunmodified, |
Label* if_ismodified) { |
@@ -2029,6 +2049,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 +2111,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); |
- |
- 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); |
- } |
+ Label stub(this), runtime(this, Label::kDeferred); |
+ BranchIfFastRegExp(context, map, &stub, &runtime); |
- 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 +2436,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 +2476,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); |
+ BranchIfFastRegExp(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 |