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