v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-string-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 "src/base/strings.h"
14#include "src/heap/heap-inl.h"
17#include "src/objects/objects.h"
19
20namespace v8 {
21namespace internal {
22
24
26 TNode<String> string, TNode<Word32T> string_instance_type) {
27 // Compute the effective offset of the first character.
28 TVARIABLE(RawPtrT, var_data);
29 Label if_sequential(this), if_external(this), if_join(this);
30 Branch(Word32Equal(Word32And(string_instance_type,
33 &if_sequential, &if_external);
34
35 BIND(&if_sequential);
36 {
42 Goto(&if_join);
43 }
44
45 BIND(&if_external);
46 {
47 var_data = LoadExternalStringResourceDataPtr(CAST(string));
48 Goto(&if_join);
49 }
50
51 BIND(&if_join);
52 return var_data.value();
53}
54
55template <typename SubjectChar, typename PatternChar>
57 const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
58 const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
59 const TNode<IntPtrT> start_position) {
60 const TNode<ExternalReference> function_addr = ExternalConstant(
62 const TNode<ExternalReference> isolate_ptr =
64
66 MachineType type_intptr = MachineType::IntPtr();
67
69 function_addr, type_intptr, std::make_pair(type_ptr, isolate_ptr),
70 std::make_pair(type_ptr, subject_ptr),
71 std::make_pair(type_intptr, subject_length),
72 std::make_pair(type_ptr, search_ptr),
73 std::make_pair(type_intptr, search_length),
74 std::make_pair(type_intptr, start_position)));
75
76 return result;
77}
79 const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
80 const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
81 const TNode<IntPtrT> start_position) {
83 subject_ptr, subject_length, search_ptr, search_length, start_position);
84}
86 const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
87 const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
88 const TNode<IntPtrT> start_position) {
90 subject_ptr, subject_length, search_ptr, search_length, start_position);
91}
93 const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
94 const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
95 const TNode<IntPtrT> start_position) {
97 subject_ptr, subject_length, search_ptr, search_length, start_position);
98}
100 const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
101 const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
102 const TNode<IntPtrT> start_position) {
104 subject_ptr, subject_length, search_ptr, search_length, start_position);
105}
107 const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
108 const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> start_position) {
109 const TNode<RawPtrT> subject_start_ptr =
110 RawPtrAdd(subject_ptr, start_position);
111 const TNode<IntPtrT> search_byte =
114 Unsigned(IntPtrSub(subject_length, start_position));
115 const TNode<ExternalReference> memchr =
116 ExternalConstant(ExternalReference::libc_memchr_function());
117 const TNode<RawPtrT> result_address = UncheckedCast<RawPtrT>(
119 std::make_pair(MachineType::Pointer(), subject_start_ptr),
120 std::make_pair(MachineType::IntPtr(), search_byte),
121 std::make_pair(MachineType::UintPtr(), search_length)));
122 return Select<IntPtrT>(
123 WordEqual(result_address, IntPtrConstant(0)),
124 [=, this] { return IntPtrConstant(-1); },
125 [=, this] {
126 return IntPtrAdd(RawPtrSub(result_address, subject_start_ptr),
127 start_position);
128 });
129}
130
132 TNode<String> right,
133 TNode<IntPtrT> length) {
134 TVARIABLE(String, var_left, left);
135 TVARIABLE(String, var_right, right);
136 Label if_equal(this), if_notequal(this), if_indirect(this, Label::kDeferred),
137 start(this, {&var_left, &var_right});
138
139 // Callers must handle the case where {lhs} and {rhs} refer to the same
140 // String object.
141 CSA_DCHECK(this, TaggedNotEqual(left, right));
142
143 CSA_DCHECK(this, IntPtrEqual(LoadStringLengthAsWord(left), length));
144 CSA_DCHECK(this, IntPtrEqual(LoadStringLengthAsWord(right), length));
145
146 Goto(&start);
147 BIND(&start);
148 TNode<String> lhs = var_left.value();
149 TNode<String> rhs = var_right.value();
150
151 TNode<Uint16T> lhs_instance_type = LoadInstanceType(lhs);
152 TNode<Uint16T> rhs_instance_type = LoadInstanceType(rhs);
153
154 StringEqual_Core(lhs, lhs_instance_type, rhs, rhs_instance_type, length,
155 &if_equal, &if_notequal, &if_indirect);
156
157 BIND(&if_indirect);
158 {
159 Label restart(this, {&var_left, &var_right});
160 // Try to unwrap indirect strings, restart the above attempt on success.
161 MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
162 rhs_instance_type, &restart);
163
164 TailCallRuntime(Runtime::kStringEqual, NoContextConstant(), lhs, rhs);
165
166 BIND(&restart);
167 GotoIf(TaggedEqual(var_left.value(), var_right.value()), &if_equal);
168 Goto(&start);
169 }
170
171 BIND(&if_equal);
172 Return(TrueConstant());
173
174 BIND(&if_notequal);
175 Return(FalseConstant());
176}
177
179 TNode<String> lhs, TNode<Word32T> lhs_instance_type, TNode<String> rhs,
180 TNode<Word32T> rhs_instance_type, TNode<IntPtrT> length, Label* if_equal,
181 Label* if_not_equal, Label* if_indirect) {
182 CSA_DCHECK(this, WordEqual(LoadStringLengthAsWord(lhs), length));
183 CSA_DCHECK(this, WordEqual(LoadStringLengthAsWord(rhs), length));
184
185 // Callers must handle the case where {lhs} and {rhs} refer to the same
186 // String object.
187 CSA_DCHECK(this, TaggedNotEqual(lhs, rhs));
188
189 // Combine the instance types into a single 16-bit value, so we can check
190 // both of them at once.
191 TNode<Word32T> both_instance_types = Word32Or(
192 lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
193
194 // Check if both {lhs} and {rhs} are internalized. Since we already know
195 // that they're not the same object, they're not equal in that case.
196 int const kBothInternalizedMask =
198 int const kBothInternalizedTag = kInternalizedTag | (kInternalizedTag << 8);
199 GotoIf(Word32Equal(Word32And(both_instance_types,
200 Int32Constant(kBothInternalizedMask)),
201 Int32Constant(kBothInternalizedTag)),
202 if_not_equal);
203
204 // Check if both {lhs} and {rhs} are direct strings, and that in case of
205 // ExternalStrings the data pointer is cached.
206 static_assert(kUncachedExternalStringTag != 0);
207 static_assert(kIsIndirectStringTag != 0);
208 int const kBothDirectStringMask =
211 GotoIfNot(Word32Equal(Word32And(both_instance_types,
212 Int32Constant(kBothDirectStringMask)),
213 Int32Constant(0)),
214 if_indirect);
215
216 Label if_skip_fast_case(this), if_fast_case(this), if_oneonebytestring(this),
217 if_twotwobytestring(this), if_onetwobytestring(this),
218 if_twoonebytestring(this);
219
220 // Dispatch based on the {lhs} and {rhs} string encoding.
221 int const kBothStringEncodingMask =
223 int const kBothExternalStringTag =
225 int const kOneOneByteStringTag = kOneByteStringTag | (kOneByteStringTag << 8);
226 int const kTwoTwoByteStringTag = kTwoByteStringTag | (kTwoByteStringTag << 8);
227 int const kOneTwoByteStringTag = kOneByteStringTag | (kTwoByteStringTag << 8);
228
229 TNode<Word32T> masked_instance_types =
230 Word32And(both_instance_types, Int32Constant(kBothStringEncodingMask));
231 TNode<Word32T> both_are_one_byte =
232 Word32Equal(masked_instance_types, Int32Constant(kOneOneByteStringTag));
233 TNode<Word32T> both_are_two_byte =
234 Word32Equal(masked_instance_types, Int32Constant(kTwoTwoByteStringTag));
235
236 // If both strings are not external we know that their payload length is
237 // kTagged sized. When they have the same type we can compare in chunks. The
238 // padding bytes are set to zero.
239 GotoIf(Word32And(both_instance_types, Int32Constant(kBothExternalStringTag)),
240 &if_skip_fast_case);
241 TVARIABLE(IntPtrT, byte_length, length);
242 GotoIf(both_are_one_byte, &if_fast_case);
243 byte_length = WordShl(byte_length.value(), IntPtrConstant(1));
244 Branch(both_are_two_byte, &if_fast_case, &if_skip_fast_case);
245 BIND(&if_fast_case);
246 StringEqual_FastLoop(lhs, lhs_instance_type, rhs, rhs_instance_type,
247 byte_length.value(), if_equal, if_not_equal);
248
249 BIND(&if_skip_fast_case);
250 GotoIf(both_are_one_byte, &if_oneonebytestring);
251 GotoIf(both_are_two_byte, &if_twotwobytestring);
252 Branch(
253 Word32Equal(masked_instance_types, Int32Constant(kOneTwoByteStringTag)),
254 &if_onetwobytestring, &if_twoonebytestring);
255
256 BIND(&if_oneonebytestring);
257 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint8(), rhs,
258 rhs_instance_type, MachineType::Uint8(), length, if_equal,
259 if_not_equal);
260
261 BIND(&if_twotwobytestring);
262 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint16(), rhs,
263 rhs_instance_type, MachineType::Uint16(), length, if_equal,
264 if_not_equal);
265
266 BIND(&if_onetwobytestring);
267 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint8(), rhs,
268 rhs_instance_type, MachineType::Uint16(), length, if_equal,
269 if_not_equal);
270
271 BIND(&if_twoonebytestring);
272 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint16(), rhs,
273 rhs_instance_type, MachineType::Uint8(), length, if_equal,
274 if_not_equal);
275}
276
278 TNode<String> lhs, TNode<Word32T> lhs_instance_type, TNode<String> rhs,
279 TNode<Word32T> rhs_instance_type, TNode<IntPtrT> byte_length,
280 Label* if_equal, Label* if_not_equal) {
281 TNode<RawPtrT> lhs_data = DirectStringData(lhs, lhs_instance_type);
282 TNode<RawPtrT> rhs_data = DirectStringData(rhs, rhs_instance_type);
283
284 const int kChunk = kTaggedSize;
285 static_assert(kObjectAlignment % kChunk == 0);
286 // Round up the byte_length to `ceiling(length / kChunk) * kChunk`
288 UncheckedCast<WordT>(IntPtrAdd(byte_length, IntPtrConstant(kChunk - 1))),
289 UncheckedCast<WordT>(IntPtrConstant(~(kChunk - 1)))));
290 TNode<RawPtrT> lhs_end = RawPtrAdd(lhs_data, rounded_up_len);
291
292#ifdef ENABLE_SLOW_DCHECKS
293 // The padding must be zeroed for chunked comparison to be correct. This loop
294 // checks all bytes being 0 from byte_length up to rounded_up_len.
295 // If we ever stop zeroing the padding, GenerateStringRelationalComparison
296 // below will also need to be updated.
297 {
298 TVARIABLE(IntPtrT, var_padding_offset, byte_length);
299 Label loop(this, &var_padding_offset), loop_end(this);
300 Goto(&loop);
301 BIND(&loop);
302 {
303 GotoIf(WordEqual(var_padding_offset.value(), rounded_up_len), &loop_end);
304
305 // Load the next byte
307 MachineType::Uint8(), lhs_data,
308 WordShl(var_padding_offset.value(),
309 ElementSizeLog2Of(MachineType::Uint8().representation()))));
311 MachineType::Uint8(), rhs_data,
312 WordShl(var_padding_offset.value(),
313 ElementSizeLog2Of(MachineType::Uint8().representation()))));
314
315 // Check the padding is zero.
316 CSA_CHECK(this, Word32Equal(lhs_value, Int32Constant(0)));
317 CSA_CHECK(this, Word32Equal(rhs_value, Int32Constant(0)));
318
319 // Advance to next byte.
320 var_padding_offset =
321 IntPtrAdd(var_padding_offset.value(), IntPtrConstant(1));
322 Goto(&loop);
323 }
324 BIND(&loop_end);
325 }
326#endif // ENABLE_SLOW_DCHECKS
327
328 // Compare strings in chunks of either 4 or 8 bytes, depending on the
329 // alignment of allocations.
330 static_assert(kChunk == ElementSizeInBytes(MachineRepresentation::kWord64) ||
332 TVARIABLE(RawPtrT, rhs_ptr, rhs_data);
333 VariableList vars({&rhs_ptr}, zone());
334
337 vars, lhs_data, lhs_end,
338 [&](TNode<RawPtrT> lhs_ptr) {
339 TNode<Word64T> lhs_value = Load<Uint64T>(lhs_ptr);
340 TNode<Word64T> rhs_value = Load<Uint64T>(rhs_ptr.value());
341 GotoIf(Word64NotEqual(lhs_value, rhs_value), if_not_equal);
342
343 // Advance {rhs_ptr} to next characters. {lhs_ptr} will be
344 // advanced along loop's {var_index}.
345 Increment(&rhs_ptr, kChunk);
346 },
348 } else {
350 vars, lhs_data, lhs_end,
351 [&](TNode<RawPtrT> lhs_ptr) {
352 TNode<Word32T> lhs_value = Load<Uint32T>(lhs_ptr);
353 TNode<Word32T> rhs_value = Load<Uint32T>(rhs_ptr.value());
354 GotoIf(Word32NotEqual(lhs_value, rhs_value), if_not_equal);
355
356 // Advance {rhs_ptr} to next characters. {lhs_ptr} will be
357 // advanced along loop's {var_index}.
358 Increment(&rhs_ptr, kChunk);
359 },
361 }
362 Goto(if_equal);
363}
364
366 TNode<String> lhs, TNode<Word32T> lhs_instance_type, MachineType lhs_type,
367 TNode<String> rhs, TNode<Word32T> rhs_instance_type, MachineType rhs_type,
368 TNode<IntPtrT> length, Label* if_equal, Label* if_not_equal) {
369 Comment("StringEqual_Loop");
370 CSA_DCHECK(this, WordEqual(LoadStringLengthAsWord(lhs), length));
371 CSA_DCHECK(this, WordEqual(LoadStringLengthAsWord(rhs), length));
372
373 // Compute the effective offset of the first character.
374 TNode<RawPtrT> lhs_data = DirectStringData(lhs, lhs_instance_type);
375 TNode<RawPtrT> rhs_data = DirectStringData(rhs, rhs_instance_type);
376 TNode<RawPtrT> lhs_end =
378 lhs_type.representation()))));
379 TVARIABLE(RawPtrT, rhs_ptr, rhs_data);
380 VariableList vars({&rhs_ptr}, zone());
381
382 // Loop over the {lhs} and {rhs} strings to see if they are equal.
384 vars, lhs_data, lhs_end,
385 [&](TNode<RawPtrT> lhs_ptr) {
386 TNode<Word32T> lhs_value =
387 UncheckedCast<Word32T>(Load(lhs_type, lhs_ptr));
388 TNode<Word32T> rhs_value =
389 UncheckedCast<Word32T>(Load(rhs_type, rhs_ptr.value()));
390
391 // Check if the characters match.
392 GotoIf(Word32NotEqual(lhs_value, rhs_value), if_not_equal);
393
394 // Advance {rhs_ptr} to next characters. {lhs_ptr} will be
395 // advanced along loop's {var_index}.
396 Increment(&rhs_ptr, ElementSizeInBytes(rhs_type.representation()));
397 },
400
401 // All characters are checked and no difference was found, so the strings
402 // are equal.
403 Goto(if_equal);
404}
405
407 TNode<Int32T> codepoint) {
408 TVARIABLE(String, var_result, EmptyStringConstant());
409
410 Label if_isword16(this), if_isword32(this), return_result(this);
411
412 Branch(Uint32LessThan(codepoint, Int32Constant(0x10000)), &if_isword16,
413 &if_isword32);
414
415 BIND(&if_isword16);
416 {
417 var_result = StringFromSingleCharCode(codepoint);
418 Goto(&return_result);
419 }
420
421 BIND(&if_isword32);
422 {
427 codepoint);
428 var_result = value;
429 Goto(&return_result);
430 }
431
432 BIND(&return_result);
433 return var_result.value();
434}
435
437 TNode<String> left,
438 TNode<String> right) {
439 // Added string can be a cons string.
440 Comment("Allocating ConsString");
441 TVARIABLE(String, first, left);
442 TNode<Int32T> left_instance_type = LoadInstanceType(left);
443 Label handle_right(this);
445 GotoIfNot(IsSetWord32(left_instance_type, kThinStringTagBit), &handle_right);
446 {
447 first = LoadObjectField<String>(left, offsetof(ThinString, actual_));
448 Goto(&handle_right);
449 }
450
451 BIND(&handle_right);
452 TVARIABLE(String, second, right);
453 TNode<Int32T> right_instance_type = LoadInstanceType(right);
454 Label allocate(this);
455 GotoIfNot(IsSetWord32(right_instance_type, kThinStringTagBit), &allocate);
456 {
457 second = LoadObjectField<String>(right, offsetof(ThinString, actual_));
458 Goto(&allocate);
459 }
460
461 BIND(&allocate);
462 // Determine the resulting ConsString map to use depending on whether
463 // any of {left} or {right} has two byte encoding.
464 static_assert(kOneByteStringTag != 0);
465 static_assert(kTwoByteStringTag == 0);
466 TNode<Int32T> combined_instance_type =
467 Word32And(left_instance_type, right_instance_type);
468 TNode<Map> result_map = CAST(Select<Object>(
469 IsSetWord32(combined_instance_type, kStringEncodingMask),
470 [=, this] { return ConsOneByteStringMapConstant(); },
471 [=, this] { return ConsTwoByteStringMapConstant(); }));
473 StoreMapNoWriteBarrier(result, result_map);
475 StoreObjectFieldNoWriteBarrier(result, offsetof(ConsString, raw_hash_field_),
478 first.value());
480 second.value());
481 return CAST(result);
482}
483
486 TNode<String> right) {
487 CSA_DCHECK(this, IsZeroOrContext(context));
488
490 Label check_right(this), runtime(this, Label::kDeferred), cons(this),
491 done(this, &result);
492
493 TNode<Uint32T> left_length = LoadStringLengthAsWord32(left);
494 GotoIfNot(Word32Equal(left_length, Uint32Constant(0)), &check_right);
495 result = right;
496 Goto(&done);
497
498 BIND(&check_right);
499 TNode<Uint32T> right_length = LoadStringLengthAsWord32(right);
500 GotoIfNot(Word32Equal(right_length, Uint32Constant(0)), &cons);
501 result = left;
502 Goto(&done);
503
504 BIND(&cons);
505 {
506 TNode<Uint32T> new_length = Uint32Add(left_length, right_length);
507
508 // If new length is greater than String::kMaxLength, goto runtime to
509 // throw. Note: we also need to invalidate the string length protector, so
510 // can't just throw here directly.
512 &runtime);
513
514 TVARIABLE(String, var_left, left);
515 TVARIABLE(String, var_right, right);
516 Label non_cons(this, {&var_left, &var_right});
517 Label slow(this, Label::kDeferred);
519 &non_cons);
520
521 result =
522 AllocateConsString(new_length, var_left.value(), var_right.value());
523 Goto(&done);
524
525 BIND(&non_cons);
526
527 Comment("Full string concatenate");
528 TNode<Int32T> left_instance_type = LoadInstanceType(var_left.value());
529 TNode<Int32T> right_instance_type = LoadInstanceType(var_right.value());
530 // Compute intersection and difference of instance types.
531
532 TNode<Int32T> ored_instance_types =
533 Word32Or(left_instance_type, right_instance_type);
534 TNode<Word32T> xored_instance_types =
535 Word32Xor(left_instance_type, right_instance_type);
536
537 // Check if both strings have the same encoding and both are sequential.
538 GotoIf(IsSetWord32(xored_instance_types, kStringEncodingMask), &runtime);
539 GotoIf(IsSetWord32(ored_instance_types, kStringRepresentationMask), &slow);
540
541 TNode<IntPtrT> word_left_length = Signed(ChangeUint32ToWord(left_length));
542 TNode<IntPtrT> word_right_length = Signed(ChangeUint32ToWord(right_length));
543
544 Label two_byte(this);
545 GotoIf(Word32Equal(Word32And(ored_instance_types,
548 &two_byte);
549 // One-byte sequential string case
550 result = AllocateNonEmptySeqOneByteString(new_length);
551 CopyStringCharacters(var_left.value(), result.value(), IntPtrConstant(0),
552 IntPtrConstant(0), word_left_length,
554 CopyStringCharacters(var_right.value(), result.value(), IntPtrConstant(0),
555 word_left_length, word_right_length,
557 Goto(&done);
558
559 BIND(&two_byte);
560 {
561 // Two-byte sequential string case
562 result = AllocateNonEmptySeqTwoByteString(new_length);
563 CopyStringCharacters(var_left.value(), result.value(), IntPtrConstant(0),
564 IntPtrConstant(0), word_left_length,
567 CopyStringCharacters(var_right.value(), result.value(), IntPtrConstant(0),
568 word_left_length, word_right_length,
571 Goto(&done);
572 }
573
574 BIND(&slow);
575 {
576 // Try to unwrap indirect strings, restart the above attempt on success.
577 MaybeDerefIndirectStrings(&var_left, left_instance_type, &var_right,
578 right_instance_type, &non_cons);
579 Goto(&runtime);
580 }
581 }
582 BIND(&runtime);
583 {
584 result = CAST(CallRuntime(Runtime::kStringAdd, context, left, right));
585 Goto(&done);
586 }
587
588 BIND(&done);
589 return result.value();
590}
591
593 TNode<String> string, TNode<Int32T> instance_type, Label* can_deref,
594 Label* cannot_deref) {
595 TNode<Int32T> representation =
597 GotoIf(Word32Equal(representation, Int32Constant(kThinStringTag)), can_deref);
599 cannot_deref);
600 // Cons string.
601 TNode<String> rhs =
602 LoadObjectField<String>(string, offsetof(ConsString, second_));
603 GotoIf(IsEmptyString(rhs), can_deref);
604 Goto(cannot_deref);
605}
606
608 TNode<Int32T> instance_type) {
609#ifdef DEBUG
610 Label can_deref(this), cannot_deref(this);
611 BranchIfCanDerefIndirectString(var_string->value(), instance_type, &can_deref,
612 &cannot_deref);
613 BIND(&cannot_deref);
614 DebugBreak(); // Should be able to dereference string.
615 Goto(&can_deref);
616 BIND(&can_deref);
617#endif // DEBUG
618
619 static_assert(static_cast<int>(offsetof(ThinString, actual_)) ==
620 static_cast<int>(offsetof(ConsString, first_)));
621 *var_string = LoadObjectField<String>(var_string->value(),
622 offsetof(ThinString, actual_));
623}
624
626 TVariable<String>* var_string, TNode<Int32T> instance_type,
627 Label* did_deref, Label* cannot_deref) {
628 Label deref(this);
629 BranchIfCanDerefIndirectString(var_string->value(), instance_type, &deref,
630 cannot_deref);
631
632 BIND(&deref);
633 {
634 DerefIndirectString(var_string, instance_type);
635 Goto(did_deref);
636 }
637}
638
640 TVariable<String>* var_left, TNode<Int32T> left_instance_type,
641 TVariable<String>* var_right, TNode<Int32T> right_instance_type,
642 Label* did_something) {
643 Label did_nothing_left(this), did_something_left(this),
644 didnt_do_anything(this);
645 MaybeDerefIndirectString(var_left, left_instance_type, &did_something_left,
646 &did_nothing_left);
647
648 BIND(&did_something_left);
649 {
650 MaybeDerefIndirectString(var_right, right_instance_type, did_something,
651 did_something);
652 }
653
654 BIND(&did_nothing_left);
655 {
656 MaybeDerefIndirectString(var_right, right_instance_type, did_something,
657 &didnt_do_anything);
658 }
659
660 BIND(&didnt_do_anything);
661 // Fall through if neither string was an indirect string.
662}
663
665 TNode<String> string, TNode<Int32T> instance_type, Label* cannot_deref) {
666 Label deref(this);
667 BranchIfCanDerefIndirectString(string, instance_type, &deref, cannot_deref);
668 BIND(&deref);
669 static_assert(static_cast<int>(offsetof(ThinString, actual_)) ==
670 static_cast<int>(offsetof(ConsString, first_)));
671 return LoadObjectField<String>(string, offsetof(ThinString, actual_));
672}
673
674TF_BUILTIN(StringAdd_CheckNone, StringBuiltinsAssembler) {
675 auto left = Parameter<String>(Descriptor::kLeft);
676 auto right = Parameter<String>(Descriptor::kRight);
678 UncheckedParameter<ContextOrEmptyContext>(Descriptor::kContext);
679 CSA_DCHECK(this, IsZeroOrContext(context));
680 Return(StringAdd(context, left, right));
681}
682
684 auto string = Parameter<String>(Descriptor::kString);
685 auto from = Parameter<Smi>(Descriptor::kFrom);
686 auto to = Parameter<Smi>(Descriptor::kTo);
687 Return(SubString(string, SmiUntag(from), SmiUntag(to)));
688}
689
692 TVARIABLE(String, var_left, left);
693 TVARIABLE(String, var_right, right);
694
695 Label if_less(this), if_equal(this), if_greater(this);
696 Label restart(this, {&var_left, &var_right});
697 Goto(&restart);
698 BIND(&restart);
699
700 TNode<String> lhs = var_left.value();
701 TNode<String> rhs = var_right.value();
702 // Fast check to see if {lhs} and {rhs} refer to the same String object.
703 GotoIf(TaggedEqual(lhs, rhs), &if_equal);
704
705 // Load instance types of {lhs} and {rhs}.
706 TNode<Uint16T> lhs_instance_type = LoadInstanceType(lhs);
707 TNode<Uint16T> rhs_instance_type = LoadInstanceType(rhs);
708
709 // Combine the instance types into a single 16-bit value, so we can check
710 // both of them at once.
711 TNode<Int32T> both_instance_types = Word32Or(
712 lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
713
714 // Check that both {lhs} and {rhs} are flat one-byte strings.
715 int const kBothSeqOneByteStringMask =
718 int const kBothSeqOneByteStringTag =
721 Label if_bothonebyteseqstrings(this), if_notbothonebyteseqstrings(this);
722 Branch(Word32Equal(Word32And(both_instance_types,
723 Int32Constant(kBothSeqOneByteStringMask)),
724 Int32Constant(kBothSeqOneByteStringTag)),
725 &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings);
726
727 BIND(&if_bothonebyteseqstrings);
728 {
729 TNode<IntPtrT> lhs_length = LoadStringLengthAsWord(lhs);
730 TNode<IntPtrT> rhs_length = LoadStringLengthAsWord(rhs);
731
732 TNode<IntPtrT> length = IntPtrMin(lhs_length, rhs_length);
733
734 // Loop over the {lhs} and {rhs} strings to see if they are equal.
735 constexpr int kBeginOffset =
737 TNode<IntPtrT> begin = IntPtrConstant(kBeginOffset);
738 TNode<IntPtrT> end = IntPtrAdd(begin, length);
739 TVARIABLE(IntPtrT, var_offset, begin);
740 Label chunk_loop(this, &var_offset), char_loop(this, &var_offset);
741 Label if_done(this);
742
743 // Unrolled first iteration.
744 GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &if_done);
745
746 constexpr int kChunkSize = kTaggedSize;
747 static_assert(
751 TNode<Uint32T> lhs_chunk =
752 Load<Uint32T>(lhs, IntPtrConstant(kBeginOffset));
753 TNode<Uint32T> rhs_chunk =
754 Load<Uint32T>(rhs, IntPtrConstant(kBeginOffset));
755 GotoIf(Word32NotEqual(lhs_chunk, rhs_chunk), &char_loop);
756 } else {
757 TNode<Uint64T> lhs_chunk =
758 Load<Uint64T>(lhs, IntPtrConstant(kBeginOffset));
759 TNode<Uint64T> rhs_chunk =
760 Load<Uint64T>(rhs, IntPtrConstant(kBeginOffset));
761 GotoIf(Word64NotEqual(lhs_chunk, rhs_chunk), &char_loop);
762 }
763
765 kHeapObjectTag + kChunkSize);
766
767 Goto(&chunk_loop);
768
769 // Try skipping over chunks of kChunkSize identical characters.
770 // This depends on padding (between strings' lengths and the actual end
771 // of the heap object) being zeroed out.
772 BIND(&chunk_loop);
773 {
774 GotoIf(IntPtrGreaterThanOrEqual(var_offset.value(), end), &if_done);
775
777 TNode<Uint32T> lhs_chunk = Load<Uint32T>(lhs, var_offset.value());
778 TNode<Uint32T> rhs_chunk = Load<Uint32T>(rhs, var_offset.value());
779 GotoIf(Word32NotEqual(lhs_chunk, rhs_chunk), &char_loop);
780 } else {
781 TNode<Uint64T> lhs_chunk = Load<Uint64T>(lhs, var_offset.value());
782 TNode<Uint64T> rhs_chunk = Load<Uint64T>(rhs, var_offset.value());
783 GotoIf(Word64NotEqual(lhs_chunk, rhs_chunk), &char_loop);
784 }
785
786 var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(kChunkSize));
787 Goto(&chunk_loop);
788 }
789
790 BIND(&char_loop);
791 {
792 GotoIf(WordEqual(var_offset.value(), end), &if_done);
793
794 TNode<Uint8T> lhs_char = Load<Uint8T>(lhs, var_offset.value());
795 TNode<Uint8T> rhs_char = Load<Uint8T>(rhs, var_offset.value());
796
797 Label if_charsdiffer(this);
798 GotoIf(Word32NotEqual(lhs_char, rhs_char), &if_charsdiffer);
799
800 var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(1));
801 Goto(&char_loop);
802
803 BIND(&if_charsdiffer);
804 Branch(Uint32LessThan(lhs_char, rhs_char), &if_less, &if_greater);
805 }
806
807 BIND(&if_done);
808 {
809 // All characters up to the min length are equal, decide based on
810 // string length.
811 GotoIf(IntPtrEqual(lhs_length, rhs_length), &if_equal);
812 Branch(IntPtrLessThan(lhs_length, rhs_length), &if_less, &if_greater);
813 }
814 }
815
816 BIND(&if_notbothonebyteseqstrings);
817 {
818 // Try to unwrap indirect strings, restart the above attempt on success.
819 MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
820 rhs_instance_type, &restart);
821 // TODO(bmeurer): Add support for two byte string relational comparisons.
822 switch (op) {
824 TailCallRuntime(Runtime::kStringLessThan, NoContextConstant(), lhs,
825 rhs);
826 break;
828 TailCallRuntime(Runtime::kStringLessThanOrEqual, NoContextConstant(),
829 lhs, rhs);
830 break;
832 TailCallRuntime(Runtime::kStringGreaterThan, NoContextConstant(), lhs,
833 rhs);
834 break;
836 TailCallRuntime(Runtime::kStringGreaterThanOrEqual, NoContextConstant(),
837 lhs, rhs);
838 break;
840 TailCallRuntime(Runtime::kStringCompare, NoContextConstant(), lhs, rhs);
841 break;
842 }
843 }
844
845 BIND(&if_less);
846 switch (op) {
849 Return(TrueConstant());
850 break;
851
854 Return(FalseConstant());
855 break;
856
858 Return(SmiConstant(-1));
859 break;
860 }
861
862 BIND(&if_equal);
863 switch (op) {
866 Return(FalseConstant());
867 break;
868
871 Return(TrueConstant());
872 break;
873
876 break;
877 }
878
879 BIND(&if_greater);
880 switch (op) {
883 Return(FalseConstant());
884 break;
885
888 Return(TrueConstant());
889 break;
890
893 break;
894 }
895}
896
898 auto left = Parameter<String>(Descriptor::kLeft);
899 auto right = Parameter<String>(Descriptor::kRight);
900 auto length = UncheckedParameter<IntPtrT>(Descriptor::kLength);
901 // Callers must handle the case where {lhs} and {rhs} refer to the same
902 // String object.
903 CSA_DCHECK(this, TaggedNotEqual(left, right));
904 GenerateStringEqual(left, right, length);
905}
906
908 auto left = Parameter<String>(Descriptor::kLeft);
909 auto right = Parameter<String>(Descriptor::kRight);
910 GenerateStringRelationalComparison(left, right, StringComparison::kLessThan);
911}
912
913TF_BUILTIN(StringLessThanOrEqual, StringBuiltinsAssembler) {
914 auto left = Parameter<String>(Descriptor::kLeft);
915 auto right = Parameter<String>(Descriptor::kRight);
916 GenerateStringRelationalComparison(left, right,
917 StringComparison::kLessThanOrEqual);
918}
919
921 auto left = Parameter<String>(Descriptor::kLeft);
922 auto right = Parameter<String>(Descriptor::kRight);
923 GenerateStringRelationalComparison(left, right,
924 StringComparison::kGreaterThan);
925}
926
928 auto left = Parameter<String>(Descriptor::kLeft);
929 auto right = Parameter<String>(Descriptor::kRight);
930 GenerateStringRelationalComparison(left, right, StringComparison::kCompare);
931}
932
933TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) {
934 auto left = Parameter<String>(Descriptor::kLeft);
935 auto right = Parameter<String>(Descriptor::kRight);
936 GenerateStringRelationalComparison(left, right,
937 StringComparison::kGreaterThanOrEqual);
938}
939
940#ifndef V8_ENABLE_EXPERIMENTAL_TSA_BUILTINS
941
942// NOTE: This needs to be kept in sync with the Turboshaft implementation in
943// `builtins-string-tsa.cc`.
944TF_BUILTIN(StringFromCodePointAt, StringBuiltinsAssembler) {
945 auto receiver = Parameter<String>(Descriptor::kReceiver);
946 auto position = UncheckedParameter<IntPtrT>(Descriptor::kPosition);
947
948 // TODO(sigurds) Figure out if passing length as argument pays off.
949 TNode<IntPtrT> length = LoadStringLengthAsWord(receiver);
950 // Load the character code at the {position} from the {receiver}.
951 TNode<Int32T> code =
952 LoadSurrogatePairAt(receiver, length, position, UnicodeEncoding::UTF16);
953 // Create a String from the UTF16 encoded code point
954 TNode<String> result = StringFromSingleUTF16EncodedCodePoint(code);
955 Return(result);
956}
957
958// -----------------------------------------------------------------------------
959// ES6 section 21.1 String Objects
960
961// ES6 #sec-string.fromcharcode
962// NOTE: This needs to be kept in sync with the Turboshaft implementation in
963// `builtins-string-tsa.cc`.
965 // TODO(ishell): use constants from Descriptor once the JSFunction linkage
966 // arguments are reordered.
967 auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount);
968 auto context = Parameter<Context>(Descriptor::kContext);
969
970 CodeStubArguments arguments(this, argc);
971 TNode<Uint32T> unsigned_argc =
972 Unsigned(TruncateIntPtrToInt32(arguments.GetLengthWithoutReceiver()));
973 // Check if we have exactly one argument (plus the implicit receiver), i.e.
974 // if the parent frame is not an inlined arguments frame.
975 Label if_oneargument(this), if_notoneargument(this);
976 Branch(IntPtrEqual(arguments.GetLengthWithoutReceiver(), IntPtrConstant(1)),
977 &if_oneargument, &if_notoneargument);
978
979 BIND(&if_oneargument);
980 {
981 // Single argument case, perform fast single character string cache lookup
982 // for one-byte code units, or fall back to creating a single character
983 // string on the fly otherwise.
984 TNode<Object> code = arguments.AtIndex(0);
985 TNode<Word32T> code32 = TruncateTaggedToWord32(context, code);
986 TNode<Int32T> code16 =
987 Signed(Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit)));
988 TNode<String> result = StringFromSingleCharCode(code16);
989 arguments.PopAndReturn(result);
990 }
991
992 TNode<Word32T> code16;
993 BIND(&if_notoneargument);
994 {
995 Label two_byte(this);
996 // Assume that the resulting string contains only one-byte characters.
997 TNode<String> one_byte_result = AllocateSeqOneByteString(unsigned_argc);
998
999 TVARIABLE(IntPtrT, var_max_index, IntPtrConstant(0));
1000
1001 // Iterate over the incoming arguments, converting them to 8-bit character
1002 // codes. Stop if any of the conversions generates a code that doesn't fit
1003 // in 8 bits.
1004 CodeStubAssembler::VariableList vars({&var_max_index}, zone());
1005 arguments.ForEach(vars, [&](TNode<Object> arg) {
1006 TNode<Word32T> code32 = TruncateTaggedToWord32(context, arg);
1007 code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
1008
1009 GotoIf(
1010 Int32GreaterThan(code16, Int32Constant(String::kMaxOneByteCharCode)),
1011 &two_byte);
1012
1013 // The {code16} fits into the SeqOneByteString {one_byte_result}.
1014 TNode<IntPtrT> offset = ElementOffsetFromIndex(
1015 var_max_index.value(), UINT8_ELEMENTS,
1017 StoreNoWriteBarrier(MachineRepresentation::kWord8, one_byte_result,
1018 offset, code16);
1019 var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
1020 });
1021 arguments.PopAndReturn(one_byte_result);
1022
1023 BIND(&two_byte);
1024
1025 // At least one of the characters in the string requires a 16-bit
1026 // representation. Allocate a SeqTwoByteString to hold the resulting
1027 // string.
1028 TNode<String> two_byte_result = AllocateSeqTwoByteString(unsigned_argc);
1029
1030 // Copy the characters that have already been put in the 8-bit string into
1031 // their corresponding positions in the new 16-bit string.
1032 TNode<IntPtrT> zero = IntPtrConstant(0);
1033 CopyStringCharacters(one_byte_result, two_byte_result, zero, zero,
1034 var_max_index.value(), String::ONE_BYTE_ENCODING,
1036
1037 // Write the character that caused the 8-bit to 16-bit fault.
1038 TNode<IntPtrT> max_index_offset = ElementOffsetFromIndex(
1039 var_max_index.value(), UINT16_ELEMENTS,
1041 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
1042 max_index_offset, code16);
1043 var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
1044
1045 // Resume copying the passed-in arguments from the same place where the
1046 // 8-bit copy stopped, but this time copying over all of the characters
1047 // using a 16-bit representation.
1048 arguments.ForEach(
1049 vars,
1050 [&](TNode<Object> arg) {
1051 TNode<Word32T> code32 = TruncateTaggedToWord32(context, arg);
1052 TNode<Word32T> code16 =
1053 Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
1054
1055 TNode<IntPtrT> offset = ElementOffsetFromIndex(
1056 var_max_index.value(), UINT16_ELEMENTS,
1058 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
1059 offset, code16);
1060 var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
1061 },
1062 var_max_index.value());
1063
1064 arguments.PopAndReturn(two_byte_result);
1065 }
1066}
1067
1068#endif // V8_ENABLE_EXPERIMENTAL_TSA_BUILTINS
1069
1071 const TNode<Context> context, const TNode<JSAny> object,
1072 const TNode<Object> maybe_string, Handle<Symbol> symbol,
1073 DescriptorIndexNameValue additional_property_to_check,
1074 const NodeFunction0& regexp_call, const NodeFunction1& generic_call) {
1075 Label out(this), no_protector(this), object_is_heapobject(this);
1076 Label get_property_lookup(this);
1077
1078 // The protector guarantees that that the Number and String wrapper
1079 // prototypes do not contain Symbol.{matchAll|replace|split} (aka.
1080 // @@matchAll, @@replace @@split).
1082 // Smi is safe thanks to the protector.
1083 GotoIf(TaggedIsSmi(object), &out);
1084 // String is safe thanks to the protector.
1085 GotoIf(IsString(CAST(object)), &out);
1086 // HeapNumber is safe thanks to the protector.
1087 Branch(IsHeapNumber(CAST(object)), &out, &object_is_heapobject);
1088
1089 BIND(&no_protector);
1090 // Smis have to go through the GetProperty lookup in case Number.prototype or
1091 // Object.prototype was modified.
1092 Branch(TaggedIsSmi(object), &get_property_lookup, &object_is_heapobject);
1093
1094 // Take the fast path for RegExps.
1095 // There's two conditions: {object} needs to be a fast regexp, and
1096 // {maybe_string} must be a string (we can't call ToString on the fast path
1097 // since it may mutate {object}).
1098 {
1099 Label stub_call(this), slow_lookup(this);
1100
1101 BIND(&object_is_heapobject);
1102 TNode<HeapObject> heap_object = CAST(object);
1103
1104 GotoIf(TaggedIsSmi(maybe_string), &slow_lookup);
1105 GotoIfNot(IsString(CAST(maybe_string)), &slow_lookup);
1106
1107 // Note we don't run a full (= permissive) check here, because passing the
1108 // check implies calling the fast variants of target builtins, which assume
1109 // we've already made their appropriate fast path checks. This is not the
1110 // case though; e.g.: some of the target builtins access flag getters.
1111 // TODO(jgruber): Handle slow flag accesses on the fast path and make this
1112 // permissive.
1113 RegExpBuiltinsAssembler regexp_asm(state());
1114 regexp_asm.BranchIfFastRegExp(
1115 context, heap_object, LoadMap(heap_object),
1117 additional_property_to_check, &stub_call, &slow_lookup);
1118
1119 BIND(&stub_call);
1120 // TODO(jgruber): Add a no-JS scope once it exists.
1121 regexp_call();
1122
1123 BIND(&slow_lookup);
1124 // Special case null and undefined to skip the property lookup.
1125 Branch(IsNullOrUndefined(heap_object), &out, &get_property_lookup);
1126 }
1127
1128 // Fall back to a slow lookup of {heap_object[symbol]}.
1129 //
1130 // The spec uses GetMethod({heap_object}, {symbol}), which has a few quirks:
1131 // * null values are turned into undefined, and
1132 // * an exception is thrown if the value is not undefined, null, or callable.
1133 // We handle the former by jumping to {out} for null values as well, while
1134 // the latter is already handled by the Call({maybe_func}) operation.
1135
1136 BIND(&get_property_lookup);
1137 const TNode<Object> maybe_func = GetProperty(context, object, symbol);
1138 GotoIf(IsUndefined(maybe_func), &out);
1139 GotoIf(IsNull(maybe_func), &out);
1140
1141 // Attempt to call the function.
1142 generic_call(maybe_func);
1143
1144 BIND(&out);
1145}
1146
1148 const TNode<Context> context, const TNode<String> string) {
1149 const TNode<String> dollar_string =
1150 HeapConstantNoHole(isolate()->factory()->dollar_string());
1151 const TNode<Smi> dollar_ix = CAST(CallBuiltin(
1152 Builtin::kStringIndexOf, context, string, dollar_string, SmiConstant(0)));
1153 return dollar_ix;
1154}
1155
1157 TNode<Context> context, TNode<String> subject_string,
1158 TNode<Smi> match_start_index, TNode<Smi> match_end_index,
1159 TNode<String> replace_string) {
1160 CSA_DCHECK(this, TaggedIsPositiveSmi(match_start_index));
1161 CSA_DCHECK(this, TaggedIsPositiveSmi(match_end_index));
1162
1163 TVARIABLE(String, var_result, replace_string);
1164 Label runtime(this), out(this);
1165
1166 // In this primitive implementation we simply look for the next '$' char in
1167 // {replace_string}. If it doesn't exist, we can simply return
1168 // {replace_string} itself. If it does, then we delegate to
1169 // String::GetSubstitution, passing in the index of the first '$' to avoid
1170 // repeated scanning work.
1171 // TODO(jgruber): Possibly extend this in the future to handle more complex
1172 // cases without runtime calls.
1173
1174 TNode<Smi> dollar_index = IndexOfDollarChar(context, replace_string);
1175 Branch(SmiIsNegative(dollar_index), &out, &runtime);
1176
1177 BIND(&runtime);
1178 {
1179 CSA_DCHECK(this, TaggedIsPositiveSmi(dollar_index));
1180
1181 const TNode<Object> matched =
1182 CallBuiltin(Builtin::kStringSubstring, context, subject_string,
1183 SmiUntag(match_start_index), SmiUntag(match_end_index));
1184 const TNode<String> replacement_string = CAST(
1185 CallRuntime(Runtime::kGetSubstitution, context, matched, subject_string,
1186 match_start_index, replace_string, dollar_index));
1187 var_result = replacement_string;
1188
1189 Goto(&out);
1190 }
1191
1192 BIND(&out);
1193 return var_result.value();
1194}
1195
1196// ES6 #sec-string.prototype.replace
1197TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
1198 Label out(this);
1199
1200 auto receiver = Parameter<JSAny>(Descriptor::kReceiver);
1201 const auto search = Parameter<JSAny>(Descriptor::kSearch);
1202 const auto replace = Parameter<JSAny>(Descriptor::kReplace);
1203 auto context = Parameter<Context>(Descriptor::kContext);
1204
1205 const TNode<Smi> smi_zero = SmiConstant(0);
1206
1207 RequireObjectCoercible(context, receiver, "String.prototype.replace");
1208
1209 // Redirect to replacer method if {search[@@replace]} is not undefined.
1210 {
1211 Label next(this);
1212
1213 MaybeCallFunctionAtSymbol(
1214 context, search, receiver, isolate()->factory()->replace_symbol(),
1215 DescriptorIndexNameValue{
1217 RootIndex::kreplace_symbol, Context::REGEXP_REPLACE_FUNCTION_INDEX},
1218 [=, this]() {
1219 Return(CallBuiltin(Builtin::kRegExpReplace, context, search, receiver,
1220 replace));
1221 },
1222 [=, this](TNode<Object> fn) {
1223 Return(Call(context, fn, search, receiver, replace));
1224 });
1225 Goto(&next);
1226
1227 BIND(&next);
1228 }
1229
1230 // Convert {receiver} and {search} to strings.
1231
1232 const TNode<String> subject_string = ToString_Inline(context, receiver);
1233 const TNode<String> search_string = ToString_Inline(context, search);
1234
1235 const TNode<IntPtrT> subject_length = LoadStringLengthAsWord(subject_string);
1236 const TNode<IntPtrT> search_length = LoadStringLengthAsWord(search_string);
1237
1238 // Fast-path single-char {search}, long cons {receiver}, and simple string
1239 // {replace}.
1240 {
1241 Label next(this);
1242
1243 GotoIfNot(WordEqual(search_length, IntPtrConstant(1)), &next);
1244 GotoIfNot(IntPtrGreaterThan(subject_length, IntPtrConstant(0xFF)), &next);
1245 GotoIf(TaggedIsSmi(replace), &next);
1246 GotoIfNot(IsString(CAST(replace)), &next);
1247
1248 TNode<String> replace_string = CAST(replace);
1249 const TNode<Uint16T> subject_instance_type =
1250 LoadInstanceType(subject_string);
1251 GotoIfNot(IsConsStringInstanceType(subject_instance_type), &next);
1252
1253 GotoIf(TaggedIsPositiveSmi(IndexOfDollarChar(context, replace_string)),
1254 &next);
1255
1256 // Searching by traversing a cons string tree and replace with cons of
1257 // slices works only when the replaced string is a single character, being
1258 // replaced by a simple string and only pays off for long strings.
1259 // TODO(jgruber): Reevaluate if this is still beneficial.
1260 // TODO(jgruber): TailCallRuntime when it correctly handles adapter frames.
1261 Return(CallRuntime(Runtime::kStringReplaceOneCharWithString, context,
1262 subject_string, search_string, replace_string));
1263
1264 BIND(&next);
1265 }
1266
1267 // TODO(jgruber): Extend StringIndexOf to handle two-byte strings and
1268 // longer substrings - we can handle up to 8 chars (one-byte) / 4 chars
1269 // (2-byte).
1270
1271 const TNode<Smi> match_start_index =
1272 CAST(CallBuiltin(Builtin::kStringIndexOf, context, subject_string,
1273 search_string, smi_zero));
1274
1275 // Early exit if no match found.
1276 {
1277 Label next(this), return_subject(this);
1278
1279 GotoIfNot(SmiIsNegative(match_start_index), &next);
1280
1281 // The spec requires to perform ToString(replace) if the {replace} is not
1282 // callable even if we are going to exit here.
1283 // Since ToString() being applied to Smi does not have side effects for
1284 // numbers we can skip it.
1285 GotoIf(TaggedIsSmi(replace), &return_subject);
1286 GotoIf(IsCallableMap(LoadMap(CAST(replace))), &return_subject);
1287
1288 // TODO(jgruber): Could introduce ToStringSideeffectsStub which only
1289 // performs observable parts of ToString.
1290 ToString_Inline(context, replace);
1291 Goto(&return_subject);
1292
1293 BIND(&return_subject);
1294 Return(subject_string);
1295
1296 BIND(&next);
1297 }
1298
1299 const TNode<Smi> match_end_index =
1300 SmiAdd(match_start_index, SmiFromIntPtr(search_length));
1301
1302 TVARIABLE(String, var_result, EmptyStringConstant());
1303
1304 // Compute the prefix.
1305 {
1306 Label next(this);
1307
1308 GotoIf(SmiEqual(match_start_index, smi_zero), &next);
1309 const TNode<String> prefix =
1310 CAST(CallBuiltin(Builtin::kStringSubstring, context, subject_string,
1311 IntPtrConstant(0), SmiUntag(match_start_index)));
1312 var_result = prefix;
1313
1314 Goto(&next);
1315 BIND(&next);
1316 }
1317
1318 // Compute the string to replace with.
1319
1320 Label if_iscallablereplace(this), if_notcallablereplace(this);
1321 GotoIf(TaggedIsSmi(replace), &if_notcallablereplace);
1322 Branch(IsCallableMap(LoadMap(CAST(replace))), &if_iscallablereplace,
1323 &if_notcallablereplace);
1324
1325 BIND(&if_iscallablereplace);
1326 {
1327 const TNode<Object> replacement =
1328 Call(context, replace, UndefinedConstant(), search_string,
1329 match_start_index, subject_string);
1330 const TNode<String> replacement_string =
1331 ToString_Inline(context, replacement);
1332 var_result = CAST(CallBuiltin(Builtin::kStringAdd_CheckNone, context,
1333 var_result.value(), replacement_string));
1334 Goto(&out);
1335 }
1336
1337 BIND(&if_notcallablereplace);
1338 {
1339 const TNode<String> replace_string = ToString_Inline(context, replace);
1340 const TNode<Object> replacement =
1341 GetSubstitution(context, subject_string, match_start_index,
1342 match_end_index, replace_string);
1343 var_result = CAST(CallBuiltin(Builtin::kStringAdd_CheckNone, context,
1344 var_result.value(), replacement));
1345 Goto(&out);
1346 }
1347
1348 BIND(&out);
1349 {
1350 const TNode<Object> suffix =
1351 CallBuiltin(Builtin::kStringSubstring, context, subject_string,
1352 SmiUntag(match_end_index), subject_length);
1353 const TNode<Object> result = CallBuiltin(
1354 Builtin::kStringAdd_CheckNone, context, var_result.value(), suffix);
1355 Return(result);
1356 }
1357}
1358
1359// ES #sec-string.prototype.matchAll
1360TF_BUILTIN(StringPrototypeMatchAll, StringBuiltinsAssembler) {
1361 char const* method_name = "String.prototype.matchAll";
1362
1363 auto context = Parameter<Context>(Descriptor::kContext);
1364 auto maybe_regexp = Parameter<JSAny>(Descriptor::kRegexp);
1365 auto receiver = Parameter<JSAny>(Descriptor::kReceiver);
1366 TNode<NativeContext> native_context = LoadNativeContext(context);
1367
1368 // 1. Let O be ? RequireObjectCoercible(this value).
1369 RequireObjectCoercible(context, receiver, method_name);
1370
1371 RegExpMatchAllAssembler regexp_asm(state());
1372 {
1373 Label fast(this), slow(this, Label::kDeferred),
1374 throw_exception(this, Label::kDeferred),
1375 throw_flags_exception(this, Label::kDeferred), next(this);
1376
1377 // 2. If regexp is neither undefined nor null, then
1378 // a. Let isRegExp be ? IsRegExp(regexp).
1379 // b. If isRegExp is true, then
1380 // i. Let flags be ? Get(regexp, "flags").
1381 // ii. Perform ? RequireObjectCoercible(flags).
1382 // iii. If ? ToString(flags) does not contain "g", throw a
1383 // TypeError exception.
1384 GotoIf(TaggedIsSmi(maybe_regexp), &next);
1385 TNode<JSAnyNotSmi> heap_maybe_regexp = CAST(maybe_regexp);
1386 regexp_asm.BranchIfFastRegExpForMatch(context, heap_maybe_regexp, &fast,
1387 &slow);
1388
1389 BIND(&fast);
1390 {
1391 TNode<BoolT> is_global =
1392 regexp_asm.FastFlagGetter(CAST(heap_maybe_regexp), JSRegExp::kGlobal);
1393 Branch(is_global, &next, &throw_exception);
1394 }
1395
1396 BIND(&slow);
1397 {
1398 GotoIfNot(regexp_asm.IsRegExp(native_context, heap_maybe_regexp), &next);
1399
1400 TNode<Object> flags = GetProperty(context, heap_maybe_regexp,
1401 isolate()->factory()->flags_string());
1402 // TODO(syg): Implement a RequireObjectCoercible with more flexible error
1403 // messages.
1404 GotoIf(IsNullOrUndefined(flags), &throw_flags_exception);
1405
1406 TNode<String> flags_string = ToString_Inline(context, flags);
1407 TNode<String> global_char_string = StringConstant("g");
1408 TNode<Smi> global_ix =
1409 CAST(CallBuiltin(Builtin::kStringIndexOf, context, flags_string,
1410 global_char_string, SmiConstant(0)));
1411 Branch(SmiEqual(global_ix, SmiConstant(-1)), &throw_exception, &next);
1412 }
1413
1414 BIND(&throw_exception);
1415 ThrowTypeError(context, MessageTemplate::kRegExpGlobalInvokedOnNonGlobal,
1416 method_name);
1417
1418 BIND(&throw_flags_exception);
1419 ThrowTypeError(context,
1420 MessageTemplate::kStringMatchAllNullOrUndefinedFlags);
1421
1422 BIND(&next);
1423 }
1424 // a. Let matcher be ? GetMethod(regexp, @@matchAll).
1425 // b. If matcher is not undefined, then
1426 // i. Return ? Call(matcher, regexp, « O »).
1427 auto if_regexp_call = [&] {
1428 // MaybeCallFunctionAtSymbol guarantees fast path is chosen only if
1429 // maybe_regexp is a fast regexp and receiver is a string.
1431
1432 Return(
1433 RegExpPrototypeMatchAllImpl(context, native_context, maybe_regexp, s));
1434 };
1435 auto if_generic_call = [=, this](TNode<Object> fn) {
1436 Return(Call(context, fn, maybe_regexp, receiver));
1437 };
1438 MaybeCallFunctionAtSymbol(
1439 context, maybe_regexp, receiver, isolate()->factory()->match_all_symbol(),
1440 DescriptorIndexNameValue{JSRegExp::kSymbolMatchAllFunctionDescriptorIndex,
1441 RootIndex::kmatch_all_symbol,
1442 Context::REGEXP_MATCH_ALL_FUNCTION_INDEX},
1443 if_regexp_call, if_generic_call);
1444
1445 // 3. Let S be ? ToString(O).
1446 TNode<String> s = ToString_Inline(context, receiver);
1447
1448 // 4. Let rx be ? RegExpCreate(R, "g").
1449 TNode<JSAny> rx = regexp_asm.RegExpCreate(context, native_context,
1450 maybe_regexp, StringConstant("g"));
1451
1452 // 5. Return ? Invoke(rx, @@matchAll, « S »).
1453 TNode<Object> match_all_func =
1454 GetProperty(context, rx, isolate()->factory()->match_all_symbol());
1455 Return(Call(context, match_all_func, rx, s));
1456}
1457
1459 TNode<NativeContext> context, TNode<String> subject_string,
1460 TNode<Smi> subject_length, TNode<Number> limit_number) {
1461 CSA_DCHECK(this, SmiGreaterThan(subject_length, SmiConstant(0)));
1462
1463 Label done(this), call_runtime(this, Label::kDeferred),
1464 fill_thehole_and_call_runtime(this, Label::kDeferred);
1465 TVARIABLE(JSArray, result_array);
1466
1467 TNode<Uint16T> instance_type = LoadInstanceType(subject_string);
1468 GotoIfNot(IsOneByteStringInstanceType(instance_type), &call_runtime);
1469
1470 // Try to use cached one byte characters.
1471 {
1472 TNode<Smi> length_smi = Select<Smi>(
1473 TaggedIsSmi(limit_number),
1474 [=, this] { return SmiMin(CAST(limit_number), subject_length); },
1475 [=] { return subject_length; });
1476 TNode<IntPtrT> length = SmiToIntPtr(length_smi);
1477
1478 ToDirectStringAssembler to_direct(state(), subject_string);
1479 to_direct.TryToDirect(&call_runtime);
1480
1481 // The extracted direct string may be two-byte even though the wrapping
1482 // string is one-byte.
1483 GotoIfNot(to_direct.IsOneByte(), &call_runtime);
1484
1485 TNode<FixedArray> elements =
1487 // Don't allocate anything while {string_data} is live!
1488 TNode<RawPtrT> string_data =
1489 to_direct.PointerToData(&fill_thehole_and_call_runtime);
1490 TNode<IntPtrT> string_data_offset = to_direct.offset();
1491 TNode<FixedArray> cache = SingleCharacterStringTableConstant();
1492
1494 IntPtrConstant(0), length,
1495 [&](TNode<IntPtrT> index) {
1496 // TODO(jkummerow): Implement a CSA version of
1497 // DisallowGarbageCollection and use that to guard
1498 // ToDirectStringAssembler.PointerToData().
1499 CSA_DCHECK(this, WordEqual(to_direct.PointerToData(&call_runtime),
1500 string_data));
1501 TNode<Int32T> char_code =
1503 IntPtrAdd(index, string_data_offset)));
1504 TNode<UintPtrT> code_index = ChangeUint32ToWord(char_code);
1505 TNode<Object> entry = LoadFixedArrayElement(cache, code_index);
1506
1507 CSA_DCHECK(this, Word32BinaryNot(IsUndefined(entry)));
1508
1509 StoreFixedArrayElement(elements, index, entry);
1510 },
1512
1514 result_array = AllocateJSArray(array_map, elements, length_smi);
1515 Goto(&done);
1516
1517 BIND(&fill_thehole_and_call_runtime);
1518 {
1520 length, RootIndex::kTheHoleValue);
1521 Goto(&call_runtime);
1522 }
1523 }
1524
1525 BIND(&call_runtime);
1526 {
1527 result_array = CAST(CallRuntime(Runtime::kStringToArray, context,
1528 subject_string, limit_number));
1529 Goto(&done);
1530 }
1531
1532 BIND(&done);
1533 return result_array.value();
1534}
1535
1536// ES6 section 21.1.3.19 String.prototype.split ( separator, limit )
1537TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
1538 const int kSeparatorArg = 0;
1539 const int kLimitArg = 1;
1540
1541 const TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
1542 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
1543 CodeStubArguments args(this, argc);
1544
1545 TNode<JSAny> receiver = args.GetReceiver();
1546 const TNode<JSAny> separator = args.GetOptionalArgumentValue(kSeparatorArg);
1547 const TNode<JSAny> limit = args.GetOptionalArgumentValue(kLimitArg);
1548 auto context = Parameter<NativeContext>(Descriptor::kContext);
1549
1550 TNode<Smi> smi_zero = SmiConstant(0);
1551
1552 RequireObjectCoercible(context, receiver, "String.prototype.split");
1553
1554 // Redirect to splitter method if {separator[@@split]} is not undefined.
1555
1556 MaybeCallFunctionAtSymbol(
1557 context, separator, receiver, isolate()->factory()->split_symbol(),
1558 DescriptorIndexNameValue{JSRegExp::kSymbolSplitFunctionDescriptorIndex,
1559 RootIndex::ksplit_symbol,
1560 Context::REGEXP_SPLIT_FUNCTION_INDEX},
1561 [&]() {
1562 args.PopAndReturn(CallBuiltin<JSAny>(Builtin::kRegExpSplit, context,
1563 separator, receiver, limit));
1564 },
1565 [&](TNode<Object> fn) {
1566 args.PopAndReturn(Call(context, fn, separator, receiver, limit));
1567 });
1568
1569 // String and integer conversions.
1570
1571 TNode<String> subject_string = ToString_Inline(context, receiver);
1572 TNode<Number> limit_number = Select<Number>(
1573 IsUndefined(limit), [=, this] { return NumberConstant(kMaxUInt32); },
1574 [=, this] { return ToUint32(context, limit); });
1575 const TNode<String> separator_string = ToString_Inline(context, separator);
1576
1577 Label return_empty_array(this);
1578
1579 // Shortcut for {limit} == 0.
1580 GotoIf(TaggedEqual(limit_number, smi_zero), &return_empty_array);
1581
1582 // ECMA-262 says that if {separator} is undefined, the result should
1583 // be an array of size 1 containing the entire string.
1584 {
1585 Label next(this);
1586 GotoIfNot(IsUndefined(separator), &next);
1587
1589 const TNode<NativeContext> native_context = LoadNativeContext(context);
1590 TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
1591
1592 TNode<Smi> length = SmiConstant(1);
1593 TNode<IntPtrT> capacity = IntPtrConstant(1);
1594 TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity, length);
1595
1596 TNode<FixedArray> fixed_array = CAST(LoadElements(result));
1597 StoreFixedArrayElement(fixed_array, 0, subject_string);
1598
1599 args.PopAndReturn(result);
1600
1601 BIND(&next);
1602 }
1603
1604 // If the separator string is empty then return the elements in the subject.
1605 {
1606 Label next(this);
1607 GotoIfNot(SmiEqual(LoadStringLengthAsSmi(separator_string), smi_zero),
1608 &next);
1609
1610 TNode<Smi> subject_length = LoadStringLengthAsSmi(subject_string);
1611 GotoIf(SmiEqual(subject_length, smi_zero), &return_empty_array);
1612
1613 args.PopAndReturn(
1614 StringToArray(context, subject_string, subject_length, limit_number));
1615
1616 BIND(&next);
1617 }
1618
1619 const TNode<JSAny> result =
1620 CallRuntime<JSAny>(Runtime::kStringSplit, context, subject_string,
1621 separator_string, limit_number);
1622 args.PopAndReturn(result);
1623
1624 BIND(&return_empty_array);
1625 {
1627 const TNode<NativeContext> native_context = LoadNativeContext(context);
1628 TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
1629
1630 TNode<Smi> length = smi_zero;
1631 TNode<IntPtrT> capacity = IntPtrConstant(0);
1632 TNode<JSArray> result_array =
1633 AllocateJSArray(kind, array_map, capacity, length);
1634
1635 args.PopAndReturn(result_array);
1636 }
1637}
1638
1640 auto string = Parameter<String>(Descriptor::kString);
1641 auto from = UncheckedParameter<IntPtrT>(Descriptor::kFrom);
1642 auto to = UncheckedParameter<IntPtrT>(Descriptor::kTo);
1643
1644 Return(SubString(string, from, to));
1645}
1646
1647
1648// Return the |word32| codepoint at {index}. Supports SeqStrings and
1649// ExternalStrings.
1650// TODO(v8:9880): Use UintPtrT here.
1652 TNode<String> string, TNode<IntPtrT> length, TNode<IntPtrT> index,
1653 UnicodeEncoding encoding) {
1654 Label handle_surrogate_pair(this), return_result(this);
1655 TVARIABLE(Int32T, var_result);
1656 TVARIABLE(Int32T, var_trail);
1657 var_result = StringCharCodeAt(string, Unsigned(index));
1658 var_trail = Int32Constant(0);
1659
1660 GotoIf(Word32NotEqual(Word32And(var_result.value(), Int32Constant(0xFC00)),
1661 Int32Constant(0xD800)),
1662 &return_result);
1663 TNode<IntPtrT> next_index = IntPtrAdd(index, IntPtrConstant(1));
1664
1665 GotoIfNot(IntPtrLessThan(next_index, length), &return_result);
1666 var_trail = StringCharCodeAt(string, Unsigned(next_index));
1667 Branch(Word32Equal(Word32And(var_trail.value(), Int32Constant(0xFC00)),
1668 Int32Constant(0xDC00)),
1669 &handle_surrogate_pair, &return_result);
1670
1671 BIND(&handle_surrogate_pair);
1672 {
1673 TNode<Int32T> lead = var_result.value();
1674 TNode<Int32T> trail = var_trail.value();
1675
1676 // Check that this path is only taken if a surrogate pair is found
1677 CSA_SLOW_DCHECK(this,
1678 Uint32GreaterThanOrEqual(lead, Int32Constant(0xD800)));
1679 CSA_SLOW_DCHECK(this, Uint32LessThan(lead, Int32Constant(0xDC00)));
1680 CSA_SLOW_DCHECK(this,
1681 Uint32GreaterThanOrEqual(trail, Int32Constant(0xDC00)));
1682 CSA_SLOW_DCHECK(this, Uint32LessThan(trail, Int32Constant(0xE000)));
1683
1684 switch (encoding) {
1686 var_result = Word32Or(
1687// Need to swap the order for big-endian platforms
1688#if V8_TARGET_BIG_ENDIAN
1689 Word32Shl(lead, Int32Constant(16)), trail);
1690#else
1691 Word32Shl(trail, Int32Constant(16)), lead);
1692#endif
1693 break;
1694
1696 // Convert UTF16 surrogate pair into |word32| code point, encoded as
1697 // UTF32.
1698 TNode<Int32T> surrogate_offset =
1699 Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00);
1700
1701 // (lead << 10) + trail + SURROGATE_OFFSET
1702 var_result = Int32Add(Word32Shl(lead, Int32Constant(10)),
1703 Int32Add(trail, surrogate_offset));
1704 break;
1705 }
1706 }
1707 Goto(&return_result);
1708 }
1709
1710 BIND(&return_result);
1711 return var_result.value();
1712}
1713
1715 Label* if_indirect) {
1716 TNode<Uint16T> instance_type = LoadInstanceType(string);
1717 CSA_DCHECK(this, Word32Equal(Word32And(instance_type,
1720 GotoIfNot(Word32Equal(Word32And(instance_type,
1723 Int32Constant(0)),
1724 if_indirect);
1725
1726 TNode<RawPtrT> string_data = DirectStringData(string, instance_type);
1727 TNode<IntPtrT> length = LoadStringLengthAsWord(string);
1728
1729 const TNode<ExternalReference> has_unpaired_surrogate =
1730 ExternalConstant(ExternalReference::has_unpaired_surrogate());
1731 return UncheckedCast<BoolT>(
1732 CallCFunction(has_unpaired_surrogate, MachineType::Uint32(),
1733 std::make_pair(MachineType::Pointer(), string_data),
1734 std::make_pair(MachineType::IntPtr(), length)));
1735}
1736
1738 TNode<String> dest,
1739 Label* if_indirect) {
1740 TNode<Uint16T> source_instance_type = LoadInstanceType(source);
1741 CSA_DCHECK(this, Word32Equal(Word32And(source_instance_type,
1744 GotoIfNot(Word32Equal(Word32And(source_instance_type,
1747 Int32Constant(0)),
1748 if_indirect);
1749
1750 TNode<RawPtrT> source_data = DirectStringData(source, source_instance_type);
1751 // The destination string is a freshly allocated SeqString, and so is always
1752 // direct.
1753 TNode<Uint16T> dest_instance_type = LoadInstanceType(dest);
1754 CSA_DCHECK(this, Word32Equal(Word32And(dest_instance_type,
1757 TNode<RawPtrT> dest_data = DirectStringData(dest, dest_instance_type);
1758 TNode<IntPtrT> length = LoadStringLengthAsWord(source);
1759 CSA_DCHECK(this, IntPtrEqual(length, LoadStringLengthAsWord(dest)));
1760
1761 const TNode<ExternalReference> replace_unpaired_surrogates =
1762 ExternalConstant(ExternalReference::replace_unpaired_surrogates());
1763 CallCFunction(replace_unpaired_surrogates, MachineType::Pointer(),
1764 std::make_pair(MachineType::Pointer(), source_data),
1765 std::make_pair(MachineType::Pointer(), dest_data),
1766 std::make_pair(MachineType::IntPtr(), length));
1767}
1768
1770 TNode<Object> object, TNode<Context> context, Label* if_true,
1771 Label* if_false) {
1772 GotoIf(TaggedIsSmi(object), if_false);
1773 GotoIfNot(IsString(CAST(object)), if_false);
1774
1775 // Check that the String iterator hasn't been modified in a way that would
1776 // affect iteration.
1777 TNode<PropertyCell> protector_cell = StringIteratorProtectorConstant();
1778 DCHECK(i::IsPropertyCell(isolate()->heap()->string_iterator_protector()));
1779 Branch(
1780 TaggedEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
1782 if_true, if_false);
1783}
1784
1785// Instantiate template due to shared library requirements.
1787 TNode<String> from_string, TNode<String> to_string,
1788 TNode<IntPtrT> from_index, TNode<IntPtrT> to_index,
1789 TNode<IntPtrT> character_count, String::Encoding from_encoding,
1790 String::Encoding to_encoding);
1791
1793 TNode<RawPtrT> from_string, TNode<String> to_string,
1794 TNode<IntPtrT> from_index, TNode<IntPtrT> to_index,
1795 TNode<IntPtrT> character_count, String::Encoding from_encoding,
1796 String::Encoding to_encoding);
1797
1798template <typename T>
1800 TNode<T> from_string, TNode<String> to_string, TNode<IntPtrT> from_index,
1801 TNode<IntPtrT> to_index, TNode<IntPtrT> character_count,
1802 String::Encoding from_encoding, String::Encoding to_encoding) {
1803 // from_string could be either a String or a RawPtrT in the case we pass in
1804 // faked sequential strings when handling external subject strings.
1805 bool from_one_byte = from_encoding == String::ONE_BYTE_ENCODING;
1806 bool to_one_byte = to_encoding == String::ONE_BYTE_ENCODING;
1807 Comment("CopyStringCharacters ",
1808 from_one_byte ? "ONE_BYTE_ENCODING" : "TWO_BYTE_ENCODING", " -> ",
1809 to_one_byte ? "ONE_BYTE_ENCODING" : "TWO_BYTE_ENCODING");
1810
1811 ElementsKind from_kind = from_one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS;
1812 ElementsKind to_kind = to_one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS;
1813 static_assert(OFFSET_OF_DATA_START(SeqOneByteString) ==
1816 TNode<IntPtrT> from_offset =
1817 ElementOffsetFromIndex(from_index, from_kind, header_size);
1818 TNode<IntPtrT> to_offset =
1819 ElementOffsetFromIndex(to_index, to_kind, header_size);
1820 TNode<IntPtrT> byte_count =
1821 ElementOffsetFromIndex(character_count, from_kind);
1822 TNode<IntPtrT> limit_offset = IntPtrAdd(from_offset, byte_count);
1823
1824 // Prepare the fast loop.
1825 MachineType type =
1826 from_one_byte ? MachineType::Uint8() : MachineType::Uint16();
1829 int from_increment = 1 << ElementsKindToShiftSize(from_kind);
1830 int to_increment = 1 << ElementsKindToShiftSize(to_kind);
1831
1832 TVARIABLE(IntPtrT, current_to_offset, to_offset);
1833 VariableList vars({&current_to_offset}, zone());
1834 int to_index_constant = 0, from_index_constant = 0;
1835 bool index_same = (from_encoding == to_encoding) &&
1836 (from_index == to_index ||
1837 (TryToInt32Constant(from_index, &from_index_constant) &&
1838 TryToInt32Constant(to_index, &to_index_constant) &&
1839 from_index_constant == to_index_constant));
1841 vars, from_offset, limit_offset,
1842 [&](TNode<IntPtrT> offset) {
1843 compiler::Node* value = Load(type, from_string, offset);
1844#if DEBUG
1845 // Copying two-byte characters to one-byte is okay if callers have
1846 // checked that this loses no information.
1847 if (v8_flags.debug_code && !from_one_byte && to_one_byte) {
1848 CSA_DCHECK(this, Uint32LessThanOrEqual(UncheckedCast<Uint32T>(value),
1849 Uint32Constant(0xFF)));
1850 }
1851#endif
1852 StoreNoWriteBarrier(rep, to_string,
1853 index_same ? offset : current_to_offset.value(),
1854 value);
1855 if (!index_same) {
1856 Increment(&current_to_offset, to_increment);
1857 }
1858 },
1860}
1861
1862// A wrapper around CopyStringCharacters which determines the correct string
1863// encoding, allocates a corresponding sequential string, and then copies the
1864// given character range using CopyStringCharacters.
1865// |from_string| must be a sequential string.
1866// 0 <= |from_index| <= |from_index| + |character_count| < from_string.length.
1867// |character_count| > 0.
1868template <typename T>
1870 TNode<T> from, TNode<BoolT> from_is_one_byte, TNode<IntPtrT> from_index,
1871 TNode<IntPtrT> character_count) {
1872 CSA_DCHECK(this, IntPtrGreaterThan(character_count, IntPtrConstant(0)));
1873
1874 Label end(this), one_byte_sequential(this), two_byte_sequential(this);
1875 TVARIABLE(String, var_result);
1876
1877 Branch(from_is_one_byte, &one_byte_sequential, &two_byte_sequential);
1878
1879 // The subject string is a sequential one-byte string.
1880 BIND(&one_byte_sequential);
1881 {
1882 TNode<String> result = AllocateNonEmptySeqOneByteString(
1883 Unsigned(TruncateIntPtrToInt32(character_count)));
1884 CopyStringCharacters<T>(from, result, from_index, IntPtrConstant(0),
1885 character_count, String::ONE_BYTE_ENCODING,
1887 var_result = result;
1888 Goto(&end);
1889 }
1890
1891 // The subject string is a sequential two-byte string.
1892 BIND(&two_byte_sequential);
1893 {
1894 // Check if the to-be-copied range happens to contain only one-byte
1895 // characters, and copy it to a one-byte string if so.
1896 // If the range is long enough, we check 8 characters at a time, to reduce
1897 // the amount of branching.
1898 // For a more readable version of this logic, see {StringFromTwoByteSlice}
1899 // in wasm.tq.
1901 from_index, UINT16_ELEMENTS,
1903 TNode<IntPtrT> end_offset = IntPtrAdd(
1904 start_offset, ElementOffsetFromIndex(character_count, UINT16_ELEMENTS));
1905 TNode<IntPtrT> eight_char_loop_end = IntPtrSub(
1906 end_offset, ElementOffsetFromIndex(IntPtrConstant(8), UINT16_ELEMENTS));
1907
1908 TVARIABLE(IntPtrT, var_cursor, start_offset);
1909 TNode<RawPtrT> raw_from;
1910 if constexpr (std::is_same_v<T, RawPtrT>) {
1911 raw_from = from;
1912 } else {
1914 }
1915 Label first_loop(this, &var_cursor), second_loop(this, &var_cursor);
1916 Label twobyte(this);
1917 Branch(IntPtrLessThanOrEqual(start_offset, eight_char_loop_end),
1918 &first_loop, &second_loop);
1919 BIND(&first_loop);
1920 {
1921 TNode<RawPtrT> chunk = RawPtrAdd(raw_from, var_cursor.value());
1922 TNode<Uint32T> c1 = Load<Uint16T>(chunk);
1924 TNode<Uint32T> bits = Word32Or(c1, c2);
1926 bits = Word32Or(bits, c3);
1928 bits = Word32Or(bits, c4);
1930 bits = Word32Or(bits, c5);
1932 bits = Word32Or(bits, c6);
1934 bits = Word32Or(bits, c7);
1936 bits = Word32Or(bits, c8);
1937 GotoIf(Uint32GreaterThan(bits, Uint32Constant(0xFF)), &twobyte);
1938 Increment(&var_cursor, 8 * sizeof(uint16_t));
1939 Branch(IntPtrLessThanOrEqual(var_cursor.value(), eight_char_loop_end),
1940 &first_loop, &second_loop);
1941 }
1942
1943 BIND(&second_loop);
1944 TVARIABLE(Uint32T, var_bits, Uint32Constant(0));
1945 VariableList vars({&var_bits}, zone());
1946 FastLoopBody<IntPtrT> one_char_loop = [&](TNode<IntPtrT> offset) {
1948 var_bits = Word32Or(var_bits.value(), c);
1949 };
1950 BuildFastLoop<IntPtrT>(vars, var_cursor, var_cursor.value(), end_offset,
1951 one_char_loop, sizeof(uint16_t),
1953 GotoIf(Uint32GreaterThan(var_bits.value(), Uint32Constant(0xFF)), &twobyte);
1954 // Fallthrough: only one-byte characters in the to-be-copied range.
1955 {
1956 TNode<String> result = AllocateNonEmptySeqOneByteString(
1957 Unsigned(TruncateIntPtrToInt32(character_count)));
1958 CopyStringCharacters<T>(from, result, from_index, IntPtrConstant(0),
1959 character_count, String::TWO_BYTE_ENCODING,
1961 var_result = result;
1962 Goto(&end);
1963 }
1964
1965 BIND(&twobyte);
1966 {
1967 TNode<String> result = AllocateNonEmptySeqTwoByteString(
1968 Unsigned(TruncateIntPtrToInt32(character_count)));
1969 CopyStringCharacters<T>(from, result, from_index, IntPtrConstant(0),
1970 character_count, String::TWO_BYTE_ENCODING,
1972 var_result = result;
1973 Goto(&end);
1974 }
1975 }
1976
1977 BIND(&end);
1978 return var_result.value();
1979}
1980
1981// TODO(v8:9880): Use UintPtrT here.
1983 TNode<IntPtrT> from,
1984 TNode<IntPtrT> to) {
1985 TVARIABLE(String, var_result);
1986 ToDirectStringAssembler to_direct(state(), string);
1987 Label end(this), runtime(this);
1988
1989 const TNode<IntPtrT> substr_length = IntPtrSub(to, from);
1990 const TNode<IntPtrT> string_length = LoadStringLengthAsWord(string);
1991
1992 // Begin dispatching based on substring length.
1993
1994 Label original_string_or_invalid_length(this);
1995 GotoIf(UintPtrGreaterThanOrEqual(substr_length, string_length),
1996 &original_string_or_invalid_length);
1997
1998 // A real substring (substr_length < string_length).
1999 Label empty(this);
2000 GotoIf(IntPtrEqual(substr_length, IntPtrConstant(0)), &empty);
2001
2002 Label single_char(this);
2003 GotoIf(IntPtrEqual(substr_length, IntPtrConstant(1)), &single_char);
2004
2005 // Deal with different string types: update the index if necessary
2006 // and extract the underlying string.
2007
2008 TNode<String> direct_string = to_direct.TryToDirect(&runtime);
2009 TNode<IntPtrT> offset = IntPtrAdd(from, to_direct.offset());
2010 const TNode<BoolT> is_one_byte = to_direct.IsOneByte();
2011
2012 // The subject string can only be external or sequential string of either
2013 // encoding at this point.
2014 Label external_string(this);
2015 {
2016 if (v8_flags.string_slices) {
2017 Label next(this);
2018
2019 // Short slice. Copy instead of slicing.
2020 GotoIf(IntPtrLessThan(substr_length,
2022 &next);
2023
2024 // Allocate new sliced string.
2025 Label one_byte_slice(this), two_byte_slice(this);
2026 Branch(is_one_byte, &one_byte_slice, &two_byte_slice);
2027
2028 BIND(&one_byte_slice);
2029 {
2030 var_result = AllocateSlicedOneByteString(
2031 Unsigned(TruncateIntPtrToInt32(substr_length)), direct_string,
2032 SmiTag(offset));
2033 Goto(&end);
2034 }
2035
2036 BIND(&two_byte_slice);
2037 {
2038 var_result = AllocateSlicedTwoByteString(
2039 Unsigned(TruncateIntPtrToInt32(substr_length)), direct_string,
2040 SmiTag(offset));
2041 Goto(&end);
2042 }
2043
2044 BIND(&next);
2045 }
2046
2047 // The subject string can only be external or sequential string of either
2048 // encoding at this point.
2049 GotoIf(to_direct.is_external(), &external_string);
2050
2051 var_result = AllocAndCopyStringCharacters(direct_string, is_one_byte,
2052 offset, substr_length);
2053 Goto(&end);
2054 }
2055
2056 // Handle external string.
2057 BIND(&external_string);
2058 {
2059 const TNode<RawPtrT> fake_sequential_string =
2060 to_direct.PointerToString(&runtime);
2061
2062 var_result = AllocAndCopyStringCharacters(
2063 fake_sequential_string, is_one_byte, offset, substr_length);
2064
2065 Goto(&end);
2066 }
2067
2068 BIND(&empty);
2069 {
2070 var_result = EmptyStringConstant();
2071 Goto(&end);
2072 }
2073
2074 // Substrings of length 1 are generated through CharCodeAt and FromCharCode.
2075 BIND(&single_char);
2076 {
2077 TNode<Int32T> char_code = StringCharCodeAt(string, Unsigned(from));
2078 var_result = StringFromSingleCharCode(char_code);
2079 Goto(&end);
2080 }
2081
2082 BIND(&original_string_or_invalid_length);
2083 {
2084 CSA_DCHECK(this, IntPtrEqual(substr_length, string_length));
2085
2086 // Equal length - check if {from, to} == {0, str.length}.
2087 GotoIf(UintPtrGreaterThan(from, IntPtrConstant(0)), &runtime);
2088
2089 // Return the original string (substr_length == string_length).
2090 var_result = string;
2091 Goto(&end);
2092 }
2093
2094 // Fall back to a runtime call.
2095 BIND(&runtime);
2096 {
2097 var_result =
2098 CAST(CallRuntime(Runtime::kStringSubstring, NoContextConstant(), string,
2099 SmiTag(from), SmiTag(to)));
2100 Goto(&end);
2101 }
2102
2103 BIND(&end);
2104 return var_result.value();
2105}
2106
2108
2109} // namespace internal
2110} // namespace v8
#define BIND(label)
#define TVARIABLE(...)
#define CSA_SLOW_DCHECK(csa,...)
#define CSA_DCHECK(csa,...)
#define CSA_CHECK(csa, x)
#define TF_BUILTIN(Name, AssemblerBase)
Builtins::Kind kind
Definition builtins.cc:40
TNode< String > AllocateSlicedOneByteString(TNode< Uint32T > length, TNode< String > parent, TNode< Smi > offset)
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< BoolT > IsNullOrUndefined(TNode< Object > object)
TNode< FixedArrayBase > AllocateFixedArray(ElementsKind kind, TNode< TIndex > capacity, AllocationFlags flags=AllocationFlag::kNone, std::optional< TNode< Map > > fixed_array_map=std::nullopt)
TNode< BoolT > IsNumberStringNotRegexpLikeProtectorCellInvalid()
TNode< Int32T > TruncateIntPtrToInt32(TNode< IntPtrT > value)
std::function< void(TNode< TIndex > index)> FastLoopBody
void StoreFixedArrayElement(TNode< FixedArray > object, int index, TNode< Object > value, WriteBarrierMode barrier_mode=UPDATE_WRITE_BARRIER, CheckBounds check_bounds=CheckBounds::kAlways)
TNode< HeapObject > AllocateInNewSpace(TNode< IntPtrT > size, AllocationFlags flags=AllocationFlag::kNone)
TNode< JSAny > GetProperty(TNode< Context > context, TNode< JSAny > receiver, Handle< Name > name)
TNode< String > StringFromSingleCharCode(TNode< Int32T > code)
TNode< Smi > SmiTag(TNode< IntPtrT > value)
void Increment(TVariable< TIndex > *variable, int value=1)
TNode< BoolT > TaggedEqual(TNode< AnyTaggedT > a, TNode< AnyTaggedT > b)
TNode< T > LoadObjectField(TNode< HeapObject > object, int offset)
TNode< BoolT > TaggedNotEqual(TNode< AnyTaggedT > a, TNode< AnyTaggedT > b)
TNode< IntPtrT > IntPtrMin(TNode< IntPtrT > left, TNode< IntPtrT > right)
TNode< BoolT > TaggedIsPositiveSmi(TNode< Object > a)
TNode< BoolT > IsZeroOrContext(TNode< Object > object)
TNode< Uint16T > StringCharCodeAt(TNode< String > string, TNode< UintPtrT > index)
TNode< BoolT > IsSetWord32(TNode< Word32T > word32)
TNode< IntPtrT > SmiUntag(TNode< Smi > value)
TNode< Map > LoadJSArrayElementsMap(ElementsKind kind, TNode< NativeContext > native_context)
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< IntPtrT > SmiToIntPtr(TNode< Smi > value)
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)
TNode< IntPtrT > ElementOffsetFromIndex(TNode< TIndex > index, ElementsKind kind, int base_size=0)
TNode< IntPtrT > LoadStringLengthAsWord(TNode< String > string)
TNode< String > AllocateSeqTwoByteString(uint32_t length, AllocationFlags flags=AllocationFlag::kNone)
TNode< Uint16T > LoadInstanceType(TNode< HeapObject > object)
TNode< String > AllocateSlicedTwoByteString(TNode< Uint32T > length, TNode< String > parent, TNode< Smi > offset)
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 > IsOneByteStringInstanceType(TNode< Int32T > instance_type)
TNode< Map > LoadMap(TNode< HeapObject > object)
TNode< BoolT > IsString(TNode< HeapObject > object)
TNode< Uint32T > LoadStringLengthAsWord32(TNode< String > string)
TNode< Smi > SmiMin(TNode< Smi > a, TNode< Smi > b)
Uint32LessThanOrEqual IntPtrGreaterThanOrEqual
TNode< RawPtrT > LoadExternalStringResourceDataPtr(TNode< ExternalString > object)
void FillFixedArrayWithValue(ElementsKind kind, TNode< FixedArrayBase > array, TNode< TIndex > from_index, TNode< TIndex > to_index, RootIndex value_root_index)
void StoreMapNoWriteBarrier(TNode< HeapObject > object, RootIndex map_root_index)
static const uint32_t kMinLength
Definition string.h:1029
static V8_EXPORT_PRIVATE ExternalReference isolate_address()
static ExternalReference search_string_raw()
static constexpr int kSymbolMatchAllFunctionDescriptorIndex
Definition js-regexp.h:124
static constexpr int kSymbolReplaceFunctionDescriptorIndex
Definition js-regexp.h:125
static constexpr int kSymbolSplitFunctionDescriptorIndex
Definition js-regexp.h:127
static constexpr MachineType Pointer()
static constexpr MachineType Uint8()
constexpr MachineRepresentation representation() const
static constexpr MachineType Uint32()
static constexpr MachineType Uint16()
static constexpr MachineType UintPtr()
static constexpr MachineType IntPtr()
static constexpr int kEmptyHashField
Definition name.h:133
static const int kProtectorValid
Definition protectors.h:15
void BranchIfFastRegExpForMatch(TNode< Context > context, TNode< HeapObject > object, Label *if_isunmodified, Label *if_ismodified)
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< BoolT > FastFlagGetter(TNode< JSRegExp > regexp, JSRegExp::Flag flag)
static const uint32_t kMinLength
Definition string.h:1130
void ReplaceUnpairedSurrogates(TNode< String > source, TNode< String > dest, Label *if_indirect)
void StringEqual_Core(TNode< String > lhs, TNode< Word32T > lhs_instance_type, TNode< String > rhs, TNode< Word32T > rhs_instance_type, TNode< IntPtrT > length, Label *if_equal, Label *if_not_equal, Label *if_indirect)
TNode< Smi > IndexOfDollarChar(const TNode< Context > context, const TNode< String > string)
void BranchIfCanDerefIndirectString(TNode< String > string, TNode< Int32T > instance_type, Label *can_deref, Label *cannot_deref)
void MaybeDerefIndirectString(TVariable< String > *var_string, TNode< Int32T > instance_type, Label *did_deref, Label *cannot_deref)
TNode< IntPtrT > SearchOneByteStringInOneByteString(const TNode< RawPtrT > subject_ptr, const TNode< IntPtrT > subject_length, const TNode< RawPtrT > search_ptr, const TNode< IntPtrT > search_length, const TNode< IntPtrT > start_position)
TNode< IntPtrT > SearchOneByteStringInTwoByteString(const TNode< RawPtrT > subject_ptr, const TNode< IntPtrT > subject_length, const TNode< RawPtrT > search_ptr, const TNode< IntPtrT > search_length, const TNode< IntPtrT > start_position)
TNode< String > AllocateConsString(TNode< Uint32T > length, TNode< String > left, TNode< String > right)
TNode< JSArray > StringToArray(TNode< NativeContext > context, TNode< String > subject_string, TNode< Smi > subject_length, TNode< Number > limit_number)
TNode< String > AllocAndCopyStringCharacters(TNode< T > from, TNode< BoolT > from_is_one_byte, TNode< IntPtrT > from_index, TNode< IntPtrT > character_count)
TNode< String > SubString(TNode< String > string, TNode< IntPtrT > from, TNode< IntPtrT > to)
void CopyStringCharacters(TNode< T > from_string, TNode< String > to_string, TNode< IntPtrT > from_index, TNode< IntPtrT > to_index, TNode< IntPtrT > character_count, String::Encoding from_encoding, String::Encoding to_encoding)
void BranchIfStringPrimitiveWithNoCustomIteration(TNode< Object > object, TNode< Context > context, Label *if_true, Label *if_false)
TNode< IntPtrT > SearchTwoByteStringInOneByteString(const TNode< RawPtrT > subject_ptr, const TNode< IntPtrT > subject_length, const TNode< RawPtrT > search_ptr, const TNode< IntPtrT > search_length, const TNode< IntPtrT > start_position)
TNode< String > StringAdd(TNode< ContextOrEmptyContext > context, TNode< String > left, TNode< String > right)
void GenerateStringRelationalComparison(TNode< String > left, TNode< String > right, StringComparison op)
TNode< RawPtrT > DirectStringData(TNode< String > string, TNode< Word32T > string_instance_type)
TNode< BoolT > HasUnpairedSurrogate(TNode< String > string, Label *if_indirect)
void DerefIndirectString(TVariable< String > *var_string, TNode< Int32T > instance_type)
TNode< IntPtrT > CallSearchStringRaw(const TNode< RawPtrT > subject_ptr, const TNode< IntPtrT > subject_length, const TNode< RawPtrT > search_ptr, const TNode< IntPtrT > search_length, const TNode< IntPtrT > start_position)
TNode< IntPtrT > SearchTwoByteStringInTwoByteString(const TNode< RawPtrT > subject_ptr, const TNode< IntPtrT > subject_length, const TNode< RawPtrT > search_ptr, const TNode< IntPtrT > search_length, const TNode< IntPtrT > start_position)
TNode< Int32T > LoadSurrogatePairAt(TNode< String > string, TNode< IntPtrT > length, TNode< IntPtrT > index, UnicodeEncoding encoding)
TNode< String > GetSubstitution(TNode< Context > context, TNode< String > subject_string, TNode< Smi > match_start_index, TNode< Smi > match_end_index, TNode< String > replace_string)
TNode< IntPtrT > SearchOneByteInOneByteString(const TNode< RawPtrT > subject_ptr, const TNode< IntPtrT > subject_length, const TNode< RawPtrT > search_ptr, const TNode< IntPtrT > start_position)
TNode< String > StringFromSingleUTF16EncodedCodePoint(TNode< Int32T > codepoint)
void MaybeCallFunctionAtSymbol(const TNode< Context > context, const TNode< JSAny > object, const TNode< Object > maybe_string, Handle< Symbol > symbol, DescriptorIndexNameValue additional_property_to_check, const NodeFunction0 &regexp_call, const NodeFunction1 &generic_call)
void StringEqual_Loop(TNode< String > lhs, TNode< Word32T > lhs_instance_type, MachineType lhs_type, TNode< String > rhs, TNode< Word32T > rhs_instance_type, MachineType rhs_type, TNode< IntPtrT > length, Label *if_equal, Label *if_not_equal)
void StringEqual_FastLoop(TNode< String > lhs, TNode< Word32T > lhs_instance_type, TNode< String > rhs, TNode< Word32T > rhs_instance_type, TNode< IntPtrT > byte_length, Label *if_equal, Label *if_not_equal)
TNode< BoolT > SmiIsNegative(TNode< Smi > value)
void MaybeDerefIndirectStrings(TVariable< String > *var_left, TNode< Int32T > left_instance_type, TVariable< String > *var_right, TNode< Int32T > right_instance_type, Label *did_something)
void GenerateStringEqual(TNode< String > left, TNode< String > right, TNode< IntPtrT > length)
std::function< void(TNode< Object > fn)> NodeFunction1
static const uint32_t kMaxLength
Definition string.h:511
static const int32_t kMaxOneByteCharCode
Definition string.h:500
static const int kMaxUtf16CodeUnit
Definition string.h:502
TNode< String > TryToDirect(Label *if_bailout)
TNode< RawPtrT > PointerToData(Label *if_bailout)
TNode< RawPtrT > PointerToString(Label *if_bailout)
TNode< BoolT > Word32NotEqual(TNode< Word32T > left, TNode< Word32T > right)
TNode< IntPtrT > IntPtrAdd(TNode< IntPtrT > left, TNode< IntPtrT > right)
TNode< Int32T > Signed(TNode< Word32T > x)
void Comment(MessageWithSourceLocation message, Args &&... args)
TNode< RawPtrT > RawPtrSub(TNode< RawPtrT > left, TNode< IntPtrT > right)
TNode< IntPtrT > IntPtrConstant(intptr_t value)
TNode< UintPtrT > ChangeUint32ToWord(TNode< Word32T > 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)
void Return(TNode< Object > value)
TNode< Uint32T > Unsigned(TNode< Word32T > x)
TNode< Int32T > Word32And(TNode< Int32T > left, TNode< Int32T > right)
TNode< T > ReinterpretCast(Node *value)
TNode< Int32T > Int32Add(TNode< Int32T > left, TNode< Int32T > right)
TNode< IntPtrT > BitcastTaggedToWord(TNode< Smi > node)
void TailCallRuntime(Runtime::FunctionId function, TNode< Object > context, TArgs... args)
TNode< BoolT > Word64NotEqual(TNode< Word64T > left, TNode< Word64T > right)
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)
TNode< IntPtrT > WordAnd(TNode< IntPtrT > left, TNode< IntPtrT > right)
bool TryToInt32Constant(TNode< IntegralT > node, int32_t *out_value)
TNode< IntPtrT > IntPtrSub(TNode< IntPtrT > left, TNode< IntPtrT > right)
TNode< ExternalReference > ExternalConstant(ExternalReference address)
TNode< Int32T > Int32Constant(int32_t value)
Node * CallCFunction(Node *function, std::optional< MachineType > return_type, CArgs... cargs)
TNode< Uint32T > Uint32Add(TNode< Uint32T > left, TNode< Uint32T > right)
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< RawPtrT > RawPtrAdd(TNode< RawPtrT > left, TNode< IntPtrT > 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)
int start
int end
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
Isolate * isolate
int32_t offset
TNode< Object > receiver
double second
ZoneVector< RpoNumber > & result
LiftoffAssembler::CacheState state
EmitFn fn
Point from
int position
Definition liveedit.cc:290
const int length_
Definition mul-fft.cc:473
constexpr unsigned CountPopulation(T value)
Definition bits.h:26
const uint32_t kStringEncodingMask
constexpr int kTaggedSize
Definition globals.h:542
constexpr intptr_t kObjectAlignment
Definition globals.h:930
const uint32_t kTwoByteStringTag
const uint32_t kUncachedExternalStringTag
const uint32_t kThinStringTagBit
const uint32_t kUncachedExternalStringMask
const uint32_t kOneByteStringTag
bool IsNullOrUndefined(Tagged< Object > obj, Isolate *isolate)
constexpr int ElementsKindToShiftSize(ElementsKind elements_kind)
const uint32_t kStringRepresentationMask
const int kHeapObjectTag
Definition v8-internal.h:72
const uint32_t kIsIndirectStringTag
V8_EXPORT_PRIVATE FlagValues v8_flags
V8_EXPORT_PRIVATE constexpr int ElementSizeLog2Of(MachineRepresentation)
const uint32_t kInternalizedTag
const uint32_t kIsNotInternalizedMask
return value
Definition map-inl.h:893
V8_EXPORT_PRIVATE constexpr int ElementSizeInBytes(MachineRepresentation)
constexpr uint32_t kMaxUInt32
Definition globals.h:387
kInstanceDescriptorsOffset kTransitionsOrPrototypeInfoOffset IsNull(value)||IsJSProxy(value)||IsWasmObject(value)||(IsJSObject(value) &&(HeapLayout
Definition map-inl.h:70
const uint32_t kIsIndirectStringMask
template const char * string
BUILTIN_FP_CALL BUILTIN_FP_CALL BUILTIN_FP_CALL BUILTIN_FP_CALL BUILTIN_FP_CALL BUILTIN_FP_CALL BUILTIN_FP_CALL BUILTIN_FP_CALL BUILTIN_FP_CALL BUILTIN_FP_CALL int size_t search_length
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877
#define DCHECK(condition)
Definition logging.h:482
#define V8_EXPORT_PRIVATE
Definition macros.h:460
#define OFFSET_OF_DATA_START(Type)