LCOV - code coverage report
Current view: top level - src/builtins - builtins-regexp-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 1122 1142 98.2 %
Date: 2017-04-26 Functions: 80 83 96.4 %

          Line data    Source code
       1             : // Copyright 2017 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/builtins/builtins-regexp-gen.h"
       6             : 
       7             : #include "src/builtins/builtins-constructor-gen.h"
       8             : #include "src/builtins/builtins-utils-gen.h"
       9             : #include "src/builtins/builtins.h"
      10             : #include "src/code-factory.h"
      11             : #include "src/code-stub-assembler.h"
      12             : #include "src/objects/regexp-match-info.h"
      13             : #include "src/regexp/regexp-macro-assembler.h"
      14             : 
      15             : namespace v8 {
      16             : namespace internal {
      17             : 
      18             : using compiler::Node;
      19             : 
      20             : // -----------------------------------------------------------------------------
      21             : // ES6 section 21.2 RegExp Objects
      22             : 
      23           0 : Node* RegExpBuiltinsAssembler::FastLoadLastIndex(Node* regexp) {
      24             :   // Load the in-object field.
      25             :   static const int field_offset =
      26             :       JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
      27         817 :   return LoadObjectField(regexp, field_offset);
      28             : }
      29             : 
      30         172 : Node* RegExpBuiltinsAssembler::SlowLoadLastIndex(Node* context, Node* regexp) {
      31             :   // Load through the GetProperty stub.
      32         344 :   return GetProperty(context, regexp, isolate()->factory()->lastIndex_string());
      33             : }
      34             : 
      35         387 : Node* RegExpBuiltinsAssembler::LoadLastIndex(Node* context, Node* regexp,
      36             :                                              bool is_fastpath) {
      37             :   return is_fastpath ? FastLoadLastIndex(regexp)
      38         473 :                      : SlowLoadLastIndex(context, regexp);
      39             : }
      40             : 
      41             : // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified
      42             : // JSRegExp instance.
      43           0 : void RegExpBuiltinsAssembler::FastStoreLastIndex(Node* regexp, Node* value) {
      44             :   // Store the in-object field.
      45             :   static const int field_offset =
      46             :       JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
      47        1118 :   StoreObjectField(regexp, field_offset, value);
      48           0 : }
      49             : 
      50         301 : void RegExpBuiltinsAssembler::SlowStoreLastIndex(Node* context, Node* regexp,
      51             :                                                  Node* value) {
      52             :   // Store through runtime.
      53             :   // TODO(ishell): Use SetPropertyStub here once available.
      54         602 :   Node* const name = HeapConstant(isolate()->factory()->lastIndex_string());
      55         301 :   Node* const language_mode = SmiConstant(Smi::FromInt(STRICT));
      56             :   CallRuntime(Runtime::kSetProperty, context, regexp, name, value,
      57         301 :               language_mode);
      58         301 : }
      59             : 
      60        1075 : void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp,
      61             :                                              Node* value, bool is_fastpath) {
      62        1075 :   if (is_fastpath) {
      63             :     FastStoreLastIndex(regexp, value);
      64             :   } else {
      65         215 :     SlowStoreLastIndex(context, regexp, value);
      66             :   }
      67        1075 : }
      68             : 
      69         172 : Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
      70             :     Node* const context, Node* const regexp, Node* const match_info,
      71             :     Node* const string) {
      72             :   CSA_ASSERT(this, IsFixedArrayMap(LoadMap(match_info)));
      73             :   CSA_ASSERT(this, IsJSRegExp(regexp));
      74             :   CSA_ASSERT(this, IsString(string));
      75             : 
      76         344 :   Label named_captures(this), out(this);
      77             : 
      78             :   Node* const num_indices = SmiUntag(LoadFixedArrayElement(
      79         172 :       match_info, RegExpMatchInfo::kNumberOfCapturesIndex));
      80         172 :   Node* const num_results = SmiTag(WordShr(num_indices, 1));
      81             :   Node* const start =
      82         172 :       LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex);
      83             :   Node* const end = LoadFixedArrayElement(
      84         172 :       match_info, RegExpMatchInfo::kFirstCaptureIndex + 1);
      85             : 
      86             :   // Calculate the substring of the first match before creating the result array
      87             :   // to avoid an unnecessary write barrier storing the first result.
      88         172 :   Node* const first = SubString(context, string, start, end);
      89             : 
      90             :   Node* const result =
      91         172 :       AllocateRegExpResult(context, num_results, start, string);
      92         172 :   Node* const result_elements = LoadElements(result);
      93             : 
      94         172 :   StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER);
      95             : 
      96             :   // If no captures exist we can skip named capture handling as well.
      97         172 :   GotoIf(SmiEqual(num_results, SmiConstant(1)), &out);
      98             : 
      99             :   // Store all remaining captures.
     100             :   Node* const limit = IntPtrAdd(
     101         172 :       IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices);
     102             : 
     103         344 :   VARIABLE(var_from_cursor, MachineType::PointerRepresentation(),
     104             :            IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2));
     105         344 :   VARIABLE(var_to_cursor, MachineType::PointerRepresentation(),
     106             :            IntPtrConstant(1));
     107             : 
     108         172 :   Variable* vars[] = {&var_from_cursor, &var_to_cursor};
     109         344 :   Label loop(this, 2, vars);
     110             : 
     111         172 :   Goto(&loop);
     112         172 :   BIND(&loop);
     113             :   {
     114         172 :     Node* const from_cursor = var_from_cursor.value();
     115         172 :     Node* const to_cursor = var_to_cursor.value();
     116         172 :     Node* const start = LoadFixedArrayElement(match_info, from_cursor);
     117             : 
     118             :     Label next_iter(this);
     119         172 :     GotoIf(SmiEqual(start, SmiConstant(-1)), &next_iter);
     120             : 
     121         172 :     Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1));
     122         172 :     Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1);
     123             : 
     124         172 :     Node* const capture = SubString(context, string, start, end);
     125         172 :     StoreFixedArrayElement(result_elements, to_cursor, capture);
     126         172 :     Goto(&next_iter);
     127             : 
     128         172 :     BIND(&next_iter);
     129         172 :     var_from_cursor.Bind(IntPtrAdd(from_cursor, IntPtrConstant(2)));
     130         172 :     var_to_cursor.Bind(IntPtrAdd(to_cursor, IntPtrConstant(1)));
     131             :     Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop,
     132         172 :            &named_captures);
     133             :   }
     134             : 
     135         172 :   BIND(&named_captures);
     136             :   {
     137             :     // We reach this point only if captures exist, implying that this is an
     138             :     // IRREGEXP JSRegExp.
     139             : 
     140             :     CSA_ASSERT(this, IsJSRegExp(regexp));
     141             :     CSA_ASSERT(this, SmiGreaterThan(num_results, SmiConstant(1)));
     142             : 
     143             :     // Preparations for named capture properties. Exit early if the result does
     144             :     // not have any named captures to minimize performance impact.
     145             : 
     146         172 :     Node* const data = LoadObjectField(regexp, JSRegExp::kDataOffset);
     147             :     CSA_ASSERT(this, SmiEqual(LoadFixedArrayElement(data, JSRegExp::kTagIndex),
     148             :                               SmiConstant(JSRegExp::IRREGEXP)));
     149             : 
     150             :     // The names fixed array associates names at even indices with a capture
     151             :     // index at odd indices.
     152             :     Node* const names =
     153         172 :         LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureNameMapIndex);
     154         172 :     GotoIf(SmiEqual(names, SmiConstant(0)), &out);
     155             : 
     156             :     // Allocate a new object to store the named capture properties.
     157             :     // TODO(jgruber): Could be optimized by adding the object map to the heap
     158             :     // root list.
     159             : 
     160         172 :     Node* const native_context = LoadNativeContext(context);
     161             :     Node* const map = LoadContextElement(
     162         172 :         native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP);
     163             :     Node* const properties =
     164         172 :         AllocateNameDictionary(NameDictionary::kInitialCapacity);
     165             : 
     166         172 :     Node* const group_object = AllocateJSObjectFromMap(map, properties);
     167             : 
     168             :     // Store it on the result as a 'group' property.
     169             : 
     170             :     {
     171         344 :       Node* const name = HeapConstant(isolate()->factory()->groups_string());
     172             :       CallRuntime(Runtime::kCreateDataProperty, context, result, name,
     173         172 :                   group_object);
     174             :     }
     175             : 
     176             :     // One or more named captures exist, add a property for each one.
     177             : 
     178             :     CSA_ASSERT(this, HasInstanceType(names, FIXED_ARRAY_TYPE));
     179         172 :     Node* const names_length = LoadAndUntagFixedArrayBaseLength(names);
     180             :     CSA_ASSERT(this, IntPtrGreaterThan(names_length, IntPtrConstant(0)));
     181             : 
     182         172 :     VARIABLE(var_i, MachineType::PointerRepresentation());
     183         172 :     var_i.Bind(IntPtrConstant(0));
     184             : 
     185         172 :     Variable* vars[] = {&var_i};
     186             :     const int vars_count = sizeof(vars) / sizeof(vars[0]);
     187         344 :     Label loop(this, vars_count, vars);
     188             : 
     189         172 :     Goto(&loop);
     190         172 :     BIND(&loop);
     191             :     {
     192         172 :       Node* const i = var_i.value();
     193         172 :       Node* const i_plus_1 = IntPtrAdd(i, IntPtrConstant(1));
     194         172 :       Node* const i_plus_2 = IntPtrAdd(i_plus_1, IntPtrConstant(1));
     195             : 
     196         172 :       Node* const name = LoadFixedArrayElement(names, i);
     197         172 :       Node* const index = LoadFixedArrayElement(names, i_plus_1);
     198             :       Node* const capture =
     199         172 :           LoadFixedArrayElement(result_elements, SmiUntag(index));
     200             : 
     201             :       CallRuntime(Runtime::kCreateDataProperty, context, group_object, name,
     202         172 :                   capture);
     203             : 
     204         172 :       var_i.Bind(i_plus_2);
     205             :       Branch(IntPtrGreaterThanOrEqual(var_i.value(), names_length), &out,
     206         172 :              &loop);
     207         172 :     }
     208             :   }
     209             : 
     210         172 :   BIND(&out);
     211         172 :   return result;
     212             : }
     213             : 
     214         860 : void RegExpBuiltinsAssembler::GetStringPointers(
     215             :     Node* const string_data, Node* const offset, Node* const last_index,
     216             :     Node* const string_length, String::Encoding encoding,
     217             :     Variable* var_string_start, Variable* var_string_end) {
     218             :   DCHECK_EQ(var_string_start->rep(), MachineType::PointerRepresentation());
     219             :   DCHECK_EQ(var_string_end->rep(), MachineType::PointerRepresentation());
     220             : 
     221             :   const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
     222             :                                 ? UINT8_ELEMENTS
     223         860 :                                 : UINT16_ELEMENTS;
     224             : 
     225             :   Node* const from_offset = ElementOffsetFromIndex(
     226         860 :       IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS);
     227         860 :   var_string_start->Bind(IntPtrAdd(string_data, from_offset));
     228             : 
     229             :   Node* const to_offset = ElementOffsetFromIndex(
     230         860 :       IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS);
     231         860 :   var_string_end->Bind(IntPtrAdd(string_data, to_offset));
     232         860 : }
     233             : 
     234         430 : Node* RegExpBuiltinsAssembler::RegExpExecInternal(Node* const context,
     235             :                                                   Node* const regexp,
     236             :                                                   Node* const string,
     237             :                                                   Node* const last_index,
     238             :                                                   Node* const match_info) {
     239             : // Just jump directly to runtime if native RegExp is not selected at compile
     240             : // time or if regexp entry in generated code is turned off runtime switch or
     241             : // at compilation.
     242             : #ifdef V8_INTERPRETED_REGEXP
     243             :   return CallRuntime(Runtime::kRegExpExec, context, regexp, string, last_index,
     244             :                      match_info);
     245             : #else   // V8_INTERPRETED_REGEXP
     246             :   CSA_ASSERT(this, TaggedIsNotSmi(regexp));
     247             :   CSA_ASSERT(this, IsJSRegExp(regexp));
     248             : 
     249             :   CSA_ASSERT(this, TaggedIsNotSmi(string));
     250             :   CSA_ASSERT(this, IsString(string));
     251             : 
     252             :   CSA_ASSERT(this, IsNumber(last_index));
     253             :   CSA_ASSERT(this, IsFixedArrayMap(LoadReceiverMap(match_info)));
     254             : 
     255         430 :   Node* const int_zero = IntPtrConstant(0);
     256             : 
     257         430 :   ToDirectStringAssembler to_direct(state(), string);
     258             : 
     259         860 :   VARIABLE(var_result, MachineRepresentation::kTagged);
     260         430 :   Label out(this), runtime(this, Label::kDeferred);
     261             : 
     262             :   // External constants.
     263             :   Node* const isolate_address =
     264         430 :       ExternalConstant(ExternalReference::isolate_address(isolate()));
     265             :   Node* const regexp_stack_memory_address_address = ExternalConstant(
     266         430 :       ExternalReference::address_of_regexp_stack_memory_address(isolate()));
     267             :   Node* const regexp_stack_memory_size_address = ExternalConstant(
     268         430 :       ExternalReference::address_of_regexp_stack_memory_size(isolate()));
     269             :   Node* const static_offsets_vector_address = ExternalConstant(
     270         430 :       ExternalReference::address_of_static_offsets_vector(isolate()));
     271             : 
     272             :   // Ensure that a RegExp stack is allocated.
     273             :   {
     274             :     Node* const stack_size =
     275         430 :         Load(MachineType::IntPtr(), regexp_stack_memory_size_address);
     276         430 :     GotoIf(IntPtrEqual(stack_size, int_zero), &runtime);
     277             :   }
     278             : 
     279         430 :   Node* const data = LoadObjectField(regexp, JSRegExp::kDataOffset);
     280             :   {
     281             :     // Check that the RegExp has been compiled (data contains a fixed array).
     282             :     CSA_ASSERT(this, TaggedIsNotSmi(data));
     283             :     CSA_ASSERT(this, HasInstanceType(data, FIXED_ARRAY_TYPE));
     284             : 
     285             :     // Check the type of the RegExp. Only continue if type is
     286             :     // JSRegExp::IRREGEXP.
     287         430 :     Node* const tag = LoadFixedArrayElement(data, JSRegExp::kTagIndex);
     288         430 :     GotoIfNot(SmiEqual(tag, SmiConstant(JSRegExp::IRREGEXP)), &runtime);
     289             : 
     290             :     // Check (number_of_captures + 1) * 2 <= offsets vector size
     291             :     // Or              number_of_captures <= offsets vector size / 2 - 1
     292             :     Node* const capture_count =
     293         430 :         LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureCountIndex);
     294             :     CSA_ASSERT(this, TaggedIsSmi(capture_count));
     295             : 
     296             :     STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2);
     297             :     GotoIf(SmiAbove(
     298             :                capture_count,
     299             :                SmiConstant(Isolate::kJSRegexpStaticOffsetsVectorSize / 2 - 1)),
     300         430 :            &runtime);
     301             :   }
     302             : 
     303             :   // Unpack the string if possible.
     304             : 
     305         430 :   to_direct.TryToDirect(&runtime);
     306             : 
     307         430 :   Node* const smi_string_length = LoadStringLength(string);
     308             : 
     309             :   // Bail out to runtime for invalid {last_index} values.
     310         430 :   GotoIfNot(TaggedIsSmi(last_index), &runtime);
     311         430 :   GotoIf(SmiAboveOrEqual(last_index, smi_string_length), &runtime);
     312             : 
     313             :   // Load the irregexp code object and offsets into the subject string. Both
     314             :   // depend on whether the string is one- or two-byte.
     315             : 
     316         430 :   Node* const int_last_index = SmiUntag(last_index);
     317             : 
     318         860 :   VARIABLE(var_string_start, MachineType::PointerRepresentation());
     319         860 :   VARIABLE(var_string_end, MachineType::PointerRepresentation());
     320         860 :   VARIABLE(var_code, MachineRepresentation::kTagged);
     321             : 
     322             :   {
     323         430 :     Node* const int_string_length = SmiUntag(smi_string_length);
     324             :     Node* const direct_string_data = to_direct.PointerToData(&runtime);
     325             : 
     326         430 :     Label next(this), if_isonebyte(this), if_istwobyte(this, Label::kDeferred);
     327             :     Branch(IsOneByteStringInstanceType(to_direct.instance_type()),
     328         430 :            &if_isonebyte, &if_istwobyte);
     329             : 
     330         430 :     BIND(&if_isonebyte);
     331             :     {
     332             :       GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
     333             :                         int_string_length, String::ONE_BYTE_ENCODING,
     334         430 :                         &var_string_start, &var_string_end);
     335             :       var_code.Bind(
     336         430 :           LoadFixedArrayElement(data, JSRegExp::kIrregexpLatin1CodeIndex));
     337         430 :       Goto(&next);
     338             :     }
     339             : 
     340         430 :     BIND(&if_istwobyte);
     341             :     {
     342             :       GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
     343             :                         int_string_length, String::TWO_BYTE_ENCODING,
     344         430 :                         &var_string_start, &var_string_end);
     345             :       var_code.Bind(
     346         430 :           LoadFixedArrayElement(data, JSRegExp::kIrregexpUC16CodeIndex));
     347         430 :       Goto(&next);
     348             :     }
     349             : 
     350         860 :     BIND(&next);
     351             :   }
     352             : 
     353             :   // Check that the irregexp code has been generated for the actual string
     354             :   // encoding. If it has, the field contains a code object otherwise it contains
     355             :   // smi (code flushing support).
     356             : 
     357         430 :   Node* const code = var_code.value();
     358         430 :   GotoIf(TaggedIsSmi(code), &runtime);
     359             :   CSA_ASSERT(this, HasInstanceType(code, CODE_TYPE));
     360             : 
     361         430 :   Label if_success(this), if_failure(this),
     362         430 :       if_exception(this, Label::kDeferred);
     363             :   {
     364         430 :     IncrementCounter(isolate()->counters()->regexp_entry_native(), 1);
     365             : 
     366             :     // Set up args for the final call into generated Irregexp code.
     367             : 
     368             :     MachineType type_int32 = MachineType::Int32();
     369             :     MachineType type_tagged = MachineType::AnyTagged();
     370             :     MachineType type_ptr = MachineType::Pointer();
     371             : 
     372             :     // Result: A NativeRegExpMacroAssembler::Result return code.
     373         430 :     MachineType retval_type = type_int32;
     374             : 
     375             :     // Argument 0: Original subject string.
     376         430 :     MachineType arg0_type = type_tagged;
     377             :     Node* const arg0 = string;
     378             : 
     379             :     // Argument 1: Previous index.
     380         430 :     MachineType arg1_type = type_int32;
     381         430 :     Node* const arg1 = TruncateWordToWord32(int_last_index);
     382             : 
     383             :     // Argument 2: Start of string data.
     384         430 :     MachineType arg2_type = type_ptr;
     385         430 :     Node* const arg2 = var_string_start.value();
     386             : 
     387             :     // Argument 3: End of string data.
     388         430 :     MachineType arg3_type = type_ptr;
     389         430 :     Node* const arg3 = var_string_end.value();
     390             : 
     391             :     // Argument 4: static offsets vector buffer.
     392         430 :     MachineType arg4_type = type_ptr;
     393             :     Node* const arg4 = static_offsets_vector_address;
     394             : 
     395             :     // Argument 5: Set the number of capture registers to zero to force global
     396             :     // regexps to behave as non-global.  This does not affect non-global
     397             :     // regexps.
     398         430 :     MachineType arg5_type = type_int32;
     399         430 :     Node* const arg5 = Int32Constant(0);
     400             : 
     401             :     // Argument 6: Start (high end) of backtracking stack memory area.
     402             :     Node* const stack_start =
     403         430 :         Load(MachineType::Pointer(), regexp_stack_memory_address_address);
     404             :     Node* const stack_size =
     405         430 :         Load(MachineType::IntPtr(), regexp_stack_memory_size_address);
     406         430 :     Node* const stack_end = IntPtrAdd(stack_start, stack_size);
     407             : 
     408         430 :     MachineType arg6_type = type_ptr;
     409             :     Node* const arg6 = stack_end;
     410             : 
     411             :     // Argument 7: Indicate that this is a direct call from JavaScript.
     412         430 :     MachineType arg7_type = type_int32;
     413         430 :     Node* const arg7 = Int32Constant(1);
     414             : 
     415             :     // Argument 8: Pass current isolate address.
     416         430 :     MachineType arg8_type = type_ptr;
     417             :     Node* const arg8 = isolate_address;
     418             : 
     419             :     Node* const code_entry =
     420             :         IntPtrAdd(BitcastTaggedToWord(code),
     421         430 :                   IntPtrConstant(Code::kHeaderSize - kHeapObjectTag));
     422             : 
     423             :     Node* const result = CallCFunction9(
     424             :         retval_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type,
     425             :         arg5_type, arg6_type, arg7_type, arg8_type, code_entry, arg0, arg1,
     426         430 :         arg2, arg3, arg4, arg5, arg6, arg7, arg8);
     427             : 
     428             :     // Check the result.
     429             :     // We expect exactly one result since we force the called regexp to behave
     430             :     // as non-global.
     431         430 :     Node* const int_result = ChangeInt32ToIntPtr(result);
     432             :     GotoIf(IntPtrEqual(int_result,
     433             :                        IntPtrConstant(NativeRegExpMacroAssembler::SUCCESS)),
     434         430 :            &if_success);
     435             :     GotoIf(IntPtrEqual(int_result,
     436             :                        IntPtrConstant(NativeRegExpMacroAssembler::FAILURE)),
     437         430 :            &if_failure);
     438             :     GotoIf(IntPtrEqual(int_result,
     439             :                        IntPtrConstant(NativeRegExpMacroAssembler::EXCEPTION)),
     440         430 :            &if_exception);
     441             : 
     442             :     CSA_ASSERT(this,
     443             :                IntPtrEqual(int_result,
     444             :                            IntPtrConstant(NativeRegExpMacroAssembler::RETRY)));
     445         430 :     Goto(&runtime);
     446             :   }
     447             : 
     448         430 :   BIND(&if_success);
     449             :   {
     450             :     // Check that the last match info has space for the capture registers and
     451             :     // the additional information. Ensure no overflow in add.
     452             :     STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset);
     453             :     Node* const available_slots =
     454             :         SmiSub(LoadFixedArrayBaseLength(match_info),
     455         430 :                SmiConstant(RegExpMatchInfo::kLastMatchOverhead));
     456             :     Node* const capture_count =
     457         430 :         LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureCountIndex);
     458             :     // Calculate number of register_count = (capture_count + 1) * 2.
     459             :     Node* const register_count =
     460         430 :         SmiShl(SmiAdd(capture_count, SmiConstant(1)), 1);
     461         430 :     GotoIf(SmiGreaterThan(register_count, available_slots), &runtime);
     462             : 
     463             :     // Fill match_info.
     464             : 
     465             :     StoreFixedArrayElement(match_info, RegExpMatchInfo::kNumberOfCapturesIndex,
     466         430 :                            register_count, SKIP_WRITE_BARRIER);
     467             :     StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastSubjectIndex,
     468         430 :                            string);
     469             :     StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastInputIndex,
     470         430 :                            string);
     471             : 
     472             :     // Fill match and capture offsets in match_info.
     473             :     {
     474             :       Node* const limit_offset = ElementOffsetFromIndex(
     475         430 :           register_count, INT32_ELEMENTS, SMI_PARAMETERS, 0);
     476             : 
     477             :       Node* const to_offset = ElementOffsetFromIndex(
     478             :           IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), FAST_ELEMENTS,
     479         430 :           INTPTR_PARAMETERS, RegExpMatchInfo::kHeaderSize - kHeapObjectTag);
     480         430 :       VARIABLE(var_to_offset, MachineType::PointerRepresentation(), to_offset);
     481             : 
     482         430 :       VariableList vars({&var_to_offset}, zone());
     483             :       BuildFastLoop(
     484             :           vars, int_zero, limit_offset,
     485         430 :           [=, &var_to_offset](Node* offset) {
     486             :             Node* const value = Load(MachineType::Int32(),
     487         430 :                                      static_offsets_vector_address, offset);
     488         430 :             Node* const smi_value = SmiFromWord32(value);
     489             :             StoreNoWriteBarrier(MachineRepresentation::kTagged, match_info,
     490         430 :                                 var_to_offset.value(), smi_value);
     491         430 :             Increment(var_to_offset, kPointerSize);
     492         430 :           },
     493         860 :           kInt32Size, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
     494             :     }
     495             : 
     496         430 :     var_result.Bind(match_info);
     497         430 :     Goto(&out);
     498             :   }
     499             : 
     500         430 :   BIND(&if_failure);
     501             :   {
     502         430 :     var_result.Bind(NullConstant());
     503         430 :     Goto(&out);
     504             :   }
     505             : 
     506         430 :   BIND(&if_exception);
     507             :   {
     508             : // A stack overflow was detected in RegExp code.
     509             : #ifdef DEBUG
     510             :     Node* const pending_exception_address = ExternalConstant(
     511             :         ExternalReference(Isolate::kPendingExceptionAddress, isolate()));
     512             :     CSA_ASSERT(this, IsTheHole(Load(MachineType::AnyTagged(),
     513             :                                     pending_exception_address)));
     514             : #endif  // DEBUG
     515         430 :     CallRuntime(Runtime::kThrowStackOverflow, context);
     516         430 :     Unreachable();
     517             :   }
     518             : 
     519         430 :   BIND(&runtime);
     520             :   {
     521             :     Node* const result = CallRuntime(Runtime::kRegExpExec, context, regexp,
     522         430 :                                      string, last_index, match_info);
     523         430 :     var_result.Bind(result);
     524         430 :     Goto(&out);
     525             :   }
     526             : 
     527         430 :   BIND(&out);
     528         860 :   return var_result.value();
     529             : #endif  // V8_INTERPRETED_REGEXP
     530             : }
     531             : 
     532             : // ES#sec-regexp.prototype.exec
     533             : // RegExp.prototype.exec ( string )
     534             : // Implements the core of RegExp.prototype.exec but without actually
     535             : // constructing the JSRegExpResult. Returns either null (if the RegExp did not
     536             : // match) or a fixed array containing match indices as returned by
     537             : // RegExpExecStub.
     538         301 : Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
     539             :     Node* const context, Node* const regexp, Node* const string,
     540             :     Label* if_didnotmatch, const bool is_fastpath) {
     541         301 :   Node* const null = NullConstant();
     542         301 :   Node* const int_zero = IntPtrConstant(0);
     543         301 :   Node* const smi_zero = SmiConstant(Smi::kZero);
     544             : 
     545         301 :   if (is_fastpath) {
     546             :     CSA_ASSERT(this, IsFastRegExpNoPrototype(context, regexp));
     547             :   } else {
     548             :     ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
     549          43 :                            "RegExp.prototype.exec");
     550             :   }
     551             : 
     552             :   CSA_ASSERT(this, IsString(string));
     553             :   CSA_ASSERT(this, IsJSRegExp(regexp));
     554             : 
     555         301 :   VARIABLE(var_result, MachineRepresentation::kTagged);
     556         301 :   Label out(this);
     557             : 
     558             :   // Load lastIndex.
     559         602 :   VARIABLE(var_lastindex, MachineRepresentation::kTagged);
     560             :   {
     561         301 :     Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath);
     562         301 :     var_lastindex.Bind(regexp_lastindex);
     563             : 
     564         301 :     if (is_fastpath) {
     565             :       // ToLength on a positive smi is a nop and can be skipped.
     566             :       CSA_ASSERT(this, TaggedIsPositiveSmi(regexp_lastindex));
     567             :     } else {
     568             :       // Omit ToLength if lastindex is a non-negative smi.
     569          43 :       Label call_tolength(this, Label::kDeferred), next(this);
     570          43 :       Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength);
     571             : 
     572          43 :       BIND(&call_tolength);
     573             :       {
     574             :         var_lastindex.Bind(
     575          43 :             CallBuiltin(Builtins::kToLength, context, regexp_lastindex));
     576          43 :         Goto(&next);
     577             :       }
     578             : 
     579          86 :       BIND(&next);
     580             :     }
     581             :   }
     582             : 
     583             :   // Check whether the regexp is global or sticky, which determines whether we
     584             :   // update last index later on.
     585         301 :   Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
     586             :   Node* const is_global_or_sticky = WordAnd(
     587         301 :       SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky));
     588             :   Node* const should_update_last_index =
     589         301 :       WordNotEqual(is_global_or_sticky, int_zero);
     590             : 
     591             :   // Grab and possibly update last index.
     592         301 :   Label run_exec(this);
     593             :   {
     594         301 :     Label if_doupdate(this), if_dontupdate(this);
     595         301 :     Branch(should_update_last_index, &if_doupdate, &if_dontupdate);
     596             : 
     597         301 :     BIND(&if_doupdate);
     598             :     {
     599         301 :       Node* const lastindex = var_lastindex.value();
     600             : 
     601             :       Label if_isoob(this, Label::kDeferred);
     602         301 :       GotoIfNot(TaggedIsSmi(lastindex), &if_isoob);
     603         301 :       Node* const string_length = LoadStringLength(string);
     604         301 :       GotoIfNot(SmiLessThanOrEqual(lastindex, string_length), &if_isoob);
     605         301 :       Goto(&run_exec);
     606             : 
     607         301 :       BIND(&if_isoob);
     608             :       {
     609         301 :         StoreLastIndex(context, regexp, smi_zero, is_fastpath);
     610         301 :         var_result.Bind(null);
     611         301 :         Goto(if_didnotmatch);
     612         301 :       }
     613             :     }
     614             : 
     615         301 :     BIND(&if_dontupdate);
     616             :     {
     617         301 :       var_lastindex.Bind(smi_zero);
     618         301 :       Goto(&run_exec);
     619         301 :     }
     620             :   }
     621             : 
     622             :   Node* match_indices;
     623         301 :   Label successful_match(this);
     624         301 :   BIND(&run_exec);
     625             :   {
     626             :     // Get last match info from the context.
     627         301 :     Node* const native_context = LoadNativeContext(context);
     628             :     Node* const last_match_info = LoadContextElement(
     629         301 :         native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
     630             : 
     631             :     // Call the exec stub.
     632             :     match_indices = RegExpExecInternal(context, regexp, string,
     633         301 :                                        var_lastindex.value(), last_match_info);
     634         301 :     var_result.Bind(match_indices);
     635             : 
     636             :     // {match_indices} is either null or the RegExpMatchInfo array.
     637             :     // Return early if exec failed, possibly updating last index.
     638         301 :     GotoIfNot(WordEqual(match_indices, null), &successful_match);
     639             : 
     640         301 :     GotoIfNot(should_update_last_index, if_didnotmatch);
     641             : 
     642         301 :     StoreLastIndex(context, regexp, smi_zero, is_fastpath);
     643         301 :     Goto(if_didnotmatch);
     644             :   }
     645             : 
     646         301 :   BIND(&successful_match);
     647             :   {
     648         301 :     GotoIfNot(should_update_last_index, &out);
     649             : 
     650             :     // Update the new last index from {match_indices}.
     651             :     Node* const new_lastindex = LoadFixedArrayElement(
     652         301 :         match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
     653             : 
     654         301 :     StoreLastIndex(context, regexp, new_lastindex, is_fastpath);
     655         301 :     Goto(&out);
     656             :   }
     657             : 
     658         301 :   BIND(&out);
     659         602 :   return var_result.value();
     660             : }
     661             : 
     662             : // ES#sec-regexp.prototype.exec
     663             : // RegExp.prototype.exec ( string )
     664         129 : Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBody(Node* const context,
     665             :                                                        Node* const regexp,
     666             :                                                        Node* const string,
     667             :                                                        const bool is_fastpath) {
     668         129 :   Node* const null = NullConstant();
     669             : 
     670         129 :   VARIABLE(var_result, MachineRepresentation::kTagged);
     671             : 
     672         129 :   Label if_didnotmatch(this), out(this);
     673             :   Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult(
     674         129 :       context, regexp, string, &if_didnotmatch, is_fastpath);
     675             : 
     676             :   // Successful match.
     677             :   {
     678             :     Node* const match_indices = indices_or_null;
     679             :     Node* const result =
     680         129 :         ConstructNewResultFromMatchInfo(context, regexp, match_indices, string);
     681         129 :     var_result.Bind(result);
     682         129 :     Goto(&out);
     683             :   }
     684             : 
     685         129 :   BIND(&if_didnotmatch);
     686             :   {
     687         129 :     var_result.Bind(null);
     688         129 :     Goto(&out);
     689             :   }
     690             : 
     691         129 :   BIND(&out);
     692         258 :   return var_result.value();
     693             : }
     694             : 
     695         430 : Node* RegExpBuiltinsAssembler::ThrowIfNotJSReceiver(
     696             :     Node* context, Node* maybe_receiver, MessageTemplate::Template msg_template,
     697             :     char const* method_name) {
     698         860 :   Label out(this), throw_exception(this, Label::kDeferred);
     699         860 :   VARIABLE(var_value_map, MachineRepresentation::kTagged);
     700             : 
     701         430 :   GotoIf(TaggedIsSmi(maybe_receiver), &throw_exception);
     702             : 
     703             :   // Load the instance type of the {value}.
     704         430 :   var_value_map.Bind(LoadMap(maybe_receiver));
     705         430 :   Node* const value_instance_type = LoadMapInstanceType(var_value_map.value());
     706             : 
     707         430 :   Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception);
     708             : 
     709             :   // The {value} is not a compatible receiver for this method.
     710         430 :   BIND(&throw_exception);
     711             :   {
     712         860 :     Node* const message_id = SmiConstant(Smi::FromInt(msg_template));
     713             :     Node* const method_name_str = HeapConstant(
     714         860 :         isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED));
     715             : 
     716             :     Node* const value_str =
     717         430 :         CallBuiltin(Builtins::kToString, context, maybe_receiver);
     718             : 
     719             :     CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str,
     720         430 :                 value_str);
     721         430 :     Unreachable();
     722             :   }
     723             : 
     724         430 :   BIND(&out);
     725         860 :   return var_value_map.value();
     726             : }
     727             : 
     728          86 : Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(Node* const context,
     729             :                                                        Node* const object,
     730             :                                                        Node* const map) {
     731          86 :   Label out(this);
     732         172 :   VARIABLE(var_result, MachineRepresentation::kWord32);
     733             : 
     734          86 :   Node* const native_context = LoadNativeContext(context);
     735             :   Node* const regexp_fun =
     736          86 :       LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
     737             :   Node* const initial_map =
     738          86 :       LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
     739          86 :   Node* const has_initialmap = WordEqual(map, initial_map);
     740             : 
     741          86 :   var_result.Bind(has_initialmap);
     742          86 :   GotoIfNot(has_initialmap, &out);
     743             : 
     744             :   // The smi check is required to omit ToLength(lastIndex) calls with possible
     745             :   // user-code execution on the fast path.
     746             :   Node* const last_index = FastLoadLastIndex(object);
     747          86 :   var_result.Bind(TaggedIsPositiveSmi(last_index));
     748          86 :   Goto(&out);
     749             : 
     750          86 :   BIND(&out);
     751         172 :   return var_result.value();
     752             : }
     753             : 
     754          43 : Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(Node* const context,
     755             :                                                        Node* const object) {
     756             :   CSA_ASSERT(this, TaggedIsNotSmi(object));
     757          43 :   return IsFastRegExpNoPrototype(context, object, LoadMap(object));
     758             : }
     759             : 
     760             : // RegExp fast path implementations rely on unmodified JSRegExp instances.
     761             : // We use a fairly coarse granularity for this and simply check whether both
     762             : // the regexp itself is unmodified (i.e. its map has not changed), its
     763             : // prototype is unmodified, and lastIndex is a non-negative smi.
     764         387 : void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
     765             :                                                  Node* const object,
     766             :                                                  Node* const map,
     767             :                                                  Label* const if_isunmodified,
     768             :                                                  Label* const if_ismodified) {
     769             :   CSA_ASSERT(this, WordEqual(LoadMap(object), map));
     770             : 
     771             :   // TODO(ishell): Update this check once map changes for constant field
     772             :   // tracking are landing.
     773             : 
     774         387 :   Node* const native_context = LoadNativeContext(context);
     775             :   Node* const regexp_fun =
     776         387 :       LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
     777             :   Node* const initial_map =
     778         387 :       LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
     779         387 :   Node* const has_initialmap = WordEqual(map, initial_map);
     780             : 
     781         387 :   GotoIfNot(has_initialmap, if_ismodified);
     782             : 
     783             :   Node* const initial_proto_initial_map =
     784         387 :       LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX);
     785         387 :   Node* const proto_map = LoadMap(LoadMapPrototype(map));
     786             :   Node* const proto_has_initialmap =
     787         387 :       WordEqual(proto_map, initial_proto_initial_map);
     788             : 
     789         387 :   GotoIfNot(proto_has_initialmap, if_ismodified);
     790             : 
     791             :   // The smi check is required to omit ToLength(lastIndex) calls with possible
     792             :   // user-code execution on the fast path.
     793             :   Node* const last_index = FastLoadLastIndex(object);
     794         387 :   Branch(TaggedIsPositiveSmi(last_index), if_isunmodified, if_ismodified);
     795         387 : }
     796             : 
     797         301 : void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
     798             :                                                  Node* const object,
     799             :                                                  Label* const if_isunmodified,
     800             :                                                  Label* const if_ismodified) {
     801             :   CSA_ASSERT(this, TaggedIsNotSmi(object));
     802             :   BranchIfFastRegExp(context, object, LoadMap(object), if_isunmodified,
     803         301 :                      if_ismodified);
     804         301 : }
     805             : 
     806           0 : Node* RegExpBuiltinsAssembler::IsFastRegExp(Node* const context,
     807             :                                             Node* const object) {
     808           0 :   Label yup(this), nope(this), out(this);
     809           0 :   VARIABLE(var_result, MachineRepresentation::kWord32);
     810             : 
     811           0 :   BranchIfFastRegExp(context, object, &yup, &nope);
     812             : 
     813           0 :   BIND(&yup);
     814           0 :   var_result.Bind(Int32Constant(1));
     815           0 :   Goto(&out);
     816             : 
     817           0 :   BIND(&nope);
     818           0 :   var_result.Bind(Int32Constant(0));
     819           0 :   Goto(&out);
     820             : 
     821           0 :   BIND(&out);
     822           0 :   return var_result.value();
     823             : }
     824             : 
     825          86 : void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* const context,
     826             :                                                        Node* const object,
     827             :                                                        Label* if_isunmodified,
     828             :                                                        Label* if_ismodified) {
     829             :   // Could be a Smi.
     830          86 :   Node* const map = LoadReceiverMap(object);
     831             : 
     832          86 :   Node* const native_context = LoadNativeContext(context);
     833             :   Node* const initial_regexp_result_map =
     834          86 :       LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX);
     835             : 
     836             :   Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified,
     837          86 :          if_ismodified);
     838          86 : }
     839             : 
     840             : // Slow path stub for RegExpPrototypeExec to decrease code size.
     841         172 : TF_BUILTIN(RegExpPrototypeExecSlow, RegExpBuiltinsAssembler) {
     842             :   Node* const regexp = Parameter(Descriptor::kReceiver);
     843             :   Node* const string = Parameter(Descriptor::kString);
     844             :   Node* const context = Parameter(Descriptor::kContext);
     845             : 
     846          43 :   Return(RegExpPrototypeExecBody(context, regexp, string, false));
     847          43 : }
     848             : 
     849             : // ES#sec-regexp.prototype.exec
     850             : // RegExp.prototype.exec ( string )
     851         172 : TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) {
     852             :   Node* const maybe_receiver = Parameter(Descriptor::kReceiver);
     853             :   Node* const maybe_string = Parameter(Descriptor::kString);
     854             :   Node* const context = Parameter(Descriptor::kContext);
     855             : 
     856             :   // Ensure {maybe_receiver} is a JSRegExp.
     857             :   ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
     858          43 :                          "RegExp.prototype.exec");
     859             :   Node* const receiver = maybe_receiver;
     860             : 
     861             :   // Convert {maybe_string} to a String.
     862          43 :   Node* const string = ToString(context, maybe_string);
     863             : 
     864          43 :   Label if_isfastpath(this), if_isslowpath(this);
     865             :   Branch(IsFastRegExpNoPrototype(context, receiver), &if_isfastpath,
     866          43 :          &if_isslowpath);
     867             : 
     868          43 :   BIND(&if_isfastpath);
     869             :   {
     870             :     Node* const result =
     871          43 :         RegExpPrototypeExecBody(context, receiver, string, true);
     872          43 :     Return(result);
     873             :   }
     874             : 
     875          43 :   BIND(&if_isslowpath);
     876             :   {
     877             :     Node* const result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow,
     878          43 :                                      context, receiver, string);
     879          43 :     Return(result);
     880          43 :   }
     881          43 : }
     882             : 
     883         172 : Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context,
     884             :                                            Node* const regexp,
     885             :                                            bool is_fastpath) {
     886         172 :   Isolate* isolate = this->isolate();
     887             : 
     888         172 :   Node* const int_zero = IntPtrConstant(0);
     889         172 :   Node* const int_one = IntPtrConstant(1);
     890         172 :   VARIABLE(var_length, MachineType::PointerRepresentation(), int_zero);
     891         344 :   VARIABLE(var_flags, MachineType::PointerRepresentation());
     892             : 
     893         172 :   Node* const is_dotall_enabled = IsDotAllEnabled(isolate);
     894             : 
     895             :   // First, count the number of characters we will need and check which flags
     896             :   // are set.
     897             : 
     898         172 :   if (is_fastpath) {
     899             :     // Refer to JSRegExp's flag property on the fast-path.
     900             :     CSA_ASSERT(this, IsJSRegExp(regexp));
     901         129 :     Node* const flags_smi = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
     902         129 :     Node* const flags_intptr = SmiUntag(flags_smi);
     903         129 :     var_flags.Bind(flags_intptr);
     904             : 
     905             : #define CASE_FOR_FLAG(FLAG)                                  \
     906             :   do {                                                       \
     907             :     Label next(this);                                        \
     908             :     GotoIfNot(IsSetWord(flags_intptr, FLAG), &next);         \
     909             :     var_length.Bind(IntPtrAdd(var_length.value(), int_one)); \
     910             :     Goto(&next);                                             \
     911             :     BIND(&next);                                             \
     912             :   } while (false)
     913             : 
     914         129 :     CASE_FOR_FLAG(JSRegExp::kGlobal);
     915         129 :     CASE_FOR_FLAG(JSRegExp::kIgnoreCase);
     916         129 :     CASE_FOR_FLAG(JSRegExp::kMultiline);
     917             :     {
     918             :       Label next(this);
     919         129 :       GotoIfNot(is_dotall_enabled, &next);
     920         129 :       CASE_FOR_FLAG(JSRegExp::kDotAll);
     921         129 :       Goto(&next);
     922         129 :       BIND(&next);
     923             :     }
     924         129 :     CASE_FOR_FLAG(JSRegExp::kUnicode);
     925         129 :     CASE_FOR_FLAG(JSRegExp::kSticky);
     926             : #undef CASE_FOR_FLAG
     927             :   } else {
     928             :     DCHECK(!is_fastpath);
     929             : 
     930             :     // Fall back to GetProperty stub on the slow-path.
     931          43 :     var_flags.Bind(int_zero);
     932             : 
     933             : #define CASE_FOR_FLAG(NAME, FLAG)                                          \
     934             :   do {                                                                     \
     935             :     Label next(this);                                                      \
     936             :     Node* const flag = GetProperty(                                        \
     937             :         context, regexp, isolate->factory()->InternalizeUtf8String(NAME)); \
     938             :     Label if_isflagset(this);                                              \
     939             :     BranchIfToBooleanIsTrue(flag, &if_isflagset, &next);                   \
     940             :     BIND(&if_isflagset);                                                   \
     941             :     var_length.Bind(IntPtrAdd(var_length.value(), int_one));               \
     942             :     var_flags.Bind(WordOr(var_flags.value(), IntPtrConstant(FLAG)));       \
     943             :     Goto(&next);                                                           \
     944             :     BIND(&next);                                                           \
     945             :   } while (false)
     946             : 
     947         129 :     CASE_FOR_FLAG("global", JSRegExp::kGlobal);
     948         129 :     CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase);
     949         129 :     CASE_FOR_FLAG("multiline", JSRegExp::kMultiline);
     950             :     {
     951             :       Label next(this);
     952          43 :       GotoIfNot(is_dotall_enabled, &next);
     953         129 :       CASE_FOR_FLAG("dotAll", JSRegExp::kDotAll);
     954          43 :       Goto(&next);
     955          43 :       BIND(&next);
     956             :     }
     957         129 :     CASE_FOR_FLAG("unicode", JSRegExp::kUnicode);
     958         129 :     CASE_FOR_FLAG("sticky", JSRegExp::kSticky);
     959             : #undef CASE_FOR_FLAG
     960             :   }
     961             : 
     962             :   // Allocate a string of the required length and fill it with the corresponding
     963             :   // char for each set flag.
     964             : 
     965             :   {
     966         172 :     Node* const result = AllocateSeqOneByteString(context, var_length.value());
     967         172 :     Node* const flags_intptr = var_flags.value();
     968             : 
     969         172 :     VARIABLE(var_offset, MachineType::PointerRepresentation(),
     970             :              IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
     971             : 
     972             : #define CASE_FOR_FLAG(FLAG, CHAR)                              \
     973             :   do {                                                         \
     974             :     Label next(this);                                          \
     975             :     GotoIfNot(IsSetWord(flags_intptr, FLAG), &next);           \
     976             :     Node* const value = Int32Constant(CHAR);                   \
     977             :     StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \
     978             :                         var_offset.value(), value);            \
     979             :     var_offset.Bind(IntPtrAdd(var_offset.value(), int_one));   \
     980             :     Goto(&next);                                               \
     981             :     BIND(&next);                                               \
     982             :   } while (false)
     983             : 
     984         172 :     CASE_FOR_FLAG(JSRegExp::kGlobal, 'g');
     985         172 :     CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i');
     986         172 :     CASE_FOR_FLAG(JSRegExp::kMultiline, 'm');
     987             :     {
     988             :       Label next(this);
     989         172 :       GotoIfNot(is_dotall_enabled, &next);
     990         172 :       CASE_FOR_FLAG(JSRegExp::kDotAll, 's');
     991         172 :       Goto(&next);
     992         172 :       BIND(&next);
     993             :     }
     994         172 :     CASE_FOR_FLAG(JSRegExp::kUnicode, 'u');
     995         172 :     CASE_FOR_FLAG(JSRegExp::kSticky, 'y');
     996             : #undef CASE_FOR_FLAG
     997             : 
     998         344 :     return result;
     999         172 :   }
    1000             : }
    1001             : 
    1002             : // ES#sec-isregexp IsRegExp ( argument )
    1003          43 : Node* RegExpBuiltinsAssembler::IsRegExp(Node* const context,
    1004             :                                         Node* const maybe_receiver) {
    1005          86 :   Label out(this), if_isregexp(this);
    1006             : 
    1007          86 :   VARIABLE(var_result, MachineRepresentation::kWord32, Int32Constant(0));
    1008             : 
    1009          43 :   GotoIf(TaggedIsSmi(maybe_receiver), &out);
    1010          43 :   GotoIfNot(IsJSReceiver(maybe_receiver), &out);
    1011             : 
    1012             :   Node* const receiver = maybe_receiver;
    1013             : 
    1014             :   // Check @@match.
    1015             :   {
    1016             :     Node* const value =
    1017          86 :         GetProperty(context, receiver, isolate()->factory()->match_symbol());
    1018             : 
    1019          43 :     Label match_isundefined(this), match_isnotundefined(this);
    1020          43 :     Branch(IsUndefined(value), &match_isundefined, &match_isnotundefined);
    1021             : 
    1022          43 :     BIND(&match_isundefined);
    1023          43 :     Branch(IsJSRegExp(receiver), &if_isregexp, &out);
    1024             : 
    1025          43 :     BIND(&match_isnotundefined);
    1026          86 :     BranchIfToBooleanIsTrue(value, &if_isregexp, &out);
    1027             :   }
    1028             : 
    1029          43 :   BIND(&if_isregexp);
    1030          43 :   var_result.Bind(Int32Constant(1));
    1031          43 :   Goto(&out);
    1032             : 
    1033          43 :   BIND(&out);
    1034          86 :   return var_result.value();
    1035             : }
    1036             : 
    1037             : // ES#sec-regexpinitialize
    1038             : // Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
    1039          86 : Node* RegExpBuiltinsAssembler::RegExpInitialize(Node* const context,
    1040             :                                                 Node* const regexp,
    1041             :                                                 Node* const maybe_pattern,
    1042             :                                                 Node* const maybe_flags) {
    1043             :   CSA_ASSERT(this, IsJSRegExp(regexp));
    1044             : 
    1045             :   // Normalize pattern.
    1046             :   Node* const pattern =
    1047          86 :       Select(IsUndefined(maybe_pattern), [=] { return EmptyStringConstant(); },
    1048          86 :              [=] { return ToString(context, maybe_pattern); },
    1049         258 :              MachineRepresentation::kTagged);
    1050             : 
    1051             :   // Normalize flags.
    1052             :   Node* const flags =
    1053          86 :       Select(IsUndefined(maybe_flags), [=] { return EmptyStringConstant(); },
    1054          86 :              [=] { return ToString(context, maybe_flags); },
    1055         258 :              MachineRepresentation::kTagged);
    1056             : 
    1057             :   // Initialize.
    1058             : 
    1059             :   return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp,
    1060          86 :                      pattern, flags);
    1061             : }
    1062             : 
    1063             : // ES #sec-get-regexp.prototype.flags
    1064         172 : TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) {
    1065             :   Node* const maybe_receiver = Parameter(Descriptor::kReceiver);
    1066             :   Node* const context = Parameter(Descriptor::kContext);
    1067             : 
    1068             :   Node* const map = ThrowIfNotJSReceiver(context, maybe_receiver,
    1069             :                                          MessageTemplate::kRegExpNonObject,
    1070          43 :                                          "RegExp.prototype.flags");
    1071             :   Node* const receiver = maybe_receiver;
    1072             : 
    1073          43 :   Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred);
    1074             :   Branch(IsFastRegExpNoPrototype(context, receiver, map), &if_isfastpath,
    1075          43 :          &if_isslowpath);
    1076             : 
    1077          43 :   BIND(&if_isfastpath);
    1078          43 :   Return(FlagsGetter(context, receiver, true));
    1079             : 
    1080          43 :   BIND(&if_isslowpath);
    1081          86 :   Return(FlagsGetter(context, receiver, false));
    1082          43 : }
    1083             : 
    1084             : // ES#sec-regexp-pattern-flags
    1085             : // RegExp ( pattern, flags )
    1086         172 : TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) {
    1087             :   Node* const pattern = Parameter(Descriptor::kPattern);
    1088             :   Node* const flags = Parameter(Descriptor::kFlags);
    1089             :   Node* const new_target = Parameter(Descriptor::kNewTarget);
    1090             :   Node* const context = Parameter(Descriptor::kContext);
    1091             : 
    1092          43 :   Isolate* isolate = this->isolate();
    1093             : 
    1094          43 :   VARIABLE(var_flags, MachineRepresentation::kTagged, flags);
    1095          86 :   VARIABLE(var_pattern, MachineRepresentation::kTagged, pattern);
    1096          86 :   VARIABLE(var_new_target, MachineRepresentation::kTagged, new_target);
    1097             : 
    1098          43 :   Node* const native_context = LoadNativeContext(context);
    1099             :   Node* const regexp_function =
    1100          43 :       LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
    1101             : 
    1102          43 :   Node* const pattern_is_regexp = IsRegExp(context, pattern);
    1103             : 
    1104             :   {
    1105             :     Label next(this);
    1106             : 
    1107          43 :     GotoIfNot(IsUndefined(new_target), &next);
    1108          43 :     var_new_target.Bind(regexp_function);
    1109             : 
    1110          43 :     GotoIfNot(pattern_is_regexp, &next);
    1111          43 :     GotoIfNot(IsUndefined(flags), &next);
    1112             : 
    1113             :     Node* const value =
    1114          43 :         GetProperty(context, pattern, isolate->factory()->constructor_string());
    1115             : 
    1116          43 :     GotoIfNot(WordEqual(value, regexp_function), &next);
    1117          43 :     Return(pattern);
    1118             : 
    1119          43 :     BIND(&next);
    1120             :   }
    1121             : 
    1122             :   {
    1123          43 :     Label next(this), if_patternisfastregexp(this),
    1124          43 :         if_patternisslowregexp(this);
    1125          43 :     GotoIf(TaggedIsSmi(pattern), &next);
    1126             : 
    1127          43 :     GotoIf(IsJSRegExp(pattern), &if_patternisfastregexp);
    1128             : 
    1129          43 :     Branch(pattern_is_regexp, &if_patternisslowregexp, &next);
    1130             : 
    1131          43 :     BIND(&if_patternisfastregexp);
    1132             :     {
    1133          43 :       Node* const source = LoadObjectField(pattern, JSRegExp::kSourceOffset);
    1134          43 :       var_pattern.Bind(source);
    1135             : 
    1136             :       {
    1137             :         Label inner_next(this);
    1138          43 :         GotoIfNot(IsUndefined(flags), &inner_next);
    1139             : 
    1140          43 :         Node* const value = FlagsGetter(context, pattern, true);
    1141          43 :         var_flags.Bind(value);
    1142          43 :         Goto(&inner_next);
    1143             : 
    1144          43 :         BIND(&inner_next);
    1145             :       }
    1146             : 
    1147          43 :       Goto(&next);
    1148             :     }
    1149             : 
    1150          43 :     BIND(&if_patternisslowregexp);
    1151             :     {
    1152             :       {
    1153             :         Node* const value =
    1154          43 :             GetProperty(context, pattern, isolate->factory()->source_string());
    1155          43 :         var_pattern.Bind(value);
    1156             :       }
    1157             : 
    1158             :       {
    1159             :         Label inner_next(this);
    1160          43 :         GotoIfNot(IsUndefined(flags), &inner_next);
    1161             : 
    1162             :         Node* const value =
    1163          43 :             GetProperty(context, pattern, isolate->factory()->flags_string());
    1164          43 :         var_flags.Bind(value);
    1165          43 :         Goto(&inner_next);
    1166             : 
    1167          43 :         BIND(&inner_next);
    1168             :       }
    1169             : 
    1170          43 :       Goto(&next);
    1171             :     }
    1172             : 
    1173          86 :     BIND(&next);
    1174             :   }
    1175             : 
    1176             :   // Allocate.
    1177             : 
    1178          86 :   VARIABLE(var_regexp, MachineRepresentation::kTagged);
    1179             :   {
    1180          43 :     Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred),
    1181          43 :         next(this);
    1182             :     Branch(WordEqual(var_new_target.value(), regexp_function),
    1183          43 :            &allocate_jsregexp, &allocate_generic);
    1184             : 
    1185          43 :     BIND(&allocate_jsregexp);
    1186             :     {
    1187             :       Node* const initial_map = LoadObjectField(
    1188          43 :           regexp_function, JSFunction::kPrototypeOrInitialMapOffset);
    1189          43 :       Node* const regexp = AllocateJSObjectFromMap(initial_map);
    1190          43 :       var_regexp.Bind(regexp);
    1191          43 :       Goto(&next);
    1192             :     }
    1193             : 
    1194          43 :     BIND(&allocate_generic);
    1195             :     {
    1196             :       ConstructorBuiltinsAssembler constructor_assembler(this->state());
    1197             :       Node* const regexp = constructor_assembler.EmitFastNewObject(
    1198          43 :           context, regexp_function, var_new_target.value());
    1199          43 :       var_regexp.Bind(regexp);
    1200          43 :       Goto(&next);
    1201             :     }
    1202             : 
    1203          86 :     BIND(&next);
    1204             :   }
    1205             : 
    1206             :   Node* const result = RegExpInitialize(context, var_regexp.value(),
    1207          43 :                                         var_pattern.value(), var_flags.value());
    1208          86 :   Return(result);
    1209          43 : }
    1210             : 
    1211             : // ES#sec-regexp.prototype.compile
    1212             : // RegExp.prototype.compile ( pattern, flags )
    1213         172 : TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) {
    1214             :   Node* const maybe_receiver = Parameter(Descriptor::kReceiver);
    1215             :   Node* const maybe_pattern = Parameter(Descriptor::kPattern);
    1216             :   Node* const maybe_flags = Parameter(Descriptor::kFlags);
    1217             :   Node* const context = Parameter(Descriptor::kContext);
    1218             : 
    1219             :   ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
    1220          43 :                          "RegExp.prototype.compile");
    1221             :   Node* const receiver = maybe_receiver;
    1222             : 
    1223          43 :   VARIABLE(var_flags, MachineRepresentation::kTagged, maybe_flags);
    1224          86 :   VARIABLE(var_pattern, MachineRepresentation::kTagged, maybe_pattern);
    1225             : 
    1226             :   // Handle a JSRegExp pattern.
    1227             :   {
    1228             :     Label next(this);
    1229             : 
    1230          43 :     GotoIf(TaggedIsSmi(maybe_pattern), &next);
    1231          43 :     GotoIfNot(IsJSRegExp(maybe_pattern), &next);
    1232             : 
    1233             :     Node* const pattern = maybe_pattern;
    1234             : 
    1235             :     // {maybe_flags} must be undefined in this case, otherwise throw.
    1236             :     {
    1237             :       Label next(this);
    1238          43 :       GotoIf(IsUndefined(maybe_flags), &next);
    1239             : 
    1240          43 :       Node* const message_id = SmiConstant(MessageTemplate::kRegExpFlags);
    1241          43 :       TailCallRuntime(Runtime::kThrowTypeError, context, message_id);
    1242             : 
    1243          43 :       BIND(&next);
    1244             :     }
    1245             : 
    1246          43 :     Node* const new_flags = FlagsGetter(context, pattern, true);
    1247          43 :     Node* const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset);
    1248             : 
    1249          43 :     var_flags.Bind(new_flags);
    1250          43 :     var_pattern.Bind(new_pattern);
    1251             : 
    1252          43 :     Goto(&next);
    1253          43 :     BIND(&next);
    1254             :   }
    1255             : 
    1256             :   Node* const result = RegExpInitialize(context, receiver, var_pattern.value(),
    1257          43 :                                         var_flags.value());
    1258          86 :   Return(result);
    1259          43 : }
    1260             : 
    1261             : // ES6 21.2.5.10.
    1262             : // ES #sec-get-regexp.prototype.source
    1263         172 : TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) {
    1264             :   Node* const receiver = Parameter(Descriptor::kReceiver);
    1265             :   Node* const context = Parameter(Descriptor::kContext);
    1266             : 
    1267             :   // Check whether we have an unmodified regexp instance.
    1268          43 :   Label if_isjsregexp(this), if_isnotjsregexp(this, Label::kDeferred);
    1269             : 
    1270          43 :   GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp);
    1271          43 :   Branch(IsJSRegExp(receiver), &if_isjsregexp, &if_isnotjsregexp);
    1272             : 
    1273          43 :   BIND(&if_isjsregexp);
    1274             :   {
    1275          43 :     Node* const source = LoadObjectField(receiver, JSRegExp::kSourceOffset);
    1276          43 :     Return(source);
    1277             :   }
    1278             : 
    1279          43 :   BIND(&if_isnotjsregexp);
    1280             :   {
    1281          43 :     Isolate* isolate = this->isolate();
    1282          43 :     Node* const native_context = LoadNativeContext(context);
    1283             :     Node* const regexp_fun =
    1284          43 :         LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
    1285             :     Node* const initial_map =
    1286          43 :         LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
    1287          43 :     Node* const initial_prototype = LoadMapPrototype(initial_map);
    1288             : 
    1289          43 :     Label if_isprototype(this), if_isnotprototype(this);
    1290             :     Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
    1291          43 :            &if_isnotprototype);
    1292             : 
    1293          43 :     BIND(&if_isprototype);
    1294             :     {
    1295             :       const int counter = v8::Isolate::kRegExpPrototypeSourceGetter;
    1296          43 :       Node* const counter_smi = SmiConstant(counter);
    1297          43 :       CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
    1298             : 
    1299             :       Node* const result =
    1300          86 :           HeapConstant(isolate->factory()->NewStringFromAsciiChecked("(?:)"));
    1301          43 :       Return(result);
    1302             :     }
    1303             : 
    1304          43 :     BIND(&if_isnotprototype);
    1305             :     {
    1306             :       Node* const message_id =
    1307          43 :           SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp));
    1308             :       Node* const method_name_str =
    1309             :           HeapConstant(isolate->factory()->NewStringFromAsciiChecked(
    1310          86 :               "RegExp.prototype.source"));
    1311             :       TailCallRuntime(Runtime::kThrowTypeError, context, message_id,
    1312          43 :                       method_name_str);
    1313          43 :     }
    1314          43 :   }
    1315          43 : }
    1316             : 
    1317             : // Fast-path implementation for flag checks on an unmodified JSRegExp instance.
    1318         430 : Node* RegExpBuiltinsAssembler::FastFlagGetter(Node* const regexp,
    1319             :                                               JSRegExp::Flag flag) {
    1320         430 :   Node* const smi_zero = SmiConstant(Smi::kZero);
    1321         430 :   Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
    1322         860 :   Node* const mask = SmiConstant(Smi::FromInt(flag));
    1323         430 :   Node* const is_flag_set = WordNotEqual(SmiAnd(flags, mask), smi_zero);
    1324             : 
    1325         430 :   return is_flag_set;
    1326             : }
    1327             : 
    1328             : // Load through the GetProperty stub.
    1329          86 : Node* RegExpBuiltinsAssembler::SlowFlagGetter(Node* const context,
    1330             :                                               Node* const regexp,
    1331             :                                               JSRegExp::Flag flag) {
    1332          86 :   Factory* factory = isolate()->factory();
    1333             : 
    1334             :   Label out(this);
    1335         172 :   VARIABLE(var_result, MachineRepresentation::kWord32);
    1336             : 
    1337             :   Handle<String> name;
    1338          86 :   switch (flag) {
    1339             :     case JSRegExp::kGlobal:
    1340             :       name = factory->global_string();
    1341          43 :       break;
    1342             :     case JSRegExp::kIgnoreCase:
    1343             :       name = factory->ignoreCase_string();
    1344           0 :       break;
    1345             :     case JSRegExp::kMultiline:
    1346             :       name = factory->multiline_string();
    1347           0 :       break;
    1348             :     case JSRegExp::kDotAll:
    1349           0 :       UNREACHABLE();  // Never called for dotAll.
    1350             :       break;
    1351             :     case JSRegExp::kSticky:
    1352             :       name = factory->sticky_string();
    1353           0 :       break;
    1354             :     case JSRegExp::kUnicode:
    1355             :       name = factory->unicode_string();
    1356          43 :       break;
    1357             :     default:
    1358           0 :       UNREACHABLE();
    1359             :   }
    1360             : 
    1361          86 :   Node* const value = GetProperty(context, regexp, name);
    1362             : 
    1363          86 :   Label if_true(this), if_false(this);
    1364          86 :   BranchIfToBooleanIsTrue(value, &if_true, &if_false);
    1365             : 
    1366          86 :   BIND(&if_true);
    1367             :   {
    1368          86 :     var_result.Bind(Int32Constant(1));
    1369          86 :     Goto(&out);
    1370             :   }
    1371             : 
    1372          86 :   BIND(&if_false);
    1373             :   {
    1374          86 :     var_result.Bind(Int32Constant(0));
    1375          86 :     Goto(&out);
    1376             :   }
    1377             : 
    1378          86 :   BIND(&out);
    1379         258 :   return var_result.value();
    1380             : }
    1381             : 
    1382         172 : Node* RegExpBuiltinsAssembler::FlagGetter(Node* const context,
    1383             :                                           Node* const regexp,
    1384             :                                           JSRegExp::Flag flag,
    1385             :                                           bool is_fastpath) {
    1386             :   return is_fastpath ? FastFlagGetter(regexp, flag)
    1387         172 :                      : SlowFlagGetter(context, regexp, flag);
    1388             : }
    1389             : 
    1390         258 : void RegExpBuiltinsAssembler::FlagGetter(Node* context, Node* receiver,
    1391             :                                          JSRegExp::Flag flag, int counter,
    1392             :                                          const char* method_name) {
    1393         258 :   Isolate* isolate = this->isolate();
    1394             : 
    1395             :   // Check whether we have an unmodified regexp instance.
    1396             :   Label if_isunmodifiedjsregexp(this),
    1397         258 :       if_isnotunmodifiedjsregexp(this, Label::kDeferred);
    1398             : 
    1399         258 :   GotoIf(TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp);
    1400             :   Branch(IsJSRegExp(receiver), &if_isunmodifiedjsregexp,
    1401         258 :          &if_isnotunmodifiedjsregexp);
    1402             : 
    1403         258 :   BIND(&if_isunmodifiedjsregexp);
    1404             :   {
    1405             :     // Refer to JSRegExp's flag property on the fast-path.
    1406         258 :     Node* const is_flag_set = FastFlagGetter(receiver, flag);
    1407         258 :     Return(SelectBooleanConstant(is_flag_set));
    1408             :   }
    1409             : 
    1410         258 :   BIND(&if_isnotunmodifiedjsregexp);
    1411             :   {
    1412         258 :     Node* const native_context = LoadNativeContext(context);
    1413             :     Node* const regexp_fun =
    1414         258 :         LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
    1415             :     Node* const initial_map =
    1416         258 :         LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
    1417         258 :     Node* const initial_prototype = LoadMapPrototype(initial_map);
    1418             : 
    1419         258 :     Label if_isprototype(this), if_isnotprototype(this);
    1420             :     Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
    1421         258 :            &if_isnotprototype);
    1422             : 
    1423         258 :     BIND(&if_isprototype);
    1424             :     {
    1425         258 :       if (counter != -1) {
    1426         215 :         Node* const counter_smi = SmiConstant(Smi::FromInt(counter));
    1427         215 :         CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
    1428             :       }
    1429         258 :       Return(UndefinedConstant());
    1430             :     }
    1431             : 
    1432         258 :     BIND(&if_isnotprototype);
    1433             :     {
    1434             :       Node* const message_id =
    1435         258 :           SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp));
    1436             :       Node* const method_name_str = HeapConstant(
    1437         516 :           isolate->factory()->NewStringFromAsciiChecked(method_name));
    1438             :       CallRuntime(Runtime::kThrowTypeError, context, message_id,
    1439         258 :                   method_name_str);
    1440         258 :       Unreachable();
    1441         258 :     }
    1442         258 :   }
    1443         258 : }
    1444             : 
    1445             : // ES6 21.2.5.4.
    1446             : // ES #sec-get-regexp.prototype.global
    1447         172 : TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) {
    1448             :   Node* context = Parameter(Descriptor::kContext);
    1449             :   Node* receiver = Parameter(Descriptor::kReceiver);
    1450             :   FlagGetter(context, receiver, JSRegExp::kGlobal,
    1451             :              v8::Isolate::kRegExpPrototypeOldFlagGetter,
    1452          43 :              "RegExp.prototype.global");
    1453          43 : }
    1454             : 
    1455             : // ES6 21.2.5.5.
    1456             : // ES #sec-get-regexp.prototype.ignorecase
    1457         172 : TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) {
    1458             :   Node* context = Parameter(Descriptor::kContext);
    1459             :   Node* receiver = Parameter(Descriptor::kReceiver);
    1460             :   FlagGetter(context, receiver, JSRegExp::kIgnoreCase,
    1461             :              v8::Isolate::kRegExpPrototypeOldFlagGetter,
    1462          43 :              "RegExp.prototype.ignoreCase");
    1463          43 : }
    1464             : 
    1465             : // ES6 21.2.5.7.
    1466             : // ES #sec-get-regexp.prototype.multiline
    1467         172 : TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) {
    1468             :   Node* context = Parameter(Descriptor::kContext);
    1469             :   Node* receiver = Parameter(Descriptor::kReceiver);
    1470             :   FlagGetter(context, receiver, JSRegExp::kMultiline,
    1471             :              v8::Isolate::kRegExpPrototypeOldFlagGetter,
    1472          43 :              "RegExp.prototype.multiline");
    1473          43 : }
    1474             : 
    1475         172 : Node* RegExpBuiltinsAssembler::IsDotAllEnabled(Isolate* isolate) {
    1476             :   Node* flag_ptr = ExternalConstant(
    1477         172 :       ExternalReference::address_of_regexp_dotall_flag(isolate));
    1478         172 :   Node* const flag_value = Load(MachineType::Int8(), flag_ptr);
    1479         172 :   return Word32NotEqual(flag_value, Int32Constant(0));
    1480             : }
    1481             : 
    1482             : // ES #sec-get-regexp.prototype.dotAll
    1483         172 : TF_BUILTIN(RegExpPrototypeDotAllGetter, RegExpBuiltinsAssembler) {
    1484             :   Node* context = Parameter(Descriptor::kContext);
    1485             :   Node* receiver = Parameter(Descriptor::kReceiver);
    1486             :   static const int kNoCounter = -1;
    1487             :   CSA_ASSERT(this, IsDotAllEnabled(isolate()));
    1488             :   FlagGetter(context, receiver, JSRegExp::kDotAll, kNoCounter,
    1489          43 :              "RegExp.prototype.dotAll");
    1490          43 : }
    1491             : 
    1492             : // ES6 21.2.5.12.
    1493             : // ES #sec-get-regexp.prototype.sticky
    1494         172 : TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) {
    1495             :   Node* context = Parameter(Descriptor::kContext);
    1496             :   Node* receiver = Parameter(Descriptor::kReceiver);
    1497             :   FlagGetter(context, receiver, JSRegExp::kSticky,
    1498             :              v8::Isolate::kRegExpPrototypeStickyGetter,
    1499          43 :              "RegExp.prototype.sticky");
    1500          43 : }
    1501             : 
    1502             : // ES6 21.2.5.15.
    1503             : // ES #sec-get-regexp.prototype.unicode
    1504         172 : TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) {
    1505             :   Node* context = Parameter(Descriptor::kContext);
    1506             :   Node* receiver = Parameter(Descriptor::kReceiver);
    1507             :   FlagGetter(context, receiver, JSRegExp::kUnicode,
    1508             :              v8::Isolate::kRegExpPrototypeUnicodeGetter,
    1509          43 :              "RegExp.prototype.unicode");
    1510          43 : }
    1511             : 
    1512             : // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
    1513         172 : Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
    1514             :                                           Node* string) {
    1515         172 :   VARIABLE(var_result, MachineRepresentation::kTagged);
    1516         172 :   Label out(this);
    1517             : 
    1518             :   // Take the slow path of fetching the exec property, calling it, and
    1519             :   // verifying its return value.
    1520             : 
    1521             :   // Get the exec property.
    1522             :   Node* const exec =
    1523         344 :       GetProperty(context, regexp, isolate()->factory()->exec_string());
    1524             : 
    1525             :   // Is {exec} callable?
    1526         172 :   Label if_iscallable(this), if_isnotcallable(this);
    1527             : 
    1528         172 :   GotoIf(TaggedIsSmi(exec), &if_isnotcallable);
    1529             : 
    1530         172 :   Node* const exec_map = LoadMap(exec);
    1531         172 :   Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable);
    1532             : 
    1533         172 :   BIND(&if_iscallable);
    1534             :   {
    1535         172 :     Callable call_callable = CodeFactory::Call(isolate());
    1536         172 :     Node* const result = CallJS(call_callable, context, exec, regexp, string);
    1537             : 
    1538         172 :     var_result.Bind(result);
    1539         172 :     GotoIf(WordEqual(result, NullConstant()), &out);
    1540             : 
    1541             :     ThrowIfNotJSReceiver(context, result,
    1542         172 :                          MessageTemplate::kInvalidRegExpExecResult, "unused");
    1543             : 
    1544         172 :     Goto(&out);
    1545             :   }
    1546             : 
    1547         172 :   BIND(&if_isnotcallable);
    1548             :   {
    1549             :     ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
    1550         172 :                            "RegExp.prototype.exec");
    1551             : 
    1552             :     Node* const result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow,
    1553         172 :                                      context, regexp, string);
    1554         172 :     var_result.Bind(result);
    1555         172 :     Goto(&out);
    1556             :   }
    1557             : 
    1558         172 :   BIND(&out);
    1559         344 :   return var_result.value();
    1560             : }
    1561             : 
    1562             : // ES#sec-regexp.prototype.test
    1563             : // RegExp.prototype.test ( S )
    1564         172 : TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) {
    1565             :   Node* const maybe_receiver = Parameter(Descriptor::kReceiver);
    1566             :   Node* const maybe_string = Parameter(Descriptor::kString);
    1567             :   Node* const context = Parameter(Descriptor::kContext);
    1568             : 
    1569             :   // Ensure {maybe_receiver} is a JSReceiver.
    1570             :   ThrowIfNotJSReceiver(context, maybe_receiver,
    1571             :                        MessageTemplate::kIncompatibleMethodReceiver,
    1572          43 :                        "RegExp.prototype.test");
    1573             :   Node* const receiver = maybe_receiver;
    1574             : 
    1575             :   // Convert {maybe_string} to a String.
    1576          43 :   Node* const string = ToString(context, maybe_string);
    1577             : 
    1578          43 :   Label fast_path(this), slow_path(this);
    1579          43 :   BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
    1580             : 
    1581          43 :   BIND(&fast_path);
    1582             :   {
    1583             :     Label if_didnotmatch(this);
    1584             :     RegExpPrototypeExecBodyWithoutResult(context, receiver, string,
    1585          43 :                                          &if_didnotmatch, true);
    1586          43 :     Return(TrueConstant());
    1587             : 
    1588          43 :     BIND(&if_didnotmatch);
    1589          43 :     Return(FalseConstant());
    1590             :   }
    1591             : 
    1592          43 :   BIND(&slow_path);
    1593             :   {
    1594             :     // Call exec.
    1595          43 :     Node* const match_indices = RegExpExec(context, receiver, string);
    1596             : 
    1597             :     // Return true iff exec matched successfully.
    1598             :     Node* const result =
    1599          43 :         SelectBooleanConstant(WordNotEqual(match_indices, NullConstant()));
    1600          43 :     Return(result);
    1601          43 :   }
    1602          43 : }
    1603             : 
    1604         129 : Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node* const string,
    1605             :                                                   Node* const index,
    1606             :                                                   Node* const is_unicode,
    1607             :                                                   bool is_fastpath) {
    1608             :   CSA_ASSERT(this, IsString(string));
    1609             :   CSA_ASSERT(this, IsNumberNormalized(index));
    1610             :   if (is_fastpath) CSA_ASSERT(this, TaggedIsPositiveSmi(index));
    1611             : 
    1612             :   // Default to last_index + 1.
    1613         129 :   Node* const index_plus_one = NumberInc(index);
    1614         129 :   VARIABLE(var_result, MachineRepresentation::kTagged, index_plus_one);
    1615             : 
    1616             :   // Advancing the index has some subtle issues involving the distinction
    1617             :   // between Smis and HeapNumbers. There's three cases:
    1618             :   // * {index} is a Smi, {index_plus_one} is a Smi. The standard case.
    1619             :   // * {index} is a Smi, {index_plus_one} overflows into a HeapNumber.
    1620             :   //   In this case we can return the result early, because
    1621             :   //   {index_plus_one} > {string}.length.
    1622             :   // * {index} is a HeapNumber, {index_plus_one} is a HeapNumber. This can only
    1623             :   //   occur when {index} is outside the Smi range since we normalize
    1624             :   //   explicitly. Again we can return early.
    1625             :   if (is_fastpath) {
    1626             :     // Must be in Smi range on the fast path. We control the value of {index}
    1627             :     // on all call-sites and can never exceed the length of the string.
    1628             :     STATIC_ASSERT(String::kMaxLength + 2 < Smi::kMaxValue);
    1629             :     CSA_ASSERT(this, TaggedIsPositiveSmi(index_plus_one));
    1630             :   }
    1631             : 
    1632         129 :   Label if_isunicode(this), out(this);
    1633         129 :   GotoIfNot(is_unicode, &out);
    1634             : 
    1635             :   // Keep this unconditional (even on the fast path) just to be safe.
    1636         129 :   Branch(TaggedIsPositiveSmi(index_plus_one), &if_isunicode, &out);
    1637             : 
    1638         129 :   BIND(&if_isunicode);
    1639             :   {
    1640         129 :     Node* const string_length = LoadStringLength(string);
    1641         129 :     GotoIfNot(SmiLessThan(index_plus_one, string_length), &out);
    1642             : 
    1643         129 :     Node* const lead = StringCharCodeAt(string, index);
    1644             :     GotoIfNot(Word32Equal(Word32And(lead, Int32Constant(0xFC00)),
    1645             :                           Int32Constant(0xD800)),
    1646         129 :               &out);
    1647             : 
    1648         129 :     Node* const trail = StringCharCodeAt(string, index_plus_one);
    1649             :     GotoIfNot(Word32Equal(Word32And(trail, Int32Constant(0xFC00)),
    1650             :                           Int32Constant(0xDC00)),
    1651         129 :               &out);
    1652             : 
    1653             :     // At a surrogate pair, return index + 2.
    1654         129 :     Node* const index_plus_two = NumberInc(index_plus_one);
    1655         129 :     var_result.Bind(index_plus_two);
    1656             : 
    1657         129 :     Goto(&out);
    1658             :   }
    1659             : 
    1660         129 :   BIND(&out);
    1661         258 :   return var_result.value();
    1662             : }
    1663             : 
    1664             : namespace {
    1665             : 
    1666             : // Utility class implementing a growable fixed array through CSA.
    1667         129 : class GrowableFixedArray {
    1668             :   typedef CodeStubAssembler::Label Label;
    1669             :   typedef CodeStubAssembler::Variable Variable;
    1670             : 
    1671             :  public:
    1672         129 :   explicit GrowableFixedArray(CodeStubAssembler* a)
    1673             :       : assembler_(a),
    1674             :         var_array_(a, MachineRepresentation::kTagged),
    1675             :         var_length_(a, MachineType::PointerRepresentation()),
    1676         129 :         var_capacity_(a, MachineType::PointerRepresentation()) {
    1677         129 :     Initialize();
    1678         129 :   }
    1679             : 
    1680         301 :   Node* length() const { return var_length_.value(); }
    1681             : 
    1682             :   Variable* var_array() { return &var_array_; }
    1683             :   Variable* var_length() { return &var_length_; }
    1684             :   Variable* var_capacity() { return &var_capacity_; }
    1685             : 
    1686         215 :   void Push(Node* const value) {
    1687         215 :     CodeStubAssembler* a = assembler_;
    1688             : 
    1689         215 :     Node* const length = var_length_.value();
    1690         215 :     Node* const capacity = var_capacity_.value();
    1691             : 
    1692         215 :     Label grow(a), store(a);
    1693         215 :     a->Branch(a->IntPtrEqual(capacity, length), &grow, &store);
    1694             : 
    1695         215 :     a->BIND(&grow);
    1696             :     {
    1697         215 :       Node* const new_capacity = NewCapacity(a, capacity);
    1698         215 :       Node* const new_array = ResizeFixedArray(length, new_capacity);
    1699             : 
    1700         215 :       var_capacity_.Bind(new_capacity);
    1701         215 :       var_array_.Bind(new_array);
    1702         215 :       a->Goto(&store);
    1703             :     }
    1704             : 
    1705         215 :     a->BIND(&store);
    1706             :     {
    1707         215 :       Node* const array = var_array_.value();
    1708         215 :       a->StoreFixedArrayElement(array, length, value);
    1709             : 
    1710         215 :       Node* const new_length = a->IntPtrAdd(length, a->IntPtrConstant(1));
    1711         215 :       var_length_.Bind(new_length);
    1712         215 :     }
    1713         215 :   }
    1714             : 
    1715         129 :   Node* ToJSArray(Node* const context) {
    1716         129 :     CodeStubAssembler* a = assembler_;
    1717             : 
    1718             :     const ElementsKind kind = FAST_ELEMENTS;
    1719             : 
    1720         129 :     Node* const native_context = a->LoadNativeContext(context);
    1721         129 :     Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context);
    1722             : 
    1723             :     // Shrink to fit if necessary.
    1724             :     {
    1725             :       Label next(a);
    1726             : 
    1727         129 :       Node* const length = var_length_.value();
    1728         129 :       Node* const capacity = var_capacity_.value();
    1729             : 
    1730         129 :       a->GotoIf(a->WordEqual(length, capacity), &next);
    1731             : 
    1732         129 :       Node* const array = ResizeFixedArray(length, length);
    1733         129 :       var_array_.Bind(array);
    1734         129 :       var_capacity_.Bind(length);
    1735         129 :       a->Goto(&next);
    1736             : 
    1737         129 :       a->BIND(&next);
    1738             :     }
    1739             : 
    1740         129 :     Node* const result_length = a->SmiTag(length());
    1741             :     Node* const result = a->AllocateUninitializedJSArrayWithoutElements(
    1742         129 :         kind, array_map, result_length, nullptr);
    1743             : 
    1744             :     // Note: We do not currently shrink the fixed array.
    1745             : 
    1746         129 :     a->StoreObjectField(result, JSObject::kElementsOffset, var_array_.value());
    1747             : 
    1748         129 :     return result;
    1749             :   }
    1750             : 
    1751             :  private:
    1752         129 :   void Initialize() {
    1753         129 :     CodeStubAssembler* a = assembler_;
    1754             : 
    1755             :     const ElementsKind kind = FAST_ELEMENTS;
    1756             : 
    1757             :     static const int kInitialArraySize = 8;
    1758         129 :     Node* const capacity = a->IntPtrConstant(kInitialArraySize);
    1759         129 :     Node* const array = a->AllocateFixedArray(kind, capacity);
    1760             : 
    1761             :     a->FillFixedArrayWithValue(kind, array, a->IntPtrConstant(0), capacity,
    1762         129 :                                Heap::kTheHoleValueRootIndex);
    1763             : 
    1764         129 :     var_array_.Bind(array);
    1765         129 :     var_capacity_.Bind(capacity);
    1766         129 :     var_length_.Bind(a->IntPtrConstant(0));
    1767         129 :   }
    1768             : 
    1769         215 :   Node* NewCapacity(CodeStubAssembler* a, Node* const current_capacity) {
    1770             :     CSA_ASSERT(a, a->IntPtrGreaterThan(current_capacity, a->IntPtrConstant(0)));
    1771             : 
    1772             :     // Growth rate is analog to JSObject::NewElementsCapacity:
    1773             :     // new_capacity = (current_capacity + (current_capacity >> 1)) + 16.
    1774             : 
    1775             :     Node* const new_capacity = a->IntPtrAdd(
    1776             :         a->IntPtrAdd(current_capacity, a->WordShr(current_capacity, 1)),
    1777         215 :         a->IntPtrConstant(16));
    1778             : 
    1779         215 :     return new_capacity;
    1780             :   }
    1781             : 
    1782             :   // Creates a new array with {new_capacity} and copies the first
    1783             :   // {element_count} elements from the current array.
    1784         344 :   Node* ResizeFixedArray(Node* const element_count, Node* const new_capacity) {
    1785         344 :     CodeStubAssembler* a = assembler_;
    1786             : 
    1787             :     CSA_ASSERT(a, a->IntPtrGreaterThan(element_count, a->IntPtrConstant(0)));
    1788             :     CSA_ASSERT(a, a->IntPtrGreaterThan(new_capacity, a->IntPtrConstant(0)));
    1789             :     CSA_ASSERT(a, a->IntPtrGreaterThanOrEqual(new_capacity, element_count));
    1790             : 
    1791             :     const ElementsKind kind = FAST_ELEMENTS;
    1792             :     const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER;
    1793             :     const CodeStubAssembler::ParameterMode mode =
    1794             :         CodeStubAssembler::INTPTR_PARAMETERS;
    1795             :     const CodeStubAssembler::AllocationFlags flags =
    1796             :         CodeStubAssembler::kAllowLargeObjectAllocation;
    1797             : 
    1798         344 :     Node* const from_array = var_array_.value();
    1799             :     Node* const to_array =
    1800         344 :         a->AllocateFixedArray(kind, new_capacity, mode, flags);
    1801             :     a->CopyFixedArrayElements(kind, from_array, kind, to_array, element_count,
    1802         344 :                               new_capacity, barrier_mode, mode);
    1803             : 
    1804         344 :     return to_array;
    1805             :   }
    1806             : 
    1807             :  private:
    1808             :   CodeStubAssembler* const assembler_;
    1809             :   Variable var_array_;
    1810             :   Variable var_length_;
    1811             :   Variable var_capacity_;
    1812             : };
    1813             : 
    1814             : }  // namespace
    1815             : 
    1816          86 : void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context,
    1817             :                                                        Node* const regexp,
    1818             :                                                        Node* const string,
    1819             :                                                        const bool is_fastpath) {
    1820             :   CSA_ASSERT(this, IsString(string));
    1821             :   if (is_fastpath) CSA_ASSERT(this, IsFastRegExp(context, regexp));
    1822             : 
    1823          86 :   Node* const null = NullConstant();
    1824          86 :   Node* const int_zero = IntPtrConstant(0);
    1825          86 :   Node* const smi_zero = SmiConstant(Smi::kZero);
    1826             : 
    1827             :   Node* const is_global =
    1828          86 :       FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath);
    1829             : 
    1830          86 :   Label if_isglobal(this), if_isnotglobal(this);
    1831          86 :   Branch(is_global, &if_isglobal, &if_isnotglobal);
    1832             : 
    1833          86 :   BIND(&if_isnotglobal);
    1834             :   {
    1835             :     Node* const result =
    1836             :         is_fastpath ? RegExpPrototypeExecBody(context, regexp, string, true)
    1837          86 :                     : RegExpExec(context, regexp, string);
    1838          86 :     Return(result);
    1839             :   }
    1840             : 
    1841          86 :   BIND(&if_isglobal);
    1842             :   {
    1843             :     Node* const is_unicode =
    1844          86 :         FlagGetter(context, regexp, JSRegExp::kUnicode, is_fastpath);
    1845             : 
    1846          86 :     StoreLastIndex(context, regexp, smi_zero, is_fastpath);
    1847             : 
    1848             :     // Allocate an array to store the resulting match strings.
    1849             : 
    1850          86 :     GrowableFixedArray array(this);
    1851             : 
    1852             :     // Loop preparations. Within the loop, collect results from RegExpExec
    1853             :     // and store match strings in the array.
    1854             : 
    1855             :     Variable* vars[] = {array.var_array(), array.var_length(),
    1856          86 :                         array.var_capacity()};
    1857         172 :     Label loop(this, 3, vars), out(this);
    1858          86 :     Goto(&loop);
    1859             : 
    1860          86 :     BIND(&loop);
    1861             :     {
    1862          86 :       VARIABLE(var_match, MachineRepresentation::kTagged);
    1863             : 
    1864          86 :       Label if_didmatch(this), if_didnotmatch(this);
    1865          86 :       if (is_fastpath) {
    1866             :         // On the fast path, grab the matching string from the raw match index
    1867             :         // array.
    1868             :         Node* const match_indices = RegExpPrototypeExecBodyWithoutResult(
    1869          43 :             context, regexp, string, &if_didnotmatch, true);
    1870             : 
    1871             :         Node* const match_from = LoadFixedArrayElement(
    1872          43 :             match_indices, RegExpMatchInfo::kFirstCaptureIndex);
    1873             :         Node* const match_to = LoadFixedArrayElement(
    1874          43 :             match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
    1875             : 
    1876          43 :         Node* match = SubString(context, string, match_from, match_to);
    1877          43 :         var_match.Bind(match);
    1878             : 
    1879          43 :         Goto(&if_didmatch);
    1880             :       } else {
    1881             :         DCHECK(!is_fastpath);
    1882          43 :         Node* const result = RegExpExec(context, regexp, string);
    1883             : 
    1884             :         Label load_match(this);
    1885          43 :         Branch(WordEqual(result, null), &if_didnotmatch, &load_match);
    1886             : 
    1887          43 :         BIND(&load_match);
    1888             :         {
    1889          43 :           Label fast_result(this), slow_result(this);
    1890          43 :           BranchIfFastRegExpResult(context, result, &fast_result, &slow_result);
    1891             : 
    1892          43 :           BIND(&fast_result);
    1893             :           {
    1894          43 :             Node* const result_fixed_array = LoadElements(result);
    1895          43 :             Node* const match = LoadFixedArrayElement(result_fixed_array, 0);
    1896             : 
    1897             :             // The match is guaranteed to be a string on the fast path.
    1898             :             CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(match)));
    1899             : 
    1900          43 :             var_match.Bind(match);
    1901          43 :             Goto(&if_didmatch);
    1902             :           }
    1903             : 
    1904          43 :           BIND(&slow_result);
    1905             :           {
    1906             :             // TODO(ishell): Use GetElement stub once it's available.
    1907          43 :             Node* const match = GetProperty(context, result, smi_zero);
    1908          43 :             var_match.Bind(ToString(context, match));
    1909          43 :             Goto(&if_didmatch);
    1910          43 :           }
    1911          43 :         }
    1912             :       }
    1913             : 
    1914          86 :       BIND(&if_didnotmatch);
    1915             :       {
    1916             :         // Return null if there were no matches, otherwise just exit the loop.
    1917          86 :         GotoIfNot(IntPtrEqual(array.length(), int_zero), &out);
    1918          86 :         Return(null);
    1919             :       }
    1920             : 
    1921          86 :       BIND(&if_didmatch);
    1922             :       {
    1923          86 :         Node* match = var_match.value();
    1924             : 
    1925             :         // Store the match, growing the fixed array if needed.
    1926             : 
    1927          86 :         array.Push(match);
    1928             : 
    1929             :         // Advance last index if the match is the empty string.
    1930             : 
    1931          86 :         Node* const match_length = LoadStringLength(match);
    1932          86 :         GotoIfNot(SmiEqual(match_length, smi_zero), &loop);
    1933             : 
    1934          86 :         Node* last_index = LoadLastIndex(context, regexp, is_fastpath);
    1935          86 :         if (is_fastpath) {
    1936             :           CSA_ASSERT(this, TaggedIsPositiveSmi(last_index));
    1937             :         } else {
    1938          43 :           last_index = CallBuiltin(Builtins::kToLength, context, last_index);
    1939             :         }
    1940             : 
    1941             :         Node* const new_last_index =
    1942          86 :             AdvanceStringIndex(string, last_index, is_unicode, is_fastpath);
    1943             : 
    1944             :         if (is_fastpath) {
    1945             :           // On the fast path, we can be certain that lastIndex can never be
    1946             :           // incremented to overflow the Smi range since the maximal string
    1947             :           // length is less than the maximal Smi value.
    1948             :           STATIC_ASSERT(String::kMaxLength < Smi::kMaxValue);
    1949             :           CSA_ASSERT(this, TaggedIsPositiveSmi(new_last_index));
    1950             :         }
    1951             : 
    1952          86 :         StoreLastIndex(context, regexp, new_last_index, is_fastpath);
    1953             : 
    1954          86 :         Goto(&loop);
    1955          86 :       }
    1956             :     }
    1957             : 
    1958          86 :     BIND(&out);
    1959             :     {
    1960             :       // Wrap the match in a JSArray.
    1961             : 
    1962          86 :       Node* const result = array.ToJSArray(context);
    1963          86 :       Return(result);
    1964          86 :     }
    1965          86 :   }
    1966          86 : }
    1967             : 
    1968             : // ES#sec-regexp.prototype-@@match
    1969             : // RegExp.prototype [ @@match ] ( string )
    1970         172 : TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) {
    1971             :   Node* const maybe_receiver = Parameter(Descriptor::kReceiver);
    1972             :   Node* const maybe_string = Parameter(Descriptor::kString);
    1973             :   Node* const context = Parameter(Descriptor::kContext);
    1974             : 
    1975             :   // Ensure {maybe_receiver} is a JSReceiver.
    1976             :   ThrowIfNotJSReceiver(context, maybe_receiver,
    1977             :                        MessageTemplate::kIncompatibleMethodReceiver,
    1978          43 :                        "RegExp.prototype.@@match");
    1979             :   Node* const receiver = maybe_receiver;
    1980             : 
    1981             :   // Convert {maybe_string} to a String.
    1982          43 :   Node* const string = ToString(context, maybe_string);
    1983             : 
    1984          43 :   Label fast_path(this), slow_path(this);
    1985          43 :   BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
    1986             : 
    1987          43 :   BIND(&fast_path);
    1988          43 :   RegExpPrototypeMatchBody(context, receiver, string, true);
    1989             : 
    1990          43 :   BIND(&slow_path);
    1991          86 :   RegExpPrototypeMatchBody(context, receiver, string, false);
    1992          43 : }
    1993             : 
    1994          43 : void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast(
    1995             :     Node* const context, Node* const regexp, Node* const string) {
    1996             :   CSA_ASSERT(this, IsFastRegExp(context, regexp));
    1997             :   CSA_ASSERT(this, IsString(string));
    1998             : 
    1999             :   // Grab the initial value of last index.
    2000             :   Node* const previous_last_index = FastLoadLastIndex(regexp);
    2001             : 
    2002             :   // Ensure last index is 0.
    2003          43 :   FastStoreLastIndex(regexp, SmiConstant(Smi::kZero));
    2004             : 
    2005             :   // Call exec.
    2006             :   Label if_didnotmatch(this);
    2007             :   Node* const match_indices = RegExpPrototypeExecBodyWithoutResult(
    2008          43 :       context, regexp, string, &if_didnotmatch, true);
    2009             : 
    2010             :   // Successful match.
    2011             :   {
    2012             :     // Reset last index.
    2013             :     FastStoreLastIndex(regexp, previous_last_index);
    2014             : 
    2015             :     // Return the index of the match.
    2016             :     Node* const index = LoadFixedArrayElement(
    2017          43 :         match_indices, RegExpMatchInfo::kFirstCaptureIndex);
    2018          43 :     Return(index);
    2019             :   }
    2020             : 
    2021          43 :   BIND(&if_didnotmatch);
    2022             :   {
    2023             :     // Reset last index and return -1.
    2024             :     FastStoreLastIndex(regexp, previous_last_index);
    2025          43 :     Return(SmiConstant(-1));
    2026          43 :   }
    2027          43 : }
    2028             : 
    2029          43 : void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow(
    2030             :     Node* const context, Node* const regexp, Node* const string) {
    2031             :   CSA_ASSERT(this, IsJSReceiver(regexp));
    2032             :   CSA_ASSERT(this, IsString(string));
    2033             : 
    2034          43 :   Isolate* const isolate = this->isolate();
    2035             : 
    2036          43 :   Node* const smi_zero = SmiConstant(Smi::kZero);
    2037             : 
    2038             :   // Grab the initial value of last index.
    2039          43 :   Node* const previous_last_index = SlowLoadLastIndex(context, regexp);
    2040             : 
    2041             :   // Ensure last index is 0.
    2042             :   {
    2043             :     Label next(this);
    2044          43 :     GotoIf(SameValue(previous_last_index, smi_zero), &next);
    2045             : 
    2046          43 :     SlowStoreLastIndex(context, regexp, smi_zero);
    2047          43 :     Goto(&next);
    2048          43 :     BIND(&next);
    2049             :   }
    2050             : 
    2051             :   // Call exec.
    2052          43 :   Node* const exec_result = RegExpExec(context, regexp, string);
    2053             : 
    2054             :   // Reset last index if necessary.
    2055             :   {
    2056             :     Label next(this);
    2057          43 :     Node* const current_last_index = SlowLoadLastIndex(context, regexp);
    2058             : 
    2059          43 :     GotoIf(SameValue(current_last_index, previous_last_index), &next);
    2060             : 
    2061          43 :     SlowStoreLastIndex(context, regexp, previous_last_index);
    2062          43 :     Goto(&next);
    2063             : 
    2064          43 :     BIND(&next);
    2065             :   }
    2066             : 
    2067             :   // Return -1 if no match was found.
    2068             :   {
    2069             :     Label next(this);
    2070          43 :     GotoIfNot(WordEqual(exec_result, NullConstant()), &next);
    2071          43 :     Return(SmiConstant(-1));
    2072          43 :     BIND(&next);
    2073             :   }
    2074             : 
    2075             :   // Return the index of the match.
    2076             :   {
    2077          43 :     Label fast_result(this), slow_result(this, Label::kDeferred);
    2078          43 :     BranchIfFastRegExpResult(context, exec_result, &fast_result, &slow_result);
    2079             : 
    2080          43 :     BIND(&fast_result);
    2081             :     {
    2082             :       Node* const index =
    2083          43 :           LoadObjectField(exec_result, JSRegExpResult::kIndexOffset);
    2084          43 :       Return(index);
    2085             :     }
    2086             : 
    2087          43 :     BIND(&slow_result);
    2088             :     {
    2089             :       Return(GetProperty(context, exec_result,
    2090          43 :                          isolate->factory()->index_string()));
    2091          43 :     }
    2092             :   }
    2093          43 : }
    2094             : 
    2095             : // ES#sec-regexp.prototype-@@search
    2096             : // RegExp.prototype [ @@search ] ( string )
    2097         172 : TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) {
    2098             :   Node* const maybe_receiver = Parameter(Descriptor::kReceiver);
    2099             :   Node* const maybe_string = Parameter(Descriptor::kString);
    2100             :   Node* const context = Parameter(Descriptor::kContext);
    2101             : 
    2102             :   // Ensure {maybe_receiver} is a JSReceiver.
    2103             :   ThrowIfNotJSReceiver(context, maybe_receiver,
    2104             :                        MessageTemplate::kIncompatibleMethodReceiver,
    2105          43 :                        "RegExp.prototype.@@search");
    2106             :   Node* const receiver = maybe_receiver;
    2107             : 
    2108             :   // Convert {maybe_string} to a String.
    2109          43 :   Node* const string = ToString(context, maybe_string);
    2110             : 
    2111          43 :   Label fast_path(this), slow_path(this);
    2112          43 :   BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
    2113             : 
    2114          43 :   BIND(&fast_path);
    2115          43 :   RegExpPrototypeSearchBodyFast(context, receiver, string);
    2116             : 
    2117          43 :   BIND(&slow_path);
    2118          86 :   RegExpPrototypeSearchBodySlow(context, receiver, string);
    2119          43 : }
    2120             : 
    2121             : // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp,
    2122             : // {string} is a String, and {limit} is a Smi.
    2123          43 : void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
    2124             :                                                        Node* const regexp,
    2125             :                                                        Node* const string,
    2126             :                                                        Node* const limit) {
    2127             :   CSA_ASSERT(this, IsFastRegExp(context, regexp));
    2128             :   CSA_ASSERT(this, TaggedIsSmi(limit));
    2129             :   CSA_ASSERT(this, IsString(string));
    2130             : 
    2131          43 :   Node* const null = NullConstant();
    2132          43 :   Node* const smi_zero = SmiConstant(0);
    2133          43 :   Node* const int_zero = IntPtrConstant(0);
    2134          43 :   Node* const int_limit = SmiUntag(limit);
    2135             : 
    2136             :   const ElementsKind kind = FAST_ELEMENTS;
    2137             :   const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
    2138             : 
    2139             :   Node* const allocation_site = nullptr;
    2140          43 :   Node* const native_context = LoadNativeContext(context);
    2141          43 :   Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
    2142             : 
    2143             :   Label return_empty_array(this, Label::kDeferred);
    2144             : 
    2145             :   // If limit is zero, return an empty array.
    2146             :   {
    2147          43 :     Label next(this), if_limitiszero(this, Label::kDeferred);
    2148          43 :     Branch(SmiEqual(limit, smi_zero), &return_empty_array, &next);
    2149          86 :     BIND(&next);
    2150             :   }
    2151             : 
    2152          43 :   Node* const string_length = LoadStringLength(string);
    2153             : 
    2154             :   // If passed the empty {string}, return either an empty array or a singleton
    2155             :   // array depending on whether the {regexp} matches.
    2156             :   {
    2157          43 :     Label next(this), if_stringisempty(this, Label::kDeferred);
    2158          43 :     Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next);
    2159             : 
    2160          43 :     BIND(&if_stringisempty);
    2161             :     {
    2162             :       Node* const last_match_info = LoadContextElement(
    2163          43 :           native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
    2164             : 
    2165             :       Node* const match_indices = RegExpExecInternal(context, regexp, string,
    2166          43 :                                                      smi_zero, last_match_info);
    2167             : 
    2168             :       Label return_singleton_array(this);
    2169             :       Branch(WordEqual(match_indices, null), &return_singleton_array,
    2170          43 :              &return_empty_array);
    2171             : 
    2172          43 :       BIND(&return_singleton_array);
    2173             :       {
    2174          43 :         Node* const length = SmiConstant(1);
    2175          43 :         Node* const capacity = IntPtrConstant(1);
    2176             :         Node* const result = AllocateJSArray(kind, array_map, capacity, length,
    2177          43 :                                              allocation_site, mode);
    2178             : 
    2179          43 :         Node* const fixed_array = LoadElements(result);
    2180          43 :         StoreFixedArrayElement(fixed_array, 0, string);
    2181             : 
    2182          43 :         Return(result);
    2183          43 :       }
    2184             :     }
    2185             : 
    2186          86 :     BIND(&next);
    2187             :   }
    2188             : 
    2189             :   // Loop preparations.
    2190             : 
    2191          86 :   GrowableFixedArray array(this);
    2192             : 
    2193          86 :   VARIABLE(var_last_matched_until, MachineRepresentation::kTagged);
    2194          86 :   VARIABLE(var_next_search_from, MachineRepresentation::kTagged);
    2195             : 
    2196          43 :   var_last_matched_until.Bind(smi_zero);
    2197          43 :   var_next_search_from.Bind(smi_zero);
    2198             : 
    2199             :   Variable* vars[] = {array.var_array(), array.var_length(),
    2200             :                       array.var_capacity(), &var_last_matched_until,
    2201          43 :                       &var_next_search_from};
    2202             :   const int vars_count = sizeof(vars) / sizeof(vars[0]);
    2203          86 :   Label loop(this, vars_count, vars), push_suffix_and_out(this), out(this);
    2204          43 :   Goto(&loop);
    2205             : 
    2206          43 :   BIND(&loop);
    2207             :   {
    2208          43 :     Node* const next_search_from = var_next_search_from.value();
    2209          43 :     Node* const last_matched_until = var_last_matched_until.value();
    2210             : 
    2211             :     CSA_ASSERT(this, TaggedIsSmi(next_search_from));
    2212             :     CSA_ASSERT(this, TaggedIsSmi(last_matched_until));
    2213             : 
    2214             :     // We're done if we've reached the end of the string.
    2215             :     {
    2216             :       Label next(this);
    2217             :       Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out,
    2218          43 :              &next);
    2219          43 :       BIND(&next);
    2220             :     }
    2221             : 
    2222             :     // Search for the given {regexp}.
    2223             : 
    2224             :     Node* const last_match_info = LoadContextElement(
    2225          43 :         native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
    2226             : 
    2227             :     Node* const match_indices = RegExpExecInternal(
    2228          43 :         context, regexp, string, next_search_from, last_match_info);
    2229             : 
    2230             :     // We're done if no match was found.
    2231             :     {
    2232             :       Label next(this);
    2233          43 :       Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next);
    2234          43 :       BIND(&next);
    2235             :     }
    2236             : 
    2237             :     Node* const match_from = LoadFixedArrayElement(
    2238          43 :         match_indices, RegExpMatchInfo::kFirstCaptureIndex);
    2239             : 
    2240             :     // We're done if the match starts beyond the string.
    2241             :     {
    2242             :       Label next(this);
    2243          43 :       Branch(WordEqual(match_from, string_length), &push_suffix_and_out, &next);
    2244          43 :       BIND(&next);
    2245             :     }
    2246             : 
    2247             :     Node* const match_to = LoadFixedArrayElement(
    2248          43 :         match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
    2249             : 
    2250             :     // Advance index and continue if the match is empty.
    2251             :     {
    2252             :       Label next(this);
    2253             : 
    2254          43 :       GotoIfNot(SmiEqual(match_to, next_search_from), &next);
    2255          43 :       GotoIfNot(SmiEqual(match_to, last_matched_until), &next);
    2256             : 
    2257          43 :       Node* const is_unicode = FastFlagGetter(regexp, JSRegExp::kUnicode);
    2258             :       Node* const new_next_search_from =
    2259          43 :           AdvanceStringIndex(string, next_search_from, is_unicode, true);
    2260          43 :       var_next_search_from.Bind(new_next_search_from);
    2261          43 :       Goto(&loop);
    2262             : 
    2263          43 :       BIND(&next);
    2264             :     }
    2265             : 
    2266             :     // A valid match was found, add the new substring to the array.
    2267             :     {
    2268             :       Node* const from = last_matched_until;
    2269             :       Node* const to = match_from;
    2270             : 
    2271          43 :       Node* const substr = SubString(context, string, from, to);
    2272          43 :       array.Push(substr);
    2273             : 
    2274          43 :       GotoIf(WordEqual(array.length(), int_limit), &out);
    2275             :     }
    2276             : 
    2277             :     // Add all captures to the array.
    2278             :     {
    2279             :       Node* const num_registers = LoadFixedArrayElement(
    2280          43 :           match_indices, RegExpMatchInfo::kNumberOfCapturesIndex);
    2281          43 :       Node* const int_num_registers = SmiUntag(num_registers);
    2282             : 
    2283          43 :       VARIABLE(var_reg, MachineType::PointerRepresentation());
    2284          43 :       var_reg.Bind(IntPtrConstant(2));
    2285             : 
    2286             :       Variable* vars[] = {array.var_array(), array.var_length(),
    2287          43 :                           array.var_capacity(), &var_reg};
    2288             :       const int vars_count = sizeof(vars) / sizeof(vars[0]);
    2289          86 :       Label nested_loop(this, vars_count, vars), nested_loop_out(this);
    2290             :       Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop,
    2291          43 :              &nested_loop_out);
    2292             : 
    2293          43 :       BIND(&nested_loop);
    2294             :       {
    2295          43 :         Node* const reg = var_reg.value();
    2296             :         Node* const from = LoadFixedArrayElement(
    2297             :             match_indices, reg,
    2298          43 :             RegExpMatchInfo::kFirstCaptureIndex * kPointerSize, mode);
    2299             :         Node* const to = LoadFixedArrayElement(
    2300             :             match_indices, reg,
    2301          43 :             (RegExpMatchInfo::kFirstCaptureIndex + 1) * kPointerSize, mode);
    2302             : 
    2303          43 :         Label select_capture(this), select_undefined(this), store_value(this);
    2304          86 :         VARIABLE(var_value, MachineRepresentation::kTagged);
    2305             :         Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined,
    2306          43 :                &select_capture);
    2307             : 
    2308          43 :         BIND(&select_capture);
    2309             :         {
    2310          43 :           Node* const substr = SubString(context, string, from, to);
    2311          43 :           var_value.Bind(substr);
    2312          43 :           Goto(&store_value);
    2313             :         }
    2314             : 
    2315          43 :         BIND(&select_undefined);
    2316             :         {
    2317          43 :           Node* const undefined = UndefinedConstant();
    2318          43 :           var_value.Bind(undefined);
    2319          43 :           Goto(&store_value);
    2320             :         }
    2321             : 
    2322          43 :         BIND(&store_value);
    2323             :         {
    2324          43 :           array.Push(var_value.value());
    2325          43 :           GotoIf(WordEqual(array.length(), int_limit), &out);
    2326             : 
    2327          43 :           Node* const new_reg = IntPtrAdd(reg, IntPtrConstant(2));
    2328          43 :           var_reg.Bind(new_reg);
    2329             : 
    2330             :           Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop,
    2331          43 :                  &nested_loop_out);
    2332          43 :         }
    2333             :       }
    2334             : 
    2335          86 :       BIND(&nested_loop_out);
    2336             :     }
    2337             : 
    2338          43 :     var_last_matched_until.Bind(match_to);
    2339          43 :     var_next_search_from.Bind(match_to);
    2340          43 :     Goto(&loop);
    2341             :   }
    2342             : 
    2343          43 :   BIND(&push_suffix_and_out);
    2344             :   {
    2345          43 :     Node* const from = var_last_matched_until.value();
    2346             :     Node* const to = string_length;
    2347             : 
    2348          43 :     Node* const substr = SubString(context, string, from, to);
    2349          43 :     array.Push(substr);
    2350             : 
    2351          43 :     Goto(&out);
    2352             :   }
    2353             : 
    2354          43 :   BIND(&out);
    2355             :   {
    2356          43 :     Node* const result = array.ToJSArray(context);
    2357          43 :     Return(result);
    2358             :   }
    2359             : 
    2360          43 :   BIND(&return_empty_array);
    2361             :   {
    2362             :     Node* const length = smi_zero;
    2363             :     Node* const capacity = int_zero;
    2364             :     Node* const result = AllocateJSArray(kind, array_map, capacity, length,
    2365          43 :                                          allocation_site, mode);
    2366          43 :     Return(result);
    2367          43 :   }
    2368          43 : }
    2369             : 
    2370             : // Helper that skips a few initial checks.
    2371         172 : TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
    2372             :   Node* const regexp = Parameter(Descriptor::kRegExp);
    2373             :   Node* const string = Parameter(Descriptor::kString);
    2374             :   Node* const maybe_limit = Parameter(Descriptor::kLimit);
    2375             :   Node* const context = Parameter(Descriptor::kContext);
    2376             : 
    2377             :   CSA_ASSERT(this, IsFastRegExp(context, regexp));
    2378             :   CSA_ASSERT(this, IsString(string));
    2379             : 
    2380             :   // TODO(jgruber): Even if map checks send us to the fast path, we still need
    2381             :   // to verify the constructor property and jump to the slow path if it has
    2382             :   // been changed.
    2383             : 
    2384             :   // Convert {maybe_limit} to a uint32, capping at the maximal smi value.
    2385          43 :   VARIABLE(var_limit, MachineRepresentation::kTagged, maybe_limit);
    2386          43 :   Label if_limitissmimax(this), limit_done(this), runtime(this);
    2387             : 
    2388          43 :   GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
    2389          43 :   GotoIf(TaggedIsPositiveSmi(maybe_limit), &limit_done);
    2390             : 
    2391          43 :   Node* const limit = ToUint32(context, maybe_limit);
    2392             :   {
    2393             :     // ToUint32(limit) could potentially change the shape of the RegExp
    2394             :     // object. Recheck that we are still on the fast path and bail to runtime
    2395             :     // otherwise.
    2396             :     {
    2397             :       Label next(this);
    2398          43 :       BranchIfFastRegExp(context, regexp, &next, &runtime);
    2399          43 :       BIND(&next);
    2400             :     }
    2401             : 
    2402          43 :     GotoIfNot(TaggedIsSmi(limit), &if_limitissmimax);
    2403             : 
    2404          43 :     var_limit.Bind(limit);
    2405          43 :     Goto(&limit_done);
    2406             :   }
    2407             : 
    2408          43 :   BIND(&if_limitissmimax);
    2409             :   {
    2410             :     // TODO(jgruber): In this case, we can probably avoid generation of limit
    2411             :     // checks in Generate_RegExpPrototypeSplitBody.
    2412          43 :     var_limit.Bind(SmiConstant(Smi::kMaxValue));
    2413          43 :     Goto(&limit_done);
    2414             :   }
    2415             : 
    2416          43 :   BIND(&limit_done);
    2417             :   {
    2418          43 :     Node* const limit = var_limit.value();
    2419          43 :     RegExpPrototypeSplitBody(context, regexp, string, limit);
    2420             :   }
    2421             : 
    2422          43 :   BIND(&runtime);
    2423             :   {
    2424             :     // The runtime call passes in limit to ensure the second ToUint32(limit)
    2425             :     // call is not observable.
    2426             :     CSA_ASSERT(this, IsNumber(limit));
    2427          43 :     Return(CallRuntime(Runtime::kRegExpSplit, context, regexp, string, limit));
    2428          43 :   }
    2429          43 : }
    2430             : 
    2431             : // ES#sec-regexp.prototype-@@split
    2432             : // RegExp.prototype [ @@split ] ( string, limit )
    2433         172 : TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
    2434             :   Node* const maybe_receiver = Parameter(Descriptor::kReceiver);
    2435             :   Node* const maybe_string = Parameter(Descriptor::kString);
    2436             :   Node* const maybe_limit = Parameter(Descriptor::kLimit);
    2437             :   Node* const context = Parameter(Descriptor::kContext);
    2438             : 
    2439             :   // Ensure {maybe_receiver} is a JSReceiver.
    2440             :   ThrowIfNotJSReceiver(context, maybe_receiver,
    2441             :                        MessageTemplate::kIncompatibleMethodReceiver,
    2442          43 :                        "RegExp.prototype.@@split");
    2443             :   Node* const receiver = maybe_receiver;
    2444             : 
    2445             :   // Convert {maybe_string} to a String.
    2446          43 :   Node* const string = ToString(context, maybe_string);
    2447             : 
    2448          43 :   Label stub(this), runtime(this, Label::kDeferred);
    2449          43 :   BranchIfFastRegExp(context, receiver, &stub, &runtime);
    2450             : 
    2451          43 :   BIND(&stub);
    2452             :   Return(CallBuiltin(Builtins::kRegExpSplit, context, receiver, string,
    2453          43 :                      maybe_limit));
    2454             : 
    2455          43 :   BIND(&runtime);
    2456             :   Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string,
    2457          86 :                      maybe_limit));
    2458          43 : }
    2459             : 
    2460          43 : Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath(
    2461             :     Node* context, Node* regexp, Node* string, Node* replace_callable) {
    2462             :   // The fast path is reached only if {receiver} is a global unmodified
    2463             :   // JSRegExp instance and {replace_callable} is callable.
    2464             : 
    2465             :   CSA_ASSERT(this, IsFastRegExp(context, regexp));
    2466             :   CSA_ASSERT(this, IsCallable(replace_callable));
    2467             :   CSA_ASSERT(this, IsString(string));
    2468             : 
    2469          43 :   Isolate* const isolate = this->isolate();
    2470             : 
    2471          43 :   Node* const null = NullConstant();
    2472          43 :   Node* const undefined = UndefinedConstant();
    2473          43 :   Node* const int_zero = IntPtrConstant(0);
    2474          43 :   Node* const int_one = IntPtrConstant(1);
    2475          43 :   Node* const smi_zero = SmiConstant(Smi::kZero);
    2476             : 
    2477          43 :   Node* const native_context = LoadNativeContext(context);
    2478             : 
    2479             :   Label out(this);
    2480          86 :   VARIABLE(var_result, MachineRepresentation::kTagged);
    2481             : 
    2482             :   // Set last index to 0.
    2483             :   FastStoreLastIndex(regexp, smi_zero);
    2484             : 
    2485             :   // Allocate {result_array}.
    2486             :   Node* result_array;
    2487             :   {
    2488             :     ElementsKind kind = FAST_ELEMENTS;
    2489          43 :     Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
    2490          43 :     Node* const capacity = IntPtrConstant(16);
    2491             :     Node* const length = smi_zero;
    2492             :     Node* const allocation_site = nullptr;
    2493             :     ParameterMode capacity_mode = CodeStubAssembler::INTPTR_PARAMETERS;
    2494             : 
    2495             :     result_array = AllocateJSArray(kind, array_map, capacity, length,
    2496          43 :                                    allocation_site, capacity_mode);
    2497             :   }
    2498             : 
    2499             :   // Call into runtime for RegExpExecMultiple.
    2500             :   Node* last_match_info =
    2501          43 :       LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
    2502             :   Node* const res = CallRuntime(Runtime::kRegExpExecMultiple, context, regexp,
    2503          43 :                                 string, last_match_info, result_array);
    2504             : 
    2505             :   // Reset last index to 0.
    2506             :   FastStoreLastIndex(regexp, smi_zero);
    2507             : 
    2508             :   // If no matches, return the subject string.
    2509          43 :   var_result.Bind(string);
    2510          43 :   GotoIf(WordEqual(res, null), &out);
    2511             : 
    2512             :   // Reload last match info since it might have changed.
    2513             :   last_match_info =
    2514          43 :       LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
    2515             : 
    2516          43 :   Node* const res_length = LoadJSArrayLength(res);
    2517          43 :   Node* const res_elems = LoadElements(res);
    2518             :   CSA_ASSERT(this, HasInstanceType(res_elems, FIXED_ARRAY_TYPE));
    2519             : 
    2520             :   Node* const num_capture_registers = LoadFixedArrayElement(
    2521          43 :       last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex);
    2522             : 
    2523          43 :   Label if_hasexplicitcaptures(this), if_noexplicitcaptures(this),
    2524          43 :       create_result(this);
    2525             :   Branch(SmiEqual(num_capture_registers, SmiConstant(Smi::FromInt(2))),
    2526          43 :          &if_noexplicitcaptures, &if_hasexplicitcaptures);
    2527             : 
    2528          43 :   BIND(&if_noexplicitcaptures);
    2529             :   {
    2530             :     // If the number of captures is two then there are no explicit captures in
    2531             :     // the regexp, just the implicit capture that captures the whole match. In
    2532             :     // this case we can simplify quite a bit and end up with something faster.
    2533             :     // The builder will consist of some integers that indicate slices of the
    2534             :     // input string and some replacements that were returned from the replace
    2535             :     // function.
    2536             : 
    2537          43 :     VARIABLE(var_match_start, MachineRepresentation::kTagged);
    2538          43 :     var_match_start.Bind(smi_zero);
    2539             : 
    2540          43 :     Node* const end = SmiUntag(res_length);
    2541          86 :     VARIABLE(var_i, MachineType::PointerRepresentation());
    2542          43 :     var_i.Bind(int_zero);
    2543             : 
    2544          43 :     Variable* vars[] = {&var_i, &var_match_start};
    2545          86 :     Label loop(this, 2, vars);
    2546          43 :     Goto(&loop);
    2547          43 :     BIND(&loop);
    2548             :     {
    2549          43 :       Node* const i = var_i.value();
    2550          43 :       GotoIfNot(IntPtrLessThan(i, end), &create_result);
    2551             : 
    2552          43 :       Node* const elem = LoadFixedArrayElement(res_elems, i);
    2553             : 
    2554          43 :       Label if_issmi(this), if_isstring(this), loop_epilogue(this);
    2555          43 :       Branch(TaggedIsSmi(elem), &if_issmi, &if_isstring);
    2556             : 
    2557          43 :       BIND(&if_issmi);
    2558             :       {
    2559             :         // Integers represent slices of the original string.
    2560          43 :         Label if_isnegativeorzero(this), if_ispositive(this);
    2561             :         BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero,
    2562          43 :                                    &if_ispositive);
    2563             : 
    2564          43 :         BIND(&if_ispositive);
    2565             :         {
    2566          43 :           Node* const int_elem = SmiUntag(elem);
    2567             :           Node* const new_match_start =
    2568             :               IntPtrAdd(WordShr(int_elem, IntPtrConstant(11)),
    2569          43 :                         WordAnd(int_elem, IntPtrConstant(0x7ff)));
    2570          43 :           var_match_start.Bind(SmiTag(new_match_start));
    2571          43 :           Goto(&loop_epilogue);
    2572             :         }
    2573             : 
    2574          43 :         BIND(&if_isnegativeorzero);
    2575             :         {
    2576          43 :           Node* const next_i = IntPtrAdd(i, int_one);
    2577          43 :           var_i.Bind(next_i);
    2578             : 
    2579          43 :           Node* const next_elem = LoadFixedArrayElement(res_elems, next_i);
    2580             : 
    2581          43 :           Node* const new_match_start = SmiSub(next_elem, elem);
    2582          43 :           var_match_start.Bind(new_match_start);
    2583          43 :           Goto(&loop_epilogue);
    2584          43 :         }
    2585             :       }
    2586             : 
    2587          43 :       BIND(&if_isstring);
    2588             :       {
    2589             :         CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(elem)));
    2590             : 
    2591          43 :         Callable call_callable = CodeFactory::Call(isolate);
    2592             :         Node* const replacement_obj =
    2593             :             CallJS(call_callable, context, replace_callable, undefined, elem,
    2594          43 :                    var_match_start.value(), string);
    2595             : 
    2596          43 :         Node* const replacement_str = ToString(context, replacement_obj);
    2597          43 :         StoreFixedArrayElement(res_elems, i, replacement_str);
    2598             : 
    2599          43 :         Node* const elem_length = LoadStringLength(elem);
    2600             :         Node* const new_match_start =
    2601          43 :             SmiAdd(var_match_start.value(), elem_length);
    2602          43 :         var_match_start.Bind(new_match_start);
    2603             : 
    2604          43 :         Goto(&loop_epilogue);
    2605             :       }
    2606             : 
    2607          43 :       BIND(&loop_epilogue);
    2608             :       {
    2609          43 :         var_i.Bind(IntPtrAdd(var_i.value(), int_one));
    2610          43 :         Goto(&loop);
    2611          43 :       }
    2612          43 :     }
    2613             :   }
    2614             : 
    2615          43 :   BIND(&if_hasexplicitcaptures);
    2616             :   {
    2617             :     Node* const from = int_zero;
    2618          43 :     Node* const to = SmiUntag(res_length);
    2619             :     const int increment = 1;
    2620             : 
    2621             :     BuildFastLoop(from, to,
    2622             :                   [this, res_elems, isolate, native_context, context, undefined,
    2623          43 :                    replace_callable](Node* index) {
    2624          43 :                     Node* const elem = LoadFixedArrayElement(res_elems, index);
    2625             : 
    2626          43 :                     Label do_continue(this);
    2627          43 :                     GotoIf(TaggedIsSmi(elem), &do_continue);
    2628             : 
    2629             :                     // elem must be an Array.
    2630             :                     // Use the apply argument as backing for global RegExp
    2631             :                     // properties.
    2632             : 
    2633             :                     CSA_ASSERT(this, HasInstanceType(elem, JS_ARRAY_TYPE));
    2634             : 
    2635             :                     // TODO(jgruber): Remove indirection through
    2636             :                     // Call->ReflectApply.
    2637          43 :                     Callable call_callable = CodeFactory::Call(isolate);
    2638             :                     Node* const reflect_apply = LoadContextElement(
    2639          43 :                         native_context, Context::REFLECT_APPLY_INDEX);
    2640             : 
    2641             :                     Node* const replacement_obj =
    2642             :                         CallJS(call_callable, context, reflect_apply, undefined,
    2643          43 :                                replace_callable, undefined, elem);
    2644             : 
    2645             :                     // Overwrite the i'th element in the results with the string
    2646             :                     // we got back from the callback function.
    2647             : 
    2648             :                     Node* const replacement_str =
    2649          43 :                         ToString(context, replacement_obj);
    2650          43 :                     StoreFixedArrayElement(res_elems, index, replacement_str);
    2651             : 
    2652          43 :                     Goto(&do_continue);
    2653          43 :                     BIND(&do_continue);
    2654          43 :                   },
    2655             :                   increment, CodeStubAssembler::INTPTR_PARAMETERS,
    2656          86 :                   CodeStubAssembler::IndexAdvanceMode::kPost);
    2657             : 
    2658          43 :     Goto(&create_result);
    2659             :   }
    2660             : 
    2661          43 :   BIND(&create_result);
    2662             :   {
    2663             :     Node* const result = CallRuntime(Runtime::kStringBuilderConcat, context,
    2664          43 :                                      res, res_length, string);
    2665          43 :     var_result.Bind(result);
    2666          43 :     Goto(&out);
    2667             :   }
    2668             : 
    2669          43 :   BIND(&out);
    2670          86 :   return var_result.value();
    2671             : }
    2672             : 
    2673          43 : Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
    2674             :     Node* context, Node* regexp, Node* string, Node* replace_string) {
    2675             :   // The fast path is reached only if {receiver} is an unmodified
    2676             :   // JSRegExp instance, {replace_value} is non-callable, and
    2677             :   // ToString({replace_value}) does not contain '$', i.e. we're doing a simple
    2678             :   // string replacement.
    2679             : 
    2680          43 :   Node* const int_zero = IntPtrConstant(0);
    2681          43 :   Node* const smi_zero = SmiConstant(Smi::kZero);
    2682             : 
    2683             :   CSA_ASSERT(this, IsFastRegExp(context, regexp));
    2684             :   CSA_ASSERT(this, IsString(replace_string));
    2685             :   CSA_ASSERT(this, IsString(string));
    2686             : 
    2687             :   Label out(this);
    2688          86 :   VARIABLE(var_result, MachineRepresentation::kTagged);
    2689             : 
    2690             :   // Load the last match info.
    2691          43 :   Node* const native_context = LoadNativeContext(context);
    2692             :   Node* const last_match_info =
    2693          43 :       LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
    2694             : 
    2695             :   // Is {regexp} global?
    2696          43 :   Label if_isglobal(this), if_isnonglobal(this);
    2697          43 :   Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
    2698             :   Node* const is_global =
    2699          43 :       WordAnd(SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal));
    2700          43 :   Branch(WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal);
    2701             : 
    2702          43 :   BIND(&if_isglobal);
    2703             :   {
    2704             :     // Hand off global regexps to runtime.
    2705             :     FastStoreLastIndex(regexp, smi_zero);
    2706             :     Node* const result =
    2707             :         CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context,
    2708          43 :                     string, regexp, replace_string, last_match_info);
    2709          43 :     var_result.Bind(result);
    2710          43 :     Goto(&out);
    2711             :   }
    2712             : 
    2713          43 :   BIND(&if_isnonglobal);
    2714             :   {
    2715             :     // Run exec, then manually construct the resulting string.
    2716             :     Label if_didnotmatch(this);
    2717             :     Node* const match_indices = RegExpPrototypeExecBodyWithoutResult(
    2718          43 :         context, regexp, string, &if_didnotmatch, true);
    2719             : 
    2720             :     // Successful match.
    2721             :     {
    2722             :       Node* const subject_start = smi_zero;
    2723             :       Node* const match_start = LoadFixedArrayElement(
    2724          43 :           match_indices, RegExpMatchInfo::kFirstCaptureIndex);
    2725             :       Node* const match_end = LoadFixedArrayElement(
    2726          43 :           match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
    2727          43 :       Node* const subject_end = LoadStringLength(string);
    2728             : 
    2729          43 :       Label if_replaceisempty(this), if_replaceisnotempty(this);
    2730          43 :       Node* const replace_length = LoadStringLength(replace_string);
    2731             :       Branch(SmiEqual(replace_length, smi_zero), &if_replaceisempty,
    2732          43 :              &if_replaceisnotempty);
    2733             : 
    2734          43 :       BIND(&if_replaceisempty);
    2735             :       {
    2736             :         // TODO(jgruber): We could skip many of the checks that using SubString
    2737             :         // here entails.
    2738             : 
    2739             :         Node* const first_part =
    2740          43 :             SubString(context, string, subject_start, match_start);
    2741             :         Node* const second_part =
    2742          43 :             SubString(context, string, match_end, subject_end);
    2743             : 
    2744          43 :         Node* const result = StringAdd(context, first_part, second_part);
    2745          43 :         var_result.Bind(result);
    2746          43 :         Goto(&out);
    2747             :       }
    2748             : 
    2749          43 :       BIND(&if_replaceisnotempty);
    2750             :       {
    2751             :         Node* const first_part =
    2752          43 :             SubString(context, string, subject_start, match_start);
    2753             :         Node* const second_part = replace_string;
    2754             :         Node* const third_part =
    2755          43 :             SubString(context, string, match_end, subject_end);
    2756             : 
    2757          43 :         Node* result = StringAdd(context, first_part, second_part);
    2758          43 :         result = StringAdd(context, result, third_part);
    2759             : 
    2760          43 :         var_result.Bind(result);
    2761          43 :         Goto(&out);
    2762          43 :       }
    2763             :     }
    2764             : 
    2765          43 :     BIND(&if_didnotmatch);
    2766             :     {
    2767          43 :       var_result.Bind(string);
    2768          43 :       Goto(&out);
    2769          43 :     }
    2770             :   }
    2771             : 
    2772          43 :   BIND(&out);
    2773          86 :   return var_result.value();
    2774             : }
    2775             : 
    2776             : // Helper that skips a few initial checks.
    2777         172 : TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
    2778             :   Node* const regexp = Parameter(Descriptor::kRegExp);
    2779             :   Node* const string = Parameter(Descriptor::kString);
    2780             :   Node* const replace_value = Parameter(Descriptor::kReplaceValue);
    2781             :   Node* const context = Parameter(Descriptor::kContext);
    2782             : 
    2783             :   CSA_ASSERT(this, IsFastRegExp(context, regexp));
    2784             :   CSA_ASSERT(this, IsString(string));
    2785             : 
    2786          43 :   Label checkreplacestring(this), if_iscallable(this),
    2787          43 :       runtime(this, Label::kDeferred);
    2788             : 
    2789             :   // 2. Is {replace_value} callable?
    2790          43 :   GotoIf(TaggedIsSmi(replace_value), &checkreplacestring);
    2791             :   Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable,
    2792          43 :          &checkreplacestring);
    2793             : 
    2794             :   // 3. Does ToString({replace_value}) contain '$'?
    2795          43 :   BIND(&checkreplacestring);
    2796             :   {
    2797             :     Node* const replace_string =
    2798          43 :         CallBuiltin(Builtins::kToString, context, replace_value);
    2799             : 
    2800             :     // ToString(replaceValue) could potentially change the shape of the RegExp
    2801             :     // object. Recheck that we are still on the fast path and bail to runtime
    2802             :     // otherwise.
    2803             :     {
    2804             :       Label next(this);
    2805          43 :       BranchIfFastRegExp(context, regexp, &next, &runtime);
    2806          43 :       BIND(&next);
    2807             :     }
    2808             : 
    2809             :     Node* const dollar_string = HeapConstant(
    2810          86 :         isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
    2811             :     Node* const dollar_ix =
    2812             :         CallBuiltin(Builtins::kStringIndexOf, context, replace_string,
    2813          43 :                     dollar_string, SmiConstant(0));
    2814          43 :     GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime);
    2815             : 
    2816             :     Return(
    2817          43 :         ReplaceSimpleStringFastPath(context, regexp, string, replace_string));
    2818             :   }
    2819             : 
    2820             :   // {regexp} is unmodified and {replace_value} is callable.
    2821          43 :   BIND(&if_iscallable);
    2822             :   {
    2823             :     Node* const replace_fn = replace_value;
    2824             : 
    2825             :     // Check if the {regexp} is global.
    2826          43 :     Label if_isglobal(this), if_isnotglobal(this);
    2827             : 
    2828          43 :     Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal);
    2829          43 :     Branch(is_global, &if_isglobal, &if_isnotglobal);
    2830             : 
    2831          43 :     BIND(&if_isglobal);
    2832          43 :     Return(ReplaceGlobalCallableFastPath(context, regexp, string, replace_fn));
    2833             : 
    2834          43 :     BIND(&if_isnotglobal);
    2835             :     Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
    2836          86 :                        context, string, regexp, replace_fn));
    2837             :   }
    2838             : 
    2839          43 :   BIND(&runtime);
    2840             :   Return(CallRuntime(Runtime::kRegExpReplace, context, regexp, string,
    2841          86 :                      replace_value));
    2842          43 : }
    2843             : 
    2844             : // ES#sec-regexp.prototype-@@replace
    2845             : // RegExp.prototype [ @@replace ] ( string, replaceValue )
    2846         172 : TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
    2847             :   Node* const maybe_receiver = Parameter(Descriptor::kReceiver);
    2848             :   Node* const maybe_string = Parameter(Descriptor::kString);
    2849             :   Node* const replace_value = Parameter(Descriptor::kReplaceValue);
    2850             :   Node* const context = Parameter(Descriptor::kContext);
    2851             : 
    2852             :   // RegExpPrototypeReplace is a bit of a beast - a summary of dispatch logic:
    2853             :   //
    2854             :   // if (!IsFastRegExp(receiver)) CallRuntime(RegExpReplace)
    2855             :   // if (IsCallable(replace)) {
    2856             :   //   if (IsGlobal(receiver)) {
    2857             :   //     // Called 'fast-path' but contains several runtime calls.
    2858             :   //     ReplaceGlobalCallableFastPath()
    2859             :   //   } else {
    2860             :   //     CallRuntime(StringReplaceNonGlobalRegExpWithFunction)
    2861             :   //   }
    2862             :   // } else {
    2863             :   //   if (replace.contains("$")) {
    2864             :   //     CallRuntime(RegExpReplace)
    2865             :   //   } else {
    2866             :   //     ReplaceSimpleStringFastPath()  // Bails to runtime for global regexps.
    2867             :   //   }
    2868             :   // }
    2869             : 
    2870             :   // Ensure {maybe_receiver} is a JSReceiver.
    2871             :   ThrowIfNotJSReceiver(context, maybe_receiver,
    2872             :                        MessageTemplate::kIncompatibleMethodReceiver,
    2873          43 :                        "RegExp.prototype.@@replace");
    2874             :   Node* const receiver = maybe_receiver;
    2875             : 
    2876             :   // Convert {maybe_string} to a String.
    2877          43 :   Node* const string = CallBuiltin(Builtins::kToString, context, maybe_string);
    2878             : 
    2879             :   // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
    2880          43 :   Label stub(this), runtime(this, Label::kDeferred);
    2881          43 :   BranchIfFastRegExp(context, receiver, &stub, &runtime);
    2882             : 
    2883          43 :   BIND(&stub);
    2884             :   Return(CallBuiltin(Builtins::kRegExpReplace, context, receiver, string,
    2885          43 :                      replace_value));
    2886             : 
    2887          43 :   BIND(&runtime);
    2888             :   Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string,
    2889          86 :                      replace_value));
    2890          43 : }
    2891             : 
    2892             : // Simple string matching functionality for internal use which does not modify
    2893             : // the last match info.
    2894         172 : TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) {
    2895             :   Node* const regexp = Parameter(Descriptor::kRegExp);
    2896             :   Node* const string = Parameter(Descriptor::kString);
    2897             :   Node* const context = Parameter(Descriptor::kContext);
    2898             : 
    2899          43 :   Node* const null = NullConstant();
    2900          43 :   Node* const smi_zero = SmiConstant(0);
    2901             : 
    2902             :   CSA_ASSERT(this, IsJSRegExp(regexp));
    2903             :   CSA_ASSERT(this, IsString(string));
    2904             : 
    2905          43 :   Node* const native_context = LoadNativeContext(context);
    2906             :   Node* const internal_match_info = LoadContextElement(
    2907          43 :       native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX);
    2908             : 
    2909             :   Node* const match_indices = RegExpExecInternal(context, regexp, string,
    2910          43 :                                                  smi_zero, internal_match_info);
    2911             : 
    2912          43 :   Label if_matched(this), if_didnotmatch(this);
    2913          43 :   Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched);
    2914             : 
    2915          43 :   BIND(&if_didnotmatch);
    2916          43 :   Return(null);
    2917             : 
    2918          43 :   BIND(&if_matched);
    2919             :   {
    2920             :     Node* result =
    2921          43 :         ConstructNewResultFromMatchInfo(context, regexp, match_indices, string);
    2922          43 :     Return(result);
    2923          43 :   }
    2924          43 : }
    2925             : 
    2926             : }  // namespace internal
    2927             : }  // namespace v8

Generated by: LCOV version 1.10