 Chromium Code Reviews
 Chromium Code Reviews Issue 2415663007:
  [regexp] RegExp.prototype.replace fast-paths  (Closed)
    
  
    Issue 2415663007:
  [regexp] RegExp.prototype.replace fast-paths  (Closed) 
  | Index: src/builtins/builtins-regexp.cc | 
| diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc | 
| index 80b16dbe160b83bf226aa9b959d87ba8c061f0da..1c2f331930401a6449dd35e96efaa56a47fd59af 100644 | 
| --- a/src/builtins/builtins-regexp.cc | 
| +++ b/src/builtins/builtins-regexp.cc | 
| @@ -750,6 +750,20 @@ BUILTIN(RegExpPrototypeSpeciesGetter) { | 
| namespace { | 
| +// Fast-path implementation for flag checks on an unmodified JSRegExp instance. | 
| +compiler::Node* FastFlagGetter(CodeStubAssembler* a, | 
| + compiler::Node* const regexp, | 
| + JSRegExp::Flag flag) { | 
| + typedef compiler::Node Node; | 
| + | 
| + Node* const smi_zero = a->SmiConstant(Smi::kZero); | 
| + Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); | 
| + Node* const mask = a->SmiConstant(Smi::FromInt(flag)); | 
| + Node* const is_flag_set = a->WordNotEqual(a->WordAnd(flags, mask), smi_zero); | 
| + | 
| + return is_flag_set; | 
| +} | 
| + | 
| void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag, | 
| v8::Isolate::UseCounterFeature counter, | 
| const char* method_name) { | 
| @@ -760,7 +774,6 @@ void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag, | 
| Node* const context = a->Parameter(3); | 
| Isolate* isolate = a->isolate(); | 
| - Node* const int_zero = a->IntPtrConstant(0); | 
| // Check whether we have an unmodified regexp instance. | 
| Label if_isunmodifiedjsregexp(a), | 
| @@ -777,13 +790,8 @@ void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag, | 
| a->Bind(&if_isunmodifiedjsregexp); | 
| { | 
| // Refer to JSRegExp's flag property on the fast-path. | 
| - Node* const flags_smi = | 
| - a->LoadObjectField(receiver, JSRegExp::kFlagsOffset); | 
| - Node* const flags_intptr = a->SmiUntag(flags_smi); | 
| - Node* const mask = a->IntPtrConstant(flag); | 
| - Node* const is_global = | 
| - a->WordNotEqual(a->WordAnd(flags_intptr, mask), int_zero); | 
| - a->Return(a->Select(is_global, a->TrueConstant(), a->FalseConstant())); | 
| + Node* const is_flag_set = FastFlagGetter(a, receiver, flag); | 
| + a->Return(a->Select(is_flag_set, a->TrueConstant(), a->FalseConstant())); | 
| 
jgruber
2016/10/15 18:23:18
TODO(me): Is this equivalent to a->Return(is_flag_
 
Yang
2016/10/17 13:30:54
Pretty sure you need to use Select, which turns th
 | 
| } | 
| a->Bind(&if_isnotunmodifiedjsregexp); | 
| @@ -1572,6 +1580,7 @@ void Builtins::Generate_RegExpPrototypeReplace(CodeStubAssembler* a) { | 
| Node* const context = a->Parameter(5); | 
| Node* const int_zero = a->IntPtrConstant(0); | 
| + Node* const smi_zero = a->SmiConstant(Smi::kZero); | 
| // Ensure {receiver} is a JSReceiver. | 
| Node* const map = | 
| @@ -1592,7 +1601,7 @@ void Builtins::Generate_RegExpPrototypeReplace(CodeStubAssembler* a) { | 
| Node* const regexp = receiver; | 
| // 2. Is {replace_value} callable? | 
| - Label checkreplacestring(a); | 
| + Label checkreplacestring(a), if_iscallable(a, Label::kDeferred); | 
| a->GotoIf(a->TaggedIsSmi(replace_value), &checkreplacestring); | 
| Node* const replace_value_map = a->LoadMap(replace_value); | 
| @@ -1600,7 +1609,7 @@ void Builtins::Generate_RegExpPrototypeReplace(CodeStubAssembler* a) { | 
| a->Word32Equal(a->Word32And(a->LoadMapBitField(replace_value_map), | 
| a->Int32Constant(1 << Map::kIsCallable)), | 
| a->Int32Constant(0)), | 
| - &checkreplacestring, &runtime); | 
| + &checkreplacestring, &if_iscallable); | 
| // 3. Does ToString({replace_value}) contain '$'? | 
| a->Bind(&checkreplacestring); | 
| @@ -1618,6 +1627,34 @@ void Builtins::Generate_RegExpPrototypeReplace(CodeStubAssembler* a) { | 
| a->Return(ReplaceFastPath(a, context, regexp, string, replace_string)); | 
| } | 
| + // {regexp} is unmodified and {replace_value} is callable. | 
| + a->Bind(&if_iscallable); | 
| + { | 
| + Node* const replace_callable = replace_value; | 
| + | 
| + // Check if the {regexp} is global. | 
| + Label if_isglobal(a), if_isnotglobal(a); | 
| + Node* const is_global = FastFlagGetter(a, regexp, JSRegExp::kGlobal); | 
| + a->Branch(is_global, &if_isglobal, &if_isnotglobal); | 
| + | 
| + a->Bind(&if_isglobal); | 
| + { | 
| + FastStoreLastIndex(a, context, regexp, smi_zero); | 
| + Node* const result = | 
| + a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithFunction, | 
| + context, string, regexp, replace_callable); | 
| + a->Return(result); | 
| + } | 
| + | 
| + a->Bind(&if_isnotglobal); | 
| + { | 
| + Node* const result = | 
| + a->CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, | 
| + context, string, regexp, replace_callable); | 
| + a->Return(result); | 
| + } | 
| + } | 
| + | 
| a->Bind(&runtime); | 
| { | 
| Node* const result = a->CallRuntime(Runtime::kRegExpReplace, context, |