v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-regexp-gen.cc
Go to the documentation of this file.
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
6
7#include <optional>
8
16#include "src/common/globals.h"
24
25namespace v8 {
26namespace internal {
27
29
30// Tail calls the regular expression interpreter.
31// static
32void Builtins::Generate_RegExpInterpreterTrampoline(MacroAssembler* masm) {
33 ExternalReference interpreter_code_entry =
34 ExternalReference::re_match_for_call_from_js();
35 masm->Jump(interpreter_code_entry);
36}
37
38// Tail calls the experimental regular expression engine.
39// static
40void Builtins::Generate_RegExpExperimentalTrampoline(MacroAssembler* masm) {
41 ExternalReference interpreter_code_entry =
42 ExternalReference::re_experimental_match_for_call_from_js();
43 masm->Jump(interpreter_code_entry);
44}
45
47
51
52// -----------------------------------------------------------------------------
53// ES6 section 21.2 RegExp Objects
54
56 TNode<Context> context, TNode<Smi> length, TNode<Smi> index,
57 TNode<String> input, TNode<JSRegExp> regexp, TNode<Number> last_index,
58 TNode<BoolT> has_indices, TNode<FixedArray>* elements_out) {
61 CSA_DCHECK(this, SmiGreaterThan(length, SmiConstant(0)));
62
63 // Allocate.
64
65 Label result_has_indices(this), allocated(this);
66 const ElementsKind elements_kind = PACKED_ELEMENTS;
67 std::optional<TNode<AllocationSite>> no_gc_site = std::nullopt;
68 TNode<IntPtrT> length_intptr = PositiveSmiUntag(length);
69 // Note: The returned `var_elements` may be in young large object space, but
70 // `var_array` is guaranteed to be in new space so we could skip write
71 // barriers below.
72 TVARIABLE(JSArray, var_array);
73 TVARIABLE(FixedArrayBase, var_elements);
74
75 GotoIf(has_indices, &result_has_indices, GotoHint::kFallthrough);
76 {
77 TNode<Map> map = CAST(LoadContextElement(LoadNativeContext(context),
78 Context::REGEXP_RESULT_MAP_INDEX));
79 std::tie(var_array, var_elements) =
81 elements_kind, map, length, no_gc_site, length_intptr,
82 AllocationFlag::kNone, JSRegExpResult::kSize);
83 Goto(&allocated);
84 }
85
86 BIND(&result_has_indices);
87 {
88 TNode<Map> map =
89 CAST(LoadContextElement(LoadNativeContext(context),
90 Context::REGEXP_RESULT_WITH_INDICES_MAP_INDEX));
91 std::tie(var_array, var_elements) =
93 elements_kind, map, length, no_gc_site, length_intptr,
94 AllocationFlag::kNone, JSRegExpResultWithIndices::kSize);
95 Goto(&allocated);
96 }
97
98 BIND(&allocated);
99
100 // Finish result initialization.
101
103 UncheckedCast<JSRegExpResult>(var_array.value());
104
105 // Load undefined value once here to avoid multiple LoadRoots.
106 TNode<Oddball> undefined_value = UncheckedCast<Oddball>(
107 CodeAssembler::LoadRoot(RootIndex::kUndefinedValue));
108
109 StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kIndexOffset, index);
110 // TODO(jgruber,turbofan): Could skip barrier but the MemoryOptimizer
111 // complains.
112 StoreObjectField(result, JSRegExpResult::kInputOffset, input);
113 StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kGroupsOffset,
114 undefined_value);
115 StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kNamesOffset,
116 undefined_value);
117
118 StoreObjectField(result, JSRegExpResult::kRegexpInputOffset, input);
119
120 // If non-smi last_index then store an SmiZero instead.
121 {
122 TNode<Smi> last_index_smi = Select<Smi>(
123 TaggedIsSmi(last_index), [=, this] { return CAST(last_index); },
124 [=, this] { return SmiZero(); });
125 StoreObjectField(result, JSRegExpResult::kRegexpLastIndexOffset,
126 last_index_smi);
127 }
128
129 Label finish_initialization(this);
130 GotoIfNot(has_indices, &finish_initialization, GotoHint::kLabel);
131 {
132 static_assert(std::is_base_of_v<JSRegExpResult, JSRegExpResultWithIndices>,
133 "JSRegExpResultWithIndices is a subclass of JSRegExpResult");
135 result, JSRegExpResultWithIndices::kIndicesOffset, undefined_value);
136 Goto(&finish_initialization);
137 }
138
139 BIND(&finish_initialization);
140
141 // Finish elements initialization.
142
143 FillFixedArrayWithValue(elements_kind, var_elements.value(), IntPtrZero(),
144 length_intptr, RootIndex::kUndefinedValue);
145
146 if (elements_out) *elements_out = CAST(var_elements.value());
147 return result;
148}
149
151 TNode<JSRegExp> regexp) {
152 // Load the in-object field.
153 static const int field_offset =
154 JSRegExp::kHeaderSize + JSRegExp::kLastIndexFieldIndex * kTaggedSize;
155 return LoadObjectField(regexp, field_offset);
156}
157
159 TNode<JSAny> regexp) {
160 return GetProperty(context, regexp, isolate()->factory()->lastIndex_string());
161}
162
163// The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified
164// JSRegExp instance.
166 TNode<Smi> value) {
167 // Store the in-object field.
168 static const int field_offset =
169 JSRegExp::kHeaderSize + JSRegExp::kLastIndexFieldIndex * kTaggedSize;
170 StoreObjectField(regexp, field_offset, value);
171}
172
174 TNode<JSAny> regexp,
175 TNode<Object> value) {
176 TNode<String> name =
177 HeapConstantNoHole(isolate()->factory()->lastIndex_string());
178 SetPropertyStrict(context, regexp, name, value);
179}
180
182 return Select<Smi>(
183 SmiEqual(LoadObjectField<Smi>(data, RegExpData::kTypeTagOffset),
185 [=, this] { return SmiConstant(JSRegExp::kAtomCaptureCount); },
186 [=, this] {
187 return LoadObjectField<Smi>(data, IrRegExpData::kCaptureCountOffset);
188 });
189}
190
192 TNode<Smi> capture_count) {
193 // See also: JSRegExp::RegistersForCaptureCount.
194 static_assert(Internals::IsValidSmi((JSRegExp::kMaxCaptures + 1) * 2));
195 return SmiShl(SmiAdd(capture_count, SmiConstant(1)), 1);
196}
197
199 TNode<Context> context, TNode<JSRegExp> regexp,
200 TNode<RegExpMatchInfo> match_info, TNode<String> string,
201 TNode<Number> last_index) {
202 Label named_captures(this), maybe_build_indices(this), out(this);
203
205 match_info, offsetof(RegExpMatchInfo, number_of_capture_registers_))));
206 TNode<Smi> num_results = SmiTag(WordShr(num_indices, 1));
209
210 // Calculate the substring of the first match before creating the result array
211 // to avoid an unnecessary write barrier storing the first result.
212
213 TNode<String> first =
214 CAST(CallBuiltin(Builtin::kSubString, context, string, start, end));
215
216 // Load flags and check if the result object needs to have indices.
217 const TNode<Smi> flags =
218 CAST(LoadObjectField(regexp, JSRegExp::kFlagsOffset));
219 const TNode<BoolT> has_indices = IsSetSmi(flags, JSRegExp::kHasIndices);
220 TNode<FixedArray> result_elements;
222 AllocateRegExpResult(context, num_results, start, string, regexp,
223 last_index, has_indices, &result_elements);
224
225 UnsafeStoreFixedArrayElement(result_elements, 0, first);
226
227 // If no captures exist we can skip named capture handling as well.
228 GotoIf(SmiEqual(num_results, SmiConstant(1)), &maybe_build_indices);
229
230 // Store all remaining captures.
231 TNode<IntPtrT> limit = num_indices;
232
233 TVARIABLE(IntPtrT, var_from_cursor, IntPtrConstant(2));
234 TVARIABLE(IntPtrT, var_to_cursor, IntPtrConstant(1));
235
236 Label loop(this, {&var_from_cursor, &var_to_cursor});
237
238 Goto(&loop);
239 BIND(&loop);
240 {
241 TNode<IntPtrT> from_cursor = var_from_cursor.value();
242 TNode<IntPtrT> to_cursor = var_to_cursor.value();
243 TNode<Smi> start_cursor = LoadArrayElement(match_info, from_cursor);
244
245 Label next_iter(this);
246 GotoIf(SmiEqual(start_cursor, SmiConstant(-1)), &next_iter);
247
248 TNode<IntPtrT> from_cursor_plus1 =
249 IntPtrAdd(from_cursor, IntPtrConstant(1));
250 TNode<Smi> end_cursor = LoadArrayElement(match_info, from_cursor_plus1);
251
252 TNode<String> capture = CAST(CallBuiltin(Builtin::kSubString, context,
253 string, start_cursor, end_cursor));
254 UnsafeStoreFixedArrayElement(result_elements, to_cursor, capture);
255 Goto(&next_iter);
256
257 BIND(&next_iter);
258 var_from_cursor = IntPtrAdd(from_cursor, IntPtrConstant(2));
259 var_to_cursor = IntPtrAdd(to_cursor, IntPtrConstant(1));
260 Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop,
261 &named_captures);
262 }
263
264 BIND(&named_captures);
265 {
266 CSA_DCHECK(this, SmiGreaterThan(num_results, SmiConstant(1)));
267
268 // Preparations for named capture properties. Exit early if the result does
269 // not have any named captures to minimize performance impact.
270
272 regexp, JSRegExp::kDataOffset, kRegExpDataIndirectPointerTag));
273
274 // We reach this point only if captures exist, implying that the assigned
275 // regexp engine must be able to handle captures.
276 CSA_SBXCHECK(this, HasInstanceType(data, IR_REG_EXP_DATA_TYPE));
277
278 // The names fixed array associates names at even indices with a capture
279 // index at odd indices.
280 TNode<Object> maybe_names =
281 LoadObjectField(data, IrRegExpData::kCaptureNameMapOffset);
282 GotoIf(TaggedEqual(maybe_names, SmiZero()), &maybe_build_indices,
284
285 // One or more named captures exist, add a property for each one.
286
287 TNode<FixedArray> names = CAST(maybe_names);
289 CSA_DCHECK(this, IntPtrGreaterThan(names_length, IntPtrZero()));
290
291 // Stash names in case we need them to build the indices array later.
292 StoreObjectField(result, JSRegExpResult::kNamesOffset, names);
293
294 // Allocate a new object to store the named capture properties.
295 // TODO(jgruber): Could be optimized by adding the object map to the heap
296 // root list.
297
298 TNode<IntPtrT> num_properties = WordSar(names_length, 1);
301 TNode<HeapObject> properties;
303 properties = AllocateSwissNameDictionary(num_properties);
304 } else {
305 properties = AllocateNameDictionary(num_properties);
306 }
307
308 TNode<JSObject> group_object = AllocateJSObjectFromMap(map, properties);
309 StoreObjectField(result, JSRegExpResult::kGroupsOffset, group_object);
310
311 TVARIABLE(IntPtrT, var_i, IntPtrZero());
312
313 Label inner_loop(this, &var_i);
314
315 Goto(&inner_loop);
316 BIND(&inner_loop);
317 {
318 TNode<IntPtrT> i = var_i.value();
320 TNode<IntPtrT> i_plus_2 = IntPtrAdd(i_plus_1, IntPtrConstant(1));
321
323 TNode<Smi> index = CAST(LoadFixedArrayElement(names, i_plus_1));
324 TNode<HeapObject> capture =
325 CAST(LoadFixedArrayElement(result_elements, SmiUntag(index)));
326
327 // TODO(v8:8213): For maintainability, we should call a CSA/Torque
328 // implementation of CreateDataProperty instead.
329
330 // At this point the spec says to call CreateDataProperty. However, we can
331 // skip most of the steps and go straight to adding/updating a dictionary
332 // entry because we know a bunch of useful facts:
333 // - All keys are non-numeric internalized strings
334 // - Receiver has no prototype
335 // - Receiver isn't used as a prototype
336 // - Receiver isn't any special object like a Promise intrinsic object
337 // - Receiver is extensible
338 // - Receiver has no interceptors
339 Label add_dictionary_property_slow(this, Label::kDeferred);
340 TVARIABLE(IntPtrT, var_name_index);
341 Label add_name_entry(this, &var_name_index),
342 duplicate_name(this, &var_name_index), next(this);
344 CAST(properties), name, &duplicate_name, &var_name_index,
345 &add_name_entry, kFindExistingOrInsertionIndex);
346 BIND(&duplicate_name);
347 GotoIf(IsUndefined(capture), &next);
348 CSA_DCHECK(this,
350 CAST(properties), var_name_index.value()),
351 UndefinedConstant()));
353 var_name_index.value(), capture);
354 Goto(&next);
355
356 BIND(&add_name_entry);
357 AddToDictionary<PropertyDictionary>(CAST(properties), name, capture,
358 &add_dictionary_property_slow,
359 var_name_index.value());
360 Goto(&next);
361
362 BIND(&next);
363 var_i = i_plus_2;
364 Branch(IntPtrGreaterThanOrEqual(var_i.value(), names_length),
365 &maybe_build_indices, &inner_loop);
366
367 BIND(&add_dictionary_property_slow);
368 // If the dictionary needs resizing, the above Add call will jump here
369 // before making any changes. This shouldn't happen because we allocated
370 // the dictionary with enough space above.
371 Unreachable();
372 }
373 }
374
375 // Build indices if needed (i.e. if the /d flag is present) after named
376 // capture groups are processed.
377 BIND(&maybe_build_indices);
378 GotoIfNot(has_indices, &out, GotoHint::kLabel);
379 {
380 const TNode<Object> maybe_names =
381 LoadObjectField(result, JSRegExpResultWithIndices::kNamesOffset);
382 const TNode<JSRegExpResultIndices> indices =
384 CallRuntime(Runtime::kRegExpBuildIndices, context, regexp,
385 match_info, maybe_names));
386 StoreObjectField(result, JSRegExpResultWithIndices::kIndicesOffset,
387 indices);
388 Goto(&out);
389 }
390
391 BIND(&out);
392 return result;
393}
394
397 TNode<IntPtrT> last_index, TNode<IntPtrT> string_length,
398 String::Encoding encoding, TVariable<RawPtrT>* var_string_start,
399 TVariable<RawPtrT>* var_string_end) {
400 DCHECK_EQ(var_string_start->rep(), MachineType::PointerRepresentation());
402
403 const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
404 ? UINT8_ELEMENTS
405 : UINT16_ELEMENTS;
406
407 TNode<IntPtrT> from_offset =
409 *var_string_start =
410 ReinterpretCast<RawPtrT>(IntPtrAdd(string_data, from_offset));
411
412 TNode<IntPtrT> to_offset =
414 *var_string_end = ReinterpretCast<RawPtrT>(IntPtrAdd(string_data, to_offset));
415}
416
417std::pair<TNode<RawPtrT>, TNode<BoolT>>
419 TNode<Smi> register_count) {
420 Label if_dynamic(this), out(this);
421 TVARIABLE(BoolT, var_is_dynamic, Int32FalseConstant());
423
424 // Too large?
425 GotoIf(SmiAbove(register_count,
427 &if_dynamic, GotoHint::kFallthrough);
428
429 auto address_of_regexp_static_result_offsets_vector = ExternalConstant(
430 ExternalReference::address_of_regexp_static_result_offsets_vector(
431 isolate()));
432 var_vector = UncheckedCast<RawPtrT>(Load(
433 MachineType::Pointer(), address_of_regexp_static_result_offsets_vector));
434
435 // Owned by someone else?
436 GotoIf(WordEqual(var_vector.value(), IntPtrConstant(0)), &if_dynamic,
438
439 // Take ownership of the static vector. See also:
440 // RegExpResultVectorScope::Initialize.
442 address_of_regexp_static_result_offsets_vector,
443 IntPtrConstant(0));
444 Goto(&out);
445
446 BIND(&if_dynamic);
447 var_is_dynamic = Int32TrueConstant();
450 ExternalConstant(ExternalReference::allocate_regexp_result_vector()),
452 std::make_pair(MachineType::Pointer(), isolate_ptr),
453 std::make_pair(MachineType::Uint32(), SmiToInt32(register_count))));
454 Goto(&out);
455
456 BIND(&out);
457 return {var_vector.value(), var_is_dynamic.value()};
458}
459
461 TNode<RawPtrT> result_vector, TNode<BoolT> is_dynamic) {
462 Label if_dynamic(this), out(this);
463
464 GotoIf(is_dynamic, &if_dynamic, GotoHint::kFallthrough);
465
466 // The vector must have been allocated.
467 CSA_DCHECK(this, WordNotEqual(result_vector, IntPtrConstant(0)));
468
469 // Return ownership of the static vector.
470 auto address_of_regexp_static_result_offsets_vector = ExternalConstant(
471 ExternalReference::address_of_regexp_static_result_offsets_vector(
472 isolate()));
476 address_of_regexp_static_result_offsets_vector)),
477 IntPtrConstant(0)));
479 address_of_regexp_static_result_offsets_vector,
480 result_vector);
481 Goto(&out);
482
483 BIND(&if_dynamic);
486 ExternalConstant(ExternalReference::free_regexp_result_vector()),
487 MachineType::Pointer() /* void */,
488 std::make_pair(MachineType::Pointer(), isolate_ptr),
489 std::make_pair(MachineType::Pointer(), result_vector));
490 Goto(&out);
491
492 BIND(&out);
493}
494
495namespace {
496
497static constexpr int kInt32SizeLog2 = 2;
498static_assert(kInt32Size == 1 << kInt32SizeLog2);
499
500} // namespace
501
502TNode<RegExpMatchInfo>
504 TNode<Context> context, TNode<RegExpMatchInfo> match_info,
505 TNode<Smi> register_count, TNode<String> subject,
506 TNode<RawPtrT> result_offsets_vector) {
507 TVARIABLE(RegExpMatchInfo, var_match_info, match_info);
508
509 // Check that the last match info has space for the capture registers.
510 {
511 Label next(this);
512 TNode<Smi> available_slots = LoadSmiArrayLength(var_match_info.value());
513 GotoIf(SmiLessThanOrEqual(register_count, available_slots), &next,
515
516 // Grow.
517 var_match_info =
518 CAST(CallRuntime(Runtime::kRegExpGrowRegExpMatchInfo, context,
519 var_match_info.value(), register_count));
520 Goto(&next);
521
522 BIND(&next);
523 }
524
525 // Fill match_info.
526 StoreObjectField(var_match_info.value(),
527 offsetof(RegExpMatchInfo, number_of_capture_registers_),
528 register_count);
529 StoreObjectField(var_match_info.value(),
530 offsetof(RegExpMatchInfo, last_subject_), subject);
531 StoreObjectField(var_match_info.value(),
532 offsetof(RegExpMatchInfo, last_input_), subject);
533
534 // Fill match and capture offsets in match_info. They are located in the
535 // region:
536 //
537 // result_offsets_vector + 0
538 // ...
539 // result_offsets_vector + register_count * kInt32Size.
540 {
541 // The offset within result_offsets_vector.
542 TNode<IntPtrT> loop_start = UncheckedCast<IntPtrT>(result_offsets_vector);
543 TNode<IntPtrT> loop_end =
544 IntPtrAdd(loop_start, SmiUntag(SmiShl(register_count, kInt32SizeLog2)));
545 // The offset within RegExpMatchInfo.
546 TNode<IntPtrT> to_offset =
548 TVARIABLE(IntPtrT, var_to_offset, to_offset);
549
550 VariableList vars({&var_to_offset}, zone());
552 vars, loop_start, loop_end,
553 [&](TNode<IntPtrT> current_register_address) {
555 Load(MachineType::Int32(), current_register_address));
556 TNode<Smi> smi_value = SmiFromInt32(value);
558 var_match_info.value(), var_to_offset.value(),
559 smi_value);
560 Increment(&var_to_offset, kTaggedSize);
561 },
563 }
564
565 return var_match_info.value();
566}
567
569 TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> string,
570 TNode<Number> last_index, Label* if_not_matched) {
571 Label out(this), not_matched(this);
572 TVARIABLE(RegExpMatchInfo, var_result);
574 regexp, JSRegExp::kDataOffset, kRegExpDataIndirectPointerTag));
575 TNode<Smi> register_count_per_match =
577 // Allocate space for one match.
578 TNode<Smi> result_offsets_vector_length = register_count_per_match;
579 TNode<RawPtrT> result_offsets_vector;
580 TNode<BoolT> result_offsets_vector_is_dynamic;
581 std::tie(result_offsets_vector, result_offsets_vector_is_dynamic) =
582 LoadOrAllocateRegExpResultVector(result_offsets_vector_length);
583
584 // Exception handling is necessary to free any allocated memory.
585 TVARIABLE(Object, var_exception);
586 Label if_exception(this, Label::kDeferred);
587
588 {
589 compiler::ScopedExceptionHandler handler(this, &if_exception,
590 &var_exception);
591
593 context, regexp, data, string, last_index, result_offsets_vector,
594 SmiToInt32(result_offsets_vector_length));
595
596 GotoIf(IntPtrEqual(num_matches, IntPtrConstant(0)), &not_matched);
597
598 CSA_DCHECK(this, IntPtrEqual(num_matches, IntPtrConstant(1)));
599 CSA_DCHECK(this, TaggedEqual(context, LoadNativeContext(context)));
600 TNode<RegExpMatchInfo> last_match_info = CAST(
601 LoadContextElement(context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
603 context, last_match_info, register_count_per_match, string,
604 result_offsets_vector);
605 Goto(&out);
606 }
607
608 BIND(&if_exception);
609 FreeRegExpResultVector(result_offsets_vector,
610 result_offsets_vector_is_dynamic);
611 CallRuntime(Runtime::kReThrow, context, var_exception.value());
612 Unreachable();
613
614 BIND(&not_matched);
615 FreeRegExpResultVector(result_offsets_vector,
616 result_offsets_vector_is_dynamic);
617 Goto(if_not_matched);
618
619 BIND(&out);
620 FreeRegExpResultVector(result_offsets_vector,
621 result_offsets_vector_is_dynamic);
622 return var_result.value();
623}
624
627 TNode<String> string, TNode<Number> last_index,
628 TNode<RawPtrT> result_offsets_vector,
629 TNode<Int32T> result_offsets_vector_length) {
631 regexp, JSRegExp::kDataOffset,
632 kRegExpDataIndirectPointerTag)));
633
634 ToDirectStringAssembler to_direct(state(), string);
635
636 TVARIABLE(UintPtrT, var_result, UintPtrConstant(0));
637 Label out(this), atom(this), runtime(this, Label::kDeferred),
638 retry_experimental(this, Label::kDeferred);
639
640 // At this point, last_index is definitely a canonicalized non-negative
641 // number, which implies that any non-Smi last_index is greater than
642 // the maximal string length. If lastIndex > string.length then the matcher
643 // must fail.
644
645 CSA_DCHECK(this, IsNumberNormalized(last_index));
646 CSA_DCHECK(this, IsNumberPositive(last_index));
647 GotoIf(TaggedIsNotSmi(last_index), &out, GotoHint::kFallthrough);
648
649 TNode<IntPtrT> int_string_length = LoadStringLengthAsWord(string);
650 TNode<IntPtrT> int_last_index = PositiveSmiUntag(CAST(last_index));
651
652 GotoIf(UintPtrGreaterThan(int_last_index, int_string_length), &out,
654
655 // Unpack the string. Note that due to SlicedString unpacking (which extracts
656 // the parent string and offset), it's not valid to replace `string` with the
657 // result of ToDirect here. Instead, we rely on in-place flattening done by
658 // String::Flatten.
659 // TODO(jgruber): Consider changing ToDirectStringAssembler behavior here
660 // since this aspect is surprising. The result of `ToDirect` could always
661 // equal the input in length and contents. SlicedString unpacking could
662 // happen in `TryToSequential`.
663 to_direct.ToDirect();
664
665 // Dispatch on the type of the RegExp.
666 // Since the type tag is in trusted space, it is safe to interpret
667 // RegExpData as IrRegExpData/AtomRegExpData in the respective branches
668 // without checks.
669 {
670 Label next(this), unreachable(this, Label::kDeferred);
671 TNode<Int32T> tag =
672 SmiToInt32(LoadObjectField<Smi>(data, RegExpData::kTypeTagOffset));
673
674 int32_t values[] = {
675 static_cast<uint8_t>(RegExpData::Type::IRREGEXP),
676 static_cast<uint8_t>(RegExpData::Type::ATOM),
677 static_cast<uint8_t>(RegExpData::Type::EXPERIMENTAL),
678 };
679 Label* labels[] = {&next, &atom, &next};
680
681 static_assert(arraysize(values) == arraysize(labels));
682 Switch(tag, &unreachable, values, labels, arraysize(values));
683
684 BIND(&unreachable);
685 Unreachable();
686
687 BIND(&next);
688 }
689
690 // Check (number_of_captures + 1) * 2 <= offsets vector size.
693 SmiFromInt32(result_offsets_vector_length)));
694
695 // Load the irregexp code or bytecode object and offsets into the subject
696 // string. Both depend on whether the string is one- or two-byte.
697
698 TVARIABLE(RawPtrT, var_string_start);
699 TVARIABLE(RawPtrT, var_string_end);
700#ifdef V8_ENABLE_SANDBOX
701 using kVarCodeT = IndirectPointerHandleT;
702#else
703 using kVarCodeT = Object;
704#endif
705 TVARIABLE(kVarCodeT, var_code);
706 TVARIABLE(Object, var_bytecode);
707
708 {
709 TNode<RawPtrT> direct_string_data = to_direct.PointerToData(&runtime);
710
711 Label next(this), if_isonebyte(this), if_istwobyte(this, Label::kDeferred);
712 Branch(to_direct.IsOneByte(), &if_isonebyte, &if_istwobyte);
713
714 BIND(&if_isonebyte);
715 {
716 GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
717 int_string_length, String::ONE_BYTE_ENCODING,
718 &var_string_start, &var_string_end);
719 var_code =
720 LoadObjectField<kVarCodeT>(data, IrRegExpData::kLatin1CodeOffset);
721 var_bytecode = LoadObjectField(data, IrRegExpData::kLatin1BytecodeOffset);
722 Goto(&next);
723 }
724
725 BIND(&if_istwobyte);
726 {
727 GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
728 int_string_length, String::TWO_BYTE_ENCODING,
729 &var_string_start, &var_string_end);
730 var_code =
731 LoadObjectField<kVarCodeT>(data, IrRegExpData::kUc16CodeOffset);
732 var_bytecode = LoadObjectField(data, IrRegExpData::kUc16BytecodeOffset);
733 Goto(&next);
734 }
735
736 BIND(&next);
737 }
738
739 // Check that the irregexp code has been generated for the actual string
740 // encoding.
741
742#ifdef V8_ENABLE_SANDBOX
743 GotoIf(
745 &runtime);
746#else
747 GotoIf(TaggedIsSmi(var_code.value()), &runtime);
748#endif
749
750 Label if_exception(this, Label::kDeferred);
751
752 {
753 IncrementCounter(isolate()->counters()->regexp_entry_native(), 1);
754
755 // Set up args for the final call into generated Irregexp code.
756
757 MachineType type_int32 = MachineType::Int32();
758 MachineType type_tagged = MachineType::AnyTagged();
760
761 // Result: A NativeRegExpMacroAssembler::Result return code.
762 MachineType retval_type = type_int32;
763
764 // Argument 0: Original subject string.
765 MachineType arg0_type = type_tagged;
766 TNode<String> arg0 = string;
767
768 // Argument 1: Previous index.
769 MachineType arg1_type = type_int32;
770 TNode<Int32T> arg1 = TruncateIntPtrToInt32(int_last_index);
771
772 // Argument 2: Start of string data. This argument is ignored in the
773 // interpreter.
774 MachineType arg2_type = type_ptr;
775 TNode<RawPtrT> arg2 = var_string_start.value();
776
777 // Argument 3: End of string data. This argument is ignored in the
778 // interpreter.
779 MachineType arg3_type = type_ptr;
780 TNode<RawPtrT> arg3 = var_string_end.value();
781
782 // Argument 4: result offsets vector.
783 MachineType arg4_type = type_ptr;
784 TNode<RawPtrT> arg4 = result_offsets_vector;
785
786 // Argument 5: Number of capture registers.
787 MachineType arg5_type = type_int32;
788 TNode<Int32T> arg5 = result_offsets_vector_length;
789
790 // Argument 6: Indicate that this is a direct call from JavaScript.
791 MachineType arg6_type = type_int32;
793
794 // Argument 7: Pass current isolate address.
795 TNode<ExternalReference> isolate_address =
797 MachineType arg7_type = type_ptr;
798 TNode<ExternalReference> arg7 = isolate_address;
799
800 // Argument 8: Regular expression data object. This argument is ignored in
801 // native irregexp code.
802 MachineType arg8_type = type_tagged;
803 TNode<IrRegExpData> arg8 = CAST(data);
804
805#ifdef V8_ENABLE_SANDBOX
806 TNode<RawPtrT> code_entry = LoadCodeEntryFromIndirectPointerHandle(
807 var_code.value(), kRegExpEntrypointTag);
808#else
809 TNode<Code> code = CAST(var_code.value());
810 TNode<RawPtrT> code_entry =
812#endif
813
814 // AIX uses function descriptors on CFunction calls. code_entry in this case
815 // may also point to a Regex interpreter entry trampoline which does not
816 // have a function descriptor. This method is ineffective on other platforms
817 // and is equivalent to CallCFunction.
820 code_entry, retval_type, std::make_pair(arg0_type, arg0),
821 std::make_pair(arg1_type, arg1), std::make_pair(arg2_type, arg2),
822 std::make_pair(arg3_type, arg3), std::make_pair(arg4_type, arg4),
823 std::make_pair(arg5_type, arg5), std::make_pair(arg6_type, arg6),
824 std::make_pair(arg7_type, arg7), std::make_pair(arg8_type, arg8)));
825
826 // Check the result.
828 var_result = UncheckedCast<UintPtrT>(int_result);
829 static_assert(RegExp::kInternalRegExpSuccess == 1);
830 static_assert(RegExp::kInternalRegExpFailure == 0);
833 &out);
834 // GotoHint::kLabel since the other two states are 1. unlikely and 2. it's
835 // okay to be a bit slower there.
836 GotoIf(
838 &runtime, GotoHint::kLabel);
839 GotoIf(IntPtrEqual(int_result,
841 &if_exception);
842
843 CSA_CHECK(this,
844 IntPtrEqual(int_result,
847 Goto(&retry_experimental);
848 }
849
850 BIND(&if_exception);
851 {
852// A stack overflow was detected in RegExp code.
853#ifdef DEBUG
854 TNode<ExternalReference> exception_address =
856 IsolateAddressId::kExceptionAddress, isolate()));
857 TNode<Object> exception = LoadFullTagged(exception_address);
858 CSA_DCHECK(this, IsTheHole(exception));
859#endif // DEBUG
860 CallRuntime(Runtime::kThrowStackOverflow, context);
861 Unreachable();
862 }
863
864 BIND(&retry_experimental);
865 {
866 // Set the implicit (untagged) arg.
867 auto vector_arg = ExternalConstant(
868 ExternalReference::Create(IsolateFieldId::kRegexpExecVectorArgument));
870 result_offsets_vector);
871 static_assert(
873 TNode<Smi> result_as_smi = CAST(CallRuntime(
874 Runtime::kRegExpExperimentalOneshotExec, context, regexp, string,
875 last_index, SmiFromInt32(result_offsets_vector_length)));
876 var_result = UncheckedCast<UintPtrT>(SmiUntag(result_as_smi));
877#ifdef DEBUG
879 IntPtrConstant(0));
880#endif // DEBUG
881 Goto(&out);
882 }
883
884 BIND(&runtime);
885 {
886 // Set the implicit (untagged) arg.
887 auto vector_arg = ExternalConstant(
888 ExternalReference::Create(IsolateFieldId::kRegexpExecVectorArgument));
890 result_offsets_vector);
891 static_assert(
893 TNode<Smi> result_as_smi = CAST(
894 CallRuntime(Runtime::kRegExpExec, context, regexp, string, last_index,
895 SmiFromInt32(result_offsets_vector_length)));
896 var_result = UncheckedCast<UintPtrT>(SmiUntag(result_as_smi));
897#ifdef DEBUG
899 IntPtrConstant(0));
900#endif // DEBUG
901 Goto(&out);
902 }
903
904 BIND(&atom);
905 {
906 var_result =
907 RegExpExecAtom(context, CAST(data), string, CAST(last_index),
908 result_offsets_vector, result_offsets_vector_length);
909 Goto(&out);
910 }
911
912 BIND(&out);
913 return var_result.value();
914}
915
917 TNode<Context> context, TNode<Object> object, TNode<Map> map) {
918 Label out(this);
919 TVARIABLE(BoolT, var_result);
920
921 var_result = Int32FalseConstant();
923
925 const TNode<HeapObject> regexp_fun =
926 CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
927 const TNode<Object> initial_map =
928 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
929 const TNode<BoolT> has_initialmap = TaggedEqual(map, initial_map);
930
931 var_result = has_initialmap;
932 GotoIfNot(has_initialmap, &out, GotoHint::kFallthrough);
933
934 // The smi check is required to omit ToLength(lastIndex) calls with possible
935 // user-code execution on the fast path.
937 var_result = TaggedIsPositiveSmi(last_index);
938 Goto(&out);
939
940 BIND(&out);
941 return var_result.value();
942}
943
949
951 TNode<Context> context, TNode<HeapObject> object, TNode<Map> map,
952 PrototypeCheckAssembler::Flags prototype_check_flags,
953 std::optional<DescriptorIndexNameValue> additional_property_to_check,
954 Label* if_isunmodified, Label* if_ismodified) {
955 CSA_DCHECK(this, TaggedEqual(LoadMap(object), map));
956
957 GotoIfForceSlowPath(if_ismodified);
958
959 // This should only be needed for String.p.(split||matchAll), but we are
960 // conservative here.
963
965 TNode<JSFunction> regexp_fun =
966 CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
967 TNode<Map> initial_map = CAST(
968 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset));
969 TNode<BoolT> has_initialmap = TaggedEqual(map, initial_map);
970
971 GotoIfNot(has_initialmap, if_ismodified, GotoHint::kFallthrough);
972
973 // The smi check is required to omit ToLength(lastIndex) calls with possible
974 // user-code execution on the fast path.
976 GotoIfNot(TaggedIsPositiveSmi(last_index), if_ismodified,
978
979 // Verify the prototype.
980
981 TNode<Map> initial_proto_initial_map = CAST(
982 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX));
983
984 DescriptorIndexNameValue properties_to_check[2];
985 int property_count = 0;
986 properties_to_check[property_count++] = DescriptorIndexNameValue{
987 JSRegExp::kExecFunctionDescriptorIndex, RootIndex::kexec_string,
988 Context::REGEXP_EXEC_FUNCTION_INDEX};
989 if (additional_property_to_check) {
990 properties_to_check[property_count++] = *additional_property_to_check;
991 }
992
993 PrototypeCheckAssembler prototype_check_assembler(
994 state(), prototype_check_flags, native_context, initial_proto_initial_map,
995 base::Vector<DescriptorIndexNameValue>(properties_to_check,
996 property_count));
997
998 TNode<HeapObject> prototype = LoadMapPrototype(map);
999 prototype_check_assembler.CheckAndBranch(prototype, if_isunmodified,
1000 if_ismodified);
1001}
1003 TNode<Context> context, TNode<HeapObject> object, Label* if_isunmodified,
1004 Label* if_ismodified) {
1006 context, object, LoadMap(object),
1009 RootIndex::ksearch_symbol,
1010 Context::REGEXP_SEARCH_FUNCTION_INDEX},
1011 if_isunmodified, if_ismodified);
1012}
1013
1015 TNode<Context> context, TNode<HeapObject> object, Label* if_isunmodified,
1016 Label* if_ismodified) {
1018 context, object, LoadMap(object),
1021 RootIndex::kmatch_symbol,
1022 Context::REGEXP_MATCH_FUNCTION_INDEX},
1023 if_isunmodified, if_ismodified);
1024}
1025
1027 TNode<Context> context, TNode<HeapObject> object, Label* if_isunmodified,
1028 Label* if_ismodified) {
1029 BranchIfFastRegExp(context, object, LoadMap(object),
1031 std::nullopt, if_isunmodified, if_ismodified);
1032}
1033
1035 TNode<Context> context, TNode<HeapObject> object, Label* if_isunmodified,
1036 Label* if_ismodified) {
1037 BranchIfFastRegExp(context, object, LoadMap(object),
1039 if_isunmodified, if_ismodified);
1040}
1041
1043 const TNode<Object> object,
1044 Label* if_isunmodified,
1045 Label* if_ismodified) {
1046 // Could be a Smi.
1047 const TNode<Map> map = LoadReceiverMap(object);
1048
1050 const TNode<Object> initial_regexp_result_map =
1051 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX);
1052
1053 Label maybe_result_with_indices(this);
1054 Branch(TaggedEqual(map, initial_regexp_result_map), if_isunmodified,
1055 &maybe_result_with_indices, BranchHint::kTrue);
1056 BIND(&maybe_result_with_indices);
1057 {
1058 static_assert(std::is_base_of_v<JSRegExpResult, JSRegExpResultWithIndices>,
1059 "JSRegExpResultWithIndices is a subclass of JSRegExpResult");
1060 const TNode<Object> initial_regexp_result_with_indices_map =
1061 LoadContextElement(native_context,
1062 Context::REGEXP_RESULT_WITH_INDICES_MAP_INDEX);
1063 Branch(TaggedEqual(map, initial_regexp_result_with_indices_map),
1064 if_isunmodified, if_ismodified);
1065 }
1066}
1067
1070 TNode<String> subject_string, TNode<Smi> last_index,
1071 TNode<RawPtrT> result_offsets_vector,
1072 TNode<Int32T> result_offsets_vector_length) {
1073 auto f = ExternalConstant(ExternalReference::re_atom_exec_raw());
1077 std::make_pair(MachineType::Pointer(), isolate_ptr),
1078 std::make_pair(MachineType::TaggedPointer(), data),
1079 std::make_pair(MachineType::TaggedPointer(), subject_string),
1080 std::make_pair(MachineType::Int32(), SmiToInt32(last_index)),
1081 std::make_pair(MachineType::Pointer(), result_offsets_vector),
1082 std::make_pair(MachineType::Int32(), result_offsets_vector_length)));
1083 return Unsigned(result);
1084}
1085
1086// Fast path stub for ATOM regexps. String matching is done by StringIndexOf,
1087// and {match_info} is updated on success.
1088// The slow path is implemented in RegExp::AtomExec.
1090 auto regexp = Parameter<JSRegExp>(Descriptor::kRegExp);
1091 auto subject_string = Parameter<String>(Descriptor::kString);
1092 auto last_index = Parameter<Smi>(Descriptor::kLastIndex);
1093 auto match_info = Parameter<RegExpMatchInfo>(Descriptor::kMatchInfo);
1094 auto context = Parameter<Context>(Descriptor::kContext);
1095
1096 CSA_DCHECK(this, TaggedIsPositiveSmi(last_index));
1097
1098 TNode<RegExpData> data = CAST(LoadTrustedPointerFromObject(
1099 regexp, JSRegExp::kDataOffset, kRegExpDataIndirectPointerTag));
1100 CSA_SBXCHECK(this, HasInstanceType(data, ATOM_REG_EXP_DATA_TYPE));
1101
1102 // Callers ensure that last_index is in-bounds.
1103 CSA_DCHECK(this,
1104 UintPtrLessThanOrEqual(SmiUntag(last_index),
1105 LoadStringLengthAsWord(subject_string)));
1106
1107 const TNode<String> needle_string =
1108 LoadObjectField<String>(data, AtomRegExpData::kPatternOffset);
1109
1110 // ATOM patterns are guaranteed to not be the empty string (these are
1111 // intercepted and replaced in JSRegExp::Initialize.
1112 //
1113 // This is especially relevant for crbug.com/1075514: atom patterns are
1114 // non-empty and thus guaranteed not to match at the end of the string.
1115 CSA_DCHECK(this, IntPtrGreaterThan(LoadStringLengthAsWord(needle_string),
1116 IntPtrConstant(0)));
1117
1118 const TNode<Smi> match_from =
1119 CAST(CallBuiltin(Builtin::kStringIndexOf, context, subject_string,
1120 needle_string, last_index));
1121
1122 Label if_failure(this), if_success(this);
1123 Branch(SmiEqual(match_from, SmiConstant(-1)), &if_failure, &if_success);
1124
1125 BIND(&if_success);
1126 {
1127 CSA_DCHECK(this, TaggedIsPositiveSmi(match_from));
1128 CSA_DCHECK(this, UintPtrLessThan(SmiUntag(match_from),
1129 LoadStringLengthAsWord(subject_string)));
1130
1131 const int kNumRegisters = 2;
1133
1134 const TNode<Smi> match_to =
1135 SmiAdd(match_from, LoadStringLengthAsSmi(needle_string));
1136
1137 StoreObjectField(match_info,
1138 offsetof(RegExpMatchInfo, number_of_capture_registers_),
1139 SmiConstant(kNumRegisters));
1140 StoreObjectField(match_info, offsetof(RegExpMatchInfo, last_subject_),
1141 subject_string);
1142 StoreObjectField(match_info, offsetof(RegExpMatchInfo, last_input_),
1143 subject_string);
1144 UnsafeStoreArrayElement(match_info, 0, match_from,
1146 UnsafeStoreArrayElement(match_info, 1, match_to, UNSAFE_SKIP_WRITE_BARRIER);
1147
1148 Return(match_info);
1149 }
1150
1151 BIND(&if_failure);
1152 Return(NullConstant());
1153}
1154
1156 TNode<JSAny> regexp,
1157 bool is_fastpath) {
1159 Label runtime(this, Label::kDeferred), done(this, &result);
1160 if (is_fastpath) {
1161 GotoIfForceSlowPath(&runtime);
1162 }
1163
1164 Isolate* isolate = this->isolate();
1165
1166 const TNode<IntPtrT> int_one = IntPtrConstant(1);
1167 TVARIABLE(Uint32T, var_length, Uint32Constant(0));
1168 TVARIABLE(IntPtrT, var_flags);
1169
1170 // First, count the number of characters we will need and check which flags
1171 // are set.
1172
1173 if (is_fastpath) {
1174 // Refer to JSRegExp's flag property on the fast-path.
1175 CSA_DCHECK(this, IsJSRegExp(CAST(regexp)));
1176 const TNode<Smi> flags_smi =
1177 CAST(LoadObjectField(CAST(regexp), JSRegExp::kFlagsOffset));
1178 var_flags = SmiUntag(flags_smi);
1179
1180#define CASE_FOR_FLAG(Lower, Camel, ...) \
1181 do { \
1182 Label next(this); \
1183 GotoIfNot(IsSetWord(var_flags.value(), JSRegExp::k##Camel), &next); \
1184 var_length = Uint32Add(var_length.value(), Uint32Constant(1)); \
1185 Goto(&next); \
1186 BIND(&next); \
1187 } while (false);
1188
1190#undef CASE_FOR_FLAG
1191 } else {
1192 DCHECK(!is_fastpath);
1193
1194 // Fall back to GetProperty stub on the slow-path.
1195 var_flags = IntPtrZero();
1196
1197#define CASE_FOR_FLAG(NAME, FLAG) \
1198 do { \
1199 Label next(this); \
1200 const TNode<Object> flag = GetProperty( \
1201 context, regexp, isolate->factory()->InternalizeUtf8String(NAME)); \
1202 Label if_isflagset(this); \
1203 BranchIfToBooleanIsTrue(flag, &if_isflagset, &next); \
1204 BIND(&if_isflagset); \
1205 var_length = Uint32Add(var_length.value(), Uint32Constant(1)); \
1206 var_flags = Signed(WordOr(var_flags.value(), IntPtrConstant(FLAG))); \
1207 Goto(&next); \
1208 BIND(&next); \
1209 } while (false)
1210
1211 CASE_FOR_FLAG("hasIndices", JSRegExp::kHasIndices);
1212 CASE_FOR_FLAG("global", JSRegExp::kGlobal);
1213 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase);
1214 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline);
1215 CASE_FOR_FLAG("dotAll", JSRegExp::kDotAll);
1216 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode);
1217 CASE_FOR_FLAG("sticky", JSRegExp::kSticky);
1218 CASE_FOR_FLAG("unicodeSets", JSRegExp::kUnicodeSets);
1219#undef CASE_FOR_FLAG
1220
1221#define CASE_FOR_FLAG(NAME, V8_FLAG_EXTERN_REF, FLAG) \
1222 do { \
1223 Label next(this); \
1224 TNode<Word32T> flag_value = UncheckedCast<Word32T>( \
1225 Load(MachineType::Uint8(), ExternalConstant(V8_FLAG_EXTERN_REF))); \
1226 GotoIf(Word32Equal(Word32And(flag_value, Int32Constant(0xFF)), \
1227 Int32Constant(0)), \
1228 &next); \
1229 const TNode<Object> flag = GetProperty( \
1230 context, regexp, isolate->factory()->InternalizeUtf8String(NAME)); \
1231 Label if_isflagset(this); \
1232 BranchIfToBooleanIsTrue(flag, &if_isflagset, &next); \
1233 BIND(&if_isflagset); \
1234 var_length = Uint32Add(var_length.value(), Uint32Constant(1)); \
1235 var_flags = Signed(WordOr(var_flags.value(), IntPtrConstant(FLAG))); \
1236 Goto(&next); \
1237 BIND(&next); \
1238 } while (false)
1239
1241 "linear",
1242 ExternalReference::address_of_enable_experimental_regexp_engine(),
1243 JSRegExp::kLinear);
1244#undef CASE_FOR_FLAG
1245 }
1246
1247 // Allocate a string of the required length and fill it with the
1248 // corresponding char for each set flag.
1249
1250 {
1251 const TNode<SeqOneByteString> string =
1252 CAST(AllocateSeqOneByteString(var_length.value()));
1253
1254 TVARIABLE(IntPtrT, var_offset,
1255 IntPtrSub(FieldSliceSeqOneByteStringChars(string).offset,
1256 IntPtrConstant(1)));
1257
1258#define CASE_FOR_FLAG(Lower, Camel, LowerCamel, Char, ...) \
1259 do { \
1260 Label next(this); \
1261 GotoIfNot(IsSetWord(var_flags.value(), JSRegExp::k##Camel), &next); \
1262 const TNode<Int32T> value = Int32Constant(Char); \
1263 StoreNoWriteBarrier(MachineRepresentation::kWord8, string, \
1264 var_offset.value(), value); \
1265 var_offset = IntPtrAdd(var_offset.value(), int_one); \
1266 Goto(&next); \
1267 BIND(&next); \
1268 } while (false);
1269
1271#undef CASE_FOR_FLAG
1272
1273 if (is_fastpath) {
1274 result = string;
1275 Goto(&done);
1276
1277 BIND(&runtime);
1278 {
1279 result =
1280 CAST(CallRuntime(Runtime::kRegExpStringFromFlags, context, regexp));
1281 Goto(&done);
1282 }
1283
1284 BIND(&done);
1285 return result.value();
1286 } else {
1287 return string;
1288 }
1289 }
1290}
1291
1292// ES#sec-regexpinitialize
1293// Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
1295 const TNode<Context> context, const TNode<JSRegExp> regexp,
1296 const TNode<Object> maybe_pattern, const TNode<Object> maybe_flags) {
1297 // Normalize pattern.
1299 IsUndefined(maybe_pattern), [=, this] { return EmptyStringConstant(); },
1300 [=, this] { return ToString_Inline(context, maybe_pattern); });
1301
1302 // Normalize flags.
1303 const TNode<Object> flags = Select<Object>(
1304 IsUndefined(maybe_flags), [=, this] { return EmptyStringConstant(); },
1305 [=, this] { return ToString_Inline(context, maybe_flags); });
1306
1307 // Initialize.
1308
1309 return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp,
1310 pattern, flags);
1311}
1312
1313// ES#sec-regexp-pattern-flags
1314// RegExp ( pattern, flags )
1316 auto pattern = Parameter<JSAny>(Descriptor::kPattern);
1317 auto flags = Parameter<JSAny>(Descriptor::kFlags);
1318 auto new_target = Parameter<JSAny>(Descriptor::kJSNewTarget);
1319 auto context = Parameter<Context>(Descriptor::kContext);
1320
1321 Isolate* isolate = this->isolate();
1322
1323 TVARIABLE(JSAny, var_flags, flags);
1324 TVARIABLE(JSAny, var_pattern, pattern);
1325 TVARIABLE(JSAny, var_new_target, new_target);
1326
1327 TNode<NativeContext> native_context = LoadNativeContext(context);
1328 TNode<JSFunction> regexp_function =
1329 CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
1330
1331 TNode<BoolT> pattern_is_regexp = IsRegExp(context, pattern);
1332
1333 {
1334 Label next(this);
1335
1336 GotoIfNot(IsUndefined(new_target), &next);
1337 var_new_target = regexp_function;
1338
1339 GotoIfNot(pattern_is_regexp, &next);
1340 GotoIfNot(IsUndefined(flags), &next);
1341
1342 TNode<Object> value =
1343 GetProperty(context, pattern, isolate->factory()->constructor_string());
1344
1345 GotoIfNot(TaggedEqual(value, regexp_function), &next);
1346 Return(pattern);
1347
1348 BIND(&next);
1349 }
1350
1351 {
1352 Label next(this), if_patternisfastregexp(this),
1353 if_patternisslowregexp(this);
1354 GotoIf(TaggedIsSmi(pattern), &next);
1355
1356 GotoIf(IsJSRegExp(CAST(pattern)), &if_patternisfastregexp);
1357
1358 Branch(pattern_is_regexp, &if_patternisslowregexp, &next);
1359
1360 BIND(&if_patternisfastregexp);
1361 {
1362 TNode<JSAny> source =
1363 CAST(LoadObjectField(CAST(pattern), JSRegExp::kSourceOffset));
1364 var_pattern = source;
1365
1366 {
1367 Label inner_next(this);
1368 GotoIfNot(IsUndefined(flags), &inner_next);
1369
1370 var_flags = FlagsGetter(context, pattern, true);
1371 Goto(&inner_next);
1372
1373 BIND(&inner_next);
1374 }
1375
1376 Goto(&next);
1377 }
1378
1379 BIND(&if_patternisslowregexp);
1380 {
1381 var_pattern =
1382 GetProperty(context, pattern, isolate->factory()->source_string());
1383
1384 {
1385 Label inner_next(this);
1386 GotoIfNot(IsUndefined(flags), &inner_next);
1387
1388 var_flags =
1389 GetProperty(context, pattern, isolate->factory()->flags_string());
1390 Goto(&inner_next);
1391
1392 BIND(&inner_next);
1393 }
1394
1395 Goto(&next);
1396 }
1397
1398 BIND(&next);
1399 }
1400
1401 // Allocate.
1402
1403 TVARIABLE(JSRegExp, var_regexp);
1404 {
1405 Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred),
1406 next(this);
1407 Branch(TaggedEqual(var_new_target.value(), regexp_function),
1408 &allocate_jsregexp, &allocate_generic);
1409
1410 BIND(&allocate_jsregexp);
1411 {
1412 const TNode<Map> initial_map = CAST(LoadObjectField(
1413 regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
1414 var_regexp = CAST(AllocateJSObjectFromMap(initial_map));
1415 Goto(&next);
1416 }
1417
1418 BIND(&allocate_generic);
1419 {
1420 ConstructorBuiltinsAssembler constructor_assembler(this->state());
1421 var_regexp = CAST(constructor_assembler.FastNewObject(
1422 context, regexp_function, CAST(var_new_target.value())));
1423 Goto(&next);
1424 }
1425
1426 BIND(&next);
1427 }
1428
1429 // Clear data field, as a GC can be triggered before it is initialized with a
1430 // correct trusted pointer handle.
1431 ClearTrustedPointerField(var_regexp.value(), JSRegExp::kDataOffset);
1432
1433 const TNode<Object> result = RegExpInitialize(
1434 context, var_regexp.value(), var_pattern.value(), var_flags.value());
1435 Return(result);
1436}
1437
1438// ES#sec-regexp.prototype.compile
1439// RegExp.prototype.compile ( pattern, flags )
1440TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) {
1441 auto maybe_receiver = Parameter<Object>(Descriptor::kReceiver);
1442 auto maybe_pattern = Parameter<Object>(Descriptor::kPattern);
1443 auto maybe_flags = Parameter<Object>(Descriptor::kFlags);
1444 auto context = Parameter<Context>(Descriptor::kContext);
1445
1446 ThrowIfNotInstanceType(context, maybe_receiver, JS_REG_EXP_TYPE,
1447 "RegExp.prototype.compile");
1448 const TNode<JSRegExp> receiver = CAST(maybe_receiver);
1449
1450 TVARIABLE(Object, var_flags, maybe_flags);
1451 TVARIABLE(Object, var_pattern, maybe_pattern);
1452
1453 // Handle a JSRegExp pattern.
1454 {
1455 Label next(this);
1456
1457 GotoIf(TaggedIsSmi(maybe_pattern), &next);
1458 GotoIfNot(IsJSRegExp(CAST(maybe_pattern)), &next);
1459
1460 // {maybe_flags} must be undefined in this case, otherwise throw.
1461 {
1462 Label maybe_flags_is_undefined(this);
1463 GotoIf(IsUndefined(maybe_flags), &maybe_flags_is_undefined);
1464
1465 ThrowTypeError(context, MessageTemplate::kRegExpFlags);
1466
1467 BIND(&maybe_flags_is_undefined);
1468 }
1469
1470 const TNode<JSRegExp> pattern = CAST(maybe_pattern);
1471 const TNode<String> new_flags = FlagsGetter(context, pattern, true);
1472 const TNode<Object> new_pattern =
1473 LoadObjectField(pattern, JSRegExp::kSourceOffset);
1474
1475 var_flags = new_flags;
1476 var_pattern = new_pattern;
1477
1478 Goto(&next);
1479 BIND(&next);
1480 }
1481
1482 const TNode<Object> result = RegExpInitialize(
1483 context, receiver, var_pattern.value(), var_flags.value());
1484 Return(result);
1485}
1486
1487// Fast-path implementation for flag checks on an unmodified JSRegExp instance.
1489 JSRegExp::Flag flag) {
1490 TNode<Smi> flags = CAST(LoadObjectField(regexp, JSRegExp::kFlagsOffset));
1491 TNode<Smi> mask = SmiConstant(flag);
1493 SmiShr(SmiAnd(flags, mask),
1494 base::bits::CountTrailingZeros(static_cast<int>(flag)))));
1495}
1496
1498 TNode<String> string, TNode<Number> index, TNode<BoolT> is_unicode,
1499 bool is_fastpath) {
1500 CSA_DCHECK(this, IsNumberNormalized(index));
1501 if (is_fastpath) CSA_DCHECK(this, TaggedIsPositiveSmi(index));
1502
1503 // Default to last_index + 1.
1504 // TODO(pwong): Consider using TrySmiAdd for the fast path to reduce generated
1505 // code.
1506 TNode<Number> index_plus_one = NumberInc(index);
1507 TVARIABLE(Number, var_result, index_plus_one);
1508
1509 // TODO(v8:9880): Given that we have to convert index from Number to UintPtrT
1510 // anyway, consider using UintPtrT index to simplify the code below.
1511
1512 // Advancing the index has some subtle issues involving the distinction
1513 // between Smis and HeapNumbers. There's three cases:
1514 // * {index} is a Smi, {index_plus_one} is a Smi. The standard case.
1515 // * {index} is a Smi, {index_plus_one} overflows into a HeapNumber.
1516 // In this case we can return the result early, because
1517 // {index_plus_one} > {string}.length.
1518 // * {index} is a HeapNumber, {index_plus_one} is a HeapNumber. This can only
1519 // occur when {index} is outside the Smi range since we normalize
1520 // explicitly. Again we can return early.
1521 if (is_fastpath) {
1522 // Must be in Smi range on the fast path. We control the value of {index}
1523 // on all call-sites and can never exceed the length of the string.
1524 static_assert(String::kMaxLength + 2 < Smi::kMaxValue);
1525 CSA_DCHECK(this, TaggedIsPositiveSmi(index_plus_one));
1526 }
1527
1528 Label if_isunicode(this), out(this);
1529 GotoIfNot(is_unicode, &out);
1530
1531 // Keep this unconditional (even on the fast path) just to be safe.
1532 Branch(TaggedIsPositiveSmi(index_plus_one), &if_isunicode, &out,
1534
1535 BIND(&if_isunicode);
1536 {
1537 TNode<UintPtrT> string_length = Unsigned(LoadStringLengthAsWord(string));
1538 TNode<UintPtrT> untagged_plus_one =
1539 Unsigned(SmiUntag(CAST(index_plus_one)));
1540 GotoIfNot(UintPtrLessThan(untagged_plus_one, string_length), &out,
1542
1543 TNode<Int32T> lead =
1544 StringCharCodeAt(string, Unsigned(SmiUntag(CAST(index))));
1546 Int32Constant(0xD800)),
1547 &out, GotoHint::kLabel);
1548
1549 TNode<Int32T> trail = StringCharCodeAt(string, untagged_plus_one);
1551 Int32Constant(0xDC00)),
1552 &out);
1553
1554 // At a surrogate pair, return index + 2.
1555 TNode<Number> index_plus_two = NumberInc(index_plus_one);
1556 var_result = index_plus_two;
1557
1558 Goto(&out);
1559 }
1560
1561 BIND(&out);
1562 return var_result.value();
1563}
1564
1565// ES#sec-createregexpstringiterator
1566// CreateRegExpStringIterator ( R, S, global, fullUnicode )
1569 TNode<String> string, TNode<BoolT> global, TNode<BoolT> full_unicode) {
1570 TNode<Map> map = CAST(LoadContextElement(
1572 Context::INITIAL_REGEXP_STRING_ITERATOR_PROTOTYPE_MAP_INDEX));
1573
1574 // 4. Let iterator be ObjectCreate(%RegExpStringIteratorPrototype%, «
1575 // [[IteratingRegExp]], [[IteratedString]], [[Global]], [[Unicode]],
1576 // [[Done]] »).
1577 TNode<HeapObject> iterator = Allocate(JSRegExpStringIterator::kHeaderSize);
1578 StoreMapNoWriteBarrier(iterator, map);
1579 StoreObjectFieldRoot(iterator,
1580 JSRegExpStringIterator::kPropertiesOrHashOffset,
1581 RootIndex::kEmptyFixedArray);
1582 StoreObjectFieldRoot(iterator, JSRegExpStringIterator::kElementsOffset,
1583 RootIndex::kEmptyFixedArray);
1584
1585 // 5. Set iterator.[[IteratingRegExp]] to R.
1587 iterator, JSRegExpStringIterator::kIteratingRegExpOffset, regexp);
1588
1589 // 6. Set iterator.[[IteratedString]] to S.
1591 iterator, JSRegExpStringIterator::kIteratedStringOffset, string);
1592
1593 // 7. Set iterator.[[Global]] to global.
1594 // 8. Set iterator.[[Unicode]] to fullUnicode.
1595 // 9. Set iterator.[[Done]] to false.
1596 TNode<Int32T> global_flag =
1598 Int32Constant(JSRegExpStringIterator::GlobalBit::kShift));
1599 TNode<Int32T> unicode_flag =
1600 Word32Shl(ReinterpretCast<Int32T>(full_unicode),
1601 Int32Constant(JSRegExpStringIterator::UnicodeBit::kShift));
1602 TNode<Int32T> iterator_flags = Word32Or(global_flag, unicode_flag);
1603 StoreObjectFieldNoWriteBarrier(iterator, JSRegExpStringIterator::kFlagsOffset,
1604 SmiFromInt32(iterator_flags));
1605
1606 return CAST(iterator);
1607}
1608
1609// Generates the fast path for @@split. {regexp} is an unmodified, non-sticky
1610// JSRegExp, {string} is a String, and {limit} is a Smi.
1612 TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> string,
1613 TNode<Smi> limit) {
1614 CSA_DCHECK(this, IsFastRegExpPermissive(context, regexp));
1615 CSA_DCHECK(this, Word32BinaryNot(FastFlagGetter(regexp, JSRegExp::kSticky)));
1616
1617 TNode<IntPtrT> int_limit = SmiUntag(limit);
1618
1619 const ElementsKind elements_kind = PACKED_ELEMENTS;
1620
1621 Label done(this);
1622 Label return_empty_array(this, Label::kDeferred);
1623 TVARIABLE(JSArray, var_result);
1624
1625 // Exception handling is necessary to free any allocated memory.
1626 TVARIABLE(Object, var_exception);
1627 Label if_exception(this, Label::kDeferred);
1628
1629 // Allocate the results vector. Allocate space for exactly one result,
1630 // forcing the engine to return after each match. This is necessary due to
1631 // the specialized AdvanceStringIndex logic below.
1633 regexp, JSRegExp::kDataOffset, kRegExpDataIndirectPointerTag));
1634 TNode<Smi> capture_count = LoadCaptureCount(data);
1635 TNode<Smi> register_count_per_match = RegistersForCaptureCount(capture_count);
1636 TNode<RawPtrT> result_offsets_vector;
1637 TNode<BoolT> result_offsets_vector_is_dynamic;
1638 std::tie(result_offsets_vector, result_offsets_vector_is_dynamic) =
1639 LoadOrAllocateRegExpResultVector(register_count_per_match);
1640 TNode<Int32T> result_offsets_vector_length =
1641 SmiToInt32(register_count_per_match);
1642
1643 {
1644 compiler::ScopedExceptionHandler handler(this, &if_exception,
1645 &var_exception);
1646
1647 // If the limit is zero, return an empty array.
1648 GotoIf(SmiEqual(limit, SmiZero()), &return_empty_array);
1649
1650 TNode<Smi> string_length = LoadStringLengthAsSmi(string);
1651
1652 // If passed the empty {string}, return either an empty array or a singleton
1653 // array depending on whether the {regexp} matches.
1654 {
1655 Label next(this), if_stringisempty(this, Label::kDeferred);
1656 Branch(SmiEqual(string_length, SmiZero()), &if_stringisempty, &next,
1658
1659 BIND(&if_stringisempty);
1660 {
1662 context, regexp, data, string, SmiZero(), result_offsets_vector,
1663 result_offsets_vector_length));
1664
1665 Label if_matched(this), if_not_matched(this);
1666 Branch(IntPtrEqual(num_matches, IntPtrConstant(0)), &if_not_matched,
1667 &if_matched);
1668
1669 BIND(&if_matched);
1670 {
1671 CSA_DCHECK(this, IntPtrEqual(num_matches, IntPtrConstant(1)));
1672 CSA_DCHECK(this, TaggedEqual(context, LoadNativeContext(context)));
1673 TNode<RegExpMatchInfo> last_match_info = CAST(LoadContextElement(
1674 context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
1675
1676 InitializeMatchInfoFromRegisters(context, last_match_info,
1677 register_count_per_match, string,
1678 result_offsets_vector);
1679 Goto(&return_empty_array);
1680 }
1681
1682 BIND(&if_not_matched);
1683 {
1684 TNode<Smi> length = SmiConstant(1);
1685 TNode<IntPtrT> capacity = IntPtrConstant(1);
1686 std::optional<TNode<AllocationSite>> allocation_site = std::nullopt;
1687 CSA_DCHECK(this, TaggedEqual(context, LoadNativeContext(context)));
1688 TNode<Map> array_map =
1689 LoadJSArrayElementsMap(elements_kind, CAST(context));
1690 var_result = AllocateJSArray(elements_kind, array_map, capacity,
1691 length, allocation_site);
1692
1693 TNode<FixedArray> fixed_array =
1694 CAST(LoadElements(var_result.value()));
1695 UnsafeStoreFixedArrayElement(fixed_array, 0, string);
1696
1697 Goto(&done);
1698 }
1699 }
1700
1701 BIND(&next);
1702 }
1703
1704 // Loop preparations.
1705
1706 GrowableFixedArray array(state());
1707
1708 TVARIABLE(Smi, var_last_matched_until, SmiZero());
1709 TVARIABLE(Smi, var_next_search_from, SmiZero());
1710
1711 Label loop(this,
1712 {array.var_array(), array.var_length(), array.var_capacity(),
1713 &var_last_matched_until, &var_next_search_from}),
1714 push_suffix_and_out(this), out(this);
1715 Goto(&loop);
1716
1717 BIND(&loop);
1718 {
1719 TNode<Smi> next_search_from = var_next_search_from.value();
1720 TNode<Smi> last_matched_until = var_last_matched_until.value();
1721
1722 // We're done if we've reached the end of the string.
1723 GotoIf(SmiEqual(next_search_from, string_length), &push_suffix_and_out);
1724
1725 // Search for the given {regexp}.
1726
1728 context, regexp, data, string, next_search_from,
1729 result_offsets_vector, result_offsets_vector_length));
1730
1731 // We're done if no match was found.
1732 GotoIf(IntPtrEqual(num_matches, IntPtrConstant(0)), &push_suffix_and_out);
1733
1734 TNode<Int32T> match_from_int32 = UncheckedCast<Int32T>(
1735 Load(MachineType::Int32(), result_offsets_vector, IntPtrConstant(0)));
1736 TNode<Smi> match_from = SmiFromInt32(match_from_int32);
1737
1738 // We're also done if the match is at the end of the string.
1739 GotoIf(SmiEqual(match_from, string_length), &push_suffix_and_out);
1740
1741 // Set the LastMatchInfo.
1742 // TODO(jgruber): We could elide all but the last of these. BUT this is
1743 // tricky due to how we omit any match at the end of the string, which
1744 // makes it hard to tell if we're at the 'last match except for
1745 // empty-match-at-end-of-string'.
1746 CSA_DCHECK(this, TaggedEqual(context, LoadNativeContext(context)));
1747 TNode<RegExpMatchInfo> match_info = CAST(
1748 LoadContextElement(context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
1750 context, match_info, register_count_per_match, string,
1751 result_offsets_vector);
1752
1753 TNode<Smi> match_to = LoadArrayElement(match_info, IntPtrConstant(1));
1754
1755 // Advance index and continue if the match is empty.
1756 {
1757 Label next(this);
1758
1759 GotoIfNot(SmiEqual(match_to, next_search_from), &next);
1760 GotoIfNot(SmiEqual(match_to, last_matched_until), &next);
1761
1762 TNode<BoolT> is_unicode =
1763 Word32Or(FastFlagGetter(regexp, JSRegExp::kUnicode),
1764 FastFlagGetter(regexp, JSRegExp::kUnicodeSets));
1765 TNode<Number> new_next_search_from =
1766 AdvanceStringIndex(string, next_search_from, is_unicode, true);
1767 var_next_search_from = CAST(new_next_search_from);
1768 Goto(&loop);
1769
1770 BIND(&next);
1771 }
1772
1773 // A valid match was found, add the new substring to the array.
1774 {
1775 TNode<Smi> from = last_matched_until;
1776 TNode<Smi> to = match_from;
1777 array.Push(CallBuiltin(Builtin::kSubString, context, string, from, to));
1778 GotoIf(WordEqual(array.length(), int_limit), &out);
1779 }
1780
1781 // Add all captures to the array.
1782 {
1783 TNode<IntPtrT> int_num_registers =
1784 PositiveSmiUntag(register_count_per_match);
1785
1786 TVARIABLE(IntPtrT, var_reg, IntPtrConstant(2));
1787
1788 Label nested_loop(this, {array.var_array(), array.var_length(),
1789 array.var_capacity(), &var_reg}),
1790 nested_loop_out(this);
1791 Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop,
1792 &nested_loop_out);
1793
1794 BIND(&nested_loop);
1795 {
1796 TNode<IntPtrT> reg = var_reg.value();
1797 TNode<Smi> from = LoadArrayElement(match_info, reg);
1798 TNode<Smi> to = LoadArrayElement(match_info, reg, 1 * kTaggedSize);
1799
1800 Label select_capture(this), select_undefined(this), store_value(this);
1801 TVARIABLE(Object, var_value);
1802 Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined,
1803 &select_capture);
1804
1805 BIND(&select_capture);
1806 {
1807 var_value =
1808 CallBuiltin(Builtin::kSubString, context, string, from, to);
1809 Goto(&store_value);
1810 }
1811
1812 BIND(&select_undefined);
1813 {
1814 var_value = UndefinedConstant();
1815 Goto(&store_value);
1816 }
1817
1818 BIND(&store_value);
1819 {
1820 array.Push(var_value.value());
1821 GotoIf(WordEqual(array.length(), int_limit), &out);
1822
1824 var_reg = new_reg;
1825
1826 Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop,
1827 &nested_loop_out);
1828 }
1829 }
1830
1831 BIND(&nested_loop_out);
1832 }
1833
1834 var_last_matched_until = match_to;
1835 var_next_search_from = match_to;
1836 Goto(&loop);
1837 }
1838
1839 BIND(&push_suffix_and_out);
1840 {
1841 TNode<Smi> from = var_last_matched_until.value();
1842 TNode<Smi> to = string_length;
1843 array.Push(CallBuiltin(Builtin::kSubString, context, string, from, to));
1844 Goto(&out);
1845 }
1846
1847 BIND(&out);
1848 {
1849 var_result = array.ToJSArray(context);
1850 Goto(&done);
1851 }
1852
1853 BIND(&return_empty_array);
1854 {
1855 TNode<Smi> length = SmiZero();
1856 TNode<IntPtrT> capacity = IntPtrZero();
1857 std::optional<TNode<AllocationSite>> allocation_site = std::nullopt;
1858 CSA_DCHECK(this, TaggedEqual(context, LoadNativeContext(context)));
1859 TNode<Map> array_map =
1860 LoadJSArrayElementsMap(elements_kind, CAST(context));
1861 var_result = AllocateJSArray(elements_kind, array_map, capacity, length,
1862 allocation_site);
1863 Goto(&done);
1864 }
1865 }
1866
1867 BIND(&if_exception);
1868 FreeRegExpResultVector(result_offsets_vector,
1869 result_offsets_vector_is_dynamic);
1870 CallRuntime(Runtime::kReThrow, context, var_exception.value());
1871 Unreachable();
1872
1873 BIND(&done);
1874 FreeRegExpResultVector(result_offsets_vector,
1875 result_offsets_vector_is_dynamic);
1876 return var_result.value();
1877}
1878
1880 TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> subject,
1881 TNode<RegExpData> data, const VariableList& merge_vars,
1882 OncePerBatchFunction once_per_batch, OncePerMatchFunction once_per_match) {
1883 CSA_DCHECK(this, IsFastRegExpPermissive(context, regexp));
1884 CSA_DCHECK(this, FastFlagGetter(regexp, JSRegExp::kGlobal));
1885
1886 // This calls into irregexp and loops over the returned result. Roughly:
1887 //
1888 // max_matches = .. that fit into the given offsets array;
1889 // num_matches_in_batch = max_matches;
1890 // index = 0;
1891 // while (num_matches_in_batch == max_matches) {
1892 // num_matches_in_batch = ExecInternal(..., index);
1893 // for (i = 0; i < num_matches_in_batch; i++) {
1894 // .. handle match i
1895 // }
1896 // index = MaybeAdvanceZeroLength(last_end_index)
1897 // }
1898
1899 Label out(this);
1900
1901 // Exception handling is necessary to free any allocated memory.
1902 TVARIABLE(Object, var_exception);
1903 Label if_exception(this, Label::kDeferred);
1904
1905 // Determine the number of result slots we want and allocate them.
1906 TNode<Smi> register_count_per_match =
1908 // TODO(jgruber): Consider a different length selection that considers the
1909 // register count per match and can go higher than the current static offsets
1910 // size. Could be helpful for patterns that 1. have many captures and 2.
1911 // match many times in the given string.
1912 TNode<Smi> result_offsets_vector_length =
1913 SmiMax(register_count_per_match,
1915 TNode<RawPtrT> result_offsets_vector;
1916 TNode<BoolT> result_offsets_vector_is_dynamic;
1917 std::tie(result_offsets_vector, result_offsets_vector_is_dynamic) =
1918 LoadOrAllocateRegExpResultVector(result_offsets_vector_length);
1919
1920 TNode<BoolT> is_unicode =
1921 Word32Or(FastFlagGetter(regexp, JSRegExp::kUnicode),
1922 FastFlagGetter(regexp, JSRegExp::kUnicodeSets));
1923
1924 TVARIABLE(IntPtrT, var_last_match_offsets_vector, IntPtrConstant(0));
1925 TVARIABLE(Int32T, var_start_of_last_match, Int32Constant(0));
1926 TVARIABLE(Int32T, var_last_index, Int32Constant(0));
1927 FastStoreLastIndex(regexp, SmiConstant(0));
1928
1929 TNode<IntPtrT> max_matches_in_batch =
1930 IntPtrDiv(SmiUntag(result_offsets_vector_length),
1931 SmiUntag(register_count_per_match));
1932 // Initialize such that we always enter the loop initially:
1933 TVARIABLE(IntPtrT, var_num_matches_in_batch, max_matches_in_batch);
1934 TVARIABLE(IntPtrT, var_num_matches, IntPtrConstant(0));
1935
1936 // Loop over multiple batch executions:
1937 VariableList outer_loop_merge_vars(
1938 {&var_num_matches_in_batch, &var_num_matches, &var_last_index,
1939 &var_start_of_last_match, &var_last_match_offsets_vector},
1940 zone());
1941 outer_loop_merge_vars.insert(outer_loop_merge_vars.end(), merge_vars.begin(),
1942 merge_vars.end());
1943 Label outer_loop(this, outer_loop_merge_vars);
1944 Label outer_loop_exit(this);
1945 Goto(&outer_loop);
1946 BIND(&outer_loop);
1947 {
1948 // Loop condition:
1949 GotoIf(
1950 IntPtrLessThan(var_num_matches_in_batch.value(), max_matches_in_batch),
1951 &outer_loop_exit);
1952
1953 compiler::ScopedExceptionHandler handler(this, &if_exception,
1954 &var_exception);
1955
1956 var_num_matches_in_batch = UncheckedCast<IntPtrT>(RegExpExecInternal(
1957 context, regexp, data, subject, SmiFromInt32(var_last_index.value()),
1958 result_offsets_vector, SmiToInt32(result_offsets_vector_length)));
1959
1960 GotoIf(IntPtrEqual(var_num_matches_in_batch.value(), IntPtrConstant(0)),
1961 &outer_loop_exit);
1962
1963 var_num_matches =
1964 IntPtrAdd(var_num_matches.value(), var_num_matches_in_batch.value());
1965
1966 // At least one match was found. Construct the result array.
1967 //
1968 // Loop over the current batch of results:
1969 {
1970 once_per_batch(var_num_matches_in_batch.value());
1971
1972 TNode<IntPtrT> register_count_per_match_intptr =
1973 SmiUntag(register_count_per_match);
1974 VariableList inner_loop_merge_vars(
1975 {&var_last_index, &var_start_of_last_match,
1976 &var_last_match_offsets_vector},
1977 zone());
1978 inner_loop_merge_vars.insert(inner_loop_merge_vars.end(),
1979 merge_vars.begin(), merge_vars.end());
1980 // Has to be IntPtrT for BuildFastLoop.
1981 TNode<IntPtrT> inner_loop_start =
1982 UncheckedCast<IntPtrT>(result_offsets_vector);
1983 TNode<IntPtrT> inner_loop_increment = WordShl(
1984 register_count_per_match_intptr, IntPtrConstant(kInt32SizeLog2));
1985 TNode<IntPtrT> inner_loop_end = IntPtrAdd(
1986 inner_loop_start,
1987 IntPtrMul(inner_loop_increment, var_num_matches_in_batch.value()));
1988
1989 TVARIABLE(IntPtrT, var_inner_loop_index);
1991 inner_loop_merge_vars, var_inner_loop_index, inner_loop_start,
1992 inner_loop_end,
1993 [&](TNode<IntPtrT> current_match_offsets_vector) {
1995 Load(MachineType::Int32(), current_match_offsets_vector,
1996 IntPtrConstant(0)));
1998 Load(MachineType::Int32(), current_match_offsets_vector,
2000
2001 once_per_match(UncheckedCast<RawPtrT>(current_match_offsets_vector),
2002 start, end);
2003
2004 var_last_match_offsets_vector = current_match_offsets_vector;
2005 var_start_of_last_match = start;
2006 var_last_index = end;
2007 },
2008 inner_loop_increment, LoopUnrollingMode::kYes,
2010 }
2011
2012 GotoIf(
2013 Word32NotEqual(var_start_of_last_match.value(), var_last_index.value()),
2014 &outer_loop, GotoHint::kLabel);
2015
2016 // For zero-length matches we need to run AdvanceStringIndex.
2017 var_last_index = SmiToInt32(CAST(AdvanceStringIndex(
2018 subject, SmiFromInt32(var_last_index.value()), is_unicode, true)));
2019
2020 Goto(&outer_loop);
2021 }
2022 BIND(&outer_loop_exit);
2023
2024 // If there were no matches, just return.
2025 GotoIf(IntPtrEqual(var_num_matches.value(), IntPtrConstant(0)), &out);
2026
2027 // Otherwise initialize the last match info and the result JSArray.
2028 CSA_DCHECK(this, TaggedEqual(context, LoadNativeContext(context)));
2029 TNode<RegExpMatchInfo> last_match_info =
2030 CAST(LoadContextElement(context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
2031
2032 InitializeMatchInfoFromRegisters(context, last_match_info,
2033 register_count_per_match, subject,
2034 var_last_match_offsets_vector.value());
2035
2036 Goto(&out);
2037
2038 BIND(&if_exception);
2039 FreeRegExpResultVector(result_offsets_vector,
2040 result_offsets_vector_is_dynamic);
2041 CallRuntime(Runtime::kReThrow, context, var_exception.value());
2042 Unreachable();
2043
2044 BIND(&out);
2045 FreeRegExpResultVector(result_offsets_vector,
2046 result_offsets_vector_is_dynamic);
2047 return var_num_matches.value();
2048}
2049
2051 TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> subject,
2052 TNode<RegExpData> data) {
2053 CSA_DCHECK(this, IsFastRegExpPermissive(context, regexp));
2054 CSA_DCHECK(this, FastFlagGetter(regexp, JSRegExp::kGlobal));
2055
2056 TVARIABLE((Union<Null, JSArray>), var_result, NullConstant());
2057 Label out(this);
2058 GrowableFixedArray array(state());
2059
2060 VariableList merge_vars(
2061 {array.var_array(), array.var_length(), array.var_capacity()}, zone());
2063 context, regexp, subject, data, merge_vars,
2064 [&](TNode<IntPtrT> num_matches_in_batch) {
2065 array.Reserve(UncheckedCast<IntPtrT>(
2066 IntPtrAdd(array.length(), num_matches_in_batch)));
2067 },
2068 [&](TNode<RawPtrT> match_offsets, TNode<Int32T> match_start,
2069 TNode<Int32T> match_end) {
2070 TNode<Smi> start = SmiFromInt32(match_start);
2071 TNode<Smi> end = SmiFromInt32(match_end);
2072
2073 // TODO(jgruber): Consider inlining this or at least reducing the number
2074 // of redundant checks.
2075 TNode<String> matched_string = CAST(
2076 CallBuiltin(Builtin::kSubString, context, subject, start, end));
2077 array.Push(matched_string);
2078 });
2079
2080 CSA_DCHECK(this, IntPtrEqual(num_matches, array.length()));
2081
2082 // No matches, return null.
2083 GotoIf(IntPtrEqual(num_matches, IntPtrConstant(0)), &out);
2084
2085 // Otherwise create the JSArray.
2086 var_result = array.ToJSArray(context);
2087 Goto(&out);
2088
2089 BIND(&out);
2090 return var_result.value();
2091}
2092
2094 TNode<Context> context, TNode<String> to_string, TNode<String> from_string,
2095 TNode<Smi> slice_start, TNode<Smi> slice_end) {
2096 // TODO(jgruber): Consider inlining this.
2097 CSA_DCHECK(this, SmiLessThanOrEqual(slice_start, slice_end));
2098 TNode<String> slice = CAST(CallBuiltin(Builtin::kSubString, context,
2099 from_string, slice_start, slice_end));
2100 return CAST(
2101 CallBuiltin(Builtin::kStringAdd_CheckNone, context, to_string, slice));
2102}
2103
2105 TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> subject,
2106 TNode<RegExpData> data, TNode<String> replace_string) {
2107 CSA_DCHECK(this, IsFastRegExpPermissive(context, regexp));
2108 CSA_DCHECK(this, FastFlagGetter(regexp, JSRegExp::kGlobal));
2109
2110 // The replace_string is 'simple' if it doesn't contain a '$' character.
2111 CSA_SLOW_DCHECK(this,
2113 context, replace_string),
2114 SmiConstant(-1)));
2115
2116 TNode<Smi> replace_string_length = LoadStringLengthAsSmi(replace_string);
2117
2118 TVARIABLE(String, var_result, EmptyStringConstant());
2119 TVARIABLE(Smi, var_last_match_end, SmiConstant(0));
2120
2121 VariableList merge_vars({&var_result, &var_last_match_end}, zone());
2123 context, regexp, subject, data, merge_vars,
2124 [&](TNode<IntPtrT> num_matches_in_batch) {},
2125 [&](TNode<RawPtrT> match_offsets, TNode<Int32T> match_start,
2126 TNode<Int32T> match_end) {
2127 TNode<Smi> start = SmiFromInt32(match_start);
2128 TNode<Smi> end = SmiFromInt32(match_end);
2129
2130 // Append the slice between this and the previous match.
2131 var_result = AppendStringSlice(context, var_result.value(), subject,
2132 var_last_match_end.value(), start);
2133
2134 // Append the replace_string.
2135 {
2136 Label next(this);
2137 GotoIf(SmiEqual(replace_string_length, SmiConstant(0)), &next);
2138
2139 var_result = CAST(CallBuiltin(Builtin::kStringAdd_CheckNone, context,
2140 var_result.value(), replace_string));
2141 Goto(&next);
2142
2143 BIND(&next);
2144 }
2145
2146 var_last_match_end = end;
2147 });
2148
2149 var_result = AppendStringSlice(context, var_result.value(), subject,
2150 var_last_match_end.value(),
2151 LoadStringLengthAsSmi(subject));
2152 return var_result.value();
2153}
2154
2156
2157} // namespace internal
2158} // namespace v8
#define BIND(label)
#define TVARIABLE(...)
#define CSA_SLOW_DCHECK(csa,...)
#define CSA_SBXCHECK(csa,...)
#define CSA_DCHECK(csa,...)
#define CSA_CHECK(csa, x)
#define CASE_FOR_FLAG(Lower, Camel,...)
#define TF_BUILTIN(Name, AssemblerBase)
Builtins::Kind kind
Definition builtins.cc:40
TNode< String > AllocateSeqOneByteString(uint32_t length, AllocationFlags flags=AllocationFlag::kNone)
TNode< JSArray > AllocateJSArray(ElementsKind kind, TNode< Map > array_map, TNode< IntPtrT > capacity, TNode< Smi > length, std::optional< TNode< AllocationSite > > allocation_site, AllocationFlags allocation_flags=AllocationFlag::kNone)
TNode< IntPtrT > LoadAndUntagFixedArrayBaseLength(TNode< FixedArrayBase > array)
TNode< Smi > SmiFromInt32(TNode< Int32T > value)
TNode< Smi > SmiShl(TNode< Smi > a, int shift)
void NameDictionaryLookup(TNode< Dictionary > dictionary, TNode< Name > unique_name, Label *if_found, TVariable< IntPtrT > *var_name_index, Label *if_not_found, LookupMode mode=kFindExisting)
TNode< Int32T > TruncateIntPtrToInt32(TNode< IntPtrT > value)
TNode< Map > LoadSlowObjectWithNullPrototypeMap(TNode< NativeContext > native_context)
TNode< TrustedObject > LoadTrustedPointerFromObject(TNode< HeapObject > object, int offset, IndirectPointerTag tag)
TNode< SwissNameDictionary > AllocateSwissNameDictionary(TNode< IntPtrT > at_least_space_for)
TNode< IntPtrT > OffsetOfElementAt(TNode< TIndex > index)
TNode< JSAny > GetProperty(TNode< Context > context, TNode< JSAny > receiver, Handle< Name > name)
TNode< Smi > SmiTag(TNode< IntPtrT > value)
void Increment(TVariable< TIndex > *variable, int value=1)
TNode< Smi > LoadSmiArrayLength(TNode< Array > array)
TNode< BoolT > IsNumberPositive(TNode< Number > number)
TNode< BoolT > TaggedEqual(TNode< AnyTaggedT > a, TNode< AnyTaggedT > b)
TNode< HeapObject > Allocate(TNode< IntPtrT > size, AllocationFlags flags=AllocationFlag::kNone)
void StoreObjectFieldRoot(TNode< HeapObject > object, int offset, RootIndex root)
TNode< FixedArrayBase > LoadElements(TNode< JSObject > object)
TNode< T > LoadObjectField(TNode< HeapObject > object, int offset)
TNode< NameDictionary > AllocateNameDictionary(int at_least_space_for)
TNode< BoolT > TaggedIsNotSmi(TNode< MaybeObject > a)
TNode< String > ToString_Inline(TNode< Context > context, TNode< Object > input)
TNode< BoolT > TaggedIsPositiveSmi(TNode< Object > a)
TNode< Smi > LoadStringLengthAsSmi(TNode< String > string)
std::pair< TNode< JSArray >, TNode< FixedArrayBase > > AllocateUninitializedJSArrayWithElements(ElementsKind kind, TNode< Map > array_map, TNode< Smi > length, std::optional< TNode< AllocationSite > > allocation_site, TNode< IntPtrT > capacity, AllocationFlags allocation_flags=AllocationFlag::kNone, int array_header_size=JSArray::kHeaderSize)
void StoreValueByKeyIndex(TNode< ContainerType > container, TNode< IntPtrT > key_index, TNode< Object > value, WriteBarrierMode write_barrier=UPDATE_WRITE_BARRIER)
TNode< Uint16T > StringCharCodeAt(TNode< String > string, TNode< UintPtrT > index)
TNode< JSPrototype > LoadMapPrototype(TNode< Map > map)
TNode< IntPtrT > SmiUntag(TNode< Smi > value)
TNode< Map > LoadJSArrayElementsMap(ElementsKind kind, TNode< NativeContext > native_context)
TNode< BoolT > IsSetSmi(TNode< Smi > smi, int untagged_mask)
TNode< BoolT > IsNumberNormalized(TNode< Number > number)
TNode< Object > LoadValueByKeyIndex(TNode< ContainerType > container, TNode< IntPtrT > key_index)
void BuildFastLoop(const VariableList &vars, TVariable< TIndex > &var_index, TNode< TIndex > start_index, TNode< TIndex > end_index, const FastLoopBody< TIndex > &body, TNode< TIndex > increment, LoopUnrollingMode unrolling_mode, IndexAdvanceMode advance_mode, IndexAdvanceDirection advance_direction)
TNode< Map > LoadReceiverMap(TNode< Object > receiver)
void StoreObjectFieldNoWriteBarrier(TNode< HeapObject > object, TNode< IntPtrT > offset, TNode< T > value)
TNode< Object > LoadFixedArrayElement(TNode< FixedArray > object, TNode< TIndex > index, int additional_offset=0, CheckBounds check_bounds=CheckBounds::kAlways)
void IncrementCounter(StatsCounter *counter, int delta)
TNode< BoolT > HasInstanceType(TNode< HeapObject > object, InstanceType type)
TNode< IntPtrT > ElementOffsetFromIndex(TNode< TIndex > index, ElementsKind kind, int base_size=0)
void UnsafeStoreFixedArrayElement(TNode< FixedArray > object, int index, TNode< Object > value, WriteBarrierMode barrier_mode=UPDATE_WRITE_BARRIER)
TNode< IntPtrT > LoadStringLengthAsWord(TNode< String > string)
TNode< Smi > SmiShr(TNode< Smi > a, int shift)
TNode< NativeContext > LoadNativeContext(TNode< Context > context)
TNode< T > Select(TNode< BoolT > condition, const NodeGenerator< T > &true_body, const NodeGenerator< T > &false_body, BranchHint branch_hint=BranchHint::kNone)
TNode< BoolT > TaggedIsSmi(TNode< MaybeObject > a)
TNode< BoolT > IsJSRegExp(TNode< HeapObject > object)
TNode< Object > SetPropertyStrict(TNode< Context > context, TNode< JSAny > receiver, TNode< Object > key, TNode< Object > value)
TNode< Map > LoadMap(TNode< HeapObject > object)
TNode< Int32T > SmiToInt32(TNode< Smi > value)
TNode< JSObject > AllocateJSObjectFromMap(TNode< Map > map, std::optional< TNode< HeapObject > > properties=std::nullopt, std::optional< TNode< FixedArray > > elements=std::nullopt, AllocationFlags flags=AllocationFlag::kNone, SlackTrackingMode slack_tracking_mode=kNoSlackTracking)
Uint32LessThanOrEqual IntPtrGreaterThanOrEqual
TNode< IntPtrT > PositiveSmiUntag(TNode< Smi > value)
void StoreObjectField(TNode< HeapObject > object, int offset, TNode< Smi > value)
void FillFixedArrayWithValue(ElementsKind kind, TNode< FixedArrayBase > array, TNode< TIndex > from_index, TNode< TIndex > to_index, RootIndex value_root_index)
TNode< TValue > LoadArrayElement(TNode< Array > array, int array_header_size, TNode< TIndex > index, int additional_offset=0)
Uint32LessThanOrEqual Int32GreaterThanOrEqual TNode< Smi > SmiMax(TNode< Smi > a, TNode< Smi > b)
void StoreMapNoWriteBarrier(TNode< HeapObject > object, RootIndex map_root_index)
void AddToDictionary(TNode< Dictionary > dictionary, TNode< Name > key, TNode< Object > value, Label *bailout, std::optional< TNode< IntPtrT > > insertion_index=std::nullopt)
TNode< RawPtrT > LoadCodeInstructionStart(TNode< Code > code, CodeEntrypointTag tag)
TNode< JSObject > FastNewObject(TNode< Context > context, TNode< JSFunction > target, TNode< JSReceiver > new_target)
static V8_EXPORT_PRIVATE ExternalReference isolate_address()
static ExternalReference Create(const SCTableReference &table_ref)
static V8_INLINE constexpr bool IsValidSmi(T value)
static const int kJSRegexpStaticOffsetsVectorSize
Definition isolate.h:1533
static constexpr uint32_t kMaxFastArrayLength
Definition js-array.h:136
static constexpr int kLastIndexFieldIndex
Definition js-regexp.h:114
static constexpr int kMaxCaptures
Definition js-regexp.h:140
static constexpr int kExecFunctionDescriptorIndex
Definition js-regexp.h:122
static constexpr int kSymbolSearchFunctionDescriptorIndex
Definition js-regexp.h:126
static constexpr int kAtomCaptureCount
Definition js-regexp.h:99
static constexpr int kSymbolMatchFunctionDescriptorIndex
Definition js-regexp.h:123
static constexpr MachineType Pointer()
static constexpr MachineType Int32()
static constexpr MachineType AnyTagged()
static constexpr MachineType Uint32()
static constexpr MachineType TaggedPointer()
static constexpr MachineRepresentation PointerRepresentation()
static constexpr MachineType IntPtr()
void CheckAndBranch(TNode< HeapObject > prototype, Label *if_unmodified, Label *if_modified)
TNode< String > RegExpReplaceGlobalSimpleString(TNode< Context > context, TNode< JSRegExp > regexp, TNode< String > subject, TNode< RegExpData > data, TNode< String > replace_string)
TNode< Object > FastLoadLastIndexBeforeSmiCheck(TNode< JSRegExp > regexp)
TNode< UintPtrT > RegExpExecAtom(TNode< Context > context, TNode< AtomRegExpData > data, TNode< String > string, TNode< Smi > last_index, TNode< RawPtrT > result_offsets_vector, TNode< Int32T > result_offsets_vector_length)
std::pair< TNode< RawPtrT >, TNode< BoolT > > LoadOrAllocateRegExpResultVector(TNode< Smi > register_count)
void BranchIfRegExpResult(const TNode< Context > context, const TNode< Object > object, Label *if_isunmodified, Label *if_ismodified)
TNode< Union< Null, JSArray > > RegExpMatchGlobal(TNode< Context > context, TNode< JSRegExp > regexp, TNode< String > subject, TNode< RegExpData > data)
TNode< UintPtrT > RegExpExecInternal(TNode< Context > context, TNode< JSRegExp > regexp, TNode< RegExpData > data, TNode< String > string, TNode< Number > last_index, TNode< RawPtrT > result_offsets_vector, TNode< Int32T > result_offsets_vector_length)
void BranchIfFastRegExpForMatch(TNode< Context > context, TNode< HeapObject > object, Label *if_isunmodified, Label *if_ismodified)
TNode< JSRegExpResult > ConstructNewResultFromMatchInfo(TNode< Context > context, TNode< JSRegExp > regexp, TNode< RegExpMatchInfo > match_info, TNode< String > string, TNode< Number > last_index)
void FastStoreLastIndex(TNode< JSRegExp > regexp, TNode< Smi > value)
TNode< Object > RegExpInitialize(const TNode< Context > context, const TNode< JSRegExp > regexp, const TNode< Object > maybe_pattern, const TNode< Object > maybe_flags)
std::function< void(TNode< IntPtrT >)> OncePerBatchFunction
TNode< JSAny > SlowLoadLastIndex(TNode< Context > context, TNode< JSAny > regexp)
TNode< RegExpMatchInfo > RegExpExecInternal_Single(TNode< Context > context, TNode< JSRegExp > regexp, TNode< String > string, TNode< Number > last_index, Label *if_not_matched)
void FreeRegExpResultVector(TNode< RawPtrT > result_vector, TNode< BoolT > is_dynamic)
TNode< Smi > LoadCaptureCount(TNode< RegExpData > data)
TNode< String > FlagsGetter(TNode< Context > context, TNode< JSAny > regexp, const bool is_fastpath)
void BranchIfFastRegExp_Strict(TNode< Context > context, TNode< HeapObject > object, Label *if_isunmodified, Label *if_ismodified)
TNode< BoolT > IsFastRegExpNoPrototype(TNode< Context > context, TNode< Object > object)
void BranchIfFastRegExp(TNode< Context > context, TNode< HeapObject > object, TNode< Map > map, PrototypeCheckAssembler::Flags prototype_check_flags, std::optional< DescriptorIndexNameValue > additional_property_to_check, Label *if_isunmodified, Label *if_ismodified)
TNode< JSRegExpResult > AllocateRegExpResult(TNode< Context > context, TNode< Smi > length, TNode< Smi > index, TNode< String > input, TNode< JSRegExp > regexp, TNode< Number > last_index, TNode< BoolT > has_indices, TNode< FixedArray > *elements_out=nullptr)
std::function< void(TNode< RawPtrT >, TNode< Int32T >, TNode< Int32T >)> OncePerMatchFunction
TNode< RegExpMatchInfo > InitializeMatchInfoFromRegisters(TNode< Context > context, TNode< RegExpMatchInfo > match_info, TNode< Smi > register_count, TNode< String > subject, TNode< RawPtrT > result_offsets_vector)
TNode< BoolT > FastFlagGetter(TNode< JSRegExp > regexp, JSRegExp::Flag flag)
TNode< Smi > RegistersForCaptureCount(TNode< Smi > capture_count)
void BranchIfFastRegExp_Permissive(TNode< Context > context, TNode< HeapObject > object, Label *if_isunmodified, Label *if_ismodified)
TNode< String > AppendStringSlice(TNode< Context > context, TNode< String > to_string, TNode< String > from_string, TNode< Smi > slice_start, TNode< Smi > slice_end)
void SlowStoreLastIndex(TNode< Context > context, TNode< JSAny > regexp, TNode< Object > value)
TNode< Number > AdvanceStringIndex(TNode< String > string, TNode< Number > index, TNode< BoolT > is_unicode, bool is_fastpath)
TNode< IntPtrT > RegExpExecInternal_Batched(TNode< Context > context, TNode< JSRegExp > regexp, TNode< String > subject, TNode< RegExpData > data, const VariableList &merge_vars, OncePerBatchFunction once_per_batch, OncePerMatchFunction once_per_match)
TNode< JSArray > RegExpPrototypeSplitBody(TNode< Context > context, TNode< JSRegExp > regexp, TNode< String > string, TNode< Smi > limit)
void BranchIfFastRegExpForSearch(TNode< Context > context, TNode< HeapObject > object, Label *if_isunmodified, Label *if_ismodified)
void 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)
TNode< JSAny > CreateRegExpStringIterator(TNode< NativeContext > native_context, TNode< JSAny > regexp, TNode< String > string, TNode< BoolT > global, TNode< BoolT > full_unicode)
static constexpr int kMinCapacity
static constexpr int kInternalRegExpException
Definition regexp.h:136
static constexpr int kInternalRegExpSuccess
Definition regexp.h:135
static constexpr int kInternalRegExpRetry
Definition regexp.h:137
static constexpr int kInternalRegExpFallbackToExperimental
Definition regexp.h:138
static constexpr int kInternalRegExpFailure
Definition regexp.h:134
static constexpr int kMaxValue
Definition smi.h:101
TNode< Smi > IndexOfDollarChar(const TNode< Context > context, const TNode< String > string)
static const uint32_t kMaxLength
Definition string.h:511
TNode< RawPtrT > PointerToData(Label *if_bailout)
T * insert(const T *pos, It first, It last)
TNode< BoolT > Word32NotEqual(TNode< Word32T > left, TNode< Word32T > right)
Node * CallCFunctionWithoutFunctionDescriptor(Node *function, MachineType return_type, CArgs... cargs)
TNode< IntPtrT > IntPtrMul(TNode< IntPtrT > left, TNode< IntPtrT > right)
TNode< IntPtrT > IntPtrAdd(TNode< IntPtrT > left, TNode< IntPtrT > right)
TNode< IntPtrT > IntPtrConstant(intptr_t value)
TNode< T > UncheckedCast(Node *value)
TNode< IntPtrT > WordShl(TNode< IntPtrT > left, TNode< IntegralT > right)
TNode< BoolT > WordEqual(TNode< WordT > left, TNode< WordT > right)
void GotoIfNot(TNode< IntegralT > condition, Label *false_label, GotoHint goto_hint=GotoHint::kNone)
TNode< IntPtrT > WordSar(TNode< IntPtrT > left, TNode< IntegralT > right)
TNode< Object > LoadFullTagged(Node *base)
TNode< Uint32T > Unsigned(TNode< Word32T > x)
TNode< Int32T > Word32And(TNode< Int32T > left, TNode< Int32T > right)
TNode< T > ReinterpretCast(Node *value)
TNode< Smi > SmiConstant(Tagged< Smi > value)
void GotoIf(TNode< IntegralT > condition, Label *true_label, GotoHint goto_hint=GotoHint::kNone)
Node * Load(MachineType type, Node *base)
TNode< IntPtrT > ChangeInt32ToIntPtr(TNode< Word32T > value)
TNode< Int32T > Word32Or(TNode< Int32T > left, TNode< Int32T > right)
TNode< Int32T > Word32Shl(TNode< Int32T > left, TNode< Int32T > right)
TNode< BoolT > IntPtrEqual(TNode< WordT > left, TNode< WordT > right)
void Switch(Node *index, Label *default_label, const int32_t *case_values, Label **case_labels, size_t case_count)
TNode< IntPtrT > IntPtrSub(TNode< IntPtrT > left, TNode< IntPtrT > right)
TNode< ExternalReference > ExternalConstant(ExternalReference address)
TNode< Int32T > Int32Constant(int32_t value)
TNode< BoolT > WordNotEqual(TNode< WordT > left, TNode< WordT > right)
Node * CallCFunction(Node *function, std::optional< MachineType > return_type, CArgs... cargs)
TNode< Uint32T > Uint32Constant(uint32_t value)
TNode< Type > HeapConstantNoHole(Handle< Type > object)
TNode< BoolT > Word32Equal(TNode< Word32T > left, TNode< Word32T > right)
TNode< T > CallRuntime(Runtime::FunctionId function, TNode< Object > context, TArgs... args)
TNode< UintPtrT > UintPtrConstant(uintptr_t value)
TNode< UintPtrT > WordShr(TNode< UintPtrT > left, TNode< IntegralT > right)
TNode< T > CallBuiltin(Builtin id, TNode< Object > context, TArgs... args)
void Branch(TNode< IntegralT > condition, Label *true_label, Label *false_label, BranchHint branch_hint=BranchHint::kNone)
void StoreNoWriteBarrier(MachineRepresentation rep, Node *base, Node *value)
#define CAST(x)
#define V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
Definition globals.h:242
int start
int end
DirectHandle< Object > new_target
Definition execution.cc:75
Isolate * isolate
int32_t offset
TNode< Object > receiver
std::string pattern
ZoneVector< RpoNumber > & result
LiftoffRegister reg
MovableLabel handler
LiftoffAssembler::CacheState state
uint32_t const mask
InstructionOperand source
constexpr unsigned CountTrailingZeros(T value)
Definition bits.h:144
constexpr int kTaggedSize
Definition globals.h:542
@ UNSAFE_SKIP_WRITE_BARRIER
Definition objects.h:53
constexpr int kInt32Size
Definition globals.h:401
constexpr IndirectPointerHandle kNullIndirectPointerHandle
template const char * string
constexpr int kNumRegisters
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define arraysize(array)
Definition macros.h:67