Chromium Code Reviews| Index: src/builtins/builtins-regexp.cc |
| diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc |
| index 15f52560625d67b0bc0e25e0e3569bc2e9e0e765..6855efab66e4edc18ece07df03488179341942f6 100644 |
| --- a/src/builtins/builtins-regexp.cc |
| +++ b/src/builtins/builtins-regexp.cc |
| @@ -226,6 +226,14 @@ void StoreLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, |
| } |
| } |
| +Node* LoadMatchInfoField(CodeStubAssembler* a, Node* const match_info, |
|
Igor Sheludko
2016/11/30 23:17:22
I think you should add CSA::LoadFieldArrayElement(
jgruber
2016/12/01 09:48:27
Will do this in a follow-up CL since it also affec
|
| + const int index) { |
| + const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
| + Node* const result = |
| + a->LoadFixedArrayElement(match_info, a->IntPtrConstant(index), 0, mode); |
| + return result; |
| +} |
| + |
| Node* ConstructNewResultFromMatchInfo(Isolate* isolate, CodeStubAssembler* a, |
| Node* context, Node* match_info, |
| Node* string) { |
| @@ -1130,21 +1138,38 @@ void Builtins::Generate_RegExpPrototypeTest(CodeAssemblerState* state) { |
| Node* const context = a.Parameter(4); |
| // Ensure {maybe_receiver} is a JSReceiver. |
| - ThrowIfNotJSReceiver(&a, isolate, context, maybe_receiver, |
| - MessageTemplate::kIncompatibleMethodReceiver, |
| - "RegExp.prototype.test"); |
| + Node* const map = ThrowIfNotJSReceiver( |
| + &a, isolate, context, maybe_receiver, |
| + MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.test"); |
| Node* const receiver = maybe_receiver; |
| // Convert {maybe_string} to a String. |
| Node* const string = a.ToString(context, maybe_string); |
| - // Call exec. |
| - Node* const match_indices = RegExpExec(&a, context, receiver, string); |
| + CLabel fast_path(&a), slow_path(&a); |
| + BranchIfFastPath(&a, context, map, &fast_path, &slow_path); |
| - // Return true iff exec matched successfully. |
| - Node* const result = a.Select(a.WordEqual(match_indices, a.NullConstant()), |
| - a.FalseConstant(), a.TrueConstant()); |
| - a.Return(result); |
| + a.Bind(&fast_path); |
| + { |
| + CLabel if_didnotmatch(&a); |
| + Generate_RegExpPrototypeExecBodyWithoutResult(&a, context, receiver, string, |
| + &if_didnotmatch, true); |
| + a.Return(a.TrueConstant()); |
| + |
| + a.Bind(&if_didnotmatch); |
| + a.Return(a.FalseConstant()); |
| + } |
| + |
| + a.Bind(&slow_path); |
| + { |
| + // Call exec. |
| + Node* const match_indices = RegExpExec(&a, context, receiver, string); |
| + |
| + // Return true iff exec matched successfully. |
| + Node* const result = a.Select(a.WordEqual(match_indices, a.NullConstant()), |
| + a.FalseConstant(), a.TrueConstant()); |
| + a.Return(result); |
| + } |
| } |
| namespace { |
| @@ -1360,45 +1385,40 @@ void Generate_RegExpPrototypeMatchBody(CodeStubAssembler* a, |
| a->Bind(&loop); |
| { |
| - Node* const result = is_fastpath ? Generate_RegExpPrototypeExecBody( |
| - a, context, regexp, string, true) |
| - : RegExpExec(a, context, regexp, string); |
| + CVariable var_match(a, MachineRepresentation::kTagged); |
| CLabel if_didmatch(a), if_didnotmatch(a); |
| - a->Branch(a->WordEqual(result, null), &if_didnotmatch, &if_didmatch); |
| - |
| - a->Bind(&if_didnotmatch); |
| - { |
| - // Return null if there were no matches, otherwise just exit the loop. |
| - a->GotoUnless(a->IntPtrEqual(array.length(), int_zero), &out); |
| - a->Return(null); |
| - } |
| - |
| - a->Bind(&if_didmatch); |
| - { |
| - Node* match = nullptr; |
| - if (is_fastpath) { |
| - // TODO(jgruber): We could optimize further here and in other |
| - // methods (e.g. @@search) by bypassing RegExp result construction. |
| - Node* const result_fixed_array = a->LoadElements(result); |
| - const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
| - match = |
| - a->LoadFixedArrayElement(result_fixed_array, int_zero, 0, mode); |
| - |
| - // The match is guaranteed to be a string on the fast path. |
| - CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(match))); |
| - } else { |
| - DCHECK(!is_fastpath); |
| - |
| - CVariable var_match(a, MachineRepresentation::kTagged); |
| - CLabel fast_result(a), slow_result(a), match_loaded(a); |
| + if (is_fastpath) { |
| + // On the fast path, grab the matching string from the raw match index |
| + // array. |
| + Node* const match_indices = |
| + Generate_RegExpPrototypeExecBodyWithoutResult( |
| + a, context, regexp, string, &if_didnotmatch, true); |
| + |
| + Node* const match_from = LoadMatchInfoField( |
| + a, match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
| + Node* const match_to = LoadMatchInfoField( |
| + a, match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
| + |
| + Node* match = a->SubString(context, string, match_from, match_to); |
| + var_match.Bind(match); |
| + |
| + a->Goto(&if_didmatch); |
| + } else { |
| + DCHECK(!is_fastpath); |
| + Node* const result = RegExpExec(a, context, regexp, string); |
| + |
| + CLabel load_match(a); |
| + a->Branch(a->WordEqual(result, null), &if_didnotmatch, &load_match); |
| + |
| + a->Bind(&load_match); |
| + { |
| + CLabel fast_result(a), slow_result(a); |
| BranchIfFastRegExpResult(a, context, a->LoadMap(result), &fast_result, |
| &slow_result); |
| a->Bind(&fast_result); |
| { |
| - // TODO(jgruber): We could optimize further here and in other |
| - // methods (e.g. @@search) by bypassing RegExp result construction. |
| Node* const result_fixed_array = a->LoadElements(result); |
| const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
| Node* const match = |
| @@ -1408,7 +1428,7 @@ void Generate_RegExpPrototypeMatchBody(CodeStubAssembler* a, |
| CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(match))); |
| var_match.Bind(match); |
| - a->Goto(&match_loaded); |
| + a->Goto(&if_didmatch); |
| } |
| a->Bind(&slow_result); |
| @@ -1419,14 +1439,22 @@ void Generate_RegExpPrototypeMatchBody(CodeStubAssembler* a, |
| Node* const match = |
| a->CallStub(getproperty_callable, context, result, name); |
| - var_match.Bind(match); |
| - a->Goto(&match_loaded); |
| + var_match.Bind(a->ToString(context, match)); |
| + a->Goto(&if_didmatch); |
| } |
| - |
| - a->Bind(&match_loaded); |
| - match = a->ToString(context, var_match.value()); |
| } |
| - DCHECK(match != nullptr); |
| + } |
| + |
| + a->Bind(&if_didnotmatch); |
| + { |
| + // Return null if there were no matches, otherwise just exit the loop. |
| + a->GotoUnless(a->IntPtrEqual(array.length(), int_zero), &out); |
| + a->Return(null); |
| + } |
| + |
| + a->Bind(&if_didmatch); |
| + { |
| + Node* match = var_match.value(); |
| // Store the match, growing the fixed array if needed. |
| @@ -1493,22 +1521,53 @@ void Builtins::Generate_RegExpPrototypeMatch(CodeAssemblerState* state) { |
| namespace { |
| -void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, |
| - Node* const receiver, |
| - Node* const string, Node* const context, |
| - bool is_fastpath) { |
| +void Generate_RegExpPrototypeSearchBodyFast(CodeStubAssembler* a, |
|
Igor Sheludko
2016/11/30 23:17:22
I think you can drop Generate_ prefixes from helpe
jgruber
2016/12/01 09:48:27
Done.
|
| + Node* const receiver, |
| + Node* const string, |
| + Node* const context) { |
| + // Grab the initial value of last index. |
| + Node* const previous_last_index = FastLoadLastIndex(a, receiver); |
| + |
| + // Ensure last index is 0. |
| + FastStoreLastIndex(a, receiver, a->SmiConstant(Smi::kZero)); |
| + |
| + // Call exec. |
| + CLabel if_didnotmatch(a); |
| + Node* const match_indices = Generate_RegExpPrototypeExecBodyWithoutResult( |
| + a, context, receiver, string, &if_didnotmatch, true); |
| + |
| + // Successful match. |
| + { |
| + // Reset last index. |
| + FastStoreLastIndex(a, receiver, previous_last_index); |
| + |
| + // Return the index of the match. |
| + Node* const index = LoadMatchInfoField(a, match_indices, |
| + RegExpMatchInfo::kFirstCaptureIndex); |
| + a->Return(index); |
| + } |
| + |
| + a->Bind(&if_didnotmatch); |
| + { |
| + // Reset last index and return -1. |
| + FastStoreLastIndex(a, receiver, previous_last_index); |
| + a->Return(a->SmiConstant(-1)); |
| + } |
| +} |
| + |
| +void Generate_RegExpPrototypeSearchBodySlow(CodeStubAssembler* a, |
| + Node* const receiver, |
| + Node* const string, |
| + Node* const context) { |
| Isolate* const isolate = a->isolate(); |
| Node* const smi_zero = a->SmiConstant(Smi::kZero); |
| // Grab the initial value of last index. |
| - Node* const previous_last_index = |
| - LoadLastIndex(a, context, receiver, is_fastpath); |
| + Node* const previous_last_index = SlowLoadLastIndex(a, context, receiver); |
| // Ensure last index is 0. |
| - if (is_fastpath) { |
| - FastStoreLastIndex(a, receiver, smi_zero); |
| - } else { |
| + { |
| CLabel next(a); |
| a->GotoIf(a->SameValue(previous_last_index, smi_zero, context), &next); |
| @@ -1518,15 +1577,10 @@ void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, |
| } |
| // Call exec. |
| - Node* const match_indices = |
| - is_fastpath |
| - ? Generate_RegExpPrototypeExecBody(a, context, receiver, string, true) |
| - : RegExpExec(a, context, receiver, string); |
| + Node* const exec_result = RegExpExec(a, context, receiver, string); |
| // Reset last index if necessary. |
| - if (is_fastpath) { |
| - FastStoreLastIndex(a, receiver, previous_last_index); |
| - } else { |
| + { |
| CLabel next(a); |
| Node* const current_last_index = SlowLoadLastIndex(a, context, receiver); |
| @@ -1535,34 +1589,28 @@ void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, |
| SlowStoreLastIndex(a, context, receiver, previous_last_index); |
| a->Goto(&next); |
| + |
| a->Bind(&next); |
| } |
| // Return -1 if no match was found. |
| { |
| CLabel next(a); |
| - a->GotoUnless(a->WordEqual(match_indices, a->NullConstant()), &next); |
| + a->GotoUnless(a->WordEqual(exec_result, a->NullConstant()), &next); |
| a->Return(a->SmiConstant(-1)); |
| a->Bind(&next); |
| } |
| // Return the index of the match. |
| - if (is_fastpath) { |
| - Node* const index = a->LoadObjectField( |
| - match_indices, JSRegExpResult::kIndexOffset, MachineType::AnyTagged()); |
| - a->Return(index); |
| - } else { |
| - DCHECK(!is_fastpath); |
| - |
| + { |
| CLabel fast_result(a), slow_result(a, CLabel::kDeferred); |
| - BranchIfFastRegExpResult(a, context, a->LoadMap(match_indices), |
| - &fast_result, &slow_result); |
| + BranchIfFastRegExpResult(a, context, a->LoadMap(exec_result), &fast_result, |
| + &slow_result); |
| a->Bind(&fast_result); |
| { |
| - Node* const index = |
| - a->LoadObjectField(match_indices, JSRegExpResult::kIndexOffset, |
| - MachineType::AnyTagged()); |
| + Node* const index = a->LoadObjectField( |
| + exec_result, JSRegExpResult::kIndexOffset, MachineType::AnyTagged()); |
|
Igor Sheludko
2016/11/30 23:17:22
AnyTagged() is default, you can drop it for readab
jgruber
2016/12/01 09:48:27
Done.
|
| a->Return(index); |
| } |
| @@ -1571,7 +1619,7 @@ void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, |
| Node* const name = a->HeapConstant(isolate->factory()->index_string()); |
| Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); |
| Node* const index = |
| - a->CallStub(getproperty_callable, context, match_indices, name); |
| + a->CallStub(getproperty_callable, context, exec_result, name); |
| a->Return(index); |
| } |
| } |
| @@ -1604,10 +1652,10 @@ void Builtins::Generate_RegExpPrototypeSearch(CodeAssemblerState* state) { |
| BranchIfFastPath(&a, context, map, &fast_path, &slow_path); |
| a.Bind(&fast_path); |
| - Generate_RegExpPrototypeSearchBody(&a, receiver, string, context, true); |
| + Generate_RegExpPrototypeSearchBodyFast(&a, receiver, string, context); |
| a.Bind(&slow_path); |
| - Generate_RegExpPrototypeSearchBody(&a, receiver, string, context, false); |
| + Generate_RegExpPrototypeSearchBodySlow(&a, receiver, string, context); |
| } |
| namespace { |