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