LCOV - code coverage report
Current view: top level - src/builtins - builtins-regexp-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 1476 1519 97.2 %
Date: 2019-03-21 Functions: 186 188 98.9 %

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

Generated by: LCOV version 1.10