v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-array-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
18#include "src/codegen/tnode.h"
19#include "src/common/globals.h"
26
27namespace v8 {
28namespace internal {
29
31
34 : CodeStubAssembler(state),
35 k_(this),
36 a_(this),
37 fully_spec_compliant_(this, {&k_, &a_}) {}
38
40 // 6. Let A be ? TypedArraySpeciesCreate(O, len).
41 TNode<JSTypedArray> original_array = CAST(o());
42 const char* method_name = "%TypedArray%.prototype.map";
43
44 TNode<JSTypedArray> a = TypedArraySpeciesCreateByLength(
45 context(), method_name, original_array, len());
46 // In the Spec and our current implementation, the length check is already
47 // performed in TypedArraySpeciesCreate.
48#ifdef DEBUG
49 Label detached_or_out_of_bounds(this), done(this);
52 a, &detached_or_out_of_bounds)));
53 Goto(&done);
54 BIND(&detached_or_out_of_bounds);
56 BIND(&done);
57#endif // DEBUG
58
59 // TODO(v8:11111): Make storing fast when the elements kinds only differ
60 // because of their RAB/GSABness.
63 a_ = a;
64}
65
66// See tc39.github.io/ecma262/#sec-%typedarray%.prototype.map.
68 TNode<Object> k_value, TNode<UintPtrT> k) {
69 // 7c. Let mapped_value be ? Call(callbackfn, T, « kValue, k, O »).
71 TNode<JSAny> mapped_value =
72 Call(context(), callbackfn(), this_arg(), k_value, k_number, o());
73 Label fast(this), slow(this), done(this), detached(this, Label::kDeferred);
74
75 // 7d. Perform ? Set(A, Pk, mapped_value, true).
76 // Since we know that A is a TypedArray, this always ends up in
77 // #sec-integer-indexed-exotic-objects-set-p-v-receiver and then
78 // tc39.github.io/ecma262/#sec-integerindexedelementset .
79 Branch(fast_typed_array_target_, &fast, &slow);
80
81 BIND(&fast);
82 // #sec-integerindexedelementset
83 // 2. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let
84 // numValue be ? ToBigInt(v).
85 // 3. Otherwise, let numValue be ? ToNumber(value).
86 TNode<Object> num_value;
88 num_value = ToBigInt(context(), mapped_value);
89 } else {
90 num_value = ToNumber_Inline(context(), mapped_value);
91 }
92
93 // The only way how this can bailout is because of a detached or out of bounds
94 // buffer.
95 // TODO(v8:4153): Consider checking IsDetachedBuffer() and calling
96 // TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric() here
97 // instead to avoid converting k_number back to UintPtrT.
98
99 // Using source_elements_kind_ (not "target elements kind") is correct here,
100 // because the fast branch is taken only when the source and the target
101 // elements kinds match.
102 EmitElementStore(CAST(a()), k_number, num_value, source_elements_kind_,
104 Goto(&done);
105
106 BIND(&slow);
107 {
108 SetPropertyStrict(context(), a(), k_number, mapped_value);
109 Goto(&done);
110 }
111
112 BIND(&detached);
113 // tc39.github.io/ecma262/#sec-integerindexedelementset
114 // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
115 ThrowTypeError(context_, MessageTemplate::kDetachedOperation, name_);
116
117 BIND(&done);
118 return a();
119}
120
122 if (argc_ == nullptr) {
123 Return(value);
124 } else {
125 CodeStubArguments args(this, argc());
126 PopAndReturn(args.GetLengthWithReceiver(), value);
127 }
128}
129
139
141 const char* name, const BuiltinResultGenerator& generator,
143 name_ = name;
144
145 // ValidateTypedArray: tc39.github.io/ecma262/#sec-validatetypedarray
146
147 Label throw_not_typed_array(this, Label::kDeferred);
148
149 GotoIf(TaggedIsSmi(receiver_), &throw_not_typed_array);
150 TNode<Map> typed_array_map = LoadMap(CAST(receiver_));
151 GotoIfNot(IsJSTypedArrayMap(typed_array_map), &throw_not_typed_array);
152
153 TNode<JSTypedArray> typed_array = CAST(receiver_);
154 o_ = typed_array;
155
156 Label throw_detached(this, Label::kDeferred);
157 len_ = LoadJSTypedArrayLengthAndCheckDetached(typed_array, &throw_detached);
158
159 Label throw_not_callable(this, Label::kDeferred);
160 Label distinguish_types(this);
161 GotoIf(TaggedIsSmi(callbackfn_), &throw_not_callable);
162 Branch(IsCallableMap(LoadMap(CAST(callbackfn_))), &distinguish_types,
163 &throw_not_callable);
164
165 BIND(&throw_not_typed_array);
166 ThrowTypeError(context_, MessageTemplate::kNotTypedArray);
167
168 BIND(&throw_not_callable);
169 ThrowTypeError(context_, MessageTemplate::kCalledNonCallable, callbackfn_);
170
171 BIND(&throw_detached);
172 ThrowTypeError(context_, MessageTemplate::kDetachedOperation, name_);
173
174 Label unexpected_instance_type(this);
175 BIND(&unexpected_instance_type);
176 Unreachable();
177
178 std::vector<int32_t> elements_kinds = {
179#define ELEMENTS_KIND(Type, type, TYPE, ctype) TYPE##_ELEMENTS,
181#undef ELEMENTS_KIND
182 };
183 std::list<Label> labels;
184 for (size_t i = 0; i < elements_kinds.size(); ++i) {
185 labels.emplace_back(this);
186 }
187 std::vector<Label*> label_ptrs;
188 for (Label& label : labels) {
189 label_ptrs.push_back(&label);
190 }
191
192 BIND(&distinguish_types);
193
194 generator(this);
195
196 TNode<JSArrayBuffer> array_buffer = LoadJSArrayBufferViewBuffer(typed_array);
197 TNode<Int32T> elements_kind = LoadMapElementsKind(typed_array_map);
198 Switch(elements_kind, &unexpected_instance_type, elements_kinds.data(),
199 label_ptrs.data(), labels.size());
200
201 size_t i = 0;
202 for (auto it = labels.begin(); it != labels.end(); ++i, ++it) {
203 BIND(&*it);
204 source_elements_kind_ = static_cast<ElementsKind>(elements_kinds[i]);
205 VisitAllTypedArrayElements(array_buffer, processor, direction, typed_array);
206 ReturnFromBuiltin(a_.value());
207 }
208}
209
211 TNode<JSArrayBuffer> array_buffer, const CallResultProcessor& processor,
213 VariableList list({&a_, &k_}, zone());
214
218 int incr = 1;
220 std::swap(start, end);
221 advance_mode = IndexAdvanceMode::kPre;
222 incr = -1;
223 }
224 k_ = start;
225
226 // TODO(v8:11111): Only RAB-backed TAs need special handling here since the
227 // backing store can shrink mid-iteration. This implementation has an
228 // overzealous check for GSAB-backed length-tracking TAs. Then again, the
229 // non-RAB/GSAB code also has an overzealous detached check for SABs.
230 ElementsKind effective_elements_kind = source_elements_kind_;
231 bool is_rab_gsab = IsRabGsabTypedArrayElementsKind(effective_elements_kind);
232 if (is_rab_gsab) {
233 effective_elements_kind =
234 GetCorrespondingNonRabGsabElementsKind(effective_elements_kind);
235 }
237 list, start, end,
238 [&](TNode<UintPtrT> index) {
239 TVARIABLE(Object, value);
240 Label detached(this, Label::kDeferred);
241 Label process(this);
242 if (is_rab_gsab) {
243 // If `index` is out of bounds, Get returns undefined.
244 CheckJSTypedArrayIndex(typed_array, index, &detached);
245 } else {
246 GotoIf(IsDetachedBuffer(array_buffer), &detached);
247 }
248 {
249 TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(typed_array);
250 value = LoadFixedTypedArrayElementAsTagged(data_ptr, index,
251 effective_elements_kind);
252 Goto(&process);
253 }
254
255 BIND(&detached);
256 {
257 value = UndefinedConstant();
258 Goto(&process);
259 }
260
261 BIND(&process);
262 {
263 k_ = index;
264 a_ = processor(this, value.value(), index);
265 }
266 },
267 incr, LoopUnrollingMode::kNo, advance_mode);
268}
269
270TF_BUILTIN(ArrayPrototypePop, CodeStubAssembler) {
271 auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount);
272 auto context = Parameter<Context>(Descriptor::kContext);
273 CSA_DCHECK(this, IsUndefined(Parameter<Object>(Descriptor::kJSNewTarget)));
274
275 CodeStubArguments args(this, argc);
276 TNode<Object> receiver = args.GetReceiver();
277
278 Label runtime(this, Label::kDeferred);
279 Label fast(this);
280
281 // Only pop in this stub if
282 // 1) the array has fast elements
283 // 2) the length is writable,
284 // 3) the elements backing store isn't copy-on-write,
285 // 4) we aren't supposed to shrink the backing store.
286
287 // 1) Check that the array has fast elements.
288 BranchIfFastJSArray(receiver, context, &fast, &runtime);
289
290 BIND(&fast);
291 {
292 TNode<JSArray> array_receiver = CAST(receiver);
293 CSA_DCHECK(this, TaggedIsPositiveSmi(LoadJSArrayLength(array_receiver)));
294 TNode<Int32T> length =
295 LoadAndUntagToWord32ObjectField(array_receiver, JSArray::kLengthOffset);
296 Label return_undefined(this), fast_elements(this);
297
298 // 2) Ensure that the length is writable.
299 EnsureArrayLengthWritable(context, LoadMap(array_receiver), &runtime);
300
301 GotoIf(Word32Equal(length, Int32Constant(0)), &return_undefined);
302
303 // 3) Check that the elements backing store isn't copy-on-write.
304 TNode<FixedArrayBase> elements = LoadElements(array_receiver);
305 GotoIf(TaggedEqual(LoadMap(elements), FixedCOWArrayMapConstant()),
306 &runtime);
307
308 TNode<Int32T> new_length = Int32Sub(length, Int32Constant(1));
309
310 // 4) Check that we're not supposed to shrink the backing store, as
311 // implemented in elements.cc:ElementsAccessorBase::SetLengthImpl.
312 TNode<Int32T> capacity = SmiToInt32(LoadFixedArrayBaseLength(elements));
313 GotoIf(Int32LessThan(
314 Int32Add(Int32Add(new_length, new_length),
316 capacity),
317 &runtime);
318
319 TNode<IntPtrT> new_length_intptr = ChangePositiveInt32ToIntPtr(new_length);
320 StoreObjectFieldNoWriteBarrier(array_receiver, JSArray::kLengthOffset,
321 SmiTag(new_length_intptr));
322
323 TNode<Int32T> elements_kind = LoadElementsKind(array_receiver);
324 GotoIf(Int32LessThanOrEqual(elements_kind,
325 Int32Constant(TERMINAL_FAST_ELEMENTS_KIND)),
326 &fast_elements);
327
328 {
329 TNode<FixedDoubleArray> elements_known_double_array =
330 ReinterpretCast<FixedDoubleArray>(elements);
331 TNode<Float64T> value = LoadFixedDoubleArrayElement(
332 elements_known_double_array, new_length_intptr, &return_undefined);
333
334 StoreFixedDoubleArrayHole(elements_known_double_array, new_length_intptr);
335 args.PopAndReturn(AllocateHeapNumberWithValue(value));
336 }
337
338 BIND(&fast_elements);
339 {
340 TNode<FixedArray> elements_known_fixed_array = CAST(elements);
341 TNode<Object> value =
342 LoadFixedArrayElement(elements_known_fixed_array, new_length_intptr);
343 StoreFixedArrayElement(elements_known_fixed_array, new_length_intptr,
344 TheHoleConstant());
345 GotoIf(TaggedEqual(value, TheHoleConstant()), &return_undefined);
346 args.PopAndReturn(CAST(value));
347 }
348
349 BIND(&return_undefined);
350 { args.PopAndReturn(UndefinedConstant()); }
351 }
352
353 BIND(&runtime);
354 {
355 // We are not using Parameter(Descriptor::kJSTarget) and loading the value
356 // from the current frame here in order to reduce register pressure on the
357 // fast path.
358 TNode<JSFunction> target = LoadTargetFromFrame();
359 TailCallJSBuiltin(Builtin::kArrayPop, context, target, UndefinedConstant(),
360 argc, InvalidDispatchHandleConstant());
361 }
362}
363
364TF_BUILTIN(ArrayPrototypePush, CodeStubAssembler) {
365 TVARIABLE(IntPtrT, arg_index);
366 Label default_label(this, &arg_index);
367 Label smi_transition(this);
368 Label object_push_pre(this);
369 Label object_push(this, &arg_index);
370 Label double_push(this, &arg_index);
371 Label double_transition(this);
372 Label runtime(this, Label::kDeferred);
373
374 auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount);
375 auto context = Parameter<Context>(Descriptor::kContext);
376 CSA_DCHECK(this, IsUndefined(Parameter<Object>(Descriptor::kJSNewTarget)));
377
378 CodeStubArguments args(this, argc);
379 TNode<Object> receiver = args.GetReceiver();
380 TNode<JSArray> array_receiver;
382
383 Label fast(this);
384 BranchIfFastJSArray(receiver, context, &fast, &runtime);
385
386 BIND(&fast);
387 {
388 array_receiver = CAST(receiver);
389 arg_index = IntPtrConstant(0);
390 kind = EnsureArrayPushable(context, LoadMap(array_receiver), &runtime);
391 GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS),
392 &object_push_pre);
393
395 BuildAppendJSArray(PACKED_SMI_ELEMENTS, array_receiver, &args,
396 &arg_index, &smi_transition);
397 args.PopAndReturn(new_length);
398 }
399
400 // If the argument is not a smi, then use a heavyweight SetProperty to
401 // transition the array for only the single next element. If the argument is
402 // a smi, the failure is due to some other reason and we should fall back on
403 // the most generic implementation for the rest of the array.
404 BIND(&smi_transition);
405 {
406 TNode<Object> arg = args.AtIndex(arg_index.value());
407 GotoIf(TaggedIsSmi(arg), &default_label);
408 TNode<Number> length = LoadJSArrayLength(array_receiver);
409 // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
410 // calling into the runtime to do the elements transition is overkill.
411 SetPropertyStrict(context, array_receiver, length, arg);
412 Increment(&arg_index);
413 // The runtime SetProperty call could have converted the array to dictionary
414 // mode, which must be detected to abort the fast-path.
415 TNode<Int32T> elements_kind = LoadElementsKind(array_receiver);
416 GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)),
417 &default_label);
418
419 GotoIfNotNumber(arg, &object_push);
420 Goto(&double_push);
421 }
422
423 BIND(&object_push_pre);
424 {
425 Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS), &double_push,
426 &object_push);
427 }
428
429 BIND(&object_push);
430 {
431 TNode<Smi> new_length = BuildAppendJSArray(
432 PACKED_ELEMENTS, array_receiver, &args, &arg_index, &default_label);
433 args.PopAndReturn(new_length);
434 }
435
436 BIND(&double_push);
437 {
439 BuildAppendJSArray(PACKED_DOUBLE_ELEMENTS, array_receiver, &args,
440 &arg_index, &double_transition);
441 args.PopAndReturn(new_length);
442 }
443
444 // If the argument is not a double, then use a heavyweight SetProperty to
445 // transition the array for only the single next element. If the argument is
446 // a double, the failure is due to some other reason and we should fall back
447 // on the most generic implementation for the rest of the array.
448 BIND(&double_transition);
449 {
450 TNode<Object> arg = args.AtIndex(arg_index.value());
451 GotoIfNumber(arg, &default_label);
452 TNode<Number> length = LoadJSArrayLength(array_receiver);
453 // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
454 // calling into the runtime to do the elements transition is overkill.
455 SetPropertyStrict(context, array_receiver, length, arg);
456 Increment(&arg_index);
457 // The runtime SetProperty call could have converted the array to dictionary
458 // mode, which must be detected to abort the fast-path.
459 TNode<Int32T> elements_kind = LoadElementsKind(array_receiver);
460 GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)),
461 &default_label);
462 Goto(&object_push);
463 }
464
465 // Fallback that stores un-processed arguments using the full, heavyweight
466 // SetProperty machinery.
467 BIND(&default_label);
468 {
469 args.ForEach(
470 [=, this](TNode<Object> arg) {
471 TNode<Number> length = LoadJSArrayLength(array_receiver);
472 SetPropertyStrict(context, array_receiver, length, arg);
473 },
474 arg_index.value());
475 args.PopAndReturn(LoadJSArrayLength(array_receiver));
476 }
477
478 BIND(&runtime);
479 {
480 // We are not using Parameter(Descriptor::kJSTarget) and loading the value
481 // from the current frame here in order to reduce register pressure on the
482 // fast path.
483 TNode<JSFunction> target = LoadTargetFromFrame();
484 TailCallJSBuiltin(Builtin::kArrayPush, context, target, UndefinedConstant(),
485 argc, InvalidDispatchHandleConstant());
486 }
487}
488
489TF_BUILTIN(ExtractFastJSArray, ArrayBuiltinsAssembler) {
490 auto context = Parameter<Context>(Descriptor::kContext);
491 auto array = Parameter<JSArray>(Descriptor::kSource);
492 TNode<BInt> begin = SmiToBInt(Parameter<Smi>(Descriptor::kBegin));
493 TNode<BInt> count = SmiToBInt(Parameter<Smi>(Descriptor::kCount));
494
495 CSA_DCHECK(this, Word32BinaryNot(IsNoElementsProtectorCellInvalid()));
496
497 Return(ExtractFastJSArray(context, array, begin, count));
498}
499
501 auto context = Parameter<Context>(Descriptor::kContext);
502 auto array = Parameter<JSArray>(Descriptor::kSource);
503
504 CSA_DCHECK(this,
505 Word32Or(Word32BinaryNot(IsHoleyFastElementsKindForRead(
506 LoadElementsKind(array))),
507 Word32BinaryNot(IsNoElementsProtectorCellInvalid())));
508
509 Return(CloneFastJSArray(context, array));
510}
511
512// This builtin copies the backing store of fast arrays, while converting any
513// holes to undefined.
514// - If there are no holes in the source, its ElementsKind will be preserved. In
515// that case, this builtin should perform as fast as CloneFastJSArray. (In fact,
516// for fast packed arrays, the behavior is equivalent to CloneFastJSArray.)
517// - If there are holes in the source, the ElementsKind of the "copy" will be
518// PACKED_ELEMENTS (such that undefined can be stored).
519TF_BUILTIN(CloneFastJSArrayFillingHoles, ArrayBuiltinsAssembler) {
520 auto context = Parameter<Context>(Descriptor::kContext);
521 auto array = Parameter<JSArray>(Descriptor::kSource);
522
523 CSA_DCHECK(this,
524 Word32Or(Word32BinaryNot(IsHoleyFastElementsKindForRead(
525 LoadElementsKind(array))),
526 Word32BinaryNot(IsNoElementsProtectorCellInvalid())));
527
528 Return(CloneFastJSArray(context, array, std::nullopt,
529 HoleConversionMode::kConvertToUndefined));
530}
531
533 public:
536
539 TVARIABLE(Object, array);
540 Label is_constructor(this), is_not_constructor(this), done(this);
541 GotoIf(TaggedIsSmi(receiver), &is_not_constructor);
542 Branch(IsConstructor(CAST(receiver)), &is_constructor, &is_not_constructor);
543
545 {
546 array = Construct(context, CAST(receiver));
547 Goto(&done);
548 }
549
550 BIND(&is_not_constructor);
551 {
552 Label allocate_js_array(this);
553
554 TNode<Map> array_map = CAST(LoadContextElement(
555 context, Context::JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX));
556
557 TNode<IntPtrT> capacity = IntPtrConstant(0);
558 TNode<Smi> length = SmiConstant(0);
559 array = AllocateJSArray(PACKED_SMI_ELEMENTS, array_map, capacity, length);
560 Goto(&done);
561 }
562
563 BIND(&done);
564 return array.value();
565 }
566
569 TNode<Number> length) {
570 TVARIABLE(Object, array);
571 Label is_constructor(this), is_not_constructor(this), done(this);
572 CSA_DCHECK(this, IsNumberNormalized(length));
573 GotoIf(TaggedIsSmi(receiver), &is_not_constructor);
574 Branch(IsConstructor(CAST(receiver)), &is_constructor, &is_not_constructor);
575
577 {
578 array = Construct(context, CAST(receiver), length);
579 Goto(&done);
580 }
581
582 BIND(&is_not_constructor);
583 {
584 array = ArrayCreate(context, length);
585 Goto(&done);
586 }
587
588 BIND(&done);
589 return array.value();
590 }
591};
592
593TF_BUILTIN(TypedArrayPrototypeMap, ArrayBuiltinsAssembler) {
594 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
595 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
596 CodeStubArguments args(this, argc);
597 auto context = Parameter<Context>(Descriptor::kContext);
598 TNode<JSAny> receiver = args.GetReceiver();
599 TNode<JSAny> callbackfn = args.GetOptionalArgumentValue(0);
600 TNode<JSAny> this_arg = args.GetOptionalArgumentValue(1);
601
602 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
603
604 GenerateIteratingTypedArrayBuiltinBody(
605 "%TypedArray%.prototype.map",
608}
609
611 public:
614
616
618
619 void Generate(SearchVariant variant, TNode<IntPtrT> argc,
620 TNode<Context> context);
622 TNode<FixedArray> elements,
623 TNode<Object> search_element,
624 TNode<Smi> array_length, TNode<Smi> from_index,
625 SimpleElementKind array_kind);
628 TNode<Object> search_element,
629 TNode<Smi> array_length, TNode<Smi> from_index);
632 TNode<Object> search_element,
633 TNode<Smi> array_length, TNode<Smi> from_index);
634
636 Label done(this);
637 GotoIf(SmiGreaterThan(length, SmiConstant(0)), &done);
638 Return(value);
639 BIND(&done);
640 }
641
642 private:
643 // Use SIMD code for arrays larger than kSIMDThreshold (in builtins that have
644 // SIMD implementations).
645 const int kSIMDThreshold = 48;
646
647 // For now, we can vectorize if:
648 // - SSE3/AVX are present (x86/x64). Note that if __AVX__ is defined, then
649 // __SSE3__ will be as well, so we just check __SSE3__.
650 // - Neon is present and the architecture is 64-bit (because Neon on 32-bit
651 // architecture lacks some instructions).
652#if defined(__SSE3__) || defined(V8_HOST_ARCH_ARM64)
653 const bool kCanVectorize = true;
654#else
655 const bool kCanVectorize = false;
656#endif
657};
658
660 TNode<IntPtrT> argc,
661 TNode<Context> context) {
662 const int kSearchElementArg = 0;
663 const int kFromIndexArg = 1;
664
665 CodeStubArguments args(this, argc);
666
667 TNode<Object> receiver = args.GetReceiver();
668 TNode<Object> search_element =
669 args.GetOptionalArgumentValue(kSearchElementArg);
670
671 TNode<IntPtrT> intptr_zero = IntPtrConstant(0);
672
673 Label init_index(this), return_not_found(this), call_runtime(this);
674
675 // Take slow path if not a JSArray, if retrieving elements requires
676 // traversing prototype, or if access checks are required.
677 BranchIfFastJSArrayForRead(receiver, context, &init_index, &call_runtime);
678
679 BIND(&init_index);
680 TVARIABLE(IntPtrT, index_var, intptr_zero);
682
683 // JSArray length is always a positive Smi for fast arrays.
684 CSA_DCHECK(this, TaggedIsPositiveSmi(LoadJSArrayLength(array)));
685 TNode<Smi> array_length = LoadFastJSArrayLength(array);
686 TNode<IntPtrT> array_length_untagged = PositiveSmiUntag(array_length);
687
688 {
689 // Initialize fromIndex.
690 Label is_smi(this), is_nonsmi(this), done(this);
691
692 // If no fromIndex was passed, default to 0.
693 GotoIf(IntPtrLessThanOrEqual(args.GetLengthWithoutReceiver(),
694 IntPtrConstant(kFromIndexArg)),
695 &done);
696
697 TNode<Object> start_from = args.AtIndex(kFromIndexArg);
698 // Handle Smis and undefined here and everything else in runtime.
699 // We must be very careful with side effects from the ToInteger conversion,
700 // as the side effects might render previously checked assumptions about
701 // the receiver being a fast JSArray and its length invalid.
702 Branch(TaggedIsSmi(start_from), &is_smi, &is_nonsmi);
703
704 BIND(&is_nonsmi);
705 {
706 GotoIfNot(IsUndefined(start_from), &call_runtime);
707 Goto(&done);
708 }
709 BIND(&is_smi);
710 {
711 TNode<IntPtrT> intptr_start_from = SmiUntag(CAST(start_from));
712 index_var = intptr_start_from;
713
714 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done);
715 // The fromIndex is negative: add it to the array's length.
716 index_var = IntPtrAdd(array_length_untagged, index_var.value());
717 // Clamp negative results at zero.
718 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done);
719 index_var = intptr_zero;
720 Goto(&done);
721 }
722 BIND(&done);
723 }
724
725 // Fail early if startIndex >= array.length.
726 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), array_length_untagged),
727 &return_not_found);
728
729 Label if_smi(this), if_smiorobjects(this), if_packed_doubles(this),
730 if_holey_doubles(this);
731
732 TNode<Int32T> elements_kind = LoadElementsKind(array);
733 TNode<FixedArrayBase> elements = LoadElements(array);
734 static_assert(PACKED_SMI_ELEMENTS == 0);
735 static_assert(HOLEY_SMI_ELEMENTS == 1);
736 static_assert(PACKED_ELEMENTS == 2);
737 static_assert(HOLEY_ELEMENTS == 3);
739 &if_smi);
741 &if_smiorobjects);
742 GotoIf(
744 &if_packed_doubles);
746 &if_holey_doubles);
749 &if_smiorobjects);
750 Goto(&return_not_found);
751
752 BIND(&if_smi);
753 {
754 Builtin builtin = (variant == kIncludes) ? Builtin::kArrayIncludesSmi
755 : Builtin::kArrayIndexOfSmi;
757 CallBuiltin<JSAny>(builtin, context, elements, search_element,
758 array_length, SmiTag(index_var.value()));
759 args.PopAndReturn(result);
760 }
761
762 BIND(&if_smiorobjects);
763 {
764 Builtin builtin = (variant == kIncludes)
765 ? Builtin::kArrayIncludesSmiOrObject
766 : Builtin::kArrayIndexOfSmiOrObject;
768 CallBuiltin<JSAny>(builtin, context, elements, search_element,
769 array_length, SmiTag(index_var.value()));
770 args.PopAndReturn(result);
771 }
772
773 BIND(&if_packed_doubles);
774 {
775 Builtin builtin = (variant == kIncludes)
776 ? Builtin::kArrayIncludesPackedDoubles
777 : Builtin::kArrayIndexOfPackedDoubles;
779 CallBuiltin<JSAny>(builtin, context, elements, search_element,
780 array_length, SmiTag(index_var.value()));
781 args.PopAndReturn(result);
782 }
783
784 BIND(&if_holey_doubles);
785 {
786 Builtin builtin = (variant == kIncludes)
787 ? Builtin::kArrayIncludesHoleyDoubles
788 : Builtin::kArrayIndexOfHoleyDoubles;
790 CallBuiltin<JSAny>(builtin, context, elements, search_element,
791 array_length, SmiTag(index_var.value()));
792 args.PopAndReturn(result);
793 }
794
795 BIND(&return_not_found);
796 if (variant == kIncludes) {
797 args.PopAndReturn(FalseConstant());
798 } else {
799 args.PopAndReturn(NumberConstant(-1));
800 }
801
802 BIND(&call_runtime);
803 {
804 TNode<Object> start_from = args.GetOptionalArgumentValue(kFromIndexArg);
805 Runtime::FunctionId function = variant == kIncludes
806 ? Runtime::kArrayIncludes_Slow
807 : Runtime::kArrayIndexOf;
808 args.PopAndReturn(CallRuntime<JSAny>(function, context, array,
809 search_element, start_from));
810 }
811}
812
814 SearchVariant variant, TNode<Context> context, TNode<FixedArray> elements,
815 TNode<Object> search_element, TNode<Smi> array_length,
816 TNode<Smi> from_index, SimpleElementKind array_kind) {
817 TVARIABLE(IntPtrT, index_var, SmiUntag(from_index));
818 TVARIABLE(Float64T, search_num);
819 TNode<IntPtrT> array_length_untagged = PositiveSmiUntag(array_length);
820
821 Label ident_loop(this, &index_var), heap_num_loop(this, &search_num),
822 string_loop(this), bigint_loop(this, &index_var),
823 undef_loop(this, &index_var), not_smi(this), not_heap_num(this),
824 return_found(this), return_not_found(this);
825
826 GotoIfNot(TaggedIsSmi(search_element), &not_smi);
827 search_num = SmiToFloat64(CAST(search_element));
828 Goto(&heap_num_loop);
829
830 BIND(&not_smi);
831 if (variant == kIncludes) {
832 GotoIf(IsUndefined(search_element), &undef_loop);
833 }
834 TNode<Map> map = LoadMap(CAST(search_element));
835 GotoIfNot(IsHeapNumberMap(map), &not_heap_num);
836 search_num = LoadHeapNumberValue(CAST(search_element));
837 Goto(&heap_num_loop);
838
839 BIND(&not_heap_num);
840 TNode<Uint16T> search_type = LoadMapInstanceType(map);
841 GotoIf(IsStringInstanceType(search_type), &string_loop);
842 GotoIf(IsBigIntInstanceType(search_type), &bigint_loop);
843
844 // Use UniqueInt32Constant instead of BoolConstant here in order to ensure
845 // that the graph structure does not depend on the value of the predicate
846 // (BoolConstant uses cached nodes).
848 {
849 Label simd_call(this);
850 Branch(
851 UintPtrLessThan(array_length_untagged, IntPtrConstant(kSIMDThreshold)),
852 &ident_loop, &simd_call);
853 BIND(&simd_call);
855 ExternalReference::array_indexof_includes_smi_or_object());
857 simd_function, MachineType::UintPtr(),
858 std::make_pair(MachineType::TaggedPointer(), elements),
859 std::make_pair(MachineType::UintPtr(), array_length_untagged),
860 std::make_pair(MachineType::UintPtr(), index_var.value()),
861 std::make_pair(MachineType::TaggedPointer(), search_element)));
862 index_var = ReinterpretCast<IntPtrT>(result);
863 Branch(IntPtrLessThan(index_var.value(), IntPtrConstant(0)),
864 &return_not_found, &return_found);
865 }
866
867 BIND(&ident_loop);
868 {
869 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
870 &return_not_found);
871 TNode<Object> element_k =
872 UnsafeLoadFixedArrayElement(elements, index_var.value());
873 GotoIf(TaggedEqual(element_k, search_element), &return_found);
874
875 Increment(&index_var);
876 Goto(&ident_loop);
877 }
878
879 if (variant == kIncludes) {
880 BIND(&undef_loop);
881
882 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
883 &return_not_found);
884 TNode<Object> element_k =
885 UnsafeLoadFixedArrayElement(elements, index_var.value());
886 GotoIf(IsUndefined(element_k), &return_found);
887 GotoIf(IsTheHole(element_k), &return_found);
888
889 Increment(&index_var);
890 Goto(&undef_loop);
891 }
892
893 BIND(&heap_num_loop);
894 {
895 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var);
896 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
897 GotoIfNot(Float64Equal(search_num.value(), search_num.value()),
898 nan_handling);
899
900 // Use UniqueInt32Constant instead of BoolConstant here in order to ensure
901 // that the graph structure does not depend on the value of the predicate
902 // (BoolConstant uses cached nodes).
904 array_kind == SimpleElementKind::kSmiOrHole),
905 &not_nan_loop);
906 {
907 Label smi_check(this), simd_call(this);
908 Branch(UintPtrLessThan(array_length_untagged,
910 &not_nan_loop, &smi_check);
911 BIND(&smi_check);
912 Branch(TaggedIsSmi(search_element), &simd_call, &not_nan_loop);
913 BIND(&simd_call);
915 ExternalReference::array_indexof_includes_smi_or_object());
917 simd_function, MachineType::UintPtr(),
918 std::make_pair(MachineType::TaggedPointer(), elements),
919 std::make_pair(MachineType::UintPtr(), array_length_untagged),
920 std::make_pair(MachineType::UintPtr(), index_var.value()),
921 std::make_pair(MachineType::TaggedPointer(), search_element)));
922 index_var = ReinterpretCast<IntPtrT>(result);
923 Branch(IntPtrLessThan(index_var.value(), IntPtrConstant(0)),
924 &return_not_found, &return_found);
925 }
926
927 BIND(&not_nan_loop);
928 {
929 Label continue_loop(this), element_k_not_smi(this);
930 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
931 &return_not_found);
932 TNode<Object> element_k =
933 UnsafeLoadFixedArrayElement(elements, index_var.value());
934 GotoIfNot(TaggedIsSmi(element_k), &element_k_not_smi);
935 Branch(Float64Equal(search_num.value(), SmiToFloat64(CAST(element_k))),
936 &return_found, &continue_loop);
937
938 BIND(&element_k_not_smi);
939 GotoIfNot(IsHeapNumber(CAST(element_k)), &continue_loop);
940 Branch(Float64Equal(search_num.value(),
941 LoadHeapNumberValue(CAST(element_k))),
942 &return_found, &continue_loop);
943
944 BIND(&continue_loop);
945 Increment(&index_var);
946 Goto(&not_nan_loop);
947 }
948
949 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
950 if (variant == kIncludes) {
951 BIND(&nan_loop);
952 Label continue_loop(this);
953 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
954 &return_not_found);
955 TNode<Object> element_k =
956 UnsafeLoadFixedArrayElement(elements, index_var.value());
957 GotoIf(TaggedIsSmi(element_k), &continue_loop);
958 GotoIfNot(IsHeapNumber(CAST(element_k)), &continue_loop);
959 BranchIfFloat64IsNaN(LoadHeapNumberValue(CAST(element_k)), &return_found,
960 &continue_loop);
961
962 BIND(&continue_loop);
963 Increment(&index_var);
964 Goto(&nan_loop);
965 }
966 }
967
968 BIND(&string_loop);
969 {
970 TNode<String> search_element_string = CAST(search_element);
971 Label continue_loop(this), next_iteration(this, &index_var),
972 slow_compare(this), runtime(this, Label::kDeferred);
974 LoadStringLengthAsWord(search_element_string);
975 Goto(&next_iteration);
976 BIND(&next_iteration);
977 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
978 &return_not_found);
979 TNode<Object> element_k =
980 UnsafeLoadFixedArrayElement(elements, index_var.value());
981 GotoIf(TaggedIsSmi(element_k), &continue_loop);
982 GotoIf(TaggedEqual(search_element_string, element_k), &return_found);
983 TNode<Uint16T> element_k_type = LoadInstanceType(CAST(element_k));
984 GotoIfNot(IsStringInstanceType(element_k_type), &continue_loop);
986 &slow_compare, &continue_loop);
987
988 BIND(&slow_compare);
989 StringBuiltinsAssembler string_asm(state());
990 string_asm.StringEqual_Core(search_element_string, search_type,
991 CAST(element_k), element_k_type, search_length,
992 &return_found, &continue_loop, &runtime);
993 BIND(&runtime);
994 TNode<Object> result = CallRuntime(Runtime::kStringEqual, context,
995 search_element_string, element_k);
996 Branch(TaggedEqual(result, TrueConstant()), &return_found, &continue_loop);
997
998 BIND(&continue_loop);
999 Increment(&index_var);
1000 Goto(&next_iteration);
1001 }
1002
1003 BIND(&bigint_loop);
1004 {
1005 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1006 &return_not_found);
1007
1008 TNode<Object> element_k =
1009 UnsafeLoadFixedArrayElement(elements, index_var.value());
1010 Label continue_loop(this);
1011 GotoIf(TaggedIsSmi(element_k), &continue_loop);
1012 GotoIfNot(IsBigInt(CAST(element_k)), &continue_loop);
1013 TNode<Object> result = CallRuntime(Runtime::kBigIntEqualToBigInt, context,
1014 search_element, element_k);
1015 Branch(TaggedEqual(result, TrueConstant()), &return_found, &continue_loop);
1016
1017 BIND(&continue_loop);
1018 Increment(&index_var);
1019 Goto(&bigint_loop);
1020 }
1021 BIND(&return_found);
1022 if (variant == kIncludes) {
1023 Return(TrueConstant());
1024 } else {
1025 Return(SmiTag(index_var.value()));
1026 }
1027
1028 BIND(&return_not_found);
1029 if (variant == kIncludes) {
1030 Return(FalseConstant());
1031 } else {
1033 }
1034}
1035
1037 SearchVariant variant, TNode<FixedDoubleArray> elements,
1038 TNode<Object> search_element, TNode<Smi> array_length,
1039 TNode<Smi> from_index) {
1040 TVARIABLE(IntPtrT, index_var, SmiUntag(from_index));
1041 TNode<IntPtrT> array_length_untagged = PositiveSmiUntag(array_length);
1042
1043 Label nan_loop(this, &index_var), not_nan_case(this),
1044 not_nan_loop(this, &index_var), hole_loop(this, &index_var),
1045 search_notnan(this), return_found(this), return_not_found(this);
1046 TVARIABLE(Float64T, search_num);
1047 search_num = Float64Constant(0);
1048
1049 GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
1050 search_num = SmiToFloat64(CAST(search_element));
1051 Goto(&not_nan_case);
1052
1053 BIND(&search_notnan);
1054 GotoIfNot(IsHeapNumber(CAST(search_element)), &return_not_found);
1055
1056 search_num = LoadHeapNumberValue(CAST(search_element));
1057
1058 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
1059 BranchIfFloat64IsNaN(search_num.value(), nan_handling, &not_nan_case);
1060
1061 BIND(&not_nan_case);
1062 // Use UniqueInt32Constant instead of BoolConstant here in order to ensure
1063 // that the graph structure does not depend on the value of the predicate
1064 // (BoolConstant uses cached nodes).
1066 {
1067 Label simd_call(this);
1068 Branch(
1069 UintPtrLessThan(array_length_untagged, IntPtrConstant(kSIMDThreshold)),
1070 &not_nan_loop, &simd_call);
1071 BIND(&simd_call);
1072 TNode<ExternalReference> simd_function =
1073 ExternalConstant(ExternalReference::array_indexof_includes_double());
1075 simd_function, MachineType::UintPtr(),
1076 std::make_pair(MachineType::TaggedPointer(), elements),
1077 std::make_pair(MachineType::UintPtr(), array_length_untagged),
1078 std::make_pair(MachineType::UintPtr(), index_var.value()),
1079 std::make_pair(MachineType::TaggedPointer(), search_element)));
1080 index_var = ReinterpretCast<IntPtrT>(result);
1081 Branch(IntPtrLessThan(index_var.value(), IntPtrConstant(0)),
1082 &return_not_found, &return_found);
1083 }
1084
1085 BIND(&not_nan_loop);
1086 {
1087 Label continue_loop(this);
1088 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1089 &return_not_found);
1090 TNode<Float64T> element_k =
1091 LoadFixedDoubleArrayElement(elements, index_var.value());
1092 Branch(Float64Equal(element_k, search_num.value()), &return_found,
1093 &continue_loop);
1094 BIND(&continue_loop);
1095 Increment(&index_var);
1096 Goto(&not_nan_loop);
1097 }
1098
1099 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
1100 if (variant == kIncludes) {
1101 BIND(&nan_loop);
1102 Label continue_loop(this);
1103 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1104 &return_not_found);
1105 TNode<Float64T> element_k =
1106 LoadFixedDoubleArrayElement(elements, index_var.value());
1107 BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop);
1108 BIND(&continue_loop);
1109 Increment(&index_var);
1110 Goto(&nan_loop);
1111 }
1112
1113 BIND(&return_found);
1114 if (variant == kIncludes) {
1115 Return(TrueConstant());
1116 } else {
1117 Return(SmiTag(index_var.value()));
1118 }
1119
1120 BIND(&return_not_found);
1121 if (variant == kIncludes) {
1122 Return(FalseConstant());
1123 } else {
1125 }
1126}
1127
1129 SearchVariant variant, TNode<FixedDoubleArray> elements,
1130 TNode<Object> search_element, TNode<Smi> array_length,
1131 TNode<Smi> from_index) {
1132 TVARIABLE(IntPtrT, index_var, SmiUntag(from_index));
1133 TNode<IntPtrT> array_length_untagged = PositiveSmiUntag(array_length);
1134
1135 Label nan_loop(this, &index_var), not_nan_case(this),
1136 not_nan_loop(this, &index_var), hole_loop(this, &index_var),
1137 search_notnan(this), return_found(this), return_not_found(this);
1138 TVARIABLE(Float64T, search_num);
1139 search_num = Float64Constant(0);
1140
1141 GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
1142 search_num = SmiToFloat64(CAST(search_element));
1143 Goto(&not_nan_case);
1144
1145 BIND(&search_notnan);
1147 GotoIf(IsUndefined(search_element), &hole_loop);
1148 }
1149 GotoIfNot(IsHeapNumber(CAST(search_element)), &return_not_found);
1150
1151 search_num = LoadHeapNumberValue(CAST(search_element));
1152
1153 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
1154 BranchIfFloat64IsNaN(search_num.value(), nan_handling, &not_nan_case);
1155
1156 BIND(&not_nan_case);
1157 // Use UniqueInt32Constant instead of BoolConstant here in order to ensure
1158 // that the graph structure does not depend on the value of the predicate
1159 // (BoolConstant uses cached nodes).
1161 {
1162 Label simd_call(this);
1163 Branch(
1164 UintPtrLessThan(array_length_untagged, IntPtrConstant(kSIMDThreshold)),
1165 &not_nan_loop, &simd_call);
1166 BIND(&simd_call);
1167 TNode<ExternalReference> simd_function =
1168 ExternalConstant(ExternalReference::array_indexof_includes_double());
1170 simd_function, MachineType::UintPtr(),
1171 std::make_pair(MachineType::TaggedPointer(), elements),
1172 std::make_pair(MachineType::UintPtr(), array_length_untagged),
1173 std::make_pair(MachineType::UintPtr(), index_var.value()),
1174 std::make_pair(MachineType::TaggedPointer(), search_element)));
1175 index_var = ReinterpretCast<IntPtrT>(result);
1176 Branch(IntPtrLessThan(index_var.value(), IntPtrConstant(0)),
1177 &return_not_found, &return_found);
1178 }
1179
1180 BIND(&not_nan_loop);
1181 {
1182 Label continue_loop(this);
1183 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1184 &return_not_found);
1185
1186 // No need for hole checking here; the following Float64Equal will
1187 // return 'not equal' for holes anyway.
1188 TNode<Float64T> element_k =
1189 LoadFixedDoubleArrayElement(elements, index_var.value());
1190
1191 Branch(Float64Equal(element_k, search_num.value()), &return_found,
1192 &continue_loop);
1193 BIND(&continue_loop);
1194 Increment(&index_var);
1195 Goto(&not_nan_loop);
1196 }
1197
1198 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
1199 if (variant == kIncludes) {
1200 BIND(&nan_loop);
1201 Label continue_loop(this);
1202 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1203 &return_not_found);
1204
1205 // Load double value or continue if it's the hole NaN.
1207 elements, index_var.value(), &continue_loop);
1208
1209 BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop);
1210 BIND(&continue_loop);
1211 Increment(&index_var);
1212 Goto(&nan_loop);
1213 }
1214
1215 // Array.p.includes treats the hole as undefined.
1217 BIND(&hole_loop);
1218 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1219 &return_not_found);
1220
1221 // Try to find undefined. If we find an explicit double encoded undefined,
1222 // go to `return_found`. For double holes, go to `return_found` only if we
1223 // check for existance of undefined. When computing the index, holes are
1224 // ignored (we don't pass a label).
1225#ifdef V8_ENABLE_EXPERIMENTAL_UNDEFINED_DOUBLE
1226 LoadFixedDoubleArrayElementWithUndefinedCheck(
1227 elements, index_var.value(), &return_found,
1228 (variant == kIncludes ? &return_found : nullptr), MachineType::None());
1229#else
1230 LoadFixedDoubleArrayElement(elements, index_var.value(), &return_found,
1232#endif // V8_ENABLE_EXPERIMENTAL_UNDEFINED_DOUBLE
1233
1234 Increment(&index_var);
1235 Goto(&hole_loop);
1236 }
1237
1238 BIND(&return_found);
1239 if (variant == kIncludes) {
1240 Return(TrueConstant());
1241 } else {
1242 Return(SmiTag(index_var.value()));
1243 }
1244
1245 BIND(&return_not_found);
1246 if (variant == kIncludes) {
1247 Return(FalseConstant());
1248 } else {
1250 }
1251}
1252
1254 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
1255 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
1256 auto context = Parameter<Context>(Descriptor::kContext);
1257
1258 Generate(kIncludes, argc, context);
1259}
1260
1262 auto context = Parameter<Context>(Descriptor::kContext);
1263 auto elements = Parameter<FixedArray>(Descriptor::kElements);
1264 auto search_element = Parameter<Object>(Descriptor::kSearchElement);
1265 auto array_length = Parameter<Smi>(Descriptor::kLength);
1266 auto from_index = Parameter<Smi>(Descriptor::kFromIndex);
1267
1268 GenerateSmiOrObject(kIncludes, context, elements, search_element,
1269 array_length, from_index, SimpleElementKind::kSmiOrHole);
1270}
1271
1272TF_BUILTIN(ArrayIncludesSmiOrObject, ArrayIncludesIndexofAssembler) {
1273 auto context = Parameter<Context>(Descriptor::kContext);
1274 auto elements = Parameter<FixedArray>(Descriptor::kElements);
1275 auto search_element = Parameter<Object>(Descriptor::kSearchElement);
1276 auto array_length = Parameter<Smi>(Descriptor::kLength);
1277 auto from_index = Parameter<Smi>(Descriptor::kFromIndex);
1278
1279 GenerateSmiOrObject(kIncludes, context, elements, search_element,
1280 array_length, from_index, SimpleElementKind::kAny);
1281}
1282
1283TF_BUILTIN(ArrayIncludesPackedDoubles, ArrayIncludesIndexofAssembler) {
1284 auto elements = Parameter<FixedArrayBase>(Descriptor::kElements);
1285 auto search_element = Parameter<Object>(Descriptor::kSearchElement);
1286 auto array_length = Parameter<Smi>(Descriptor::kLength);
1287 auto from_index = Parameter<Smi>(Descriptor::kFromIndex);
1288
1289 ReturnIfEmpty(array_length, FalseConstant());
1290 GeneratePackedDoubles(kIncludes, CAST(elements), search_element, array_length,
1291 from_index);
1292}
1293
1294TF_BUILTIN(ArrayIncludesHoleyDoubles, ArrayIncludesIndexofAssembler) {
1295 auto elements = Parameter<FixedArrayBase>(Descriptor::kElements);
1296 auto search_element = Parameter<Object>(Descriptor::kSearchElement);
1297 auto array_length = Parameter<Smi>(Descriptor::kLength);
1298 auto from_index = Parameter<Smi>(Descriptor::kFromIndex);
1299
1300 ReturnIfEmpty(array_length, FalseConstant());
1301 GenerateHoleyDoubles(kIncludes, CAST(elements), search_element, array_length,
1302 from_index);
1303}
1304
1306 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
1307 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
1308 auto context = Parameter<Context>(Descriptor::kContext);
1309
1310 Generate(kIndexOf, argc, context);
1311}
1312
1314 auto context = Parameter<Context>(Descriptor::kContext);
1315 auto elements = Parameter<FixedArray>(Descriptor::kElements);
1316 auto search_element = Parameter<Object>(Descriptor::kSearchElement);
1317 auto array_length = Parameter<Smi>(Descriptor::kLength);
1318 auto from_index = Parameter<Smi>(Descriptor::kFromIndex);
1319
1320 GenerateSmiOrObject(kIndexOf, context, elements, search_element, array_length,
1321 from_index, SimpleElementKind::kSmiOrHole);
1322}
1323
1325 auto context = Parameter<Context>(Descriptor::kContext);
1326 auto elements = Parameter<FixedArray>(Descriptor::kElements);
1327 auto search_element = Parameter<Object>(Descriptor::kSearchElement);
1328 auto array_length = Parameter<Smi>(Descriptor::kLength);
1329 auto from_index = Parameter<Smi>(Descriptor::kFromIndex);
1330
1331 GenerateSmiOrObject(kIndexOf, context, elements, search_element, array_length,
1332 from_index, SimpleElementKind::kAny);
1333}
1334
1335TF_BUILTIN(ArrayIndexOfPackedDoubles, ArrayIncludesIndexofAssembler) {
1336 auto elements = Parameter<FixedArrayBase>(Descriptor::kElements);
1337 auto search_element = Parameter<Object>(Descriptor::kSearchElement);
1338 auto array_length = Parameter<Smi>(Descriptor::kLength);
1339 auto from_index = Parameter<Smi>(Descriptor::kFromIndex);
1340
1341 ReturnIfEmpty(array_length, NumberConstant(-1));
1342 GeneratePackedDoubles(kIndexOf, CAST(elements), search_element, array_length,
1343 from_index);
1344}
1345
1346TF_BUILTIN(ArrayIndexOfHoleyDoubles, ArrayIncludesIndexofAssembler) {
1347 auto elements = Parameter<FixedArrayBase>(Descriptor::kElements);
1348 auto search_element = Parameter<Object>(Descriptor::kSearchElement);
1349 auto array_length = Parameter<Smi>(Descriptor::kLength);
1350 auto from_index = Parameter<Smi>(Descriptor::kFromIndex);
1351
1352 ReturnIfEmpty(array_length, NumberConstant(-1));
1353 GenerateHoleyDoubles(kIndexOf, CAST(elements), search_element, array_length,
1354 from_index);
1355}
1356
1357// ES #sec-array.prototype.values
1358TF_BUILTIN(ArrayPrototypeValues, CodeStubAssembler) {
1359 auto context = Parameter<NativeContext>(Descriptor::kContext);
1360 auto receiver = Parameter<Object>(Descriptor::kReceiver);
1361 Return(CreateArrayIterator(context, ToObject_Inline(context, receiver),
1363}
1364
1365// ES #sec-array.prototype.entries
1366TF_BUILTIN(ArrayPrototypeEntries, CodeStubAssembler) {
1367 auto context = Parameter<NativeContext>(Descriptor::kContext);
1368 auto receiver = Parameter<Object>(Descriptor::kReceiver);
1369 Return(CreateArrayIterator(context, ToObject_Inline(context, receiver),
1371}
1372
1373// ES #sec-array.prototype.keys
1374TF_BUILTIN(ArrayPrototypeKeys, CodeStubAssembler) {
1375 auto context = Parameter<NativeContext>(Descriptor::kContext);
1376 auto receiver = Parameter<Object>(Descriptor::kReceiver);
1377 Return(CreateArrayIterator(context, ToObject_Inline(context, receiver),
1379}
1380
1381// ES #sec-%arrayiteratorprototype%.next
1382TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) {
1383 const char* method_name = "Array Iterator.prototype.next";
1384
1385 auto context = Parameter<Context>(Descriptor::kContext);
1386 auto maybe_iterator = Parameter<Object>(Descriptor::kReceiver);
1387
1388 TVARIABLE(Boolean, var_done, TrueConstant());
1389 TVARIABLE(Object, var_value, UndefinedConstant());
1390
1391 Label allocate_entry_if_needed(this);
1392 Label allocate_iterator_result(this);
1393 Label if_typedarray(this), if_other(this, Label::kDeferred), if_array(this),
1394 if_generic(this, Label::kDeferred);
1395 Label set_done(this, Label::kDeferred);
1396
1397 // If O does not have all of the internal slots of an Array Iterator Instance
1398 // (22.1.5.3), throw a TypeError exception
1399 ThrowIfNotInstanceType(context, maybe_iterator, JS_ARRAY_ITERATOR_TYPE,
1400 method_name);
1401
1402 TNode<JSArrayIterator> iterator = CAST(maybe_iterator);
1403
1404 // Let a be O.[[IteratedObject]].
1405 TNode<JSReceiver> array = LoadJSArrayIteratorIteratedObject(iterator);
1406
1407 // Let index be O.[[ArrayIteratorNextIndex]].
1408 TNode<Number> index = LoadJSArrayIteratorNextIndex(iterator);
1409 CSA_DCHECK(this, IsNumberNonNegativeSafeInteger(index));
1410
1411 // Dispatch based on the type of the {array}.
1412 TNode<Map> array_map = LoadMap(array);
1413 TNode<Uint16T> array_type = LoadMapInstanceType(array_map);
1414 GotoIf(InstanceTypeEqual(array_type, JS_ARRAY_TYPE), &if_array);
1415 Branch(InstanceTypeEqual(array_type, JS_TYPED_ARRAY_TYPE), &if_typedarray,
1416 &if_other);
1417
1418 BIND(&if_array);
1419 {
1420 // If {array} is a JSArray, then the {index} must be in Unsigned32 range.
1421 CSA_DCHECK(this, IsNumberArrayIndex(index));
1422
1423 // Check that the {index} is within range for the {array}. We handle all
1424 // kinds of JSArray's here, so we do the computation on Uint32.
1425 TNode<Uint32T> index32 = ChangeNonNegativeNumberToUint32(index);
1426 TNode<Uint32T> length32 =
1427 ChangeNonNegativeNumberToUint32(LoadJSArrayLength(CAST(array)));
1428 GotoIfNot(Uint32LessThan(index32, length32), &set_done);
1429 StoreJSArrayIteratorNextIndex(
1430 iterator, ChangeUint32ToTagged(Uint32Add(index32, Uint32Constant(1))));
1431
1432 var_done = FalseConstant();
1433 var_value = index;
1434
1435 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
1436 iterator, JSArrayIterator::kKindOffset),
1437 Int32Constant(static_cast<int>(IterationKind::kKeys))),
1438 &allocate_iterator_result);
1439
1440 Label if_hole(this, Label::kDeferred);
1441 TNode<Int32T> elements_kind = LoadMapElementsKind(array_map);
1442 TNode<FixedArrayBase> elements = LoadElements(CAST(array));
1443 GotoIfForceSlowPath(&if_generic);
1444 var_value = LoadFixedArrayBaseElementAsTagged(
1445 elements, Signed(ChangeUint32ToWord(index32)), elements_kind,
1446 &if_generic, &if_hole);
1447 Goto(&allocate_entry_if_needed);
1448
1449 BIND(&if_hole);
1450 {
1451 GotoIf(IsNoElementsProtectorCellInvalid(), &if_generic);
1452 GotoIfNot(IsPrototypeInitialArrayPrototype(context, array_map),
1453 &if_generic);
1454 var_value = UndefinedConstant();
1455 Goto(&allocate_entry_if_needed);
1456 }
1457 }
1458
1459 BIND(&if_other);
1460 {
1461 // We cannot enter here with either JSArray's or JSTypedArray's.
1462 CSA_DCHECK(this, Word32BinaryNot(IsJSArray(array)));
1463 CSA_DCHECK(this, Word32BinaryNot(IsJSTypedArray(array)));
1464
1465 // Check that the {index} is within the bounds of the {array}s "length".
1466 TNode<Number> length = CAST(
1467 CallBuiltin(Builtin::kToLength, context,
1468 GetProperty(context, array, factory()->length_string())));
1469 GotoIfNumberGreaterThanOrEqual(index, length, &set_done);
1470 StoreJSArrayIteratorNextIndex(iterator, NumberInc(index));
1471
1472 var_done = FalseConstant();
1473 var_value = index;
1474
1475 Branch(Word32Equal(LoadAndUntagToWord32ObjectField(
1476 iterator, JSArrayIterator::kKindOffset),
1477 Int32Constant(static_cast<int>(IterationKind::kKeys))),
1478 &allocate_iterator_result, &if_generic);
1479 }
1480
1481 BIND(&set_done);
1482 {
1483 // Change the [[ArrayIteratorNextIndex]] such that the {iterator} will
1484 // never produce values anymore, because it will always fail the bounds
1485 // check. Note that this is different from what the specification does,
1486 // which is changing the [[IteratedObject]] to undefined, because leaving
1487 // [[IteratedObject]] alone helps TurboFan to generate better code with
1488 // the inlining in JSCallReducer::ReduceArrayIteratorPrototypeNext().
1489 //
1490 // The terminal value we chose here depends on the type of the {array},
1491 // for JSArray's we use kMaxUInt32 so that TurboFan can always use
1492 // Word32 representation for fast-path indices (and this is safe since
1493 // the "length" of JSArray's is limited to Unsigned32 range). For other
1494 // JSReceiver's we have to use kMaxSafeInteger, since the "length" can
1495 // be any arbitrary value in the safe integer range.
1496 //
1497 // Note specifically that JSTypedArray's will never take this path, so
1498 // we don't need to worry about their maximum value.
1499 CSA_DCHECK(this, Word32BinaryNot(IsJSTypedArray(array)));
1500 TNode<Number> max_length =
1501 SelectConstant(IsJSArray(array), NumberConstant(kMaxUInt32),
1502 NumberConstant(kMaxSafeInteger));
1503 StoreJSArrayIteratorNextIndex(iterator, max_length);
1504 Goto(&allocate_iterator_result);
1505 }
1506
1507 BIND(&if_generic);
1508 {
1509 var_value = GetProperty(context, array, index);
1510 Goto(&allocate_entry_if_needed);
1511 }
1512
1513 BIND(&if_typedarray);
1514 {
1515 // Overflowing uintptr range also means end of iteration.
1516 TNode<UintPtrT> index_uintptr =
1517 ChangeSafeIntegerNumberToUintPtr(index, &allocate_iterator_result);
1518
1519 // If we go outside of the {length}, we don't need to update the
1520 // [[ArrayIteratorNextIndex]] anymore, since a JSTypedArray's
1521 // length cannot change anymore, so this {iterator} will never
1522 // produce values again anyways.
1523 Label detached(this);
1524 TNode<UintPtrT> length =
1525 LoadJSTypedArrayLengthAndCheckDetached(CAST(array), &detached);
1526 GotoIfNot(UintPtrLessThan(index_uintptr, length),
1527 &allocate_iterator_result);
1528 // TODO(v8:4153): Consider storing next index as uintptr. Update this and
1529 // the relevant TurboFan code.
1530 StoreJSArrayIteratorNextIndex(
1531 iterator,
1532 ChangeUintPtrToTagged(UintPtrAdd(index_uintptr, UintPtrConstant(1))));
1533
1534 var_done = FalseConstant();
1535 var_value = index;
1536
1537 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
1538 iterator, JSArrayIterator::kKindOffset),
1539 Int32Constant(static_cast<int>(IterationKind::kKeys))),
1540 &allocate_iterator_result);
1541
1542 TNode<Int32T> elements_kind = LoadMapElementsKind(array_map);
1543 TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(CAST(array));
1544 var_value = LoadFixedTypedArrayElementAsTagged(data_ptr, index_uintptr,
1545 elements_kind);
1546 Goto(&allocate_entry_if_needed);
1547
1548 BIND(&detached);
1549 ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
1550 }
1551
1552 BIND(&allocate_entry_if_needed);
1553 {
1554 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
1555 iterator, JSArrayIterator::kKindOffset),
1556 Int32Constant(static_cast<int>(IterationKind::kValues))),
1557 &allocate_iterator_result);
1558
1560 AllocateJSIteratorResultForEntry(context, index, var_value.value());
1561 Return(result);
1562 }
1563
1564 BIND(&allocate_iterator_result);
1565 {
1567 AllocateJSIteratorResult(context, var_value.value(), var_done.value());
1568 Return(result);
1569 }
1570}
1571
1573 // This is a trampoline to ArrayConstructorImpl which just adds
1574 // allocation_site parameter value and sets new_target if necessary.
1575 auto context = Parameter<Context>(Descriptor::kContext);
1576 auto function = Parameter<JSFunction>(Descriptor::kTarget);
1577 auto new_target = Parameter<Object>(Descriptor::kNewTarget);
1578 auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
1579
1580 // If new_target is undefined, then this is the 'Call' case, so set new_target
1581 // to function.
1582 new_target =
1583 SelectConstant<Object>(IsUndefined(new_target), function, new_target);
1584
1585 // Run the native code for the Array function called as a normal function.
1586 TNode<Oddball> no_gc_site = UndefinedConstant();
1587 TailCallBuiltin(Builtin::kArrayConstructorImpl, context, function, new_target,
1588 argc, no_gc_site);
1589}
1590
1592 const Callable& callable, TNode<Context> context, TNode<JSFunction> target,
1593 TNode<HeapObject> allocation_site_or_undefined, TNode<Int32T> argc) {
1594 TNode<Code> code = HeapConstantNoHole(callable.code());
1595
1596 // We are going to call here ArrayNoArgumentsConstructor or
1597 // ArraySingleArgumentsConstructor which in addition to the register arguments
1598 // also expect some number of arguments on the expression stack.
1599 // Since
1600 // 1) incoming JS arguments are still on the stack,
1601 // 2) the ArrayNoArgumentsConstructor, ArraySingleArgumentsConstructor and
1602 // ArrayNArgumentsConstructor are defined so that the register arguments
1603 // are passed on the same registers,
1604 // in order to be able to generate a tail call to those builtins we do the
1605 // following trick here: we tail call to the constructor builtin using
1606 // ArrayNArgumentsConstructorDescriptor, so the tail call instruction
1607 // pops the current frame but leaves all the incoming JS arguments on the
1608 // expression stack so that the target builtin can still find them where it
1609 // expects.
1611 allocation_site_or_undefined, argc);
1612}
1613
1615 TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc,
1617 std::optional<TNode<AllocationSite>> allocation_site) {
1618 if (mode == DISABLE_ALLOCATION_SITES) {
1621
1622 TailCallArrayConstructorStub(callable, context, target, UndefinedConstant(),
1623 argc);
1624 } else {
1625 DCHECK_EQ(mode, DONT_OVERRIDE);
1626 DCHECK(allocation_site);
1627 TNode<Int32T> elements_kind = LoadElementsKind(*allocation_site);
1628
1629 // TODO(ishell): Compute the builtin index dynamically instead of
1630 // iterating over all expected elements kinds.
1631 int last_index =
1633 for (int i = 0; i <= last_index; ++i) {
1634 Label next(this);
1636 GotoIfNot(Word32Equal(elements_kind, Int32Constant(kind)), &next);
1637
1638 Callable callable =
1640
1641 TailCallArrayConstructorStub(callable, context, target, *allocation_site,
1642 argc);
1643
1644 BIND(&next);
1645 }
1646
1647 // If we reached this point there is a problem.
1648 Abort(AbortReason::kUnexpectedElementsKindInArrayConstructor);
1649 }
1650}
1651
1653 TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc,
1655 std::optional<TNode<AllocationSite>> allocation_site) {
1656 if (mode == DISABLE_ALLOCATION_SITES) {
1658 ElementsKind holey_initial = GetHoleyElementsKind(initial);
1660 isolate(), holey_initial, mode);
1661
1662 TailCallArrayConstructorStub(callable, context, target, UndefinedConstant(),
1663 argc);
1664 } else {
1665 DCHECK_EQ(mode, DONT_OVERRIDE);
1666 DCHECK(allocation_site);
1667 TNode<Smi> transition_info = LoadTransitionInfo(*allocation_site);
1668
1669 // Least significant bit in fast array elements kind means holeyness.
1670 static_assert(PACKED_SMI_ELEMENTS == 0);
1671 static_assert(HOLEY_SMI_ELEMENTS == 1);
1672 static_assert(PACKED_ELEMENTS == 2);
1673 static_assert(HOLEY_ELEMENTS == 3);
1674 static_assert(PACKED_DOUBLE_ELEMENTS == 4);
1675 static_assert(HOLEY_DOUBLE_ELEMENTS == 5);
1676
1677 Label normal_sequence(this);
1678 TVARIABLE(Int32T, var_elements_kind,
1680 SmiToInt32(transition_info))));
1681 // Is the low bit set? If so, we are holey and that is good.
1682 int fast_elements_kind_holey_mask =
1684 GotoIf(IsSetSmi(transition_info, fast_elements_kind_holey_mask),
1685 &normal_sequence);
1686 {
1687 // Make elements kind holey and update elements kind in the type info.
1688 var_elements_kind = Word32Or(var_elements_kind.value(), Int32Constant(1));
1690 *allocation_site,
1691 offsetof(AllocationSite, transition_info_or_boilerplate_),
1692 SmiOr(transition_info, SmiConstant(fast_elements_kind_holey_mask)));
1693 Goto(&normal_sequence);
1694 }
1695 BIND(&normal_sequence);
1696
1697 // TODO(ishell): Compute the builtin index dynamically instead of
1698 // iterating over all expected elements kinds.
1699 // TODO(ishell): Given that the code above ensures that the elements kind
1700 // is holey we can skip checking with non-holey elements kinds.
1701 int last_index =
1703 for (int i = 0; i <= last_index; ++i) {
1704 Label next(this);
1706 GotoIfNot(Word32Equal(var_elements_kind.value(), Int32Constant(kind)),
1707 &next);
1708
1709 Callable callable =
1711
1712 TailCallArrayConstructorStub(callable, context, target, *allocation_site,
1713 argc);
1714
1715 BIND(&next);
1716 }
1717
1718 // If we reached this point there is a problem.
1719 Abort(AbortReason::kUnexpectedElementsKindInArrayConstructor);
1720 }
1721}
1722
1724 TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc,
1726 std::optional<TNode<AllocationSite>> allocation_site) {
1728 Label check_one_case(this), fallthrough(this);
1729 GotoIfNot(IntPtrEqual(args.GetLengthWithoutReceiver(), IntPtrConstant(0)),
1730 &check_one_case);
1731 CreateArrayDispatchNoArgument(context, target, argc, mode, allocation_site);
1732
1733 BIND(&check_one_case);
1734 GotoIfNot(IntPtrEqual(args.GetLengthWithoutReceiver(), IntPtrConstant(1)),
1735 &fallthrough);
1736 CreateArrayDispatchSingleArgument(context, target, argc, mode,
1737 allocation_site);
1738
1739 BIND(&fallthrough);
1740}
1741
1742TF_BUILTIN(ArrayConstructorImpl, ArrayBuiltinsAssembler) {
1743 auto target = Parameter<JSFunction>(Descriptor::kTarget);
1744 auto new_target = Parameter<Object>(Descriptor::kNewTarget);
1745 auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
1746 auto maybe_allocation_site =
1747 Parameter<HeapObject>(Descriptor::kAllocationSite);
1748
1749 // Initial map for the builtin Array functions should be Map.
1750 CSA_DCHECK(this, IsMap(CAST(LoadObjectField(
1751 target, JSFunction::kPrototypeOrInitialMapOffset))));
1752
1753 // We should either have undefined or a valid AllocationSite
1754 CSA_DCHECK(this, Word32Or(IsUndefined(maybe_allocation_site),
1755 IsAllocationSite(maybe_allocation_site)));
1756
1757 // "Enter" the context of the Array function.
1758 TNode<Context> context =
1759 CAST(LoadObjectField(target, JSFunction::kContextOffset));
1760
1761 Label runtime(this, Label::kDeferred);
1762 GotoIf(TaggedNotEqual(target, new_target), &runtime);
1763
1764 Label no_info(this);
1765 // If the feedback vector is the undefined value call an array constructor
1766 // that doesn't use AllocationSites.
1767 GotoIf(IsUndefined(maybe_allocation_site), &no_info);
1768
1769 GenerateDispatchToArrayStub(context, target, argc, DONT_OVERRIDE,
1770 CAST(maybe_allocation_site));
1771 Goto(&runtime);
1772
1773 BIND(&no_info);
1774 GenerateDispatchToArrayStub(context, target, argc, DISABLE_ALLOCATION_SITES);
1775 Goto(&runtime);
1776
1777 BIND(&runtime);
1778 GenerateArrayNArgumentsConstructor(context, target, new_target, argc,
1779 maybe_allocation_site);
1780}
1781
1783 TNode<Context> context, TNode<JSAnyNotSmi> array_function,
1784 TNode<Map> array_map, TNode<Object> array_size,
1785 TNode<HeapObject> allocation_site, ElementsKind elements_kind,
1786 AllocationSiteMode mode) {
1787 Label ok(this);
1788 Label smi_size(this);
1789 Label small_smi_size(this);
1790 Label call_runtime(this, Label::kDeferred);
1791
1792 Branch(TaggedIsSmi(array_size), &smi_size, &call_runtime);
1793
1794 BIND(&smi_size);
1795 {
1796 TNode<Smi> array_size_smi = CAST(array_size);
1797
1798 if (IsFastPackedElementsKind(elements_kind)) {
1799 Label abort(this, Label::kDeferred);
1800 Branch(SmiEqual(array_size_smi, SmiConstant(0)), &small_smi_size, &abort);
1801
1802 BIND(&abort);
1803 TNode<Smi> reason =
1804 SmiConstant(AbortReason::kAllocatingNonEmptyPackedArray);
1805 TailCallRuntime(Runtime::kAbort, context, reason);
1806 } else {
1807 Branch(SmiAboveOrEqual(array_size_smi,
1809 &call_runtime, &small_smi_size);
1810 }
1811
1812 BIND(&small_smi_size);
1813 {
1815 elements_kind, array_map, array_size_smi, array_size_smi,
1817 ? std::optional<TNode<AllocationSite>>(std::nullopt)
1818 : CAST(allocation_site));
1819 Return(array);
1820 }
1821 }
1822
1823 BIND(&call_runtime);
1824 {
1825 TailCallRuntimeNewArray(context, array_function, array_size, array_function,
1826 allocation_site);
1827 }
1828}
1829
1834 Parameter<HeapObject>(Descriptor::kFunction), JSFunction::kContextOffset);
1835 bool track_allocation_site =
1837 std::optional<TNode<AllocationSite>> allocation_site =
1838 track_allocation_site
1839 ? Parameter<AllocationSite>(Descriptor::kAllocationSite)
1840 : std::optional<TNode<AllocationSite>>(std::nullopt);
1844 SmiConstant(0), allocation_site);
1845 Return(array);
1846}
1847
1851 auto context = Parameter<Context>(Descriptor::kContext);
1852 auto function = Parameter<JSAnyNotSmi>(Descriptor::kFunction);
1854 CAST(LoadObjectField(function, JSFunction::kContextOffset));
1856
1857 AllocationSiteMode allocation_site_mode = DONT_TRACK_ALLOCATION_SITE;
1858 if (mode == DONT_OVERRIDE) {
1859 allocation_site_mode = AllocationSite::ShouldTrack(kind)
1862 }
1863
1864 auto array_size = Parameter<Object>(Descriptor::kArraySizeSmiParameter);
1865 // allocation_site can be Undefined or an AllocationSite
1866 auto allocation_site = Parameter<HeapObject>(Descriptor::kAllocationSite);
1867
1868 GenerateConstructor(context, function, array_map, array_size, allocation_site,
1869 kind, allocation_site_mode);
1870}
1871
1874 TNode<Int32T> argc, TNode<HeapObject> maybe_allocation_site) {
1875 // Replace incoming JS receiver argument with the target.
1876 // TODO(ishell): Avoid replacing the target on the stack and just add it
1877 // as another additional parameter for Runtime::kNewArray.
1879 args.SetReceiver(target);
1880
1881 // Adjust arguments count for the runtime call:
1882 // +2 for new_target and maybe_allocation_site.
1883 argc = Int32Add(TruncateIntPtrToInt32(args.GetLengthWithReceiver()),
1884 Int32Constant(2));
1885 TailCallRuntime(Runtime::kNewArray, argc, context, new_target,
1886 maybe_allocation_site);
1887}
1888
1889TF_BUILTIN(ArrayNArgumentsConstructor, ArrayBuiltinsAssembler) {
1890 auto context = Parameter<Context>(Descriptor::kContext);
1891 auto target = Parameter<JSFunction>(Descriptor::kFunction);
1892 auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
1893 auto maybe_allocation_site =
1894 Parameter<HeapObject>(Descriptor::kAllocationSite);
1895
1896 GenerateArrayNArgumentsConstructor(context, target, target, argc,
1897 maybe_allocation_site);
1898}
1899
1900#define GENERATE_ARRAY_CTOR(name, kind_camel, kind_caps, mode_camel, \
1901 mode_caps) \
1902 TF_BUILTIN(Array##name##Constructor_##kind_camel##_##mode_camel, \
1903 ArrayBuiltinsAssembler) { \
1904 GenerateArray##name##Constructor(kind_caps, mode_caps); \
1905 }
1906
1907// The ArrayNoArgumentConstructor builtin family.
1924
1925// The ArraySingleArgumentConstructor builtin family.
1926GENERATE_ARRAY_CTOR(SingleArgument, PackedSmi, PACKED_SMI_ELEMENTS,
1930GENERATE_ARRAY_CTOR(SingleArgument, PackedSmi, PACKED_SMI_ELEMENTS,
1934GENERATE_ARRAY_CTOR(SingleArgument, Packed, PACKED_ELEMENTS,
1938GENERATE_ARRAY_CTOR(SingleArgument, PackedDouble, PACKED_DOUBLE_ELEMENTS,
1942
1943#undef GENERATE_ARRAY_CTOR
1944
1946 public:
1949
1950 // If `item` is an object or an array, deep-clone it and jump to `cloned`.
1952 TVariable<Object>& current_allocation_site,
1953 TNode<Context> context, Label* cloned,
1954 Label* not_cloned, Label* bailout) {
1955 Label is_object(this, &current_allocation_site),
1956 is_array(this, &current_allocation_site);
1957
1958 GotoIf(TaggedIsSmi(item), not_cloned);
1959 GotoIf(IsJSArray(CAST(item)), &is_array);
1960 GotoIf(IsJSObject(CAST(item)), &is_object);
1961 Goto(not_cloned);
1962
1963 BIND(&is_array);
1964 {
1965 // Consume the next AllocationSite. All objects inside this array, as well
1966 // as all sibling objects (until a new array is encountered) will use this
1967 // AllocationSite. E.g., in [1, 2, {a: 3}, [4, 5], {b: 6}], the object {a:
1968 // 3} uses the topmost AllocationSite, and the object {b: 6} uses the
1969 // AllocationSite of [4, 5].
1971 current_allocation_site =
1972 LoadNestedAllocationSite(CAST(current_allocation_site.value()));
1973
1974 // Ensure we're consuming the AllocationSites in the correct order.
1975 CSA_DCHECK(
1976 this,
1977 TaggedEqual(LoadBoilerplate(CAST(current_allocation_site.value())),
1978 item));
1979 }
1980
1981 auto clone_and_next_allocation_site = CallBuiltin<PairT<Object, Object>>(
1982 Builtin::kCreateArrayFromSlowBoilerplateHelper, context,
1983 current_allocation_site.value(), item);
1984
1985 clone = Projection<0>(clone_and_next_allocation_site);
1986 GotoIf(IsUndefined(clone.value()), bailout);
1987 current_allocation_site = Projection<1>(clone_and_next_allocation_site);
1988 Goto(cloned);
1989 }
1990
1991 BIND(&is_object);
1992 {
1993 auto clone_and_next_allocation_site = CallBuiltin<PairT<Object, Object>>(
1994 Builtin::kCreateObjectFromSlowBoilerplateHelper, context,
1995 current_allocation_site.value(), item);
1996 clone = Projection<0>(clone_and_next_allocation_site);
1997 GotoIf(IsUndefined(clone.value()), bailout);
1998 current_allocation_site = Projection<1>(clone_and_next_allocation_site);
1999 Goto(cloned);
2000 }
2001 }
2002
2004 TNode<Smi> length, TNode<Int32T> elements_kind,
2005 TVariable<Object>& current_allocation_site,
2006 TNode<Context> context, Label* done,
2007 Label* bailout) {
2008 CSA_DCHECK(this, SmiNotEqual(length, SmiConstant(0)));
2009
2010 auto loop_body = [&](TNode<IntPtrT> index) {
2011 TVARIABLE(Object, clone);
2012 Label cloned(this, &clone),
2013 done_with_element(this, &current_allocation_site);
2014
2015 TNode<Object> element = LoadFixedArrayElement(CAST(elements), index);
2016 CloneIfObjectOrArray(element, clone, current_allocation_site, context,
2017 &cloned, &done_with_element, bailout);
2018
2019 BIND(&cloned);
2020 {
2021 StoreFixedArrayElement(CAST(elements), index, clone.value());
2022 Goto(&done_with_element);
2023 }
2024
2025 BIND(&done_with_element);
2026 };
2027 VariableList loop_vars({&current_allocation_site}, zone());
2029 PositiveSmiUntag(length), loop_body, 1,
2031 Goto(done);
2032 }
2033};
2034
2035TF_BUILTIN(CreateArrayFromSlowBoilerplate, SlowBoilerplateCloneAssembler) {
2036 auto context = Parameter<Context>(Descriptor::kContext);
2037 auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
2038 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
2039
2040 Label call_runtime(this);
2041
2042 TNode<Object> maybe_allocation_site =
2043 CAST(LoadFeedbackVectorSlot(feedback_vector, slot));
2044 GotoIfNot(HasBoilerplate(maybe_allocation_site), &call_runtime);
2045
2046 TNode<AllocationSite> allocation_site = CAST(maybe_allocation_site);
2047 TNode<JSArray> boilerplate = CAST(LoadBoilerplate(allocation_site));
2048
2049 {
2050 auto clone_and_next_allocation_site = CallBuiltin<PairT<Object, Object>>(
2051 Builtin::kCreateArrayFromSlowBoilerplateHelper, context,
2052 allocation_site, boilerplate);
2053 TNode<Object> result = Projection<0>(clone_and_next_allocation_site);
2054
2055 GotoIf(IsUndefined(result), &call_runtime);
2056 Return(result);
2057 }
2058
2059 BIND(&call_runtime);
2060 {
2061 auto boilerplate_descriptor = Parameter<ArrayBoilerplateDescription>(
2062 Descriptor::kBoilerplateDescriptor);
2063 auto flags = Parameter<Smi>(Descriptor::kFlags);
2065 CallRuntime(Runtime::kCreateArrayLiteral, context, feedback_vector,
2066 slot, boilerplate_descriptor, flags);
2067 Return(result);
2068 }
2069}
2070
2071TF_BUILTIN(CreateObjectFromSlowBoilerplate, SlowBoilerplateCloneAssembler) {
2072 auto context = Parameter<Context>(Descriptor::kContext);
2073 auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
2074 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
2075
2076 Label call_runtime(this);
2077
2078 TNode<Object> maybe_allocation_site =
2079 CAST(LoadFeedbackVectorSlot(feedback_vector, slot));
2080 GotoIfNot(HasBoilerplate(maybe_allocation_site), &call_runtime);
2081
2082 TNode<AllocationSite> allocation_site = CAST(maybe_allocation_site);
2083 TNode<JSObject> boilerplate = LoadBoilerplate(allocation_site);
2084
2085 {
2086 auto clone_and_next_allocation_site = CallBuiltin<PairT<Object, Object>>(
2087 Builtin::kCreateObjectFromSlowBoilerplateHelper, context,
2088 allocation_site, boilerplate);
2089 TNode<Object> result = Projection<0>(clone_and_next_allocation_site);
2090
2091 GotoIf(IsUndefined(result), &call_runtime);
2092 Return(result);
2093 }
2094
2095 BIND(&call_runtime);
2096 {
2097 auto boilerplate_descriptor = Parameter<ObjectBoilerplateDescription>(
2098 Descriptor::kBoilerplateDescriptor);
2099 auto flags = Parameter<Smi>(Descriptor::kFlags);
2101 CallRuntime(Runtime::kCreateObjectLiteral, context, feedback_vector,
2102 slot, boilerplate_descriptor, flags);
2103 Return(result);
2104 }
2105}
2106
2107TF_BUILTIN(CreateArrayFromSlowBoilerplateHelper,
2109 auto context = Parameter<Context>(Descriptor::kContext);
2110 auto allocation_site = Parameter<AllocationSite>(Descriptor::kAllocationSite);
2111 auto boilerplate = Parameter<JSArray>(Descriptor::kBoilerplate);
2112
2113 PerformStackCheck(context);
2114
2115 TNode<FixedArrayBase> boilerplate_elements = LoadElements(boilerplate);
2116 TNode<Smi> length = LoadFixedArrayBaseLength(boilerplate_elements);
2117
2118 // If the array contains other arrays (either directly or inside objects),
2119 // the AllocationSite tree is stored as a list (AllocationSite::nested_site)
2120 // in pre-order. See AllocationSiteUsageContext.
2121 TVARIABLE(Object, current_allocation_site);
2122 current_allocation_site = allocation_site;
2123
2124 Label done(this, &current_allocation_site),
2125 bailout(this, &current_allocation_site, Label::kDeferred);
2126
2127 // Keep in sync with ArrayLiteralBoilerplateBuilder::IsFastCloningSupported.
2128 // TODO(42204675): Detect this in advance when constructing the boilerplate.
2129 GotoIf(
2130 SmiAboveOrEqual(
2131 length,
2133 &bailout);
2134
2135 // First clone the array as if was a simple, shallow array:
2136 TNode<JSArray> array;
2138 array = CloneFastJSArray(context, boilerplate, allocation_site);
2139 } else {
2140 array = CloneFastJSArray(context, boilerplate);
2141 }
2142
2143 // Then fix up each element by cloning it (if it's an object or an array).
2144 TNode<FixedArrayBase> elements = LoadElements(array);
2145
2146 // If the boilerplate array is COW, it won't contain objects or arrays.
2147 GotoIf(TaggedEqual(LoadMap(elements), FixedCOWArrayMapConstant()), &done);
2148
2149 // If the elements kind is not between PACKED_ELEMENTS and HOLEY_ELEMENTS, it
2150 // cannot contain objects or arrays.
2151 TNode<Int32T> elements_kind = LoadElementsKind(boilerplate);
2152 GotoIf(Uint32GreaterThan(
2153 Unsigned(Int32Sub(elements_kind, Int32Constant(PACKED_ELEMENTS))),
2154 Uint32Constant(HOLEY_ELEMENTS - PACKED_ELEMENTS)),
2155 &done);
2156
2157 GotoIf(SmiEqual(length, SmiConstant(0)), &done);
2158 CloneElementsOfFixedArray(elements, length, elements_kind,
2159 current_allocation_site, context, &done, &bailout);
2160 BIND(&done);
2161 { Return(array, current_allocation_site.value()); }
2162
2163 BIND(&bailout);
2164 { Return(UndefinedConstant(), UndefinedConstant()); }
2165}
2166
2167TF_BUILTIN(CreateObjectFromSlowBoilerplateHelper,
2169 auto context = Parameter<Context>(Descriptor::kContext);
2170 auto allocation_site = Parameter<AllocationSite>(Descriptor::kAllocationSite);
2171 auto boilerplate = Parameter<JSObject>(Descriptor::kBoilerplate);
2172
2173 PerformStackCheck(context);
2174
2175 TVARIABLE(Object, current_allocation_site);
2176 current_allocation_site = allocation_site;
2177
2178 Label bailout(this, &current_allocation_site);
2179
2180 // Keep in sync with ObjectLiteralBoilerplateBuilder::IsFastCloningSupported.
2181 // The property count needs to be below
2182 // ConstructorBuiltins::kMaximumClonedShallowObjectProperties.
2183 // CreateShallowObjectLiteral already bails out if all properties don't fit
2184 // in-object, so we don't need to check the property count here.
2185 // TODO(42204675): Detect this in advance when constructing the boilerplate.
2186 TNode<Int32T> elements_kind = LoadElementsKind(boilerplate);
2187 GotoIf(
2188 Int32GreaterThan(elements_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
2189 &bailout);
2190
2191 constexpr bool kBailoutIfDictionaryPropertiesTrue = true;
2192 ConstructorBuiltinsAssembler constructor_assembler(state());
2193 TNode<JSObject> object =
2194 CAST(constructor_assembler.CreateShallowObjectLiteral(
2195 allocation_site, boilerplate, &bailout,
2196 kBailoutIfDictionaryPropertiesTrue));
2197
2198 // Fix up the object properties and elements and consume the correct amount of
2199 // AllocationSites. To iterate the AllocationSites in the correct order, we
2200 // need to first iterate the in-object properties and then the elements.
2201
2202 // Assert that there aren't any out of object properties (if there are, we
2203 // must have bailed out already):
2204 CSA_DCHECK(this, IsEmptyFixedArray(LoadFastProperties(boilerplate)));
2205
2206 // In-object properties:
2207 {
2208 auto loop_body = [&](TNode<IntPtrT> offset) {
2209 TVARIABLE(Object, clone);
2210 Label cloned(this, &clone),
2211 done_with_field(this, &current_allocation_site);
2212
2213 TNode<Object> field = LoadObjectField(object, offset);
2214 CloneIfObjectOrArray(field, clone, current_allocation_site, context,
2215 &cloned, &done_with_field, &bailout);
2216
2217 BIND(&cloned);
2218 {
2219 StoreObjectField(object, offset, clone.value());
2220 Goto(&done_with_field);
2221 }
2222
2223 BIND(&done_with_field);
2224 };
2225
2226 TNode<Map> boilerplate_map = LoadMap(boilerplate);
2227 TNode<IntPtrT> instance_size =
2228 TimesTaggedSize(LoadMapInstanceSizeInWords(boilerplate_map));
2229 VariableList loop_vars({&current_allocation_site}, zone());
2230 BuildFastLoop<IntPtrT>(loop_vars, IntPtrConstant(JSObject::kHeaderSize),
2231 instance_size, loop_body, kTaggedSize,
2232 LoopUnrollingMode::kYes, IndexAdvanceMode::kPost);
2233 }
2234
2235 // Elements:
2236 {
2237 Label done_with_elements(this);
2238 TNode<FixedArrayBase> elements = LoadElements(object);
2239 GotoIf(IsEmptyFixedArray(elements), &done_with_elements);
2240
2241 // Object elements are never COW and never SMI_ELEMENTS etc.
2242 CloneElementsOfFixedArray(elements, LoadFixedArrayBaseLength(elements),
2243 LoadElementsKind(object), current_allocation_site,
2244 context, &done_with_elements, &bailout);
2245 BIND(&done_with_elements);
2246 }
2247
2248 Return(object, current_allocation_site.value());
2249
2250 BIND(&bailout);
2251 {
2252 // We can't solve this case by calling into Runtime_CreateObjectLiteral,
2253 // since it's currently not suitable for creating a nested objects (e.g.,
2254 // doesn't return the next AllocationSite).
2255 Return(UndefinedConstant(), UndefinedConstant());
2256 }
2257}
2258
2260
2261} // namespace internal
2262} // namespace v8
#define BIND(label)
#define GENERATE_ARRAY_CTOR(name, kind_camel, kind_caps, mode_camel, mode_caps)
#define ELEMENTS_KIND(Type, type, TYPE, ctype)
#define TVARIABLE(...)
#define CSA_DCHECK(csa,...)
#define TF_BUILTIN(Name, AssemblerBase)
Builtins::Kind kind
Definition builtins.cc:40
static constexpr U encode(T value)
Definition bit-field.h:55
static bool ShouldTrack(ElementsKind boilerplate_elements_kind)
void GenerateArrayNArgumentsConstructor(TNode< Context > context, TNode< JSFunction > target, TNode< Object > new_target, TNode< Int32T > argc, TNode< HeapObject > maybe_allocation_site)
void ReturnFromBuiltin(TNode< Object > value)
std::function< void(ArrayBuiltinsAssembler *masm)> BuiltinResultGenerator
ArrayBuiltinsAssembler(compiler::CodeAssemblerState *state)
TNode< JSAny > TypedArrayMapProcessor(TNode< Object > k_value, TNode< UintPtrT > k)
void InitIteratingArrayBuiltinBody(TNode< Context > context, TNode< JSAny > receiver, TNode< Object > callbackfn, TNode< JSAny > this_arg, TNode< IntPtrT > argc)
void GenerateIteratingTypedArrayBuiltinBody(const char *name, const BuiltinResultGenerator &generator, const CallResultProcessor &processor, ForEachDirection direction=ForEachDirection::kForward)
void GenerateDispatchToArrayStub(TNode< Context > context, TNode< JSFunction > target, TNode< Int32T > argc, AllocationSiteOverrideMode mode, std::optional< TNode< AllocationSite > > allocation_site=std::nullopt)
void GenerateConstructor(TNode< Context > context, TNode< JSAnyNotSmi > array_function, TNode< Map > array_map, TNode< Object > array_size, TNode< HeapObject > allocation_site, ElementsKind elements_kind, AllocationSiteMode mode)
void CreateArrayDispatchSingleArgument(TNode< Context > context, TNode< JSFunction > target, TNode< Int32T > argc, AllocationSiteOverrideMode mode, std::optional< TNode< AllocationSite > > allocation_site)
void GenerateArraySingleArgumentConstructor(ElementsKind kind, AllocationSiteOverrideMode mode)
void TailCallArrayConstructorStub(const Callable &callable, TNode< Context > context, TNode< JSFunction > target, TNode< HeapObject > allocation_site_or_undefined, TNode< Int32T > argc)
std::function< TNode< JSAny >( ArrayBuiltinsAssembler *masm, TNode< Object > k_value, TNode< UintPtrT > k)> CallResultProcessor
void GenerateArrayNoArgumentConstructor(ElementsKind kind, AllocationSiteOverrideMode mode)
void CreateArrayDispatchNoArgument(TNode< Context > context, TNode< JSFunction > target, TNode< Int32T > argc, AllocationSiteOverrideMode mode, std::optional< TNode< AllocationSite > > allocation_site)
void VisitAllTypedArrayElements(TNode< JSArrayBuffer > array_buffer, const CallResultProcessor &processor, ForEachDirection direction, TNode< JSTypedArray > typed_array)
void GenerateSmiOrObject(SearchVariant variant, TNode< Context > context, TNode< FixedArray > elements, TNode< Object > search_element, TNode< Smi > array_length, TNode< Smi > from_index, SimpleElementKind array_kind)
void Generate(SearchVariant variant, TNode< IntPtrT > argc, TNode< Context > context)
void GeneratePackedDoubles(SearchVariant variant, TNode< FixedDoubleArray > elements, TNode< Object > search_element, TNode< Smi > array_length, TNode< Smi > from_index)
ArrayIncludesIndexofAssembler(compiler::CodeAssemblerState *state)
void GenerateHoleyDoubles(SearchVariant variant, TNode< FixedDoubleArray > elements, TNode< Object > search_element, TNode< Smi > array_length, TNode< Smi > from_index)
void ReturnIfEmpty(TNode< Smi > length, TNode< Object > value)
TNode< Object > ConstructArrayLike(TNode< Context > context, TNode< Object > receiver, TNode< Number > length)
TNode< Object > ConstructArrayLike(TNode< Context > context, TNode< Object > receiver)
ArrayPopulatorAssembler(compiler::CodeAssemblerState *state)
Handle< Code > code() const
Definition callable.h:22
static Callable ArrayNoArgumentConstructor(Isolate *isolate, ElementsKind kind, AllocationSiteOverrideMode override_mode)
static Callable ArraySingleArgumentConstructor(Isolate *isolate, ElementsKind kind, AllocationSiteOverrideMode override_mode)
TNode< BoolT > IsJSObject(TNode< HeapObject > object)
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< Smi > LoadTransitionInfo(TNode< AllocationSite > allocation_site)
TNode< BoolT > IsJSTypedArrayMap(TNode< Map > map)
TNode< BoolT > IsConstructor(TNode< HeapObject > object)
TNode< Number > ChangeUintPtrToTagged(TNode< UintPtrT > value)
TNode< BoolT > IsCallableMap(TNode< Map > map)
TNode< UintPtrT > LoadJSTypedArrayLengthAndCheckDetached(TNode< JSTypedArray > typed_array, Label *detached)
TNode< Numeric > LoadFixedTypedArrayElementAsTagged(TNode< RawPtrT > data_pointer, TNode< UintPtrT > index, ElementsKind elements_kind)
TNode< Int32T > TruncateIntPtrToInt32(TNode< IntPtrT > value)
TNode< JSArrayBuffer > LoadJSArrayBufferViewBuffer(TNode< JSArrayBufferView > array_buffer_view)
void StoreFixedArrayElement(TNode< FixedArray > object, int index, TNode< Object > value, WriteBarrierMode barrier_mode=UPDATE_WRITE_BARRIER, CheckBounds check_bounds=CheckBounds::kAlways)
TNode< Object > UnsafeLoadFixedArrayElement(TNode< FixedArray > object, TNode< IntPtrT > index, int additional_offset=0)
TNode< JSReceiver > Construct(TNode< Context > context, TNode< JSReceiver > new_target, TArgs... args)
void ThrowTypeError(TNode< Context > context, MessageTemplate message, char const *arg0=nullptr, char const *arg1=nullptr)
TNode< Smi > SmiTag(TNode< IntPtrT > value)
void Increment(TVariable< TIndex > *variable, int value=1)
TNode< BoolT > TaggedEqual(TNode< AnyTaggedT > a, TNode< AnyTaggedT > b)
TNode< FixedArrayBase > LoadElements(TNode< JSObject > object)
TNode< T > LoadObjectField(TNode< HeapObject > object, int offset)
TNode< JSObject > LoadBoilerplate(TNode< AllocationSite > allocation_site)
TNode< BoolT > TaggedIsPositiveSmi(TNode< Object > a)
TNode< BoolT > ElementsKindEqual(TNode< Int32T > a, TNode< Int32T > b)
TNode< Number > ToNumber_Inline(TNode< Context > context, TNode< Object > input)
void EmitElementStore(TNode< JSObject > object, TNode< Object > key, TNode< Object > value, ElementsKind elements_kind, KeyedAccessStoreMode store_mode, Label *bailout, TNode< Context > context, TVariable< Object > *maybe_converted_value=nullptr)
TNode< BoolT > IsFastPackedElementsKind(TNode< Int32T > elements_kind)
TNode< IntPtrT > SmiUntag(TNode< Smi > value)
TNode< Map > LoadJSArrayElementsMap(ElementsKind kind, TNode< NativeContext > native_context)
TNode< Object > LoadNestedAllocationSite(TNode< AllocationSite > allocation_site)
void TailCallRuntimeNewArray(TNode< Context > context, TNode< JSAny > receiver, TNode< Object > length, TNode< Object > new_target, TNode< Object > allocation_site)
TNode< RawPtrT > LoadJSTypedArrayDataPtr(TNode< JSTypedArray > typed_array)
TNode< BoolT > IsSetSmi(TNode< Smi > smi, int untagged_mask)
TNode< BoolT > IsElementsKindLessThanOrEqual(TNode< Int32T > target_kind, ElementsKind reference_kind)
TNode< BoolT > IsJSArray(TNode< HeapObject > object)
TNode< Uint32T > DecodeWord32(TNode< Word32T > word32)
TNode< JSArray > ArrayCreate(TNode< Context > context, TNode< Number > length)
TNode< BoolT > IsNumberNormalized(TNode< Number > number)
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< Float64T > SmiToFloat64(TNode< Smi > value)
TNode< Int32T > LoadElementsKind(TNode< HeapObject > object)
TNode< BoolT > IsBigIntInstanceType(TNode< Int32T > instance_type)
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< JSAny > Call(TNode< Context > context, TNode< TCallable > callable, ConvertReceiverMode mode, TNode< JSAny > receiver, TArgs... args)
TNode< Uint16T > LoadMapInstanceType(TNode< Map > map)
TNode< IntPtrT > LoadStringLengthAsWord(TNode< String > string)
TNode< Uint16T > LoadInstanceType(TNode< HeapObject > object)
void CheckJSTypedArrayIndex(TNode< JSTypedArray > typed_array, TNode< UintPtrT > index, Label *detached_or_out_of_bounds)
TNode< BoolT > TaggedIsSmi(TNode< MaybeObject > a)
TNode< Smi > LoadFastJSArrayLength(TNode< JSArray > array)
TNode< Float64T > LoadHeapNumberValue(TNode< HeapObject > object)
TNode< BigInt > ToBigInt(TNode< Context > context, TNode< Object > input)
TNode< Object > SetPropertyStrict(TNode< Context > context, TNode< JSAny > receiver, TNode< Object > key, TNode< Object > value)
TNode< Float64T > LoadFixedDoubleArrayElement(TNode< FixedDoubleArray > object, TNode< IntPtrT > index, Label *if_hole=nullptr, MachineType machine_type=MachineType::Float64())
TNode< Map > LoadMap(TNode< HeapObject > object)
TNode< Int32T > SmiToInt32(TNode< Smi > value)
TNode< Int32T > LoadMapElementsKind(TNode< Map > map)
Uint32LessThanOrEqual IntPtrGreaterThanOrEqual
TNode< BoolT > IsStringInstanceType(TNode< Int32T > instance_type)
TNode< IntPtrT > PositiveSmiUntag(TNode< Smi > value)
TNode< BoolT > IsBigInt(TNode< HeapObject > object)
void BranchIfFloat64IsNaN(TNode< Float64T > value, Label *if_true, Label *if_false)
TNode< HeapObject > CreateShallowObjectLiteral(TNode< FeedbackVector > feedback_vector, TNode< TaggedIndex > slot, Label *call_runtime)
static const int kInitialMaxFastElementArray
Definition js-array.h:144
static const int kPreallocatedArrayElements
Definition js-array.h:122
static const uint32_t kMinAddedElementsCapacity
Definition js-objects.h:649
static constexpr MachineType None()
static constexpr MachineType TaggedPointer()
static constexpr MachineType UintPtr()
void CloneElementsOfFixedArray(TNode< FixedArrayBase > elements, TNode< Smi > length, TNode< Int32T > elements_kind, TVariable< Object > &current_allocation_site, TNode< Context > context, Label *done, Label *bailout)
void CloneIfObjectOrArray(TNode< Object > item, TVariable< Object > &clone, TVariable< Object > &current_allocation_site, TNode< Context > context, Label *cloned, Label *not_cloned, Label *bailout)
SlowBoilerplateCloneAssembler(compiler::CodeAssemblerState *state)
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< IntPtrT > IntPtrAdd(TNode< IntPtrT > left, TNode< IntPtrT > right)
TNode< Int32T > Signed(TNode< Word32T > x)
TNode< IntPtrT > IntPtrConstant(intptr_t value)
TNode< T > UncheckedCast(Node *value)
void GotoIfNot(TNode< IntegralT > condition, Label *false_label, GotoHint goto_hint=GotoHint::kNone)
void Return(TNode< Object > value)
void PopAndReturn(Node *pop, Node *value)
TNode< Int32T > UniqueInt32Constant(int32_t value)
TNode< T > ReinterpretCast(Node *value)
TNode< Int32T > Int32Add(TNode< Int32T > left, TNode< Int32T > right)
void TailCallRuntime(Runtime::FunctionId function, TNode< Object > context, TArgs... args)
TNode< Smi > SmiConstant(Tagged< Smi > value)
void GotoIf(TNode< IntegralT > condition, Label *true_label, GotoHint goto_hint=GotoHint::kNone)
TNode< Int32T > Word32Or(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< typename std::tuple_element< index, std::tuple< T1, T2 > >::type > Projection(TNode< PairT< T1, T2 > > value)
TNode< Float64T > Float64Constant(double value)
TNode< ExternalReference > ExternalConstant(ExternalReference address)
TNode< Int32T > Int32Constant(int32_t value)
Node * CallCFunction(Node *function, std::optional< MachineType > return_type, CArgs... cargs)
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)
void TailCallStub(const CallInterfaceDescriptor &descriptor, TNode< Code > target, TNode< Object > context, TArgs... args)
TNode< Number > NumberConstant(double value)
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)
TNode< T > Parameter(int value, const SourceLocation &loc=SourceLocation::Current())
#define CAST(x)
Handle< Code > code
#define V8_EXPERIMENTAL_UNDEFINED_DOUBLE_BOOL
Definition globals.h:359
int start
int end
#define RAB_GSAB_TYPED_ARRAYS(V)
#define TYPED_ARRAYS(V)
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
DirectHandle< Object > new_target
Definition execution.cc:75
Label label
#define V8_ALLOCATION_SITE_TRACKING_BOOL
int32_t offset
TNode< Context > context
TNode< Object > target
TNode< Object > this_arg
TNode< Object > receiver
ArrayReduceDirection direction
ZoneVector< RpoNumber > & result
LiftoffAssembler::CacheState state
constexpr int kTaggedSize
Definition globals.h:542
constexpr double kMaxSafeInteger
Definition globals.h:1985
ElementsKind GetFastElementsKindFromSequenceIndex(int sequence_number)
@ TRACK_ALLOCATION_SITE
Definition globals.h:1936
@ DONT_TRACK_ALLOCATION_SITE
Definition globals.h:1935
DONT_OVERRIDE DISABLE_ALLOCATION_SITES Holey
bool IsRabGsabTypedArrayElementsKind(ElementsKind kind)
DONT_OVERRIDE DISABLE_ALLOCATION_SITES DISABLE_ALLOCATION_SITES HoleyDouble
@ LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND
@ TERMINAL_FAST_ELEMENTS_KIND
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in name
Definition flags.cc:2086
ElementsKind GetCorrespondingNonRabGsabElementsKind(ElementsKind typed_array_kind)
int GetSequenceIndexFromFastElementsKind(ElementsKind elements_kind)
ElementsKind GetHoleyElementsKind(ElementsKind packed_kind)
@ DISABLE_ALLOCATION_SITES
DONT_OVERRIDE DISABLE_ALLOCATION_SITES HOLEY_ELEMENTS
bool IsBigIntTypedArrayElementsKind(ElementsKind kind)
DONT_OVERRIDE DISABLE_ALLOCATION_SITES DISABLE_ALLOCATION_SITES HOLEY_DOUBLE_ELEMENTS
ElementsKind GetInitialFastElementsKind()
constexpr uint32_t kMaxUInt32
Definition globals.h:387
DONT_OVERRIDE DisableAllocationSites
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 DCHECK_EQ(v1, v2)
Definition logging.h:485