LCOV - code coverage report
Current view: top level - src/builtins - builtins-regexp-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 1175 1195 98.3 %
Date: 2017-10-20 Functions: 87 90 96.7 %

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

Generated by: LCOV version 1.10