• 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/codegen/code-factory.h"
12 #include "src/codegen/code-stub-assembler.h"
13 #include "src/codegen/macro-assembler.h"
14 #include "src/execution/protectors.h"
15 #include "src/heap/factory-inl.h"
16 #include "src/logging/counters.h"
17 #include "src/objects/js-regexp-string-iterator.h"
18 #include "src/objects/js-regexp.h"
19 #include "src/objects/regexp-match-info.h"
20 #include "src/regexp/regexp.h"
21 
22 namespace v8 {
23 namespace internal {
24 
25 using compiler::Node;
26 
27 // Tail calls the regular expression interpreter.
28 // static
Generate_RegExpInterpreterTrampoline(MacroAssembler * masm)29 void Builtins::Generate_RegExpInterpreterTrampoline(MacroAssembler* masm) {
30   ExternalReference interpreter_code_entry =
31       ExternalReference::re_match_for_call_from_js();
32   masm->Jump(interpreter_code_entry);
33 }
34 
35 // Tail calls the experimental regular expression engine.
36 // static
Generate_RegExpExperimentalTrampoline(MacroAssembler * masm)37 void Builtins::Generate_RegExpExperimentalTrampoline(MacroAssembler* masm) {
38   ExternalReference interpreter_code_entry =
39       ExternalReference::re_experimental_match_for_call_from_js();
40   masm->Jump(interpreter_code_entry);
41 }
42 
SmiZero()43 TNode<Smi> RegExpBuiltinsAssembler::SmiZero() { return SmiConstant(0); }
44 
IntPtrZero()45 TNode<IntPtrT> RegExpBuiltinsAssembler::IntPtrZero() {
46   return IntPtrConstant(0);
47 }
48 
49 // If code is a builtin, return the address to the (possibly embedded) builtin
50 // code entry, otherwise return the entry of the code object itself.
LoadCodeObjectEntry(TNode<Code> code)51 TNode<RawPtrT> RegExpBuiltinsAssembler::LoadCodeObjectEntry(TNode<Code> code) {
52   TVARIABLE(RawPtrT, var_result);
53 
54   Label if_code_is_off_heap(this), out(this);
55   TNode<Int32T> builtin_index =
56       LoadObjectField<Int32T>(code, Code::kBuiltinIndexOffset);
57   {
58     GotoIfNot(Word32Equal(builtin_index, Int32Constant(Builtins::kNoBuiltinId)),
59               &if_code_is_off_heap);
60     var_result = ReinterpretCast<RawPtrT>(
61         IntPtrAdd(BitcastTaggedToWord(code),
62                   IntPtrConstant(Code::kHeaderSize - kHeapObjectTag)));
63     Goto(&out);
64   }
65 
66   BIND(&if_code_is_off_heap);
67   {
68     TNode<IntPtrT> builtin_entry_offset_from_isolate_root =
69         IntPtrAdd(IntPtrConstant(IsolateData::builtin_entry_table_offset()),
70                   ChangeInt32ToIntPtr(Word32Shl(
71                       builtin_index, Int32Constant(kSystemPointerSizeLog2))));
72 
73     var_result = ReinterpretCast<RawPtrT>(
74         Load(MachineType::Pointer(),
75              ExternalConstant(ExternalReference::isolate_root(isolate())),
76              builtin_entry_offset_from_isolate_root));
77     Goto(&out);
78   }
79 
80   BIND(&out);
81   return var_result.value();
82 }
83 
84 // -----------------------------------------------------------------------------
85 // ES6 section 21.2 RegExp Objects
86 
AllocateRegExpResult(TNode<Context> context,TNode<Smi> length,TNode<Smi> index,TNode<String> input,TNode<JSRegExp> regexp,TNode<Number> last_index,TNode<FixedArray> * elements_out)87 TNode<JSRegExpResult> RegExpBuiltinsAssembler::AllocateRegExpResult(
88     TNode<Context> context, TNode<Smi> length, TNode<Smi> index,
89     TNode<String> input, TNode<JSRegExp> regexp, TNode<Number> last_index,
90     TNode<FixedArray>* elements_out) {
91   CSA_ASSERT(this, SmiLessThanOrEqual(
92                        length, SmiConstant(JSArray::kMaxFastArrayLength)));
93   CSA_ASSERT(this, SmiGreaterThan(length, SmiConstant(0)));
94 
95   // Allocate.
96 
97   const ElementsKind elements_kind = PACKED_ELEMENTS;
98   TNode<Map> map = CAST(LoadContextElement(LoadNativeContext(context),
99                                            Context::REGEXP_RESULT_MAP_INDEX));
100   base::Optional<TNode<AllocationSite>> no_allocation_site = base::nullopt;
101   TNode<IntPtrT> length_intptr = SmiUntag(length);
102 
103   // Note: The returned `elements` may be in young large object space, but
104   // `array` is guaranteed to be in new space so we could skip write barriers
105   // below.
106   TNode<JSArray> array;
107   TNode<FixedArrayBase> elements;
108   std::tie(array, elements) = AllocateUninitializedJSArrayWithElements(
109       elements_kind, map, length, no_allocation_site, length_intptr,
110       kAllowLargeObjectAllocation, JSRegExpResult::kSize);
111 
112   // Finish result initialization.
113 
114   TNode<JSRegExpResult> result = UncheckedCast<JSRegExpResult>(array);
115 
116   // Load undefined value once here to avoid multiple LoadRoots.
117   TNode<Oddball> undefined_value = UncheckedCast<Oddball>(
118       CodeAssembler::LoadRoot(RootIndex::kUndefinedValue));
119 
120   StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kIndexOffset, index);
121   // TODO(jgruber,tebbi): Could skip barrier but the MemoryOptimizer complains.
122   StoreObjectField(result, JSRegExpResult::kInputOffset, input);
123   StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kGroupsOffset,
124                                  undefined_value);
125   StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kNamesOffset,
126                                  undefined_value);
127 
128   // Stash regexp in order to re-execute and build JSRegExpResultIndices lazily
129   // when the 'indices' property is accessed.
130   StoreObjectField(result, JSRegExpResult::kCachedIndicesOrRegexpOffset,
131                    regexp);
132   StoreObjectField(result, JSRegExpResult::kRegexpInputOffset, input);
133 
134   // If non-smi last_index then store an SmiZero instead.
135   {
136     TNode<Smi> last_index_smi = Select<Smi>(
137         TaggedIsSmi(last_index), [=] { return CAST(last_index); },
138         [=] { return SmiZero(); });
139     StoreObjectField(result, JSRegExpResult::kRegexpLastIndexOffset,
140                      last_index_smi);
141   }
142 
143   // Finish elements initialization.
144 
145   FillFixedArrayWithValue(elements_kind, elements, IntPtrZero(), length_intptr,
146                           RootIndex::kUndefinedValue);
147 
148   if (elements_out) *elements_out = CAST(elements);
149   return result;
150 }
151 
FastLoadLastIndexBeforeSmiCheck(TNode<JSRegExp> regexp)152 TNode<Object> RegExpBuiltinsAssembler::FastLoadLastIndexBeforeSmiCheck(
153     TNode<JSRegExp> regexp) {
154   // Load the in-object field.
155   static const int field_offset =
156       JSRegExp::kHeaderSize + JSRegExp::kLastIndexFieldIndex * kTaggedSize;
157   return LoadObjectField(regexp, field_offset);
158 }
159 
SlowLoadLastIndex(TNode<Context> context,TNode<Object> regexp)160 TNode<Object> RegExpBuiltinsAssembler::SlowLoadLastIndex(TNode<Context> context,
161                                                          TNode<Object> regexp) {
162   return GetProperty(context, regexp, isolate()->factory()->lastIndex_string());
163 }
164 
165 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified
166 // JSRegExp instance.
FastStoreLastIndex(TNode<JSRegExp> regexp,TNode<Smi> value)167 void RegExpBuiltinsAssembler::FastStoreLastIndex(TNode<JSRegExp> regexp,
168                                                  TNode<Smi> value) {
169   // Store the in-object field.
170   static const int field_offset =
171       JSRegExp::kHeaderSize + JSRegExp::kLastIndexFieldIndex * kTaggedSize;
172   StoreObjectField(regexp, field_offset, value);
173 }
174 
SlowStoreLastIndex(TNode<Context> context,TNode<Object> regexp,TNode<Object> value)175 void RegExpBuiltinsAssembler::SlowStoreLastIndex(TNode<Context> context,
176                                                  TNode<Object> regexp,
177                                                  TNode<Object> value) {
178   TNode<String> name = HeapConstant(isolate()->factory()->lastIndex_string());
179   SetPropertyStrict(context, regexp, name, value);
180 }
181 
ConstructNewResultFromMatchInfo(TNode<Context> context,TNode<JSRegExp> regexp,TNode<RegExpMatchInfo> match_info,TNode<String> string,TNode<Number> last_index)182 TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
183     TNode<Context> context, TNode<JSRegExp> regexp,
184     TNode<RegExpMatchInfo> match_info, TNode<String> string,
185     TNode<Number> last_index) {
186   Label named_captures(this), out(this);
187 
188   TNode<IntPtrT> num_indices = SmiUntag(CAST(UnsafeLoadFixedArrayElement(
189       match_info, RegExpMatchInfo::kNumberOfCapturesIndex)));
190   TNode<Smi> num_results = SmiTag(WordShr(num_indices, 1));
191   TNode<Smi> start = CAST(UnsafeLoadFixedArrayElement(
192       match_info, RegExpMatchInfo::kFirstCaptureIndex));
193   TNode<Smi> end = CAST(UnsafeLoadFixedArrayElement(
194       match_info, RegExpMatchInfo::kFirstCaptureIndex + 1));
195 
196   // Calculate the substring of the first match before creating the result array
197   // to avoid an unnecessary write barrier storing the first result.
198 
199   TNode<String> first =
200       CAST(CallBuiltin(Builtins::kSubString, context, string, start, end));
201 
202   TNode<FixedArray> result_elements;
203   TNode<JSRegExpResult> result =
204       AllocateRegExpResult(context, num_results, start, string, regexp,
205                            last_index, &result_elements);
206 
207   UnsafeStoreFixedArrayElement(result_elements, 0, first);
208 
209   // If no captures exist we can skip named capture handling as well.
210   GotoIf(SmiEqual(num_results, SmiConstant(1)), &out);
211 
212   // Store all remaining captures.
213   TNode<IntPtrT> limit = IntPtrAdd(
214       IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices);
215 
216   TVARIABLE(IntPtrT, var_from_cursor,
217             IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2));
218   TVARIABLE(IntPtrT, var_to_cursor, IntPtrConstant(1));
219 
220   Label loop(this, {&var_from_cursor, &var_to_cursor});
221 
222   Goto(&loop);
223   BIND(&loop);
224   {
225     TNode<IntPtrT> from_cursor = var_from_cursor.value();
226     TNode<IntPtrT> to_cursor = var_to_cursor.value();
227     TNode<Smi> start =
228         CAST(UnsafeLoadFixedArrayElement(match_info, from_cursor));
229 
230     Label next_iter(this);
231     GotoIf(SmiEqual(start, SmiConstant(-1)), &next_iter);
232 
233     TNode<IntPtrT> from_cursor_plus1 =
234         IntPtrAdd(from_cursor, IntPtrConstant(1));
235     TNode<Smi> end =
236         CAST(UnsafeLoadFixedArrayElement(match_info, from_cursor_plus1));
237 
238     TNode<String> capture =
239         CAST(CallBuiltin(Builtins::kSubString, context, string, start, end));
240     UnsafeStoreFixedArrayElement(result_elements, to_cursor, capture);
241     Goto(&next_iter);
242 
243     BIND(&next_iter);
244     var_from_cursor = IntPtrAdd(from_cursor, IntPtrConstant(2));
245     var_to_cursor = IntPtrAdd(to_cursor, IntPtrConstant(1));
246     Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop,
247            &named_captures);
248   }
249 
250   BIND(&named_captures);
251   {
252     CSA_ASSERT(this, SmiGreaterThan(num_results, SmiConstant(1)));
253 
254     // Preparations for named capture properties. Exit early if the result does
255     // not have any named captures to minimize performance impact.
256 
257     TNode<FixedArray> data =
258         CAST(LoadObjectField(regexp, JSRegExp::kDataOffset));
259 
260     // We reach this point only if captures exist, implying that the assigned
261     // regexp engine must be able to handle captures.
262     CSA_ASSERT(
263         this,
264         Word32Or(
265             SmiEqual(CAST(LoadFixedArrayElement(data, JSRegExp::kTagIndex)),
266                      SmiConstant(JSRegExp::IRREGEXP)),
267             SmiEqual(CAST(LoadFixedArrayElement(data, JSRegExp::kTagIndex)),
268                      SmiConstant(JSRegExp::EXPERIMENTAL))));
269 
270     // The names fixed array associates names at even indices with a capture
271     // index at odd indices.
272     TNode<Object> maybe_names =
273         LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureNameMapIndex);
274     GotoIf(TaggedEqual(maybe_names, SmiZero()), &out);
275 
276     // One or more named captures exist, add a property for each one.
277 
278     TNode<FixedArray> names = CAST(maybe_names);
279     TNode<IntPtrT> names_length = LoadAndUntagFixedArrayBaseLength(names);
280     CSA_ASSERT(this, IntPtrGreaterThan(names_length, IntPtrZero()));
281 
282     // Stash names in case we need them to build the indices array later.
283     StoreObjectField(result, JSRegExpResult::kNamesOffset, names);
284 
285     // Allocate a new object to store the named capture properties.
286     // TODO(jgruber): Could be optimized by adding the object map to the heap
287     // root list.
288 
289     TNode<IntPtrT> num_properties = WordSar(names_length, 1);
290     TNode<NativeContext> native_context = LoadNativeContext(context);
291     TNode<Map> map = LoadSlowObjectWithNullPrototypeMap(native_context);
292     TNode<NameDictionary> properties =
293         AllocateNameDictionary(num_properties, kAllowLargeObjectAllocation);
294 
295     TNode<JSObject> group_object = AllocateJSObjectFromMap(map, properties);
296     StoreObjectField(result, JSRegExpResult::kGroupsOffset, group_object);
297 
298     TVARIABLE(IntPtrT, var_i, IntPtrZero());
299 
300     Label loop(this, &var_i);
301 
302     Goto(&loop);
303     BIND(&loop);
304     {
305       TNode<IntPtrT> i = var_i.value();
306       TNode<IntPtrT> i_plus_1 = IntPtrAdd(i, IntPtrConstant(1));
307       TNode<IntPtrT> i_plus_2 = IntPtrAdd(i_plus_1, IntPtrConstant(1));
308 
309       TNode<String> name = CAST(LoadFixedArrayElement(names, i));
310       TNode<Smi> index = CAST(LoadFixedArrayElement(names, i_plus_1));
311       TNode<HeapObject> capture =
312           CAST(LoadFixedArrayElement(result_elements, SmiUntag(index)));
313 
314       // TODO(v8:8213): For maintainability, we should call a CSA/Torque
315       // implementation of CreateDataProperty instead.
316 
317       // At this point the spec says to call CreateDataProperty. However, we can
318       // skip most of the steps and go straight to adding a dictionary entry
319       // because we know a bunch of useful facts:
320       // - All keys are non-numeric internalized strings
321       // - No keys repeat
322       // - Receiver has no prototype
323       // - Receiver isn't used as a prototype
324       // - Receiver isn't any special object like a Promise intrinsic object
325       // - Receiver is extensible
326       // - Receiver has no interceptors
327       Label add_dictionary_property_slow(this, Label::kDeferred);
328       Add<NameDictionary>(properties, name, capture,
329                           &add_dictionary_property_slow);
330 
331       var_i = i_plus_2;
332       Branch(IntPtrGreaterThanOrEqual(var_i.value(), names_length), &out,
333              &loop);
334 
335       BIND(&add_dictionary_property_slow);
336       // If the dictionary needs resizing, the above Add call will jump here
337       // before making any changes. This shouldn't happen because we allocated
338       // the dictionary with enough space above.
339       Unreachable();
340     }
341   }
342 
343   BIND(&out);
344   return result;
345 }
346 
GetStringPointers(TNode<RawPtrT> string_data,TNode<IntPtrT> offset,TNode<IntPtrT> last_index,TNode<IntPtrT> string_length,String::Encoding encoding,TVariable<RawPtrT> * var_string_start,TVariable<RawPtrT> * var_string_end)347 void RegExpBuiltinsAssembler::GetStringPointers(
348     TNode<RawPtrT> string_data, TNode<IntPtrT> offset,
349     TNode<IntPtrT> last_index, TNode<IntPtrT> string_length,
350     String::Encoding encoding, TVariable<RawPtrT>* var_string_start,
351     TVariable<RawPtrT>* var_string_end) {
352   DCHECK_EQ(var_string_start->rep(), MachineType::PointerRepresentation());
353   DCHECK_EQ(var_string_end->rep(), MachineType::PointerRepresentation());
354 
355   const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
356                                 ? UINT8_ELEMENTS
357                                 : UINT16_ELEMENTS;
358 
359   TNode<IntPtrT> from_offset =
360       ElementOffsetFromIndex(IntPtrAdd(offset, last_index), kind);
361   *var_string_start =
362       ReinterpretCast<RawPtrT>(IntPtrAdd(string_data, from_offset));
363 
364   TNode<IntPtrT> to_offset =
365       ElementOffsetFromIndex(IntPtrAdd(offset, string_length), kind);
366   *var_string_end = ReinterpretCast<RawPtrT>(IntPtrAdd(string_data, to_offset));
367 }
368 
RegExpExecInternal(TNode<Context> context,TNode<JSRegExp> regexp,TNode<String> string,TNode<Number> last_index,TNode<RegExpMatchInfo> match_info)369 TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
370     TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> string,
371     TNode<Number> last_index, TNode<RegExpMatchInfo> match_info) {
372   ToDirectStringAssembler to_direct(state(), string);
373 
374   TVARIABLE(HeapObject, var_result);
375   Label out(this), atom(this), runtime(this, Label::kDeferred),
376       retry_experimental(this, Label::kDeferred);
377 
378   // External constants.
379   TNode<ExternalReference> isolate_address =
380       ExternalConstant(ExternalReference::isolate_address(isolate()));
381   TNode<ExternalReference> regexp_stack_memory_top_address = ExternalConstant(
382       ExternalReference::address_of_regexp_stack_memory_top_address(isolate()));
383   TNode<ExternalReference> static_offsets_vector_address = ExternalConstant(
384       ExternalReference::address_of_static_offsets_vector(isolate()));
385 
386   // At this point, last_index is definitely a canonicalized non-negative
387   // number, which implies that any non-Smi last_index is greater than
388   // the maximal string length. If lastIndex > string.length then the matcher
389   // must fail.
390 
391   Label if_failure(this);
392 
393   CSA_ASSERT(this, IsNumberNormalized(last_index));
394   CSA_ASSERT(this, IsNumberPositive(last_index));
395   GotoIf(TaggedIsNotSmi(last_index), &if_failure);
396 
397   TNode<IntPtrT> int_string_length = LoadStringLengthAsWord(string);
398   TNode<IntPtrT> int_last_index = SmiUntag(CAST(last_index));
399 
400   GotoIf(UintPtrGreaterThan(int_last_index, int_string_length), &if_failure);
401 
402   // Since the RegExp has been compiled, data contains a fixed array.
403   TNode<FixedArray> data = CAST(LoadObjectField(regexp, JSRegExp::kDataOffset));
404   {
405     // Dispatch on the type of the RegExp.
406     {
407       Label next(this), unreachable(this, Label::kDeferred);
408       TNode<Int32T> tag = LoadAndUntagToWord32FixedArrayElement(
409           data, IntPtrConstant(JSRegExp::kTagIndex));
410 
411       int32_t values[] = {
412           JSRegExp::IRREGEXP,
413           JSRegExp::ATOM,
414           JSRegExp::EXPERIMENTAL,
415       };
416       Label* labels[] = {&next, &atom, &next};
417 
418       STATIC_ASSERT(arraysize(values) == arraysize(labels));
419       Switch(tag, &unreachable, values, labels, arraysize(values));
420 
421       BIND(&unreachable);
422       Unreachable();
423 
424       BIND(&next);
425     }
426 
427     // Check (number_of_captures + 1) * 2 <= offsets vector size
428     // Or              number_of_captures <= offsets vector size / 2 - 1
429     TNode<Smi> capture_count = CAST(UnsafeLoadFixedArrayElement(
430         data, JSRegExp::kIrregexpCaptureCountIndex));
431 
432     const int kOffsetsSize = Isolate::kJSRegexpStaticOffsetsVectorSize;
433     STATIC_ASSERT(kOffsetsSize >= 2);
434     GotoIf(SmiAbove(capture_count, SmiConstant(kOffsetsSize / 2 - 1)),
435            &runtime);
436   }
437 
438   // Unpack the string if possible.
439 
440   to_direct.TryToDirect(&runtime);
441 
442   // Load the irregexp code or bytecode object and offsets into the subject
443   // string. Both depend on whether the string is one- or two-byte.
444 
445   TVARIABLE(RawPtrT, var_string_start);
446   TVARIABLE(RawPtrT, var_string_end);
447   TVARIABLE(Object, var_code);
448   TVARIABLE(Object, var_bytecode);
449 
450   {
451     TNode<RawPtrT> direct_string_data = to_direct.PointerToData(&runtime);
452 
453     Label next(this), if_isonebyte(this), if_istwobyte(this, Label::kDeferred);
454     Branch(IsOneByteStringInstanceType(to_direct.instance_type()),
455            &if_isonebyte, &if_istwobyte);
456 
457     BIND(&if_isonebyte);
458     {
459       GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
460                         int_string_length, String::ONE_BYTE_ENCODING,
461                         &var_string_start, &var_string_end);
462       var_code =
463           UnsafeLoadFixedArrayElement(data, JSRegExp::kIrregexpLatin1CodeIndex);
464       var_bytecode = UnsafeLoadFixedArrayElement(
465           data, JSRegExp::kIrregexpLatin1BytecodeIndex);
466       Goto(&next);
467     }
468 
469     BIND(&if_istwobyte);
470     {
471       GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
472                         int_string_length, String::TWO_BYTE_ENCODING,
473                         &var_string_start, &var_string_end);
474       var_code =
475           UnsafeLoadFixedArrayElement(data, JSRegExp::kIrregexpUC16CodeIndex);
476       var_bytecode = UnsafeLoadFixedArrayElement(
477           data, JSRegExp::kIrregexpUC16BytecodeIndex);
478       Goto(&next);
479     }
480 
481     BIND(&next);
482   }
483 
484   // Check that the irregexp code has been generated for the actual string
485   // encoding. If it has, the field contains a code object; and otherwise it
486   // contains the uninitialized sentinel as a smi.
487 #ifdef DEBUG
488   {
489     Label next(this);
490     GotoIfNot(TaggedIsSmi(var_code.value()), &next);
491     CSA_ASSERT(this, SmiEqual(CAST(var_code.value()),
492                               SmiConstant(JSRegExp::kUninitializedValue)));
493     Goto(&next);
494     BIND(&next);
495   }
496 #endif
497 
498   GotoIf(TaggedIsSmi(var_code.value()), &runtime);
499   TNode<Code> code = CAST(var_code.value());
500 
501   Label if_success(this), if_exception(this, Label::kDeferred);
502   {
503     IncrementCounter(isolate()->counters()->regexp_entry_native(), 1);
504 
505     // Set up args for the final call into generated Irregexp code.
506 
507     MachineType type_int32 = MachineType::Int32();
508     MachineType type_tagged = MachineType::AnyTagged();
509     MachineType type_ptr = MachineType::Pointer();
510 
511     // Result: A NativeRegExpMacroAssembler::Result return code.
512     MachineType retval_type = type_int32;
513 
514     // Argument 0: Original subject string.
515     MachineType arg0_type = type_tagged;
516     TNode<String> arg0 = string;
517 
518     // Argument 1: Previous index.
519     MachineType arg1_type = type_int32;
520     TNode<Int32T> arg1 = TruncateIntPtrToInt32(int_last_index);
521 
522     // Argument 2: Start of string data. This argument is ignored in the
523     // interpreter.
524     MachineType arg2_type = type_ptr;
525     TNode<RawPtrT> arg2 = var_string_start.value();
526 
527     // Argument 3: End of string data. This argument is ignored in the
528     // interpreter.
529     MachineType arg3_type = type_ptr;
530     TNode<RawPtrT> arg3 = var_string_end.value();
531 
532     // Argument 4: static offsets vector buffer.
533     MachineType arg4_type = type_ptr;
534     TNode<ExternalReference> arg4 = static_offsets_vector_address;
535 
536     // Argument 5: Number of capture registers.
537     // Setting this to the number of registers required to store all captures
538     // forces global regexps to behave as non-global.
539     TNode<Smi> capture_count = CAST(UnsafeLoadFixedArrayElement(
540         data, JSRegExp::kIrregexpCaptureCountIndex));
541     // capture_count is the number of captures without the match itself.
542     // Required registers = (capture_count + 1) * 2.
543     STATIC_ASSERT(Internals::IsValidSmi((JSRegExp::kMaxCaptures + 1) * 2));
544     TNode<Smi> register_count =
545         SmiShl(SmiAdd(capture_count, SmiConstant(1)), 1);
546 
547     MachineType arg5_type = type_int32;
548     TNode<Int32T> arg5 = SmiToInt32(register_count);
549 
550     // Argument 6: Start (high end) of backtracking stack memory area. This
551     // argument is ignored in the interpreter.
552     TNode<RawPtrT> stack_top = UncheckedCast<RawPtrT>(
553         Load(MachineType::Pointer(), regexp_stack_memory_top_address));
554 
555     MachineType arg6_type = type_ptr;
556     TNode<RawPtrT> arg6 = stack_top;
557 
558     // Argument 7: Indicate that this is a direct call from JavaScript.
559     MachineType arg7_type = type_int32;
560     TNode<Int32T> arg7 = Int32Constant(RegExp::CallOrigin::kFromJs);
561 
562     // Argument 8: Pass current isolate address.
563     MachineType arg8_type = type_ptr;
564     TNode<ExternalReference> arg8 = isolate_address;
565 
566     // Argument 9: Regular expression object. This argument is ignored in native
567     // irregexp code.
568     MachineType arg9_type = type_tagged;
569     TNode<JSRegExp> arg9 = regexp;
570 
571     TNode<RawPtrT> code_entry = LoadCodeObjectEntry(code);
572 
573     // AIX uses function descriptors on CFunction calls. code_entry in this case
574     // may also point to a Regex interpreter entry trampoline which does not
575     // have a function descriptor. This method is ineffective on other platforms
576     // and is equivalent to CallCFunction.
577     TNode<Int32T> result =
578         UncheckedCast<Int32T>(CallCFunctionWithoutFunctionDescriptor(
579             code_entry, retval_type, std::make_pair(arg0_type, arg0),
580             std::make_pair(arg1_type, arg1), std::make_pair(arg2_type, arg2),
581             std::make_pair(arg3_type, arg3), std::make_pair(arg4_type, arg4),
582             std::make_pair(arg5_type, arg5), std::make_pair(arg6_type, arg6),
583             std::make_pair(arg7_type, arg7), std::make_pair(arg8_type, arg8),
584             std::make_pair(arg9_type, arg9)));
585 
586     // Check the result.
587     // We expect exactly one result since we force the called regexp to behave
588     // as non-global.
589     TNode<IntPtrT> int_result = ChangeInt32ToIntPtr(result);
590     GotoIf(
591         IntPtrEqual(int_result, IntPtrConstant(RegExp::kInternalRegExpSuccess)),
592         &if_success);
593     GotoIf(
594         IntPtrEqual(int_result, IntPtrConstant(RegExp::kInternalRegExpFailure)),
595         &if_failure);
596     GotoIf(IntPtrEqual(int_result,
597                        IntPtrConstant(RegExp::kInternalRegExpException)),
598            &if_exception);
599     GotoIf(IntPtrEqual(
600                int_result,
601                IntPtrConstant(RegExp::kInternalRegExpFallbackToExperimental)),
602            &retry_experimental);
603 
604     CSA_ASSERT(this, IntPtrEqual(int_result,
605                                  IntPtrConstant(RegExp::kInternalRegExpRetry)));
606     Goto(&runtime);
607   }
608 
609   BIND(&if_success);
610   {
611     // Check that the last match info has space for the capture registers and
612     // the additional information. Ensure no overflow in add.
613     STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset);
614     TNode<Smi> available_slots =
615         SmiSub(LoadFixedArrayBaseLength(match_info),
616                SmiConstant(RegExpMatchInfo::kLastMatchOverhead));
617     TNode<Smi> capture_count = CAST(UnsafeLoadFixedArrayElement(
618         data, JSRegExp::kIrregexpCaptureCountIndex));
619     // Calculate number of register_count = (capture_count + 1) * 2.
620     TNode<Smi> register_count =
621         SmiShl(SmiAdd(capture_count, SmiConstant(1)), 1);
622     GotoIf(SmiGreaterThan(register_count, available_slots), &runtime);
623 
624     // Fill match_info.
625     UnsafeStoreFixedArrayElement(
626         match_info, RegExpMatchInfo::kNumberOfCapturesIndex, register_count);
627     UnsafeStoreFixedArrayElement(match_info, RegExpMatchInfo::kLastSubjectIndex,
628                                  string);
629     UnsafeStoreFixedArrayElement(match_info, RegExpMatchInfo::kLastInputIndex,
630                                  string);
631 
632     // Fill match and capture offsets in match_info.
633     {
634       TNode<IntPtrT> limit_offset =
635           ElementOffsetFromIndex(register_count, INT32_ELEMENTS, 0);
636 
637       TNode<IntPtrT> to_offset = ElementOffsetFromIndex(
638           IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), PACKED_ELEMENTS,
639           RegExpMatchInfo::kHeaderSize - kHeapObjectTag);
640       TVARIABLE(IntPtrT, var_to_offset, to_offset);
641 
642       VariableList vars({&var_to_offset}, zone());
643       BuildFastLoop<IntPtrT>(
644           vars, IntPtrZero(), limit_offset,
645           [&](TNode<IntPtrT> offset) {
646             TNode<Int32T> value = UncheckedCast<Int32T>(Load(
647                 MachineType::Int32(), static_offsets_vector_address, offset));
648             TNode<Smi> smi_value = SmiFromInt32(value);
649             StoreNoWriteBarrier(MachineRepresentation::kTagged, match_info,
650                                 var_to_offset.value(), smi_value);
651             Increment(&var_to_offset, kTaggedSize);
652           },
653           kInt32Size, IndexAdvanceMode::kPost);
654     }
655 
656     var_result = match_info;
657     Goto(&out);
658   }
659 
660   BIND(&if_failure);
661   {
662     var_result = NullConstant();
663     Goto(&out);
664   }
665 
666   BIND(&if_exception);
667   {
668 // A stack overflow was detected in RegExp code.
669 #ifdef DEBUG
670     TNode<ExternalReference> pending_exception_address =
671         ExternalConstant(ExternalReference::Create(
672             IsolateAddressId::kPendingExceptionAddress, isolate()));
673     CSA_ASSERT(this, IsTheHole(Load(MachineType::AnyTagged(),
674                                     pending_exception_address)));
675 #endif  // DEBUG
676     CallRuntime(Runtime::kThrowStackOverflow, context);
677     Unreachable();
678   }
679 
680   BIND(&retry_experimental);
681   {
682     var_result =
683         CAST(CallRuntime(Runtime::kRegExpExperimentalOneshotExec, context,
684                          regexp, string, last_index, match_info));
685     Goto(&out);
686   }
687 
688   BIND(&runtime);
689   {
690     var_result = CAST(CallRuntime(Runtime::kRegExpExec, context, regexp, string,
691                                   last_index, match_info));
692     Goto(&out);
693   }
694 
695   BIND(&atom);
696   {
697     // TODO(jgruber): A call with 4 args stresses register allocation, this
698     // should probably just be inlined.
699     var_result = CAST(CallBuiltin(Builtins::kRegExpExecAtom, context, regexp,
700                                   string, last_index, match_info));
701     Goto(&out);
702   }
703 
704   BIND(&out);
705   return var_result.value();
706 }
707 
IsFastRegExpNoPrototype(TNode<Context> context,TNode<Object> object,TNode<Map> map)708 TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(
709     TNode<Context> context, TNode<Object> object, TNode<Map> map) {
710   Label out(this);
711   TVARIABLE(BoolT, var_result);
712 
713 #ifdef V8_ENABLE_FORCE_SLOW_PATH
714   var_result = Int32FalseConstant();
715   GotoIfForceSlowPath(&out);
716 #endif
717 
718   const TNode<NativeContext> native_context = LoadNativeContext(context);
719   const TNode<HeapObject> regexp_fun =
720       CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
721   const TNode<Object> initial_map =
722       LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
723   const TNode<BoolT> has_initialmap = TaggedEqual(map, initial_map);
724 
725   var_result = has_initialmap;
726   GotoIfNot(has_initialmap, &out);
727 
728   // The smi check is required to omit ToLength(lastIndex) calls with possible
729   // user-code execution on the fast path.
730   TNode<Object> last_index = FastLoadLastIndexBeforeSmiCheck(CAST(object));
731   var_result = TaggedIsPositiveSmi(last_index);
732   Goto(&out);
733 
734   BIND(&out);
735   return var_result.value();
736 }
737 
IsFastRegExpNoPrototype(TNode<Context> context,TNode<Object> object)738 TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(
739     TNode<Context> context, TNode<Object> object) {
740   CSA_ASSERT(this, TaggedIsNotSmi(object));
741   return IsFastRegExpNoPrototype(context, object, LoadMap(CAST(object)));
742 }
743 
BranchIfFastRegExp(TNode<Context> context,TNode<HeapObject> object,TNode<Map> map,PrototypeCheckAssembler::Flags prototype_check_flags,base::Optional<DescriptorIndexNameValue> additional_property_to_check,Label * if_isunmodified,Label * if_ismodified)744 void RegExpBuiltinsAssembler::BranchIfFastRegExp(
745     TNode<Context> context, TNode<HeapObject> object, TNode<Map> map,
746     PrototypeCheckAssembler::Flags prototype_check_flags,
747     base::Optional<DescriptorIndexNameValue> additional_property_to_check,
748     Label* if_isunmodified, Label* if_ismodified) {
749   CSA_ASSERT(this, TaggedEqual(LoadMap(object), map));
750 
751   GotoIfForceSlowPath(if_ismodified);
752 
753   // This should only be needed for String.p.(split||matchAll), but we are
754   // conservative here.
755   GotoIf(IsRegExpSpeciesProtectorCellInvalid(), if_ismodified);
756 
757   TNode<NativeContext> native_context = LoadNativeContext(context);
758   TNode<JSFunction> regexp_fun =
759       CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
760   TNode<Map> initial_map = CAST(
761       LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset));
762   TNode<BoolT> has_initialmap = TaggedEqual(map, initial_map);
763 
764   GotoIfNot(has_initialmap, if_ismodified);
765 
766   // The smi check is required to omit ToLength(lastIndex) calls with possible
767   // user-code execution on the fast path.
768   TNode<Object> last_index = FastLoadLastIndexBeforeSmiCheck(CAST(object));
769   GotoIfNot(TaggedIsPositiveSmi(last_index), if_ismodified);
770 
771   // Verify the prototype.
772 
773   TNode<Map> initial_proto_initial_map = CAST(
774       LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX));
775 
776   DescriptorIndexNameValue properties_to_check[2];
777   int property_count = 0;
778   properties_to_check[property_count++] = DescriptorIndexNameValue{
779       JSRegExp::kExecFunctionDescriptorIndex, RootIndex::kexec_string,
780       Context::REGEXP_EXEC_FUNCTION_INDEX};
781   if (additional_property_to_check) {
782     properties_to_check[property_count++] = *additional_property_to_check;
783   }
784 
785   PrototypeCheckAssembler prototype_check_assembler(
786       state(), prototype_check_flags, native_context, initial_proto_initial_map,
787       Vector<DescriptorIndexNameValue>(properties_to_check, property_count));
788 
789   TNode<HeapObject> prototype = LoadMapPrototype(map);
790   prototype_check_assembler.CheckAndBranch(prototype, if_isunmodified,
791                                            if_ismodified);
792 }
793 
BranchIfFastRegExp_Strict(TNode<Context> context,TNode<HeapObject> object,Label * if_isunmodified,Label * if_ismodified)794 void RegExpBuiltinsAssembler::BranchIfFastRegExp_Strict(
795     TNode<Context> context, TNode<HeapObject> object, Label* if_isunmodified,
796     Label* if_ismodified) {
797   BranchIfFastRegExp(context, object, LoadMap(object),
798                      PrototypeCheckAssembler::kCheckPrototypePropertyConstness,
799                      base::nullopt, if_isunmodified, if_ismodified);
800 }
801 
BranchIfFastRegExp_Permissive(TNode<Context> context,TNode<HeapObject> object,Label * if_isunmodified,Label * if_ismodified)802 void RegExpBuiltinsAssembler::BranchIfFastRegExp_Permissive(
803     TNode<Context> context, TNode<HeapObject> object, Label* if_isunmodified,
804     Label* if_ismodified) {
805   BranchIfFastRegExp(context, object, LoadMap(object),
806                      PrototypeCheckAssembler::kCheckFull, base::nullopt,
807                      if_isunmodified, if_ismodified);
808 }
809 
BranchIfRegExpResult(const TNode<Context> context,const TNode<Object> object,Label * if_isunmodified,Label * if_ismodified)810 void RegExpBuiltinsAssembler::BranchIfRegExpResult(const TNode<Context> context,
811                                                    const TNode<Object> object,
812                                                    Label* if_isunmodified,
813                                                    Label* if_ismodified) {
814   // Could be a Smi.
815   const TNode<Map> map = LoadReceiverMap(object);
816 
817   const TNode<NativeContext> native_context = LoadNativeContext(context);
818   const TNode<Object> initial_regexp_result_map =
819       LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX);
820 
821   Branch(TaggedEqual(map, initial_regexp_result_map), if_isunmodified,
822          if_ismodified);
823 }
824 
825 // Fast path stub for ATOM regexps. String matching is done by StringIndexOf,
826 // and {match_info} is updated on success.
827 // The slow path is implemented in RegExp::AtomExec.
TF_BUILTIN(RegExpExecAtom,RegExpBuiltinsAssembler)828 TF_BUILTIN(RegExpExecAtom, RegExpBuiltinsAssembler) {
829   auto regexp = Parameter<JSRegExp>(Descriptor::kRegExp);
830   auto subject_string = Parameter<String>(Descriptor::kString);
831   auto last_index = Parameter<Smi>(Descriptor::kLastIndex);
832   auto match_info = Parameter<FixedArray>(Descriptor::kMatchInfo);
833   auto context = Parameter<Context>(Descriptor::kContext);
834 
835   CSA_ASSERT(this, TaggedIsPositiveSmi(last_index));
836 
837   TNode<FixedArray> data = CAST(LoadObjectField(regexp, JSRegExp::kDataOffset));
838   CSA_ASSERT(
839       this,
840       SmiEqual(CAST(UnsafeLoadFixedArrayElement(data, JSRegExp::kTagIndex)),
841                SmiConstant(JSRegExp::ATOM)));
842 
843   // Callers ensure that last_index is in-bounds.
844   CSA_ASSERT(this,
845              UintPtrLessThanOrEqual(SmiUntag(last_index),
846                                     LoadStringLengthAsWord(subject_string)));
847 
848   const TNode<String> needle_string =
849       CAST(UnsafeLoadFixedArrayElement(data, JSRegExp::kAtomPatternIndex));
850 
851   const TNode<Smi> match_from =
852       CAST(CallBuiltin(Builtins::kStringIndexOf, context, subject_string,
853                        needle_string, last_index));
854 
855   Label if_failure(this), if_success(this);
856   Branch(SmiEqual(match_from, SmiConstant(-1)), &if_failure, &if_success);
857 
858   BIND(&if_success);
859   {
860     CSA_ASSERT(this, TaggedIsPositiveSmi(match_from));
861     CSA_ASSERT(this, UintPtrLessThan(SmiUntag(match_from),
862                                      LoadStringLengthAsWord(subject_string)));
863 
864     const int kNumRegisters = 2;
865     STATIC_ASSERT(RegExpMatchInfo::kInitialCaptureIndices >= kNumRegisters);
866 
867     const TNode<Smi> match_to =
868         SmiAdd(match_from, LoadStringLengthAsSmi(needle_string));
869 
870     UnsafeStoreFixedArrayElement(match_info,
871                                  RegExpMatchInfo::kNumberOfCapturesIndex,
872                                  SmiConstant(kNumRegisters));
873     UnsafeStoreFixedArrayElement(match_info, RegExpMatchInfo::kLastSubjectIndex,
874                                  subject_string);
875     UnsafeStoreFixedArrayElement(match_info, RegExpMatchInfo::kLastInputIndex,
876                                  subject_string);
877     UnsafeStoreFixedArrayElement(
878         match_info, RegExpMatchInfo::kFirstCaptureIndex, match_from);
879     UnsafeStoreFixedArrayElement(
880         match_info, RegExpMatchInfo::kFirstCaptureIndex + 1, match_to);
881 
882     Return(match_info);
883   }
884 
885   BIND(&if_failure);
886   Return(NullConstant());
887 }
888 
TF_BUILTIN(RegExpExecInternal,RegExpBuiltinsAssembler)889 TF_BUILTIN(RegExpExecInternal, RegExpBuiltinsAssembler) {
890   auto regexp = Parameter<JSRegExp>(Descriptor::kRegExp);
891   auto string = Parameter<String>(Descriptor::kString);
892   auto last_index = Parameter<Number>(Descriptor::kLastIndex);
893   auto match_info = Parameter<RegExpMatchInfo>(Descriptor::kMatchInfo);
894   auto context = Parameter<Context>(Descriptor::kContext);
895 
896   CSA_ASSERT(this, IsNumberNormalized(last_index));
897   CSA_ASSERT(this, IsNumberPositive(last_index));
898 
899   Return(RegExpExecInternal(context, regexp, string, last_index, match_info));
900 }
901 
FlagsGetter(TNode<Context> context,TNode<Object> regexp,bool is_fastpath)902 TNode<String> RegExpBuiltinsAssembler::FlagsGetter(TNode<Context> context,
903                                                    TNode<Object> regexp,
904                                                    bool is_fastpath) {
905   Isolate* isolate = this->isolate();
906 
907   const TNode<IntPtrT> int_one = IntPtrConstant(1);
908   TVARIABLE(Uint32T, var_length, Uint32Constant(0));
909   TVARIABLE(IntPtrT, var_flags);
910 
911   // First, count the number of characters we will need and check which flags
912   // are set.
913 
914   if (is_fastpath) {
915     // Refer to JSRegExp's flag property on the fast-path.
916     CSA_ASSERT(this, IsJSRegExp(CAST(regexp)));
917     const TNode<Smi> flags_smi =
918         CAST(LoadObjectField(CAST(regexp), JSRegExp::kFlagsOffset));
919     var_flags = SmiUntag(flags_smi);
920 
921 #define CASE_FOR_FLAG(FLAG)                                        \
922   do {                                                             \
923     Label next(this);                                              \
924     GotoIfNot(IsSetWord(var_flags.value(), FLAG), &next);          \
925     var_length = Uint32Add(var_length.value(), Uint32Constant(1)); \
926     Goto(&next);                                                   \
927     BIND(&next);                                                   \
928   } while (false)
929 
930     CASE_FOR_FLAG(JSRegExp::kGlobal);
931     CASE_FOR_FLAG(JSRegExp::kIgnoreCase);
932     CASE_FOR_FLAG(JSRegExp::kLinear);
933     CASE_FOR_FLAG(JSRegExp::kMultiline);
934     CASE_FOR_FLAG(JSRegExp::kDotAll);
935     CASE_FOR_FLAG(JSRegExp::kUnicode);
936     CASE_FOR_FLAG(JSRegExp::kSticky);
937 #undef CASE_FOR_FLAG
938   } else {
939     DCHECK(!is_fastpath);
940 
941     // Fall back to GetProperty stub on the slow-path.
942     var_flags = IntPtrZero();
943 
944 #define CASE_FOR_FLAG(NAME, FLAG)                                          \
945   do {                                                                     \
946     Label next(this);                                                      \
947     const TNode<Object> flag = GetProperty(                                \
948         context, regexp, isolate->factory()->InternalizeUtf8String(NAME)); \
949     Label if_isflagset(this);                                              \
950     BranchIfToBooleanIsTrue(flag, &if_isflagset, &next);                   \
951     BIND(&if_isflagset);                                                   \
952     var_length = Uint32Add(var_length.value(), Uint32Constant(1));         \
953     var_flags = Signed(WordOr(var_flags.value(), IntPtrConstant(FLAG)));   \
954     Goto(&next);                                                           \
955     BIND(&next);                                                           \
956   } while (false)
957 
958     CASE_FOR_FLAG("global", JSRegExp::kGlobal);
959     CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase);
960     CASE_FOR_FLAG("multiline", JSRegExp::kMultiline);
961     CASE_FOR_FLAG("dotAll", JSRegExp::kDotAll);
962     CASE_FOR_FLAG("unicode", JSRegExp::kUnicode);
963     CASE_FOR_FLAG("sticky", JSRegExp::kSticky);
964 #undef CASE_FOR_FLAG
965 
966     {
967       Label next(this);
968 
969       // Check the runtime value of FLAG_enable_experimental_regexp_engine
970       // first.
971       TNode<Word32T> flag_value = UncheckedCast<Word32T>(
972           Load(MachineType::Uint8(),
973                ExternalConstant(
974                    ExternalReference::
975                        address_of_enable_experimental_regexp_engine())));
976       GotoIf(Word32Equal(Word32And(flag_value, Int32Constant(0xFF)),
977                          Int32Constant(0)),
978              &next);
979 
980       const TNode<Object> flag = GetProperty(
981           context, regexp, isolate->factory()->InternalizeUtf8String("linear"));
982       Label if_isflagset(this);
983       BranchIfToBooleanIsTrue(flag, &if_isflagset, &next);
984       BIND(&if_isflagset);
985       var_length = Uint32Add(var_length.value(), Uint32Constant(1));
986       var_flags =
987           Signed(WordOr(var_flags.value(), IntPtrConstant(JSRegExp::kLinear)));
988       Goto(&next);
989       BIND(&next);
990     }
991   }
992 
993   // Allocate a string of the required length and fill it with the corresponding
994   // char for each set flag.
995 
996   {
997     const TNode<String> result = AllocateSeqOneByteString(var_length.value());
998 
999     TVARIABLE(IntPtrT, var_offset,
1000               IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
1001 
1002 #define CASE_FOR_FLAG(FLAG, CHAR)                              \
1003   do {                                                         \
1004     Label next(this);                                          \
1005     GotoIfNot(IsSetWord(var_flags.value(), FLAG), &next);      \
1006     const TNode<Int32T> value = Int32Constant(CHAR);           \
1007     StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \
1008                         var_offset.value(), value);            \
1009     var_offset = IntPtrAdd(var_offset.value(), int_one);       \
1010     Goto(&next);                                               \
1011     BIND(&next);                                               \
1012   } while (false)
1013 
1014     CASE_FOR_FLAG(JSRegExp::kGlobal, 'g');
1015     CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i');
1016     CASE_FOR_FLAG(JSRegExp::kLinear, 'l');
1017     CASE_FOR_FLAG(JSRegExp::kMultiline, 'm');
1018     CASE_FOR_FLAG(JSRegExp::kDotAll, 's');
1019     CASE_FOR_FLAG(JSRegExp::kUnicode, 'u');
1020     CASE_FOR_FLAG(JSRegExp::kSticky, 'y');
1021 #undef CASE_FOR_FLAG
1022 
1023     return result;
1024   }
1025 }
1026 
1027 // ES#sec-regexpinitialize
1028 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
RegExpInitialize(const TNode<Context> context,const TNode<JSRegExp> regexp,const TNode<Object> maybe_pattern,const TNode<Object> maybe_flags)1029 TNode<Object> RegExpBuiltinsAssembler::RegExpInitialize(
1030     const TNode<Context> context, const TNode<JSRegExp> regexp,
1031     const TNode<Object> maybe_pattern, const TNode<Object> maybe_flags) {
1032   // Normalize pattern.
1033   const TNode<Object> pattern = Select<Object>(
1034       IsUndefined(maybe_pattern), [=] { return EmptyStringConstant(); },
1035       [=] { return ToString_Inline(context, maybe_pattern); });
1036 
1037   // Normalize flags.
1038   const TNode<Object> flags = Select<Object>(
1039       IsUndefined(maybe_flags), [=] { return EmptyStringConstant(); },
1040       [=] { return ToString_Inline(context, maybe_flags); });
1041 
1042   // Initialize.
1043 
1044   return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp,
1045                      pattern, flags);
1046 }
1047 
1048 // ES#sec-regexp-pattern-flags
1049 // RegExp ( pattern, flags )
TF_BUILTIN(RegExpConstructor,RegExpBuiltinsAssembler)1050 TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) {
1051   auto pattern = Parameter<Object>(Descriptor::kPattern);
1052   auto flags = Parameter<Object>(Descriptor::kFlags);
1053   auto new_target = Parameter<Object>(Descriptor::kJSNewTarget);
1054   auto context = Parameter<Context>(Descriptor::kContext);
1055 
1056   Isolate* isolate = this->isolate();
1057 
1058   TVARIABLE(Object, var_flags, flags);
1059   TVARIABLE(Object, var_pattern, pattern);
1060   TVARIABLE(Object, var_new_target, new_target);
1061 
1062   TNode<NativeContext> native_context = LoadNativeContext(context);
1063   TNode<JSFunction> regexp_function =
1064       CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
1065 
1066   TNode<BoolT> pattern_is_regexp = IsRegExp(context, pattern);
1067 
1068   {
1069     Label next(this);
1070 
1071     GotoIfNot(IsUndefined(new_target), &next);
1072     var_new_target = regexp_function;
1073 
1074     GotoIfNot(pattern_is_regexp, &next);
1075     GotoIfNot(IsUndefined(flags), &next);
1076 
1077     TNode<Object> value =
1078         GetProperty(context, pattern, isolate->factory()->constructor_string());
1079 
1080     GotoIfNot(TaggedEqual(value, regexp_function), &next);
1081     Return(pattern);
1082 
1083     BIND(&next);
1084   }
1085 
1086   {
1087     Label next(this), if_patternisfastregexp(this),
1088         if_patternisslowregexp(this);
1089     GotoIf(TaggedIsSmi(pattern), &next);
1090 
1091     GotoIf(IsJSRegExp(CAST(pattern)), &if_patternisfastregexp);
1092 
1093     Branch(pattern_is_regexp, &if_patternisslowregexp, &next);
1094 
1095     BIND(&if_patternisfastregexp);
1096     {
1097       TNode<Object> source =
1098           LoadObjectField(CAST(pattern), JSRegExp::kSourceOffset);
1099       var_pattern = source;
1100 
1101       {
1102         Label inner_next(this);
1103         GotoIfNot(IsUndefined(flags), &inner_next);
1104 
1105         var_flags = FlagsGetter(context, pattern, true);
1106         Goto(&inner_next);
1107 
1108         BIND(&inner_next);
1109       }
1110 
1111       Goto(&next);
1112     }
1113 
1114     BIND(&if_patternisslowregexp);
1115     {
1116       var_pattern =
1117           GetProperty(context, pattern, isolate->factory()->source_string());
1118 
1119       {
1120         Label inner_next(this);
1121         GotoIfNot(IsUndefined(flags), &inner_next);
1122 
1123         var_flags =
1124             GetProperty(context, pattern, isolate->factory()->flags_string());
1125         Goto(&inner_next);
1126 
1127         BIND(&inner_next);
1128       }
1129 
1130       Goto(&next);
1131     }
1132 
1133     BIND(&next);
1134   }
1135 
1136   // Allocate.
1137 
1138   TVARIABLE(JSRegExp, var_regexp);
1139   {
1140     Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred),
1141         next(this);
1142     Branch(TaggedEqual(var_new_target.value(), regexp_function),
1143            &allocate_jsregexp, &allocate_generic);
1144 
1145     BIND(&allocate_jsregexp);
1146     {
1147       const TNode<Map> initial_map = CAST(LoadObjectField(
1148           regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
1149       var_regexp = CAST(AllocateJSObjectFromMap(initial_map));
1150       Goto(&next);
1151     }
1152 
1153     BIND(&allocate_generic);
1154     {
1155       ConstructorBuiltinsAssembler constructor_assembler(this->state());
1156       var_regexp = CAST(constructor_assembler.FastNewObject(
1157           context, regexp_function, CAST(var_new_target.value())));
1158       Goto(&next);
1159     }
1160 
1161     BIND(&next);
1162   }
1163 
1164   const TNode<Object> result = RegExpInitialize(
1165       context, var_regexp.value(), var_pattern.value(), var_flags.value());
1166   Return(result);
1167 }
1168 
1169 // ES#sec-regexp.prototype.compile
1170 // RegExp.prototype.compile ( pattern, flags )
TF_BUILTIN(RegExpPrototypeCompile,RegExpBuiltinsAssembler)1171 TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) {
1172   auto maybe_receiver = Parameter<Object>(Descriptor::kReceiver);
1173   auto maybe_pattern = Parameter<Object>(Descriptor::kPattern);
1174   auto maybe_flags = Parameter<Object>(Descriptor::kFlags);
1175   auto context = Parameter<Context>(Descriptor::kContext);
1176 
1177   ThrowIfNotInstanceType(context, maybe_receiver, JS_REG_EXP_TYPE,
1178                          "RegExp.prototype.compile");
1179   const TNode<JSRegExp> receiver = CAST(maybe_receiver);
1180 
1181   TVARIABLE(Object, var_flags, maybe_flags);
1182   TVARIABLE(Object, var_pattern, maybe_pattern);
1183 
1184   // Handle a JSRegExp pattern.
1185   {
1186     Label next(this);
1187 
1188     GotoIf(TaggedIsSmi(maybe_pattern), &next);
1189     GotoIfNot(IsJSRegExp(CAST(maybe_pattern)), &next);
1190 
1191     // {maybe_flags} must be undefined in this case, otherwise throw.
1192     {
1193       Label next(this);
1194       GotoIf(IsUndefined(maybe_flags), &next);
1195 
1196       ThrowTypeError(context, MessageTemplate::kRegExpFlags);
1197 
1198       BIND(&next);
1199     }
1200 
1201     const TNode<JSRegExp> pattern = CAST(maybe_pattern);
1202     const TNode<String> new_flags = FlagsGetter(context, pattern, true);
1203     const TNode<Object> new_pattern =
1204         LoadObjectField(pattern, JSRegExp::kSourceOffset);
1205 
1206     var_flags = new_flags;
1207     var_pattern = new_pattern;
1208 
1209     Goto(&next);
1210     BIND(&next);
1211   }
1212 
1213   const TNode<Object> result = RegExpInitialize(
1214       context, receiver, var_pattern.value(), var_flags.value());
1215   Return(result);
1216 }
1217 
1218 // Fast-path implementation for flag checks on an unmodified JSRegExp instance.
FastFlagGetter(TNode<JSRegExp> regexp,JSRegExp::Flag flag)1219 TNode<BoolT> RegExpBuiltinsAssembler::FastFlagGetter(TNode<JSRegExp> regexp,
1220                                                      JSRegExp::Flag flag) {
1221   TNode<Smi> flags = CAST(LoadObjectField(regexp, JSRegExp::kFlagsOffset));
1222   TNode<Smi> mask = SmiConstant(flag);
1223   return ReinterpretCast<BoolT>(SmiToInt32(
1224       SmiShr(SmiAnd(flags, mask),
1225              base::bits::CountTrailingZeros(static_cast<int>(flag)))));
1226 }
1227 
1228 // Load through the GetProperty stub.
SlowFlagGetter(TNode<Context> context,TNode<Object> regexp,JSRegExp::Flag flag)1229 TNode<BoolT> RegExpBuiltinsAssembler::SlowFlagGetter(TNode<Context> context,
1230                                                      TNode<Object> regexp,
1231                                                      JSRegExp::Flag flag) {
1232   Label out(this), if_true(this), if_false(this);
1233   TVARIABLE(BoolT, var_result);
1234 
1235   // Only enabled based on a runtime flag.
1236   if (flag == JSRegExp::kLinear) {
1237     TNode<Word32T> flag_value = UncheckedCast<Word32T>(Load(
1238         MachineType::Uint8(),
1239         ExternalConstant(ExternalReference::
1240                              address_of_enable_experimental_regexp_engine())));
1241     GotoIf(Word32Equal(Word32And(flag_value, Int32Constant(0xFF)),
1242                        Int32Constant(0)),
1243            &if_false);
1244   }
1245 
1246   Handle<String> name;
1247   switch (flag) {
1248     case JSRegExp::kNone:
1249       UNREACHABLE();
1250     case JSRegExp::kGlobal:
1251       name = isolate()->factory()->global_string();
1252       break;
1253     case JSRegExp::kIgnoreCase:
1254       name = isolate()->factory()->ignoreCase_string();
1255       break;
1256     case JSRegExp::kMultiline:
1257       name = isolate()->factory()->multiline_string();
1258       break;
1259     case JSRegExp::kDotAll:
1260       UNREACHABLE();  // Never called for dotAll.
1261       break;
1262     case JSRegExp::kSticky:
1263       name = isolate()->factory()->sticky_string();
1264       break;
1265     case JSRegExp::kUnicode:
1266       name = isolate()->factory()->unicode_string();
1267       break;
1268     case JSRegExp::kLinear:
1269       name = isolate()->factory()->linear_string();
1270       break;
1271   }
1272 
1273   TNode<Object> value = GetProperty(context, regexp, name);
1274   BranchIfToBooleanIsTrue(value, &if_true, &if_false);
1275 
1276   BIND(&if_true);
1277   var_result = BoolConstant(true);
1278   Goto(&out);
1279 
1280   BIND(&if_false);
1281   var_result = BoolConstant(false);
1282   Goto(&out);
1283 
1284   BIND(&out);
1285   return var_result.value();
1286 }
1287 
FlagGetter(TNode<Context> context,TNode<Object> regexp,JSRegExp::Flag flag,bool is_fastpath)1288 TNode<BoolT> RegExpBuiltinsAssembler::FlagGetter(TNode<Context> context,
1289                                                  TNode<Object> regexp,
1290                                                  JSRegExp::Flag flag,
1291                                                  bool is_fastpath) {
1292   return is_fastpath ? FastFlagGetter(CAST(regexp), flag)
1293                      : SlowFlagGetter(context, regexp, flag);
1294 }
1295 
AdvanceStringIndex(TNode<String> string,TNode<Number> index,TNode<BoolT> is_unicode,bool is_fastpath)1296 TNode<Number> RegExpBuiltinsAssembler::AdvanceStringIndex(
1297     TNode<String> string, TNode<Number> index, TNode<BoolT> is_unicode,
1298     bool is_fastpath) {
1299   CSA_ASSERT(this, IsNumberNormalized(index));
1300   if (is_fastpath) CSA_ASSERT(this, TaggedIsPositiveSmi(index));
1301 
1302   // Default to last_index + 1.
1303   // TODO(pwong): Consider using TrySmiAdd for the fast path to reduce generated
1304   // code.
1305   TNode<Number> index_plus_one = NumberInc(index);
1306   TVARIABLE(Number, var_result, index_plus_one);
1307 
1308   // TODO(v8:9880): Given that we have to convert index from Number to UintPtrT
1309   // anyway, consider using UintPtrT index to simplify the code below.
1310 
1311   // Advancing the index has some subtle issues involving the distinction
1312   // between Smis and HeapNumbers. There's three cases:
1313   // * {index} is a Smi, {index_plus_one} is a Smi. The standard case.
1314   // * {index} is a Smi, {index_plus_one} overflows into a HeapNumber.
1315   //   In this case we can return the result early, because
1316   //   {index_plus_one} > {string}.length.
1317   // * {index} is a HeapNumber, {index_plus_one} is a HeapNumber. This can only
1318   //   occur when {index} is outside the Smi range since we normalize
1319   //   explicitly. Again we can return early.
1320   if (is_fastpath) {
1321     // Must be in Smi range on the fast path. We control the value of {index}
1322     // on all call-sites and can never exceed the length of the string.
1323     STATIC_ASSERT(String::kMaxLength + 2 < Smi::kMaxValue);
1324     CSA_ASSERT(this, TaggedIsPositiveSmi(index_plus_one));
1325   }
1326 
1327   Label if_isunicode(this), out(this);
1328   GotoIfNot(is_unicode, &out);
1329 
1330   // Keep this unconditional (even on the fast path) just to be safe.
1331   Branch(TaggedIsPositiveSmi(index_plus_one), &if_isunicode, &out);
1332 
1333   BIND(&if_isunicode);
1334   {
1335     TNode<UintPtrT> string_length = Unsigned(LoadStringLengthAsWord(string));
1336     TNode<UintPtrT> untagged_plus_one =
1337         Unsigned(SmiUntag(CAST(index_plus_one)));
1338     GotoIfNot(UintPtrLessThan(untagged_plus_one, string_length), &out);
1339 
1340     TNode<Int32T> lead =
1341         StringCharCodeAt(string, Unsigned(SmiUntag(CAST(index))));
1342     GotoIfNot(Word32Equal(Word32And(lead, Int32Constant(0xFC00)),
1343                           Int32Constant(0xD800)),
1344               &out);
1345 
1346     TNode<Int32T> trail = StringCharCodeAt(string, untagged_plus_one);
1347     GotoIfNot(Word32Equal(Word32And(trail, Int32Constant(0xFC00)),
1348                           Int32Constant(0xDC00)),
1349               &out);
1350 
1351     // At a surrogate pair, return index + 2.
1352     TNode<Number> index_plus_two = NumberInc(index_plus_one);
1353     var_result = index_plus_two;
1354 
1355     Goto(&out);
1356   }
1357 
1358   BIND(&out);
1359   return var_result.value();
1360 }
1361 
1362 // ES#sec-createregexpstringiterator
1363 // CreateRegExpStringIterator ( R, S, global, fullUnicode )
CreateRegExpStringIterator(TNode<NativeContext> native_context,TNode<Object> regexp,TNode<String> string,TNode<BoolT> global,TNode<BoolT> full_unicode)1364 TNode<Object> RegExpMatchAllAssembler::CreateRegExpStringIterator(
1365     TNode<NativeContext> native_context, TNode<Object> regexp,
1366     TNode<String> string, TNode<BoolT> global, TNode<BoolT> full_unicode) {
1367   TNode<Map> map = CAST(LoadContextElement(
1368       native_context,
1369       Context::INITIAL_REGEXP_STRING_ITERATOR_PROTOTYPE_MAP_INDEX));
1370 
1371   // 4. Let iterator be ObjectCreate(%RegExpStringIteratorPrototype%, «
1372   // [[IteratingRegExp]], [[IteratedString]], [[Global]], [[Unicode]],
1373   // [[Done]] »).
1374   TNode<HeapObject> iterator = Allocate(JSRegExpStringIterator::kHeaderSize);
1375   StoreMapNoWriteBarrier(iterator, map);
1376   StoreObjectFieldRoot(iterator,
1377                        JSRegExpStringIterator::kPropertiesOrHashOffset,
1378                        RootIndex::kEmptyFixedArray);
1379   StoreObjectFieldRoot(iterator, JSRegExpStringIterator::kElementsOffset,
1380                        RootIndex::kEmptyFixedArray);
1381 
1382   // 5. Set iterator.[[IteratingRegExp]] to R.
1383   StoreObjectFieldNoWriteBarrier(
1384       iterator, JSRegExpStringIterator::kIteratingRegExpOffset, regexp);
1385 
1386   // 6. Set iterator.[[IteratedString]] to S.
1387   StoreObjectFieldNoWriteBarrier(
1388       iterator, JSRegExpStringIterator::kIteratedStringOffset, string);
1389 
1390   // 7. Set iterator.[[Global]] to global.
1391   // 8. Set iterator.[[Unicode]] to fullUnicode.
1392   // 9. Set iterator.[[Done]] to false.
1393   TNode<Int32T> global_flag =
1394       Word32Shl(ReinterpretCast<Int32T>(global),
1395                 Int32Constant(JSRegExpStringIterator::GlobalBit::kShift));
1396   TNode<Int32T> unicode_flag =
1397       Word32Shl(ReinterpretCast<Int32T>(full_unicode),
1398                 Int32Constant(JSRegExpStringIterator::UnicodeBit::kShift));
1399   TNode<Int32T> iterator_flags = Word32Or(global_flag, unicode_flag);
1400   StoreObjectFieldNoWriteBarrier(iterator, JSRegExpStringIterator::kFlagsOffset,
1401                                  SmiFromInt32(iterator_flags));
1402 
1403   return iterator;
1404 }
1405 
1406 // Generates the fast path for @@split. {regexp} is an unmodified, non-sticky
1407 // JSRegExp, {string} is a String, and {limit} is a Smi.
RegExpPrototypeSplitBody(TNode<Context> context,TNode<JSRegExp> regexp,TNode<String> string,const TNode<Smi> limit)1408 TNode<JSArray> RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(
1409     TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> string,
1410     const TNode<Smi> limit) {
1411   CSA_ASSERT(this, IsFastRegExpPermissive(context, regexp));
1412   CSA_ASSERT(this, Word32BinaryNot(FastFlagGetter(regexp, JSRegExp::kSticky)));
1413 
1414   const TNode<IntPtrT> int_limit = SmiUntag(limit);
1415 
1416   const ElementsKind kind = PACKED_ELEMENTS;
1417 
1418   const TNode<NativeContext> native_context = LoadNativeContext(context);
1419   TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
1420 
1421   Label return_empty_array(this, Label::kDeferred);
1422   TVARIABLE(JSArray, var_result);
1423   Label done(this);
1424 
1425   // If limit is zero, return an empty array.
1426   {
1427     Label next(this), if_limitiszero(this, Label::kDeferred);
1428     Branch(SmiEqual(limit, SmiZero()), &return_empty_array, &next);
1429     BIND(&next);
1430   }
1431 
1432   const TNode<Smi> string_length = LoadStringLengthAsSmi(string);
1433 
1434   // If passed the empty {string}, return either an empty array or a singleton
1435   // array depending on whether the {regexp} matches.
1436   {
1437     Label next(this), if_stringisempty(this, Label::kDeferred);
1438     Branch(SmiEqual(string_length, SmiZero()), &if_stringisempty, &next);
1439 
1440     BIND(&if_stringisempty);
1441     {
1442       const TNode<Object> last_match_info = LoadContextElement(
1443           native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
1444 
1445       const TNode<Object> match_indices =
1446           CallBuiltin(Builtins::kRegExpExecInternal, context, regexp, string,
1447                       SmiZero(), last_match_info);
1448 
1449       Label return_singleton_array(this);
1450       Branch(IsNull(match_indices), &return_singleton_array,
1451              &return_empty_array);
1452 
1453       BIND(&return_singleton_array);
1454       {
1455         TNode<Smi> length = SmiConstant(1);
1456         TNode<IntPtrT> capacity = IntPtrConstant(1);
1457         base::Optional<TNode<AllocationSite>> allocation_site = base::nullopt;
1458         var_result =
1459             AllocateJSArray(kind, array_map, capacity, length, allocation_site);
1460 
1461         TNode<FixedArray> fixed_array = CAST(LoadElements(var_result.value()));
1462         UnsafeStoreFixedArrayElement(fixed_array, 0, string);
1463 
1464         Goto(&done);
1465       }
1466     }
1467 
1468     BIND(&next);
1469   }
1470 
1471   // Loop preparations.
1472 
1473   GrowableFixedArray array(state());
1474 
1475   TVARIABLE(Smi, var_last_matched_until, SmiZero());
1476   TVARIABLE(Smi, var_next_search_from, SmiZero());
1477 
1478   Label loop(this, {array.var_array(), array.var_length(), array.var_capacity(),
1479                     &var_last_matched_until, &var_next_search_from}),
1480       push_suffix_and_out(this), out(this);
1481   Goto(&loop);
1482 
1483   BIND(&loop);
1484   {
1485     const TNode<Smi> next_search_from = var_next_search_from.value();
1486     const TNode<Smi> last_matched_until = var_last_matched_until.value();
1487 
1488     // We're done if we've reached the end of the string.
1489     {
1490       Label next(this);
1491       Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out,
1492              &next);
1493       BIND(&next);
1494     }
1495 
1496     // Search for the given {regexp}.
1497 
1498     const TNode<Object> last_match_info = LoadContextElement(
1499         native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
1500 
1501     const TNode<HeapObject> match_indices_ho =
1502         CAST(CallBuiltin(Builtins::kRegExpExecInternal, context, regexp, string,
1503                          next_search_from, last_match_info));
1504 
1505     // We're done if no match was found.
1506     {
1507       Label next(this);
1508       Branch(IsNull(match_indices_ho), &push_suffix_and_out, &next);
1509       BIND(&next);
1510     }
1511 
1512     TNode<FixedArray> match_indices = CAST(match_indices_ho);
1513     const TNode<Smi> match_from = CAST(UnsafeLoadFixedArrayElement(
1514         match_indices, RegExpMatchInfo::kFirstCaptureIndex));
1515 
1516     // We're done if the match starts beyond the string.
1517     {
1518       Label next(this);
1519       Branch(SmiEqual(match_from, string_length), &push_suffix_and_out, &next);
1520       BIND(&next);
1521     }
1522 
1523     const TNode<Smi> match_to = CAST(UnsafeLoadFixedArrayElement(
1524         match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1));
1525 
1526     // Advance index and continue if the match is empty.
1527     {
1528       Label next(this);
1529 
1530       GotoIfNot(SmiEqual(match_to, next_search_from), &next);
1531       GotoIfNot(SmiEqual(match_to, last_matched_until), &next);
1532 
1533       const TNode<BoolT> is_unicode =
1534           FastFlagGetter(regexp, JSRegExp::kUnicode);
1535       const TNode<Number> new_next_search_from =
1536           AdvanceStringIndex(string, next_search_from, is_unicode, true);
1537       var_next_search_from = CAST(new_next_search_from);
1538       Goto(&loop);
1539 
1540       BIND(&next);
1541     }
1542 
1543     // A valid match was found, add the new substring to the array.
1544     {
1545       const TNode<Smi> from = last_matched_until;
1546       const TNode<Smi> to = match_from;
1547       array.Push(CallBuiltin(Builtins::kSubString, context, string, from, to));
1548       GotoIf(WordEqual(array.length(), int_limit), &out);
1549     }
1550 
1551     // Add all captures to the array.
1552     {
1553       const TNode<Smi> num_registers = CAST(LoadFixedArrayElement(
1554           match_indices, RegExpMatchInfo::kNumberOfCapturesIndex));
1555       const TNode<IntPtrT> int_num_registers = SmiUntag(num_registers);
1556 
1557       TVARIABLE(IntPtrT, var_reg, IntPtrConstant(2));
1558 
1559       Label nested_loop(this, {array.var_array(), array.var_length(),
1560                                array.var_capacity(), &var_reg}),
1561           nested_loop_out(this);
1562       Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop,
1563              &nested_loop_out);
1564 
1565       BIND(&nested_loop);
1566       {
1567         const TNode<IntPtrT> reg = var_reg.value();
1568         const TNode<Object> from = LoadFixedArrayElement(
1569             match_indices, reg,
1570             RegExpMatchInfo::kFirstCaptureIndex * kTaggedSize);
1571         const TNode<Smi> to = CAST(LoadFixedArrayElement(
1572             match_indices, reg,
1573             (RegExpMatchInfo::kFirstCaptureIndex + 1) * kTaggedSize));
1574 
1575         Label select_capture(this), select_undefined(this), store_value(this);
1576         TVARIABLE(Object, var_value);
1577         Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined,
1578                &select_capture);
1579 
1580         BIND(&select_capture);
1581         {
1582           var_value =
1583               CallBuiltin(Builtins::kSubString, context, string, from, to);
1584           Goto(&store_value);
1585         }
1586 
1587         BIND(&select_undefined);
1588         {
1589           var_value = UndefinedConstant();
1590           Goto(&store_value);
1591         }
1592 
1593         BIND(&store_value);
1594         {
1595           array.Push(var_value.value());
1596           GotoIf(WordEqual(array.length(), int_limit), &out);
1597 
1598           const TNode<IntPtrT> new_reg = IntPtrAdd(reg, IntPtrConstant(2));
1599           var_reg = new_reg;
1600 
1601           Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop,
1602                  &nested_loop_out);
1603         }
1604       }
1605 
1606       BIND(&nested_loop_out);
1607     }
1608 
1609     var_last_matched_until = match_to;
1610     var_next_search_from = match_to;
1611     Goto(&loop);
1612   }
1613 
1614   BIND(&push_suffix_and_out);
1615   {
1616     const TNode<Smi> from = var_last_matched_until.value();
1617     const TNode<Smi> to = string_length;
1618     array.Push(CallBuiltin(Builtins::kSubString, context, string, from, to));
1619     Goto(&out);
1620   }
1621 
1622   BIND(&out);
1623   {
1624     var_result = array.ToJSArray(context);
1625     Goto(&done);
1626   }
1627 
1628   BIND(&return_empty_array);
1629   {
1630     TNode<Smi> length = SmiZero();
1631     TNode<IntPtrT> capacity = IntPtrZero();
1632     base::Optional<TNode<AllocationSite>> allocation_site = base::nullopt;
1633     var_result =
1634         AllocateJSArray(kind, array_map, capacity, length, allocation_site);
1635     Goto(&done);
1636   }
1637 
1638   BIND(&done);
1639   return var_result.value();
1640 }
1641 
1642 }  // namespace internal
1643 }  // namespace v8
1644