Line data Source code
1 : // Copyright 2017 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/builtins/builtins-array-gen.h"
6 :
7 : #include "src/builtins/builtins-iterator-gen.h"
8 : #include "src/builtins/builtins-string-gen.h"
9 : #include "src/builtins/builtins-typed-array-gen.h"
10 : #include "src/builtins/builtins-utils-gen.h"
11 : #include "src/builtins/builtins.h"
12 : #include "src/code-stub-assembler.h"
13 : #include "src/frame-constants.h"
14 : #include "src/heap/factory-inl.h"
15 : #include "src/objects/allocation-site-inl.h"
16 : #include "src/objects/arguments-inl.h"
17 : #include "src/objects/property-cell.h"
18 : #include "torque-generated/builtins-typed-array-createtypedarray-from-dsl-gen.h"
19 :
20 : namespace v8 {
21 : namespace internal {
22 :
23 : using Node = compiler::Node;
24 : using IteratorRecord = IteratorBuiltinsFromDSLAssembler::IteratorRecord;
25 :
26 1568 : ArrayBuiltinsAssembler::ArrayBuiltinsAssembler(
27 : compiler::CodeAssemblerState* state)
28 : : CodeStubAssembler(state),
29 : k_(this, MachineRepresentation::kTagged),
30 : a_(this, MachineRepresentation::kTagged),
31 3136 : to_(this, MachineRepresentation::kTagged, SmiConstant(0)),
32 9408 : fully_spec_compliant_(this, {&k_, &a_, &to_}) {}
33 :
34 56 : void ArrayBuiltinsAssembler::TypedArrayMapResultGenerator() {
35 : // 6. Let A be ? TypedArraySpeciesCreate(O, len).
36 : TNode<JSTypedArray> original_array = CAST(o());
37 56 : TNode<Smi> length = CAST(len_);
38 : const char* method_name = "%TypedArray%.prototype.map";
39 :
40 : TypedArrayCreatetypedarrayBuiltinsFromDSLAssembler typedarray_asm(state());
41 : TNode<JSTypedArray> a = typedarray_asm.TypedArraySpeciesCreateByLength(
42 56 : context(), method_name, original_array, length);
43 : // In the Spec and our current implementation, the length check is already
44 : // performed in TypedArraySpeciesCreate.
45 : CSA_ASSERT(this, SmiLessThanOrEqual(CAST(len_), LoadJSTypedArrayLength(a)));
46 : fast_typed_array_target_ =
47 224 : Word32Equal(LoadInstanceType(LoadElements(original_array)),
48 280 : LoadInstanceType(LoadElements(a)));
49 56 : a_.Bind(a);
50 56 : }
51 :
52 : // See tc39.github.io/ecma262/#sec-%typedarray%.prototype.map.
53 616 : Node* ArrayBuiltinsAssembler::TypedArrayMapProcessor(Node* k_value, Node* k) {
54 : // 8. c. Let mapped_value be ? Call(callbackfn, T, « kValue, k, O »).
55 1232 : Node* mapped_value = CallJS(CodeFactory::Call(isolate()), context(),
56 616 : callbackfn(), this_arg(), k_value, k, o());
57 616 : Label fast(this), slow(this), done(this), detached(this, Label::kDeferred);
58 :
59 : // 8. d. Perform ? Set(A, Pk, mapped_value, true).
60 : // Since we know that A is a TypedArray, this always ends up in
61 : // #sec-integer-indexed-exotic-objects-set-p-v-receiver and then
62 : // tc39.github.io/ecma262/#sec-integerindexedelementset .
63 1232 : Branch(fast_typed_array_target_, &fast, &slow);
64 :
65 616 : BIND(&fast);
66 : // #sec-integerindexedelementset
67 : // 5. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let
68 : // numValue be ? ToBigInt(v).
69 : // 6. Otherwise, let numValue be ? ToNumber(value).
70 : Node* num_value;
71 616 : if (source_elements_kind_ == BIGINT64_ELEMENTS ||
72 : source_elements_kind_ == BIGUINT64_ELEMENTS) {
73 224 : num_value = ToBigInt(context(), mapped_value);
74 : } else {
75 1008 : num_value = ToNumber_Inline(context(), mapped_value);
76 : }
77 : // The only way how this can bailout is because of a detached buffer.
78 616 : EmitElementStore(a(), k, num_value, source_elements_kind_,
79 : KeyedAccessStoreMode::STANDARD_STORE, &detached,
80 616 : context());
81 616 : Goto(&done);
82 :
83 616 : BIND(&slow);
84 : SetPropertyStrict(context(), CAST(a()), CAST(k), CAST(mapped_value));
85 616 : Goto(&done);
86 :
87 616 : BIND(&detached);
88 : // tc39.github.io/ecma262/#sec-integerindexedelementset
89 : // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
90 1232 : ThrowTypeError(context_, MessageTemplate::kDetachedOperation, name_);
91 :
92 616 : BIND(&done);
93 616 : return a();
94 : }
95 :
96 616 : void ArrayBuiltinsAssembler::NullPostLoopAction() {}
97 :
98 0 : void ArrayBuiltinsAssembler::FillFixedArrayWithSmiZero(
99 : TNode<FixedArray> array, TNode<Smi> smi_length) {
100 : CSA_ASSERT(this, Word32BinaryNot(IsFixedDoubleArray(array)));
101 :
102 0 : TNode<IntPtrT> length = SmiToIntPtr(smi_length);
103 : TNode<WordT> byte_length = TimesTaggedSize(length);
104 : CSA_ASSERT(this, UintPtrLessThan(length, byte_length));
105 :
106 : static const int32_t fa_base_data_offset =
107 : FixedArray::kHeaderSize - kHeapObjectTag;
108 : TNode<IntPtrT> backing_store = IntPtrAdd(
109 0 : BitcastTaggedToWord(array), IntPtrConstant(fa_base_data_offset));
110 :
111 : // Call out to memset to perform initialization.
112 : TNode<ExternalReference> memset =
113 0 : ExternalConstant(ExternalReference::libc_memset_function());
114 : STATIC_ASSERT(kSizetSize == kIntptrSize);
115 : CallCFunction(memset, MachineType::Pointer(),
116 : std::make_pair(MachineType::Pointer(), backing_store),
117 0 : std::make_pair(MachineType::IntPtr(), IntPtrConstant(0)),
118 0 : std::make_pair(MachineType::UintPtr(), byte_length));
119 0 : }
120 :
121 616 : void ArrayBuiltinsAssembler::ReturnFromBuiltin(Node* value) {
122 616 : if (argc_ == nullptr) {
123 0 : Return(value);
124 : } else {
125 : // argc_ doesn't include the receiver, so it has to be added back in
126 : // manually.
127 1232 : PopAndReturn(IntPtrAdd(argc_, IntPtrConstant(1)), value);
128 : }
129 616 : }
130 :
131 0 : void ArrayBuiltinsAssembler::InitIteratingArrayBuiltinBody(
132 : TNode<Context> context, TNode<Object> receiver, Node* callbackfn,
133 : Node* this_arg, TNode<IntPtrT> argc) {
134 : context_ = context;
135 : receiver_ = receiver;
136 56 : callbackfn_ = callbackfn;
137 56 : this_arg_ = this_arg;
138 : argc_ = argc;
139 0 : }
140 :
141 56 : void ArrayBuiltinsAssembler::GenerateIteratingTypedArrayBuiltinBody(
142 : const char* name, const BuiltinResultGenerator& generator,
143 : const CallResultProcessor& processor, const PostLoopAction& action,
144 : ForEachDirection direction) {
145 56 : name_ = name;
146 :
147 : // ValidateTypedArray: tc39.github.io/ecma262/#sec-validatetypedarray
148 :
149 112 : Label throw_not_typed_array(this, Label::kDeferred);
150 :
151 112 : GotoIf(TaggedIsSmi(receiver_), &throw_not_typed_array);
152 112 : GotoIfNot(HasInstanceType(CAST(receiver_), JS_TYPED_ARRAY_TYPE),
153 56 : &throw_not_typed_array);
154 :
155 : TNode<JSTypedArray> typed_array = CAST(receiver_);
156 : o_ = typed_array;
157 :
158 : TNode<JSArrayBuffer> array_buffer =
159 56 : LoadJSArrayBufferViewBuffer(typed_array);
160 112 : ThrowIfArrayBufferIsDetached(context_, array_buffer, name_);
161 :
162 112 : len_ = LoadJSTypedArrayLength(typed_array);
163 :
164 56 : Label throw_not_callable(this, Label::kDeferred);
165 56 : Label distinguish_types(this);
166 168 : GotoIf(TaggedIsSmi(callbackfn_), &throw_not_callable);
167 224 : Branch(IsCallableMap(LoadMap(callbackfn_)), &distinguish_types,
168 56 : &throw_not_callable);
169 :
170 56 : BIND(&throw_not_typed_array);
171 56 : ThrowTypeError(context_, MessageTemplate::kNotTypedArray);
172 :
173 56 : BIND(&throw_not_callable);
174 112 : ThrowTypeError(context_, MessageTemplate::kCalledNonCallable, callbackfn_);
175 :
176 56 : Label unexpected_instance_type(this);
177 56 : BIND(&unexpected_instance_type);
178 56 : Unreachable();
179 :
180 : std::vector<int32_t> instance_types = {
181 : #define INSTANCE_TYPE(Type, type, TYPE, ctype) FIXED_##TYPE##_ARRAY_TYPE,
182 : TYPED_ARRAYS(INSTANCE_TYPE)
183 : #undef INSTANCE_TYPE
184 : };
185 : std::list<Label> labels;
186 1288 : for (size_t i = 0; i < instance_types.size(); ++i) {
187 1232 : labels.emplace_back(this);
188 : }
189 : std::vector<Label*> label_ptrs;
190 672 : for (Label& label : labels) {
191 1232 : label_ptrs.push_back(&label);
192 : }
193 :
194 56 : BIND(&distinguish_types);
195 :
196 : generator(this);
197 :
198 56 : if (direction == ForEachDirection::kForward) {
199 112 : k_.Bind(SmiConstant(0));
200 : } else {
201 0 : k_.Bind(NumberDec(len()));
202 : }
203 : CSA_ASSERT(this, IsSafeInteger(k()));
204 112 : Node* instance_type = LoadInstanceType(LoadElements(typed_array));
205 : Switch(instance_type, &unexpected_instance_type, instance_types.data(),
206 56 : label_ptrs.data(), labels.size());
207 :
208 : size_t i = 0;
209 1288 : for (auto it = labels.begin(); it != labels.end(); ++i, ++it) {
210 616 : BIND(&*it);
211 616 : Label done(this);
212 616 : source_elements_kind_ = ElementsKindForInstanceType(
213 616 : static_cast<InstanceType>(instance_types[i]));
214 : // TODO(tebbi): Silently cancelling the loop on buffer detachment is a
215 : // spec violation. Should go to &throw_detached and throw a TypeError
216 : // instead.
217 : VisitAllTypedArrayElements(array_buffer, processor, &done, direction,
218 616 : typed_array);
219 616 : Goto(&done);
220 : // No exception, return success
221 616 : BIND(&done);
222 : action(this);
223 616 : ReturnFromBuiltin(a_.value());
224 : }
225 56 : }
226 :
227 616 : ElementsKind ArrayBuiltinsAssembler::ElementsKindForInstanceType(
228 : InstanceType type) {
229 616 : switch (type) {
230 : #define INSTANCE_TYPE_TO_ELEMENTS_KIND(Type, type, TYPE, ctype) \
231 : case FIXED_##TYPE##_ARRAY_TYPE: \
232 : return TYPE##_ELEMENTS;
233 :
234 56 : TYPED_ARRAYS(INSTANCE_TYPE_TO_ELEMENTS_KIND)
235 : #undef INSTANCE_TYPE_TO_ELEMENTS_KIND
236 :
237 : default:
238 0 : UNREACHABLE();
239 : }
240 : }
241 :
242 616 : void ArrayBuiltinsAssembler::VisitAllTypedArrayElements(
243 : Node* array_buffer, const CallResultProcessor& processor, Label* detached,
244 : ForEachDirection direction, TNode<JSTypedArray> typed_array) {
245 1232 : VariableList list({&a_, &k_, &to_}, zone());
246 :
247 616 : FastLoopBody body = [&](Node* index) {
248 6776 : GotoIf(IsDetachedBuffer(array_buffer), detached);
249 616 : Node* elements = LoadElements(typed_array);
250 : Node* base_ptr =
251 616 : LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset);
252 : Node* external_ptr =
253 1232 : LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset,
254 616 : MachineType::Pointer());
255 1848 : Node* data_ptr = IntPtrAdd(BitcastTaggedToWord(base_ptr), external_ptr);
256 616 : Node* value = LoadFixedTypedArrayElementAsTagged(
257 1232 : data_ptr, index, source_elements_kind_, SMI_PARAMETERS);
258 616 : k_.Bind(index);
259 1232 : a_.Bind(processor(this, value, index));
260 616 : };
261 1232 : Node* start = SmiConstant(0);
262 : Node* end = len_;
263 : IndexAdvanceMode advance_mode = IndexAdvanceMode::kPost;
264 : int incr = 1;
265 616 : if (direction == ForEachDirection::kReverse) {
266 : std::swap(start, end);
267 : advance_mode = IndexAdvanceMode::kPre;
268 : incr = -1;
269 : }
270 616 : BuildFastLoop(list, start, end, body, incr, ParameterMode::SMI_PARAMETERS,
271 616 : advance_mode);
272 616 : }
273 :
274 : // Perform ArraySpeciesCreate (ES6 #sec-arrayspeciescreate).
275 0 : void ArrayBuiltinsAssembler::GenerateArraySpeciesCreate(TNode<Number> len) {
276 0 : Label runtime(this, Label::kDeferred), done(this);
277 :
278 0 : Node* const original_map = LoadMap(o());
279 0 : GotoIfNot(
280 0 : InstanceTypeEqual(LoadMapInstanceType(original_map), JS_ARRAY_TYPE),
281 0 : &runtime);
282 :
283 0 : GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map),
284 0 : &runtime);
285 :
286 0 : Node* species_protector = ArraySpeciesProtectorConstant();
287 : Node* value =
288 : LoadObjectField(species_protector, PropertyCell::kValueOffset);
289 0 : Node* const protector_invalid = SmiConstant(Isolate::kProtectorInvalid);
290 0 : GotoIf(WordEqual(value, protector_invalid), &runtime);
291 :
292 0 : GotoIfNot(TaggedIsPositiveSmi(len), &runtime);
293 0 : GotoIfNot(
294 0 : IsValidFastJSArrayCapacity(len, CodeStubAssembler::SMI_PARAMETERS),
295 0 : &runtime);
296 :
297 : // We need to be conservative and start with holey because the builtins
298 : // that create output arrays aren't guaranteed to be called for every
299 : // element in the input array (maybe the callback deletes an element).
300 : const ElementsKind elements_kind =
301 : GetHoleyElementsKind(GetInitialFastElementsKind());
302 0 : TNode<Context> native_context = LoadNativeContext(context());
303 : TNode<Map> array_map =
304 0 : LoadJSArrayElementsMap(elements_kind, native_context);
305 0 : a_.Bind(AllocateJSArray(PACKED_SMI_ELEMENTS, array_map, len, CAST(len),
306 : nullptr, CodeStubAssembler::SMI_PARAMETERS,
307 0 : kAllowLargeObjectAllocation));
308 :
309 0 : Goto(&done);
310 :
311 0 : BIND(&runtime);
312 : {
313 : // 5. Let A be ? ArraySpeciesCreate(O, len).
314 : TNode<JSReceiver> constructor =
315 : CAST(CallRuntime(Runtime::kArraySpeciesConstructor, context(), o()));
316 0 : a_.Bind(Construct(context(), constructor, len));
317 0 : Goto(&fully_spec_compliant_);
318 : }
319 :
320 0 : BIND(&done);
321 0 : }
322 :
323 168 : TF_BUILTIN(ArrayPrototypePop, CodeStubAssembler) {
324 : TNode<Int32T> argc =
325 : UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
326 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
327 : CSA_ASSERT(this, IsUndefined(Parameter(Descriptor::kJSNewTarget)));
328 :
329 168 : CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
330 56 : TNode<Object> receiver = args.GetReceiver();
331 :
332 56 : Label runtime(this, Label::kDeferred);
333 56 : Label fast(this);
334 :
335 : // Only pop in this stub if
336 : // 1) the array has fast elements
337 : // 2) the length is writable,
338 : // 3) the elements backing store isn't copy-on-write,
339 : // 4) we aren't supposed to shrink the backing store.
340 :
341 : // 1) Check that the array has fast elements.
342 56 : BranchIfFastJSArray(receiver, context, &fast, &runtime);
343 :
344 56 : BIND(&fast);
345 : {
346 : TNode<JSArray> array_receiver = CAST(receiver);
347 : CSA_ASSERT(this, TaggedIsPositiveSmi(LoadJSArrayLength(array_receiver)));
348 : TNode<IntPtrT> length =
349 56 : LoadAndUntagObjectField(array_receiver, JSArray::kLengthOffset);
350 56 : Label return_undefined(this), fast_elements(this);
351 168 : GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &return_undefined);
352 :
353 : // 2) Ensure that the length is writable.
354 56 : EnsureArrayLengthWritable(LoadMap(array_receiver), &runtime);
355 :
356 : // 3) Check that the elements backing store isn't copy-on-write.
357 : TNode<FixedArrayBase> elements = LoadElements(array_receiver);
358 168 : GotoIf(WordEqual(LoadMap(elements), LoadRoot(RootIndex::kFixedCOWArrayMap)),
359 56 : &runtime);
360 :
361 56 : TNode<IntPtrT> new_length = IntPtrSub(length, IntPtrConstant(1));
362 :
363 : // 4) Check that we're not supposed to shrink the backing store, as
364 : // implemented in elements.cc:ElementsAccessorBase::SetLengthImpl.
365 112 : TNode<IntPtrT> capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
366 112 : GotoIf(IntPtrLessThan(
367 : IntPtrAdd(IntPtrAdd(new_length, new_length),
368 56 : IntPtrConstant(JSObject::kMinAddedElementsCapacity)),
369 56 : capacity),
370 56 : &runtime);
371 :
372 112 : StoreObjectFieldNoWriteBarrier(array_receiver, JSArray::kLengthOffset,
373 : SmiTag(new_length));
374 :
375 56 : TNode<Int32T> elements_kind = LoadElementsKind(array_receiver);
376 112 : GotoIf(Int32LessThanOrEqual(elements_kind,
377 112 : Int32Constant(TERMINAL_FAST_ELEMENTS_KIND)),
378 56 : &fast_elements);
379 :
380 : Node* value = LoadFixedDoubleArrayElement(CAST(elements), new_length,
381 56 : &return_undefined);
382 :
383 56 : StoreFixedDoubleArrayHole(CAST(elements), new_length);
384 112 : args.PopAndReturn(AllocateHeapNumberWithValue(value));
385 :
386 56 : BIND(&fast_elements);
387 : {
388 : Node* value = LoadFixedArrayElement(CAST(elements), new_length);
389 112 : StoreFixedArrayElement(CAST(elements), new_length, TheHoleConstant());
390 112 : GotoIf(WordEqual(value, TheHoleConstant()), &return_undefined);
391 56 : args.PopAndReturn(value);
392 : }
393 :
394 56 : BIND(&return_undefined);
395 112 : { args.PopAndReturn(UndefinedConstant()); }
396 : }
397 :
398 56 : BIND(&runtime);
399 : {
400 : // We are not using Parameter(Descriptor::kJSTarget) and loading the value
401 : // from the current frame here in order to reduce register pressure on the
402 : // fast path.
403 56 : TNode<JSFunction> target = LoadTargetFromFrame();
404 112 : TailCallBuiltin(Builtins::kArrayPop, context, target, UndefinedConstant(),
405 56 : argc);
406 : }
407 56 : }
408 :
409 224 : TF_BUILTIN(ArrayPrototypePush, CodeStubAssembler) {
410 56 : TVARIABLE(IntPtrT, arg_index);
411 56 : Label default_label(this, &arg_index);
412 56 : Label smi_transition(this);
413 56 : Label object_push_pre(this);
414 56 : Label object_push(this, &arg_index);
415 56 : Label double_push(this, &arg_index);
416 56 : Label double_transition(this);
417 56 : Label runtime(this, Label::kDeferred);
418 :
419 : // TODO(ishell): use constants from Descriptor once the JSFunction linkage
420 : // arguments are reordered.
421 : TNode<Int32T> argc =
422 : UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
423 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
424 : CSA_ASSERT(this, IsUndefined(Parameter(Descriptor::kJSNewTarget)));
425 :
426 168 : CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
427 56 : TNode<Object> receiver = args.GetReceiver();
428 : TNode<JSArray> array_receiver;
429 : Node* kind = nullptr;
430 :
431 56 : Label fast(this);
432 56 : BranchIfFastJSArray(receiver, context, &fast, &runtime);
433 :
434 56 : BIND(&fast);
435 : {
436 : array_receiver = CAST(receiver);
437 56 : arg_index = IntPtrConstant(0);
438 112 : kind = EnsureArrayPushable(LoadMap(array_receiver), &runtime);
439 112 : GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS),
440 56 : &object_push_pre);
441 :
442 112 : Node* new_length = BuildAppendJSArray(PACKED_SMI_ELEMENTS, array_receiver,
443 56 : &args, &arg_index, &smi_transition);
444 56 : args.PopAndReturn(new_length);
445 : }
446 :
447 : // If the argument is not a smi, then use a heavyweight SetProperty to
448 : // transition the array for only the single next element. If the argument is
449 : // a smi, the failure is due to some other reason and we should fall back on
450 : // the most generic implementation for the rest of the array.
451 56 : BIND(&smi_transition);
452 : {
453 112 : Node* arg = args.AtIndex(arg_index.value());
454 112 : GotoIf(TaggedIsSmi(arg), &default_label);
455 112 : Node* length = LoadJSArrayLength(array_receiver);
456 : // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
457 : // calling into the runtime to do the elements transition is overkill.
458 : SetPropertyStrict(context, array_receiver, CAST(length), CAST(arg));
459 56 : Increment(&arg_index);
460 : // The runtime SetProperty call could have converted the array to dictionary
461 : // mode, which must be detected to abort the fast-path.
462 112 : Node* kind = LoadElementsKind(array_receiver);
463 168 : GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)),
464 56 : &default_label);
465 :
466 56 : GotoIfNotNumber(arg, &object_push);
467 56 : Goto(&double_push);
468 : }
469 :
470 56 : BIND(&object_push_pre);
471 : {
472 112 : Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS), &double_push,
473 56 : &object_push);
474 : }
475 :
476 56 : BIND(&object_push);
477 : {
478 112 : Node* new_length = BuildAppendJSArray(PACKED_ELEMENTS, array_receiver,
479 56 : &args, &arg_index, &default_label);
480 56 : args.PopAndReturn(new_length);
481 : }
482 :
483 56 : BIND(&double_push);
484 : {
485 : Node* new_length =
486 56 : BuildAppendJSArray(PACKED_DOUBLE_ELEMENTS, array_receiver, &args,
487 56 : &arg_index, &double_transition);
488 56 : args.PopAndReturn(new_length);
489 : }
490 :
491 : // If the argument is not a double, then use a heavyweight SetProperty to
492 : // transition the array for only the single next element. If the argument is
493 : // a double, the failure is due to some other reason and we should fall back
494 : // on the most generic implementation for the rest of the array.
495 56 : BIND(&double_transition);
496 : {
497 112 : Node* arg = args.AtIndex(arg_index.value());
498 56 : GotoIfNumber(arg, &default_label);
499 112 : Node* length = LoadJSArrayLength(array_receiver);
500 : // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
501 : // calling into the runtime to do the elements transition is overkill.
502 : SetPropertyStrict(context, array_receiver, CAST(length), CAST(arg));
503 56 : Increment(&arg_index);
504 : // The runtime SetProperty call could have converted the array to dictionary
505 : // mode, which must be detected to abort the fast-path.
506 112 : Node* kind = LoadElementsKind(array_receiver);
507 168 : GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)),
508 56 : &default_label);
509 56 : Goto(&object_push);
510 : }
511 :
512 : // Fallback that stores un-processed arguments using the full, heavyweight
513 : // SetProperty machinery.
514 56 : BIND(&default_label);
515 : {
516 56 : args.ForEach(
517 168 : [this, array_receiver, context](Node* arg) {
518 112 : Node* length = LoadJSArrayLength(array_receiver);
519 56 : SetPropertyStrict(context, array_receiver, CAST(length), CAST(arg));
520 56 : },
521 56 : arg_index.value());
522 112 : args.PopAndReturn(LoadJSArrayLength(array_receiver));
523 : }
524 :
525 56 : BIND(&runtime);
526 : {
527 : // We are not using Parameter(Descriptor::kJSTarget) and loading the value
528 : // from the current frame here in order to reduce register pressure on the
529 : // fast path.
530 56 : TNode<JSFunction> target = LoadTargetFromFrame();
531 112 : TailCallBuiltin(Builtins::kArrayPush, context, target, UndefinedConstant(),
532 56 : argc);
533 : }
534 56 : }
535 :
536 224 : TF_BUILTIN(ExtractFastJSArray, ArrayBuiltinsAssembler) {
537 : ParameterMode mode = OptimalParameterMode();
538 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
539 : Node* array = Parameter(Descriptor::kSource);
540 56 : Node* begin = TaggedToParameter(Parameter(Descriptor::kBegin), mode);
541 : Node* count = TaggedToParameter(Parameter(Descriptor::kCount), mode);
542 :
543 : CSA_ASSERT(this, IsJSArray(array));
544 : CSA_ASSERT(this, Word32BinaryNot(IsNoElementsProtectorCellInvalid()));
545 :
546 112 : Return(ExtractFastJSArray(context, array, begin, count, mode));
547 56 : }
548 :
549 112 : TF_BUILTIN(CloneFastJSArray, ArrayBuiltinsAssembler) {
550 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
551 : TNode<JSArray> array = CAST(Parameter(Descriptor::kSource));
552 :
553 : CSA_ASSERT(this,
554 : Word32Or(Word32BinaryNot(
555 : IsHoleyFastElementsKind(LoadElementsKind(array))),
556 : Word32BinaryNot(IsNoElementsProtectorCellInvalid())));
557 :
558 : ParameterMode mode = OptimalParameterMode();
559 112 : Return(CloneFastJSArray(context, array, mode));
560 56 : }
561 :
562 : // This builtin copies the backing store of fast arrays, while converting any
563 : // holes to undefined.
564 : // - If there are no holes in the source, its ElementsKind will be preserved. In
565 : // that case, this builtin should perform as fast as CloneFastJSArray. (In fact,
566 : // for fast packed arrays, the behavior is equivalent to CloneFastJSArray.)
567 : // - If there are holes in the source, the ElementsKind of the "copy" will be
568 : // PACKED_ELEMENTS (such that undefined can be stored).
569 112 : TF_BUILTIN(CloneFastJSArrayFillingHoles, ArrayBuiltinsAssembler) {
570 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
571 : TNode<JSArray> array = CAST(Parameter(Descriptor::kSource));
572 :
573 : CSA_ASSERT(this,
574 : Word32Or(Word32BinaryNot(
575 : IsHoleyFastElementsKind(LoadElementsKind(array))),
576 : Word32BinaryNot(IsNoElementsProtectorCellInvalid())));
577 :
578 : ParameterMode mode = OptimalParameterMode();
579 112 : Return(CloneFastJSArray(context, array, mode, nullptr,
580 56 : HoleConversionMode::kConvertToUndefined));
581 56 : }
582 :
583 56 : class ArrayPopulatorAssembler : public CodeStubAssembler {
584 : public:
585 : explicit ArrayPopulatorAssembler(compiler::CodeAssemblerState* state)
586 56 : : CodeStubAssembler(state) {}
587 :
588 56 : TNode<Object> ConstructArrayLike(TNode<Context> context,
589 : TNode<Object> receiver) {
590 56 : TVARIABLE(Object, array);
591 56 : Label is_constructor(this), is_not_constructor(this), done(this);
592 112 : GotoIf(TaggedIsSmi(receiver), &is_not_constructor);
593 112 : Branch(IsConstructor(CAST(receiver)), &is_constructor, &is_not_constructor);
594 :
595 56 : BIND(&is_constructor);
596 : {
597 : array = Construct(context, CAST(receiver));
598 56 : Goto(&done);
599 : }
600 :
601 56 : BIND(&is_not_constructor);
602 : {
603 56 : Label allocate_js_array(this);
604 :
605 56 : TNode<Map> array_map = CAST(LoadContextElement(
606 : context, Context::JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX));
607 :
608 168 : array = AllocateJSArray(PACKED_SMI_ELEMENTS, array_map, SmiConstant(0),
609 : SmiConstant(0), nullptr,
610 112 : ParameterMode::SMI_PARAMETERS);
611 56 : Goto(&done);
612 : }
613 :
614 56 : BIND(&done);
615 56 : return array.value();
616 : }
617 :
618 56 : TNode<Object> ConstructArrayLike(TNode<Context> context,
619 : TNode<Object> receiver,
620 : TNode<Number> length) {
621 56 : TVARIABLE(Object, array);
622 56 : Label is_constructor(this), is_not_constructor(this), done(this);
623 : CSA_ASSERT(this, IsNumberNormalized(length));
624 112 : GotoIf(TaggedIsSmi(receiver), &is_not_constructor);
625 112 : Branch(IsConstructor(CAST(receiver)), &is_constructor, &is_not_constructor);
626 :
627 56 : BIND(&is_constructor);
628 : {
629 : array = Construct(context, CAST(receiver), length);
630 56 : Goto(&done);
631 : }
632 :
633 56 : BIND(&is_not_constructor);
634 : {
635 112 : array = ArrayCreate(context, length);
636 56 : Goto(&done);
637 : }
638 :
639 56 : BIND(&done);
640 56 : return array.value();
641 : }
642 : };
643 :
644 : // ES #sec-array.from
645 224 : TF_BUILTIN(ArrayFrom, ArrayPopulatorAssembler) {
646 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
647 : TNode<Int32T> argc =
648 : UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
649 :
650 168 : CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
651 56 : TNode<Object> items = args.GetOptionalArgumentValue(0);
652 56 : TNode<Object> receiver = args.GetReceiver();
653 :
654 56 : Label fast_iterate(this), normal_iterate(this);
655 :
656 : // Use fast path if:
657 : // * |items| is the only argument, and
658 : // * the receiver is the Array function.
659 168 : GotoIfNot(Word32Equal(argc, Int32Constant(1)), &normal_iterate);
660 : TNode<Object> array_function = LoadContextElement(
661 112 : LoadNativeContext(context), Context::ARRAY_FUNCTION_INDEX);
662 56 : Branch(WordEqual(array_function, receiver), &fast_iterate, &normal_iterate);
663 :
664 56 : BIND(&fast_iterate);
665 : {
666 56 : IteratorBuiltinsAssembler iterator_assembler(state());
667 : TVARIABLE(Object, var_fast_result);
668 : iterator_assembler.FastIterableToList(context, items, &var_fast_result,
669 56 : &normal_iterate);
670 56 : args.PopAndReturn(var_fast_result.value());
671 : }
672 :
673 56 : BIND(&normal_iterate);
674 56 : TNode<Object> map_function = args.GetOptionalArgumentValue(1);
675 :
676 : // If map_function is not undefined, then ensure it's callable else throw.
677 : {
678 56 : Label no_error(this), error(this);
679 112 : GotoIf(IsUndefined(map_function), &no_error);
680 112 : GotoIf(TaggedIsSmi(map_function), &error);
681 112 : Branch(IsCallable(CAST(map_function)), &no_error, &error);
682 :
683 56 : BIND(&error);
684 56 : ThrowTypeError(context, MessageTemplate::kCalledNonCallable, map_function);
685 :
686 56 : BIND(&no_error);
687 : }
688 :
689 56 : Label iterable(this), not_iterable(this), finished(this), if_exception(this);
690 :
691 56 : TNode<Object> this_arg = args.GetOptionalArgumentValue(2);
692 : // The spec doesn't require ToObject to be called directly on the iterable
693 : // branch, but it's part of GetMethod that is in the spec.
694 56 : TNode<JSReceiver> array_like = ToObject_Inline(context, items);
695 :
696 : TVARIABLE(Object, array);
697 : TVARIABLE(Number, length);
698 :
699 : // Determine whether items[Symbol.iterator] is defined:
700 56 : IteratorBuiltinsAssembler iterator_assembler(state());
701 : Node* iterator_method =
702 112 : iterator_assembler.GetIteratorMethod(context, array_like);
703 112 : Branch(IsNullOrUndefined(iterator_method), ¬_iterable, &iterable);
704 :
705 56 : BIND(&iterable);
706 : {
707 112 : TVARIABLE(Number, index, SmiConstant(0));
708 : TVARIABLE(Object, var_exception);
709 56 : Label loop(this, &index), loop_done(this),
710 56 : on_exception(this, Label::kDeferred),
711 56 : index_overflow(this, Label::kDeferred);
712 :
713 : // Check that the method is callable.
714 : {
715 56 : Label get_method_not_callable(this, Label::kDeferred), next(this);
716 112 : GotoIf(TaggedIsSmi(iterator_method), &get_method_not_callable);
717 112 : GotoIfNot(IsCallable(CAST(iterator_method)), &get_method_not_callable);
718 56 : Goto(&next);
719 :
720 56 : BIND(&get_method_not_callable);
721 : ThrowTypeError(context, MessageTemplate::kCalledNonCallable,
722 56 : iterator_method);
723 :
724 56 : BIND(&next);
725 : }
726 :
727 : // Construct the output array with empty length.
728 56 : array = ConstructArrayLike(context, receiver);
729 :
730 : // Actually get the iterator and throw if the iterator method does not yield
731 : // one.
732 : IteratorRecord iterator_record =
733 56 : iterator_assembler.GetIterator(context, items, iterator_method);
734 :
735 56 : TNode<Context> native_context = LoadNativeContext(context);
736 : TNode<Object> fast_iterator_result_map =
737 56 : LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
738 :
739 56 : Goto(&loop);
740 :
741 56 : BIND(&loop);
742 : {
743 : // Loop while iterator is not done.
744 : TNode<Object> next = iterator_assembler.IteratorStep(
745 56 : context, iterator_record, &loop_done, fast_iterator_result_map);
746 56 : TVARIABLE(Object, value,
747 : CAST(iterator_assembler.IteratorValue(
748 : context, next, fast_iterator_result_map)));
749 :
750 : // If a map_function is supplied then call it (using this_arg as
751 : // receiver), on the value returned from the iterator. Exceptions are
752 : // caught so the iterator can be closed.
753 : {
754 56 : Label next(this);
755 112 : GotoIf(IsUndefined(map_function), &next);
756 :
757 : CSA_ASSERT(this, IsCallable(CAST(map_function)));
758 112 : Node* v = CallJS(CodeFactory::Call(isolate()), context, map_function,
759 56 : this_arg, value.value(), index.value());
760 56 : GotoIfException(v, &on_exception, &var_exception);
761 : value = CAST(v);
762 56 : Goto(&next);
763 56 : BIND(&next);
764 : }
765 :
766 : // Store the result in the output object (catching any exceptions so the
767 : // iterator can be closed).
768 : Node* define_status =
769 : CallRuntime(Runtime::kCreateDataProperty, context, array.value(),
770 : index.value(), value.value());
771 56 : GotoIfException(define_status, &on_exception, &var_exception);
772 :
773 112 : index = NumberInc(index.value());
774 :
775 : // The spec requires that we throw an exception if index reaches 2^53-1,
776 : // but an empty loop would take >100 days to do this many iterations. To
777 : // actually run for that long would require an iterator that never set
778 : // done to true and a target array which somehow never ran out of memory,
779 : // e.g. a proxy that discarded the values. Ignoring this case just means
780 : // we would repeatedly call CreateDataProperty with index = 2^53.
781 : CSA_ASSERT_BRANCH(this, [&](Label* ok, Label* not_ok) {
782 : BranchIfNumberRelationalComparison(Operation::kLessThan, index.value(),
783 : NumberConstant(kMaxSafeInteger), ok,
784 : not_ok);
785 : });
786 56 : Goto(&loop);
787 : }
788 :
789 56 : BIND(&loop_done);
790 : {
791 : length = index;
792 56 : Goto(&finished);
793 : }
794 :
795 56 : BIND(&on_exception);
796 : {
797 : // Close the iterator, rethrowing either the passed exception or
798 : // exceptions thrown during the close.
799 : iterator_assembler.IteratorCloseOnException(context, iterator_record,
800 56 : var_exception.value());
801 : }
802 : }
803 :
804 56 : BIND(¬_iterable);
805 : {
806 : // Treat array_like as an array and try to get its length.
807 112 : length = ToLength_Inline(
808 168 : context, GetProperty(context, array_like, factory()->length_string()));
809 :
810 : // Construct an array using the receiver as constructor with the same length
811 : // as the input array.
812 56 : array = ConstructArrayLike(context, receiver, length.value());
813 :
814 112 : TVARIABLE(Number, index, SmiConstant(0));
815 :
816 : // TODO(ishell): remove <Object, Object>
817 112 : GotoIf(WordEqual<Object, Object>(length.value(), SmiConstant(0)),
818 56 : &finished);
819 :
820 : // Loop from 0 to length-1.
821 : {
822 56 : Label loop(this, &index);
823 56 : Goto(&loop);
824 56 : BIND(&loop);
825 : TVARIABLE(Object, value);
826 :
827 : value = GetProperty(context, array_like, index.value());
828 :
829 : // If a map_function is supplied then call it (using this_arg as
830 : // receiver), on the value retrieved from the array.
831 : {
832 56 : Label next(this);
833 112 : GotoIf(IsUndefined(map_function), &next);
834 :
835 : CSA_ASSERT(this, IsCallable(CAST(map_function)));
836 112 : value = CAST(CallJS(CodeFactory::Call(isolate()), context, map_function,
837 : this_arg, value.value(), index.value()));
838 56 : Goto(&next);
839 56 : BIND(&next);
840 : }
841 :
842 : // Store the result in the output object.
843 : CallRuntime(Runtime::kCreateDataProperty, context, array.value(),
844 : index.value(), value.value());
845 112 : index = NumberInc(index.value());
846 : BranchIfNumberRelationalComparison(Operation::kLessThan, index.value(),
847 56 : length.value(), &loop, &finished);
848 : }
849 : }
850 :
851 56 : BIND(&finished);
852 :
853 : // Finally set the length on the output and return it.
854 56 : SetPropertyLength(context, array.value(), length.value());
855 56 : args.PopAndReturn(array.value());
856 56 : }
857 :
858 168 : TF_BUILTIN(TypedArrayPrototypeMap, ArrayBuiltinsAssembler) {
859 : TNode<IntPtrT> argc =
860 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
861 56 : CodeStubArguments args(this, argc);
862 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
863 56 : TNode<Object> receiver = args.GetReceiver();
864 112 : Node* callbackfn = args.GetOptionalArgumentValue(0);
865 112 : Node* this_arg = args.GetOptionalArgumentValue(1);
866 :
867 : InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
868 :
869 224 : GenerateIteratingTypedArrayBuiltinBody(
870 : "%TypedArray%.prototype.map",
871 : &ArrayBuiltinsAssembler::TypedArrayMapResultGenerator,
872 : &ArrayBuiltinsAssembler::TypedArrayMapProcessor,
873 56 : &ArrayBuiltinsAssembler::NullPostLoopAction);
874 56 : }
875 :
876 168 : TF_BUILTIN(ArrayIsArray, CodeStubAssembler) {
877 : TNode<Object> object = CAST(Parameter(Descriptor::kArg));
878 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
879 :
880 56 : Label call_runtime(this), return_true(this), return_false(this);
881 :
882 112 : GotoIf(TaggedIsSmi(object), &return_false);
883 56 : TNode<Int32T> instance_type = LoadInstanceType(CAST(object));
884 :
885 112 : GotoIf(InstanceTypeEqual(instance_type, JS_ARRAY_TYPE), &return_true);
886 :
887 : // TODO(verwaest): Handle proxies in-place.
888 112 : Branch(InstanceTypeEqual(instance_type, JS_PROXY_TYPE), &call_runtime,
889 56 : &return_false);
890 :
891 56 : BIND(&return_true);
892 112 : Return(TrueConstant());
893 :
894 56 : BIND(&return_false);
895 112 : Return(FalseConstant());
896 :
897 56 : BIND(&call_runtime);
898 56 : Return(CallRuntime(Runtime::kArrayIsArray, context, object));
899 56 : }
900 :
901 448 : class ArrayIncludesIndexofAssembler : public CodeStubAssembler {
902 : public:
903 : explicit ArrayIncludesIndexofAssembler(compiler::CodeAssemblerState* state)
904 448 : : CodeStubAssembler(state) {}
905 :
906 : enum SearchVariant { kIncludes, kIndexOf };
907 :
908 : void Generate(SearchVariant variant, TNode<IntPtrT> argc,
909 : TNode<Context> context);
910 : void GenerateSmiOrObject(SearchVariant variant, Node* context, Node* elements,
911 : Node* search_element, Node* array_length,
912 : Node* from_index);
913 : void GeneratePackedDoubles(SearchVariant variant, Node* elements,
914 : Node* search_element, Node* array_length,
915 : Node* from_index);
916 : void GenerateHoleyDoubles(SearchVariant variant, Node* elements,
917 : Node* search_element, Node* array_length,
918 : Node* from_index);
919 : };
920 :
921 112 : void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant,
922 : TNode<IntPtrT> argc,
923 : TNode<Context> context) {
924 : const int kSearchElementArg = 0;
925 : const int kFromIndexArg = 1;
926 :
927 112 : CodeStubArguments args(this, argc);
928 :
929 112 : TNode<Object> receiver = args.GetReceiver();
930 : TNode<Object> search_element =
931 112 : args.GetOptionalArgumentValue(kSearchElementArg);
932 :
933 224 : Node* intptr_zero = IntPtrConstant(0);
934 :
935 112 : Label init_index(this), return_not_found(this), call_runtime(this);
936 :
937 : // Take slow path if not a JSArray, if retrieving elements requires
938 : // traversing prototype, or if access checks are required.
939 112 : BranchIfFastJSArray(receiver, context, &init_index, &call_runtime);
940 :
941 112 : BIND(&init_index);
942 224 : VARIABLE(index_var, MachineType::PointerRepresentation(), intptr_zero);
943 : TNode<JSArray> array = CAST(receiver);
944 :
945 : // JSArray length is always a positive Smi for fast arrays.
946 : CSA_ASSERT(this, TaggedIsPositiveSmi(LoadJSArrayLength(array)));
947 224 : Node* array_length = LoadFastJSArrayLength(array);
948 224 : Node* array_length_untagged = SmiUntag(array_length);
949 :
950 : {
951 : // Initialize fromIndex.
952 112 : Label is_smi(this), is_nonsmi(this), done(this);
953 :
954 : // If no fromIndex was passed, default to 0.
955 336 : GotoIf(IntPtrLessThanOrEqual(argc, IntPtrConstant(kFromIndexArg)), &done);
956 :
957 224 : Node* start_from = args.AtIndex(kFromIndexArg);
958 : // Handle Smis and undefined here and everything else in runtime.
959 : // We must be very careful with side effects from the ToInteger conversion,
960 : // as the side effects might render previously checked assumptions about
961 : // the receiver being a fast JSArray and its length invalid.
962 224 : Branch(TaggedIsSmi(start_from), &is_smi, &is_nonsmi);
963 :
964 112 : BIND(&is_nonsmi);
965 : {
966 224 : GotoIfNot(IsUndefined(start_from), &call_runtime);
967 112 : Goto(&done);
968 : }
969 112 : BIND(&is_smi);
970 : {
971 224 : Node* intptr_start_from = SmiUntag(start_from);
972 112 : index_var.Bind(intptr_start_from);
973 :
974 336 : GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done);
975 : // The fromIndex is negative: add it to the array's length.
976 336 : index_var.Bind(IntPtrAdd(array_length_untagged, index_var.value()));
977 : // Clamp negative results at zero.
978 336 : GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done);
979 112 : index_var.Bind(intptr_zero);
980 112 : Goto(&done);
981 : }
982 112 : BIND(&done);
983 : }
984 :
985 : // Fail early if startIndex >= array.length.
986 336 : GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), array_length_untagged),
987 112 : &return_not_found);
988 :
989 112 : Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this);
990 :
991 112 : TNode<Int32T> elements_kind = LoadElementsKind(array);
992 : Node* elements = LoadElements(array);
993 : STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
994 : STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
995 : STATIC_ASSERT(PACKED_ELEMENTS == 2);
996 : STATIC_ASSERT(HOLEY_ELEMENTS == 3);
997 336 : GotoIf(Uint32LessThanOrEqual(elements_kind, Int32Constant(HOLEY_ELEMENTS)),
998 112 : &if_smiorobjects);
999 336 : GotoIf(Word32Equal(elements_kind, Int32Constant(PACKED_DOUBLE_ELEMENTS)),
1000 112 : &if_packed_doubles);
1001 336 : GotoIf(Word32Equal(elements_kind, Int32Constant(HOLEY_DOUBLE_ELEMENTS)),
1002 112 : &if_holey_doubles);
1003 112 : Goto(&return_not_found);
1004 :
1005 112 : BIND(&if_smiorobjects);
1006 : {
1007 : Callable callable =
1008 : (variant == kIncludes)
1009 : ? Builtins::CallableFor(isolate(),
1010 : Builtins::kArrayIncludesSmiOrObject)
1011 : : Builtins::CallableFor(isolate(),
1012 112 : Builtins::kArrayIndexOfSmiOrObject);
1013 224 : Node* result = CallStub(callable, context, elements, search_element,
1014 336 : array_length, SmiTag(index_var.value()));
1015 112 : args.PopAndReturn(result);
1016 : }
1017 :
1018 112 : BIND(&if_packed_doubles);
1019 : {
1020 : Callable callable =
1021 : (variant == kIncludes)
1022 : ? Builtins::CallableFor(isolate(),
1023 : Builtins::kArrayIncludesPackedDoubles)
1024 : : Builtins::CallableFor(isolate(),
1025 112 : Builtins::kArrayIndexOfPackedDoubles);
1026 224 : Node* result = CallStub(callable, context, elements, search_element,
1027 336 : array_length, SmiTag(index_var.value()));
1028 112 : args.PopAndReturn(result);
1029 : }
1030 :
1031 112 : BIND(&if_holey_doubles);
1032 : {
1033 : Callable callable =
1034 : (variant == kIncludes)
1035 : ? Builtins::CallableFor(isolate(),
1036 : Builtins::kArrayIncludesHoleyDoubles)
1037 : : Builtins::CallableFor(isolate(),
1038 112 : Builtins::kArrayIndexOfHoleyDoubles);
1039 224 : Node* result = CallStub(callable, context, elements, search_element,
1040 336 : array_length, SmiTag(index_var.value()));
1041 112 : args.PopAndReturn(result);
1042 : }
1043 :
1044 112 : BIND(&return_not_found);
1045 112 : if (variant == kIncludes) {
1046 112 : args.PopAndReturn(FalseConstant());
1047 : } else {
1048 112 : args.PopAndReturn(NumberConstant(-1));
1049 : }
1050 :
1051 112 : BIND(&call_runtime);
1052 : {
1053 : Node* start_from =
1054 336 : args.GetOptionalArgumentValue(kFromIndexArg, UndefinedConstant());
1055 : Runtime::FunctionId function = variant == kIncludes
1056 : ? Runtime::kArrayIncludes_Slow
1057 112 : : Runtime::kArrayIndexOf;
1058 : args.PopAndReturn(
1059 112 : CallRuntime(function, context, array, search_element, start_from));
1060 : }
1061 112 : }
1062 :
1063 112 : void ArrayIncludesIndexofAssembler::GenerateSmiOrObject(
1064 : SearchVariant variant, Node* context, Node* elements, Node* search_element,
1065 : Node* array_length, Node* from_index) {
1066 336 : VARIABLE(index_var, MachineType::PointerRepresentation(),
1067 : SmiUntag(from_index));
1068 224 : VARIABLE(search_num, MachineRepresentation::kFloat64);
1069 224 : Node* array_length_untagged = SmiUntag(array_length);
1070 :
1071 112 : Label ident_loop(this, &index_var), heap_num_loop(this, &search_num),
1072 112 : string_loop(this), bigint_loop(this, &index_var),
1073 112 : undef_loop(this, &index_var), not_smi(this), not_heap_num(this),
1074 112 : return_found(this), return_not_found(this);
1075 :
1076 224 : GotoIfNot(TaggedIsSmi(search_element), ¬_smi);
1077 224 : search_num.Bind(SmiToFloat64(search_element));
1078 112 : Goto(&heap_num_loop);
1079 :
1080 112 : BIND(¬_smi);
1081 112 : if (variant == kIncludes) {
1082 112 : GotoIf(IsUndefined(search_element), &undef_loop);
1083 : }
1084 224 : Node* map = LoadMap(search_element);
1085 224 : GotoIfNot(IsHeapNumberMap(map), ¬_heap_num);
1086 224 : search_num.Bind(LoadHeapNumberValue(search_element));
1087 112 : Goto(&heap_num_loop);
1088 :
1089 112 : BIND(¬_heap_num);
1090 224 : Node* search_type = LoadMapInstanceType(map);
1091 224 : GotoIf(IsStringInstanceType(search_type), &string_loop);
1092 224 : GotoIf(IsBigIntInstanceType(search_type), &bigint_loop);
1093 112 : Goto(&ident_loop);
1094 :
1095 112 : BIND(&ident_loop);
1096 : {
1097 336 : GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1098 112 : &return_not_found);
1099 : Node* element_k =
1100 112 : UnsafeLoadFixedArrayElement(CAST(elements), index_var.value());
1101 224 : GotoIf(WordEqual(element_k, search_element), &return_found);
1102 :
1103 112 : Increment(&index_var);
1104 112 : Goto(&ident_loop);
1105 : }
1106 :
1107 112 : if (variant == kIncludes) {
1108 56 : BIND(&undef_loop);
1109 :
1110 168 : GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1111 56 : &return_not_found);
1112 : Node* element_k =
1113 56 : UnsafeLoadFixedArrayElement(CAST(elements), index_var.value());
1114 112 : GotoIf(IsUndefined(element_k), &return_found);
1115 112 : GotoIf(IsTheHole(element_k), &return_found);
1116 :
1117 56 : Increment(&index_var);
1118 56 : Goto(&undef_loop);
1119 : }
1120 :
1121 112 : BIND(&heap_num_loop);
1122 : {
1123 112 : Label nan_loop(this, &index_var), not_nan_loop(this, &index_var);
1124 112 : Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
1125 112 : BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop);
1126 :
1127 112 : BIND(¬_nan_loop);
1128 : {
1129 112 : Label continue_loop(this), not_smi(this);
1130 336 : GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1131 112 : &return_not_found);
1132 : Node* element_k =
1133 112 : UnsafeLoadFixedArrayElement(CAST(elements), index_var.value());
1134 224 : GotoIfNot(TaggedIsSmi(element_k), ¬_smi);
1135 448 : Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)),
1136 112 : &return_found, &continue_loop);
1137 :
1138 112 : BIND(¬_smi);
1139 224 : GotoIfNot(IsHeapNumber(element_k), &continue_loop);
1140 448 : Branch(Float64Equal(search_num.value(), LoadHeapNumberValue(element_k)),
1141 112 : &return_found, &continue_loop);
1142 :
1143 112 : BIND(&continue_loop);
1144 112 : Increment(&index_var);
1145 112 : Goto(¬_nan_loop);
1146 : }
1147 :
1148 : // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
1149 112 : if (variant == kIncludes) {
1150 56 : BIND(&nan_loop);
1151 56 : Label continue_loop(this);
1152 168 : GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1153 56 : &return_not_found);
1154 : Node* element_k =
1155 56 : UnsafeLoadFixedArrayElement(CAST(elements), index_var.value());
1156 112 : GotoIf(TaggedIsSmi(element_k), &continue_loop);
1157 112 : GotoIfNot(IsHeapNumber(CAST(element_k)), &continue_loop);
1158 112 : BranchIfFloat64IsNaN(LoadHeapNumberValue(element_k), &return_found,
1159 56 : &continue_loop);
1160 :
1161 56 : BIND(&continue_loop);
1162 56 : Increment(&index_var);
1163 56 : Goto(&nan_loop);
1164 : }
1165 : }
1166 :
1167 112 : BIND(&string_loop);
1168 : {
1169 : TNode<String> search_element_string = CAST(search_element);
1170 112 : Label continue_loop(this), next_iteration(this, &index_var),
1171 112 : slow_compare(this), runtime(this, Label::kDeferred);
1172 : TNode<IntPtrT> search_length =
1173 112 : LoadStringLengthAsWord(search_element_string);
1174 112 : Goto(&next_iteration);
1175 112 : BIND(&next_iteration);
1176 336 : GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1177 112 : &return_not_found);
1178 : Node* element_k =
1179 112 : UnsafeLoadFixedArrayElement(CAST(elements), index_var.value());
1180 224 : GotoIf(TaggedIsSmi(element_k), &continue_loop);
1181 112 : GotoIf(WordEqual(search_element_string, element_k), &return_found);
1182 224 : Node* element_k_type = LoadInstanceType(element_k);
1183 224 : GotoIfNot(IsStringInstanceType(element_k_type), &continue_loop);
1184 336 : Branch(WordEqual(search_length, LoadStringLengthAsWord(element_k)),
1185 112 : &slow_compare, &continue_loop);
1186 :
1187 112 : BIND(&slow_compare);
1188 : StringBuiltinsAssembler string_asm(state());
1189 : string_asm.StringEqual_Core(context, search_element_string, search_type,
1190 : element_k, element_k_type, search_length,
1191 112 : &return_found, &continue_loop, &runtime);
1192 112 : BIND(&runtime);
1193 : TNode<Object> result = CallRuntime(Runtime::kStringEqual, context,
1194 : search_element_string, element_k);
1195 224 : Branch(WordEqual(result, TrueConstant()), &return_found, &continue_loop);
1196 :
1197 112 : BIND(&continue_loop);
1198 112 : Increment(&index_var);
1199 112 : Goto(&next_iteration);
1200 : }
1201 :
1202 112 : BIND(&bigint_loop);
1203 : {
1204 336 : GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1205 112 : &return_not_found);
1206 :
1207 : Node* element_k =
1208 112 : UnsafeLoadFixedArrayElement(CAST(elements), index_var.value());
1209 112 : Label continue_loop(this);
1210 224 : GotoIf(TaggedIsSmi(element_k), &continue_loop);
1211 224 : GotoIfNot(IsBigInt(CAST(element_k)), &continue_loop);
1212 : TNode<Object> result = CallRuntime(Runtime::kBigIntEqualToBigInt, context,
1213 : search_element, element_k);
1214 224 : Branch(WordEqual(result, TrueConstant()), &return_found, &continue_loop);
1215 :
1216 112 : BIND(&continue_loop);
1217 112 : Increment(&index_var);
1218 112 : Goto(&bigint_loop);
1219 : }
1220 112 : BIND(&return_found);
1221 112 : if (variant == kIncludes) {
1222 112 : Return(TrueConstant());
1223 : } else {
1224 168 : Return(SmiTag(index_var.value()));
1225 : }
1226 :
1227 112 : BIND(&return_not_found);
1228 112 : if (variant == kIncludes) {
1229 112 : Return(FalseConstant());
1230 : } else {
1231 112 : Return(NumberConstant(-1));
1232 : }
1233 112 : }
1234 :
1235 112 : void ArrayIncludesIndexofAssembler::GeneratePackedDoubles(SearchVariant variant,
1236 : Node* elements,
1237 : Node* search_element,
1238 : Node* array_length,
1239 : Node* from_index) {
1240 336 : VARIABLE(index_var, MachineType::PointerRepresentation(),
1241 : SmiUntag(from_index));
1242 224 : Node* array_length_untagged = SmiUntag(array_length);
1243 :
1244 112 : Label nan_loop(this, &index_var), not_nan_loop(this, &index_var),
1245 112 : hole_loop(this, &index_var), search_notnan(this), return_found(this),
1246 112 : return_not_found(this);
1247 224 : VARIABLE(search_num, MachineRepresentation::kFloat64);
1248 224 : search_num.Bind(Float64Constant(0));
1249 :
1250 224 : GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
1251 224 : search_num.Bind(SmiToFloat64(search_element));
1252 112 : Goto(¬_nan_loop);
1253 :
1254 112 : BIND(&search_notnan);
1255 224 : GotoIfNot(IsHeapNumber(search_element), &return_not_found);
1256 :
1257 224 : search_num.Bind(LoadHeapNumberValue(search_element));
1258 :
1259 112 : Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
1260 112 : BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop);
1261 :
1262 112 : BIND(¬_nan_loop);
1263 : {
1264 112 : Label continue_loop(this);
1265 336 : GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1266 112 : &return_not_found);
1267 224 : Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(),
1268 224 : MachineType::Float64());
1269 336 : Branch(Float64Equal(element_k, search_num.value()), &return_found,
1270 112 : &continue_loop);
1271 112 : BIND(&continue_loop);
1272 112 : Increment(&index_var);
1273 112 : Goto(¬_nan_loop);
1274 : }
1275 :
1276 : // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
1277 112 : if (variant == kIncludes) {
1278 56 : BIND(&nan_loop);
1279 56 : Label continue_loop(this);
1280 168 : GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1281 56 : &return_not_found);
1282 112 : Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(),
1283 112 : MachineType::Float64());
1284 56 : BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop);
1285 56 : BIND(&continue_loop);
1286 56 : Increment(&index_var);
1287 56 : Goto(&nan_loop);
1288 : }
1289 :
1290 112 : BIND(&return_found);
1291 112 : if (variant == kIncludes) {
1292 112 : Return(TrueConstant());
1293 : } else {
1294 168 : Return(SmiTag(index_var.value()));
1295 : }
1296 :
1297 112 : BIND(&return_not_found);
1298 112 : if (variant == kIncludes) {
1299 112 : Return(FalseConstant());
1300 : } else {
1301 112 : Return(NumberConstant(-1));
1302 : }
1303 112 : }
1304 :
1305 112 : void ArrayIncludesIndexofAssembler::GenerateHoleyDoubles(SearchVariant variant,
1306 : Node* elements,
1307 : Node* search_element,
1308 : Node* array_length,
1309 : Node* from_index) {
1310 336 : VARIABLE(index_var, MachineType::PointerRepresentation(),
1311 : SmiUntag(from_index));
1312 224 : Node* array_length_untagged = SmiUntag(array_length);
1313 :
1314 112 : Label nan_loop(this, &index_var), not_nan_loop(this, &index_var),
1315 112 : hole_loop(this, &index_var), search_notnan(this), return_found(this),
1316 112 : return_not_found(this);
1317 224 : VARIABLE(search_num, MachineRepresentation::kFloat64);
1318 224 : search_num.Bind(Float64Constant(0));
1319 :
1320 224 : GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
1321 224 : search_num.Bind(SmiToFloat64(search_element));
1322 112 : Goto(¬_nan_loop);
1323 :
1324 112 : BIND(&search_notnan);
1325 112 : if (variant == kIncludes) {
1326 112 : GotoIf(IsUndefined(search_element), &hole_loop);
1327 : }
1328 224 : GotoIfNot(IsHeapNumber(search_element), &return_not_found);
1329 :
1330 224 : search_num.Bind(LoadHeapNumberValue(search_element));
1331 :
1332 112 : Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
1333 112 : BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop);
1334 :
1335 112 : BIND(¬_nan_loop);
1336 : {
1337 112 : Label continue_loop(this);
1338 336 : GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1339 112 : &return_not_found);
1340 :
1341 : // No need for hole checking here; the following Float64Equal will
1342 : // return 'not equal' for holes anyway.
1343 224 : Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(),
1344 224 : MachineType::Float64());
1345 :
1346 336 : Branch(Float64Equal(element_k, search_num.value()), &return_found,
1347 112 : &continue_loop);
1348 112 : BIND(&continue_loop);
1349 112 : Increment(&index_var);
1350 112 : Goto(¬_nan_loop);
1351 : }
1352 :
1353 : // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
1354 112 : if (variant == kIncludes) {
1355 56 : BIND(&nan_loop);
1356 56 : Label continue_loop(this);
1357 168 : GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1358 56 : &return_not_found);
1359 :
1360 : // Load double value or continue if it's the hole NaN.
1361 112 : Node* element_k = LoadFixedDoubleArrayElement(
1362 : elements, index_var.value(), MachineType::Float64(), 0,
1363 112 : INTPTR_PARAMETERS, &continue_loop);
1364 :
1365 56 : BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop);
1366 56 : BIND(&continue_loop);
1367 56 : Increment(&index_var);
1368 56 : Goto(&nan_loop);
1369 : }
1370 :
1371 : // Array.p.includes treats the hole as undefined.
1372 112 : if (variant == kIncludes) {
1373 56 : BIND(&hole_loop);
1374 168 : GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1375 56 : &return_not_found);
1376 :
1377 : // Check if the element is a double hole, but don't load it.
1378 : LoadFixedDoubleArrayElement(elements, index_var.value(),
1379 : MachineType::None(), 0, INTPTR_PARAMETERS,
1380 112 : &return_found);
1381 :
1382 56 : Increment(&index_var);
1383 56 : Goto(&hole_loop);
1384 : }
1385 :
1386 112 : BIND(&return_found);
1387 112 : if (variant == kIncludes) {
1388 112 : Return(TrueConstant());
1389 : } else {
1390 168 : Return(SmiTag(index_var.value()));
1391 : }
1392 :
1393 112 : BIND(&return_not_found);
1394 112 : if (variant == kIncludes) {
1395 112 : Return(FalseConstant());
1396 : } else {
1397 112 : Return(NumberConstant(-1));
1398 : }
1399 112 : }
1400 :
1401 280 : TF_BUILTIN(ArrayIncludes, ArrayIncludesIndexofAssembler) {
1402 : TNode<IntPtrT> argc =
1403 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
1404 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1405 :
1406 56 : Generate(kIncludes, argc, context);
1407 56 : }
1408 :
1409 224 : TF_BUILTIN(ArrayIncludesSmiOrObject, ArrayIncludesIndexofAssembler) {
1410 : Node* context = Parameter(Descriptor::kContext);
1411 : Node* elements = Parameter(Descriptor::kElements);
1412 : Node* search_element = Parameter(Descriptor::kSearchElement);
1413 : Node* array_length = Parameter(Descriptor::kLength);
1414 : Node* from_index = Parameter(Descriptor::kFromIndex);
1415 :
1416 56 : GenerateSmiOrObject(kIncludes, context, elements, search_element,
1417 56 : array_length, from_index);
1418 56 : }
1419 :
1420 224 : TF_BUILTIN(ArrayIncludesPackedDoubles, ArrayIncludesIndexofAssembler) {
1421 : Node* elements = Parameter(Descriptor::kElements);
1422 : Node* search_element = Parameter(Descriptor::kSearchElement);
1423 : Node* array_length = Parameter(Descriptor::kLength);
1424 : Node* from_index = Parameter(Descriptor::kFromIndex);
1425 :
1426 56 : GeneratePackedDoubles(kIncludes, elements, search_element, array_length,
1427 56 : from_index);
1428 56 : }
1429 :
1430 224 : TF_BUILTIN(ArrayIncludesHoleyDoubles, ArrayIncludesIndexofAssembler) {
1431 : Node* elements = Parameter(Descriptor::kElements);
1432 : Node* search_element = Parameter(Descriptor::kSearchElement);
1433 : Node* array_length = Parameter(Descriptor::kLength);
1434 : Node* from_index = Parameter(Descriptor::kFromIndex);
1435 :
1436 56 : GenerateHoleyDoubles(kIncludes, elements, search_element, array_length,
1437 56 : from_index);
1438 56 : }
1439 :
1440 280 : TF_BUILTIN(ArrayIndexOf, ArrayIncludesIndexofAssembler) {
1441 : TNode<IntPtrT> argc =
1442 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
1443 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1444 :
1445 56 : Generate(kIndexOf, argc, context);
1446 56 : }
1447 :
1448 224 : TF_BUILTIN(ArrayIndexOfSmiOrObject, ArrayIncludesIndexofAssembler) {
1449 : Node* context = Parameter(Descriptor::kContext);
1450 : Node* elements = Parameter(Descriptor::kElements);
1451 : Node* search_element = Parameter(Descriptor::kSearchElement);
1452 : Node* array_length = Parameter(Descriptor::kLength);
1453 : Node* from_index = Parameter(Descriptor::kFromIndex);
1454 :
1455 56 : GenerateSmiOrObject(kIndexOf, context, elements, search_element, array_length,
1456 56 : from_index);
1457 56 : }
1458 :
1459 224 : TF_BUILTIN(ArrayIndexOfPackedDoubles, ArrayIncludesIndexofAssembler) {
1460 : Node* elements = Parameter(Descriptor::kElements);
1461 : Node* search_element = Parameter(Descriptor::kSearchElement);
1462 : Node* array_length = Parameter(Descriptor::kLength);
1463 : Node* from_index = Parameter(Descriptor::kFromIndex);
1464 :
1465 56 : GeneratePackedDoubles(kIndexOf, elements, search_element, array_length,
1466 56 : from_index);
1467 56 : }
1468 :
1469 224 : TF_BUILTIN(ArrayIndexOfHoleyDoubles, ArrayIncludesIndexofAssembler) {
1470 : Node* elements = Parameter(Descriptor::kElements);
1471 : Node* search_element = Parameter(Descriptor::kSearchElement);
1472 : Node* array_length = Parameter(Descriptor::kLength);
1473 : Node* from_index = Parameter(Descriptor::kFromIndex);
1474 :
1475 56 : GenerateHoleyDoubles(kIndexOf, elements, search_element, array_length,
1476 56 : from_index);
1477 56 : }
1478 :
1479 : // ES #sec-array.prototype.values
1480 224 : TF_BUILTIN(ArrayPrototypeValues, CodeStubAssembler) {
1481 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1482 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1483 224 : Return(CreateArrayIterator(context, ToObject_Inline(context, receiver),
1484 112 : IterationKind::kValues));
1485 56 : }
1486 :
1487 : // ES #sec-array.prototype.entries
1488 224 : TF_BUILTIN(ArrayPrototypeEntries, CodeStubAssembler) {
1489 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1490 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1491 224 : Return(CreateArrayIterator(context, ToObject_Inline(context, receiver),
1492 112 : IterationKind::kEntries));
1493 56 : }
1494 :
1495 : // ES #sec-array.prototype.keys
1496 224 : TF_BUILTIN(ArrayPrototypeKeys, CodeStubAssembler) {
1497 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1498 56 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1499 224 : Return(CreateArrayIterator(context, ToObject_Inline(context, receiver),
1500 112 : IterationKind::kKeys));
1501 56 : }
1502 :
1503 : // ES #sec-%arrayiteratorprototype%.next
1504 168 : TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) {
1505 : const char* method_name = "Array Iterator.prototype.next";
1506 :
1507 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1508 : Node* iterator = Parameter(Descriptor::kReceiver);
1509 :
1510 168 : VARIABLE(var_done, MachineRepresentation::kTagged, TrueConstant());
1511 168 : VARIABLE(var_value, MachineRepresentation::kTagged, UndefinedConstant());
1512 :
1513 56 : Label allocate_entry_if_needed(this);
1514 56 : Label allocate_iterator_result(this);
1515 56 : Label if_typedarray(this), if_other(this, Label::kDeferred), if_array(this),
1516 56 : if_generic(this, Label::kDeferred);
1517 56 : Label set_done(this, Label::kDeferred);
1518 :
1519 : // If O does not have all of the internal slots of an Array Iterator Instance
1520 : // (22.1.5.3), throw a TypeError exception
1521 : ThrowIfNotInstanceType(context, iterator, JS_ARRAY_ITERATOR_TYPE,
1522 56 : method_name);
1523 :
1524 : // Let a be O.[[IteratedObject]].
1525 : TNode<JSReceiver> array =
1526 : CAST(LoadObjectField(iterator, JSArrayIterator::kIteratedObjectOffset));
1527 :
1528 : // Let index be O.[[ArrayIteratorNextIndex]].
1529 : TNode<Number> index =
1530 : CAST(LoadObjectField(iterator, JSArrayIterator::kNextIndexOffset));
1531 : CSA_ASSERT(this, IsNumberNonNegativeSafeInteger(index));
1532 :
1533 : // Dispatch based on the type of the {array}.
1534 56 : TNode<Map> array_map = LoadMap(array);
1535 56 : TNode<Int32T> array_type = LoadMapInstanceType(array_map);
1536 112 : GotoIf(InstanceTypeEqual(array_type, JS_ARRAY_TYPE), &if_array);
1537 112 : Branch(InstanceTypeEqual(array_type, JS_TYPED_ARRAY_TYPE), &if_typedarray,
1538 56 : &if_other);
1539 :
1540 56 : BIND(&if_array);
1541 : {
1542 : // If {array} is a JSArray, then the {index} must be in Unsigned32 range.
1543 : CSA_ASSERT(this, IsNumberArrayIndex(index));
1544 :
1545 : // Check that the {index} is within range for the {array}. We handle all
1546 : // kinds of JSArray's here, so we do the computation on Uint32.
1547 56 : TNode<Uint32T> index32 = ChangeNumberToUint32(index);
1548 : TNode<Uint32T> length32 =
1549 56 : ChangeNumberToUint32(LoadJSArrayLength(CAST(array)));
1550 112 : GotoIfNot(Uint32LessThan(index32, length32), &set_done);
1551 : StoreObjectField(
1552 : iterator, JSArrayIterator::kNextIndexOffset,
1553 224 : ChangeUint32ToTagged(Unsigned(Int32Add(index32, Int32Constant(1)))));
1554 :
1555 112 : var_done.Bind(FalseConstant());
1556 56 : var_value.Bind(index);
1557 :
1558 224 : GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
1559 : iterator, JSArrayIterator::kKindOffset),
1560 168 : Int32Constant(static_cast<int>(IterationKind::kKeys))),
1561 56 : &allocate_iterator_result);
1562 :
1563 56 : Label if_hole(this, Label::kDeferred);
1564 56 : TNode<Int32T> elements_kind = LoadMapElementsKind(array_map);
1565 56 : TNode<FixedArrayBase> elements = LoadElements(CAST(array));
1566 56 : GotoIfForceSlowPath(&if_generic);
1567 112 : var_value.Bind(LoadFixedArrayBaseElementAsTagged(
1568 112 : elements, Signed(ChangeUint32ToWord(index32)), elements_kind,
1569 56 : &if_generic, &if_hole));
1570 56 : Goto(&allocate_entry_if_needed);
1571 :
1572 56 : BIND(&if_hole);
1573 : {
1574 112 : GotoIf(IsNoElementsProtectorCellInvalid(), &if_generic);
1575 112 : GotoIfNot(IsPrototypeInitialArrayPrototype(context, array_map),
1576 56 : &if_generic);
1577 112 : var_value.Bind(UndefinedConstant());
1578 56 : Goto(&allocate_entry_if_needed);
1579 : }
1580 : }
1581 :
1582 56 : BIND(&if_other);
1583 : {
1584 : // We cannot enter here with either JSArray's or JSTypedArray's.
1585 : CSA_ASSERT(this, Word32BinaryNot(IsJSArray(array)));
1586 : CSA_ASSERT(this, Word32BinaryNot(IsJSTypedArray(array)));
1587 :
1588 : // Check that the {index} is within the bounds of the {array}s "length".
1589 168 : TNode<Number> length = CAST(
1590 : CallBuiltin(Builtins::kToLength, context,
1591 : GetProperty(context, array, factory()->length_string())));
1592 56 : GotoIfNumberGreaterThanOrEqual(index, length, &set_done);
1593 : StoreObjectField(iterator, JSArrayIterator::kNextIndexOffset,
1594 112 : NumberInc(index));
1595 :
1596 112 : var_done.Bind(FalseConstant());
1597 56 : var_value.Bind(index);
1598 :
1599 224 : Branch(Word32Equal(LoadAndUntagToWord32ObjectField(
1600 : iterator, JSArrayIterator::kKindOffset),
1601 168 : Int32Constant(static_cast<int>(IterationKind::kKeys))),
1602 56 : &allocate_iterator_result, &if_generic);
1603 : }
1604 :
1605 56 : BIND(&set_done);
1606 : {
1607 : // Change the [[ArrayIteratorNextIndex]] such that the {iterator} will
1608 : // never produce values anymore, because it will always fail the bounds
1609 : // check. Note that this is different from what the specification does,
1610 : // which is changing the [[IteratedObject]] to undefined, because leaving
1611 : // [[IteratedObject]] alone helps TurboFan to generate better code with
1612 : // the inlining in JSCallReducer::ReduceArrayIteratorPrototypeNext().
1613 : //
1614 : // The terminal value we chose here depends on the type of the {array},
1615 : // for JSArray's we use kMaxUInt32 so that TurboFan can always use
1616 : // Word32 representation for fast-path indices (and this is safe since
1617 : // the "length" of JSArray's is limited to Unsigned32 range). For other
1618 : // JSReceiver's we have to use kMaxSafeInteger, since the "length" can
1619 : // be any arbitrary value in the safe integer range.
1620 : //
1621 : // Note specifically that JSTypedArray's will never take this path, so
1622 : // we don't need to worry about their maximum value.
1623 : CSA_ASSERT(this, Word32BinaryNot(IsJSTypedArray(array)));
1624 : TNode<Number> max_length =
1625 : SelectConstant(IsJSArray(array), NumberConstant(kMaxUInt32),
1626 168 : NumberConstant(kMaxSafeInteger));
1627 56 : StoreObjectField(iterator, JSArrayIterator::kNextIndexOffset, max_length);
1628 56 : Goto(&allocate_iterator_result);
1629 : }
1630 :
1631 56 : BIND(&if_generic);
1632 : {
1633 56 : var_value.Bind(GetProperty(context, array, index));
1634 56 : Goto(&allocate_entry_if_needed);
1635 : }
1636 :
1637 56 : BIND(&if_typedarray);
1638 : {
1639 : // If {array} is a JSTypedArray, the {index} must always be a Smi.
1640 : CSA_ASSERT(this, TaggedIsSmi(index));
1641 :
1642 : // Check that the {array}s buffer wasn't detached.
1643 56 : ThrowIfArrayBufferViewBufferIsDetached(context, CAST(array), method_name);
1644 :
1645 : // If we go outside of the {length}, we don't need to update the
1646 : // [[ArrayIteratorNextIndex]] anymore, since a JSTypedArray's
1647 : // length cannot change anymore, so this {iterator} will never
1648 : // produce values again anyways.
1649 56 : TNode<Smi> length = LoadJSTypedArrayLength(CAST(array));
1650 112 : GotoIfNot(SmiBelow(CAST(index), length), &allocate_iterator_result);
1651 : StoreObjectFieldNoWriteBarrier(iterator, JSArrayIterator::kNextIndexOffset,
1652 112 : SmiInc(CAST(index)));
1653 :
1654 112 : var_done.Bind(FalseConstant());
1655 56 : var_value.Bind(index);
1656 :
1657 224 : GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
1658 : iterator, JSArrayIterator::kKindOffset),
1659 168 : Int32Constant(static_cast<int>(IterationKind::kKeys))),
1660 56 : &allocate_iterator_result);
1661 :
1662 56 : TNode<Int32T> elements_kind = LoadMapElementsKind(array_map);
1663 : Node* elements = LoadElements(CAST(array));
1664 : Node* base_ptr =
1665 : LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset);
1666 : Node* external_ptr =
1667 56 : LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset,
1668 56 : MachineType::Pointer());
1669 : TNode<WordT> data_ptr =
1670 112 : IntPtrAdd(BitcastTaggedToWord(base_ptr), external_ptr);
1671 112 : var_value.Bind(LoadFixedTypedArrayElementAsTagged(data_ptr, CAST(index),
1672 56 : elements_kind));
1673 56 : Goto(&allocate_entry_if_needed);
1674 : }
1675 :
1676 56 : BIND(&allocate_entry_if_needed);
1677 : {
1678 224 : GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
1679 : iterator, JSArrayIterator::kKindOffset),
1680 168 : Int32Constant(static_cast<int>(IterationKind::kValues))),
1681 56 : &allocate_iterator_result);
1682 :
1683 : Node* result =
1684 56 : AllocateJSIteratorResultForEntry(context, index, var_value.value());
1685 56 : Return(result);
1686 : }
1687 :
1688 56 : BIND(&allocate_iterator_result);
1689 : {
1690 : Node* result =
1691 56 : AllocateJSIteratorResult(context, var_value.value(), var_done.value());
1692 56 : Return(result);
1693 : }
1694 56 : }
1695 :
1696 112 : class ArrayFlattenAssembler : public CodeStubAssembler {
1697 : public:
1698 : explicit ArrayFlattenAssembler(compiler::CodeAssemblerState* state)
1699 112 : : CodeStubAssembler(state) {}
1700 :
1701 : // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
1702 112 : Node* FlattenIntoArray(Node* context, Node* target, Node* source,
1703 : Node* source_length, Node* start, Node* depth,
1704 : Node* mapper_function = nullptr,
1705 : Node* this_arg = nullptr) {
1706 : CSA_ASSERT(this, IsJSReceiver(target));
1707 : CSA_ASSERT(this, IsJSReceiver(source));
1708 : CSA_ASSERT(this, IsNumberPositive(source_length));
1709 : CSA_ASSERT(this, IsNumberPositive(start));
1710 : CSA_ASSERT(this, IsNumber(depth));
1711 :
1712 : // 1. Let targetIndex be start.
1713 224 : VARIABLE(var_target_index, MachineRepresentation::kTagged, start);
1714 :
1715 : // 2. Let sourceIndex be 0.
1716 336 : VARIABLE(var_source_index, MachineRepresentation::kTagged, SmiConstant(0));
1717 :
1718 : // 3. Repeat...
1719 336 : Label loop(this, {&var_target_index, &var_source_index}), done_loop(this);
1720 112 : Goto(&loop);
1721 112 : BIND(&loop);
1722 : {
1723 112 : Node* const source_index = var_source_index.value();
1724 112 : Node* const target_index = var_target_index.value();
1725 :
1726 : // ...while sourceIndex < sourceLen
1727 112 : GotoIfNumberGreaterThanOrEqual(source_index, source_length, &done_loop);
1728 :
1729 : // a. Let P be ! ToString(sourceIndex).
1730 : // b. Let exists be ? HasProperty(source, P).
1731 : CSA_ASSERT(this,
1732 : SmiGreaterThanOrEqual(CAST(source_index), SmiConstant(0)));
1733 : Node* const exists =
1734 224 : HasProperty(context, source, source_index, kHasProperty);
1735 :
1736 : // c. If exists is true, then
1737 112 : Label next(this);
1738 224 : GotoIfNot(IsTrue(exists), &next);
1739 : {
1740 : // i. Let element be ? Get(source, P).
1741 : Node* element = GetProperty(context, source, source_index);
1742 :
1743 : // ii. If mapperFunction is present, then
1744 112 : if (mapper_function != nullptr) {
1745 : CSA_ASSERT(this, Word32Or(IsUndefined(mapper_function),
1746 : IsCallable(mapper_function)));
1747 : DCHECK_NOT_NULL(this_arg);
1748 :
1749 : // 1. Set element to ? Call(mapperFunction, thisArg , « element,
1750 : // sourceIndex, source »).
1751 : element =
1752 112 : CallJS(CodeFactory::Call(isolate()), context, mapper_function,
1753 56 : this_arg, element, source_index, source);
1754 : }
1755 :
1756 : // iii. Let shouldFlatten be false.
1757 112 : Label if_flatten_array(this), if_flatten_proxy(this, Label::kDeferred),
1758 112 : if_noflatten(this);
1759 : // iv. If depth > 0, then
1760 224 : GotoIfNumberGreaterThanOrEqual(SmiConstant(0), depth, &if_noflatten);
1761 : // 1. Set shouldFlatten to ? IsArray(element).
1762 224 : GotoIf(TaggedIsSmi(element), &if_noflatten);
1763 224 : GotoIf(IsJSArray(element), &if_flatten_array);
1764 224 : GotoIfNot(IsJSProxy(element), &if_noflatten);
1765 224 : Branch(IsTrue(CallRuntime(Runtime::kArrayIsArray, context, element)),
1766 112 : &if_flatten_proxy, &if_noflatten);
1767 :
1768 112 : BIND(&if_flatten_array);
1769 : {
1770 : CSA_ASSERT(this, IsJSArray(element));
1771 :
1772 : // 1. Let elementLen be ? ToLength(? Get(element, "length")).
1773 : Node* const element_length =
1774 : LoadObjectField(element, JSArray::kLengthOffset);
1775 :
1776 : // 2. Set targetIndex to ? FlattenIntoArray(target, element,
1777 : // elementLen, targetIndex,
1778 : // depth - 1).
1779 : var_target_index.Bind(
1780 224 : CallBuiltin(Builtins::kFlattenIntoArray, context, target, element,
1781 336 : element_length, target_index, NumberDec(depth)));
1782 112 : Goto(&next);
1783 : }
1784 :
1785 112 : BIND(&if_flatten_proxy);
1786 : {
1787 : CSA_ASSERT(this, IsJSProxy(element));
1788 :
1789 : // 1. Let elementLen be ? ToLength(? Get(element, "length")).
1790 224 : Node* const element_length = ToLength_Inline(
1791 224 : context, GetProperty(context, element, LengthStringConstant()));
1792 :
1793 : // 2. Set targetIndex to ? FlattenIntoArray(target, element,
1794 : // elementLen, targetIndex,
1795 : // depth - 1).
1796 : var_target_index.Bind(
1797 224 : CallBuiltin(Builtins::kFlattenIntoArray, context, target, element,
1798 336 : element_length, target_index, NumberDec(depth)));
1799 112 : Goto(&next);
1800 : }
1801 :
1802 112 : BIND(&if_noflatten);
1803 : {
1804 : // 1. If targetIndex >= 2^53-1, throw a TypeError exception.
1805 112 : Label throw_error(this, Label::kDeferred);
1806 : GotoIfNumberGreaterThanOrEqual(
1807 224 : target_index, NumberConstant(kMaxSafeInteger), &throw_error);
1808 :
1809 : // 2. Perform ? CreateDataPropertyOrThrow(target,
1810 : // ! ToString(targetIndex),
1811 : // element).
1812 : CallRuntime(Runtime::kCreateDataProperty, context, target,
1813 : target_index, element);
1814 :
1815 : // 3. Increase targetIndex by 1.
1816 224 : var_target_index.Bind(NumberInc(target_index));
1817 112 : Goto(&next);
1818 :
1819 112 : BIND(&throw_error);
1820 : ThrowTypeError(context, MessageTemplate::kFlattenPastSafeLength,
1821 112 : source_length, target_index);
1822 : }
1823 : }
1824 112 : BIND(&next);
1825 :
1826 : // d. Increase sourceIndex by 1.
1827 224 : var_source_index.Bind(NumberInc(source_index));
1828 112 : Goto(&loop);
1829 : }
1830 :
1831 112 : BIND(&done_loop);
1832 224 : return var_target_index.value();
1833 : }
1834 : };
1835 :
1836 : // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
1837 224 : TF_BUILTIN(FlattenIntoArray, ArrayFlattenAssembler) {
1838 : Node* const context = Parameter(Descriptor::kContext);
1839 : Node* const target = Parameter(Descriptor::kTarget);
1840 : Node* const source = Parameter(Descriptor::kSource);
1841 : Node* const source_length = Parameter(Descriptor::kSourceLength);
1842 : Node* const start = Parameter(Descriptor::kStart);
1843 : Node* const depth = Parameter(Descriptor::kDepth);
1844 :
1845 : // FlattenIntoArray might get called recursively, check stack for overflow
1846 : // manually as it has stub linkage.
1847 56 : PerformStackCheck(CAST(context));
1848 :
1849 112 : Return(
1850 56 : FlattenIntoArray(context, target, source, source_length, start, depth));
1851 56 : }
1852 :
1853 : // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
1854 224 : TF_BUILTIN(FlatMapIntoArray, ArrayFlattenAssembler) {
1855 : Node* const context = Parameter(Descriptor::kContext);
1856 : Node* const target = Parameter(Descriptor::kTarget);
1857 : Node* const source = Parameter(Descriptor::kSource);
1858 : Node* const source_length = Parameter(Descriptor::kSourceLength);
1859 : Node* const start = Parameter(Descriptor::kStart);
1860 : Node* const depth = Parameter(Descriptor::kDepth);
1861 : Node* const mapper_function = Parameter(Descriptor::kMapperFunction);
1862 : Node* const this_arg = Parameter(Descriptor::kThisArg);
1863 :
1864 112 : Return(FlattenIntoArray(context, target, source, source_length, start, depth,
1865 56 : mapper_function, this_arg));
1866 56 : }
1867 :
1868 : // https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flat
1869 224 : TF_BUILTIN(ArrayPrototypeFlat, CodeStubAssembler) {
1870 : TNode<IntPtrT> const argc =
1871 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
1872 56 : CodeStubArguments args(this, argc);
1873 : TNode<Context> const context = CAST(Parameter(Descriptor::kContext));
1874 56 : TNode<Object> const receiver = args.GetReceiver();
1875 56 : TNode<Object> const depth = args.GetOptionalArgumentValue(0);
1876 :
1877 : // 1. Let O be ? ToObject(this value).
1878 56 : TNode<JSReceiver> const o = ToObject_Inline(context, receiver);
1879 :
1880 : // 2. Let sourceLen be ? ToLength(? Get(O, "length")).
1881 : TNode<Number> const source_length =
1882 112 : ToLength_Inline(context, GetProperty(context, o, LengthStringConstant()));
1883 :
1884 : // 3. Let depthNum be 1.
1885 112 : TVARIABLE(Number, var_depth_num, SmiConstant(1));
1886 :
1887 : // 4. If depth is not undefined, then
1888 56 : Label done(this);
1889 112 : GotoIf(IsUndefined(depth), &done);
1890 : {
1891 : // a. Set depthNum to ? ToInteger(depth).
1892 112 : var_depth_num = ToInteger_Inline(context, depth);
1893 56 : Goto(&done);
1894 : }
1895 56 : BIND(&done);
1896 :
1897 : // 5. Let A be ? ArraySpeciesCreate(O, 0).
1898 : TNode<JSReceiver> const constructor =
1899 : CAST(CallRuntime(Runtime::kArraySpeciesConstructor, context, o));
1900 56 : Node* const a = Construct(context, constructor, SmiConstant(0));
1901 :
1902 : // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum).
1903 : CallBuiltin(Builtins::kFlattenIntoArray, context, a, o, source_length,
1904 112 : SmiConstant(0), var_depth_num.value());
1905 :
1906 : // 7. Return A.
1907 56 : args.PopAndReturn(a);
1908 56 : }
1909 :
1910 : // https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap
1911 224 : TF_BUILTIN(ArrayPrototypeFlatMap, CodeStubAssembler) {
1912 : TNode<IntPtrT> const argc =
1913 56 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
1914 56 : CodeStubArguments args(this, argc);
1915 : TNode<Context> const context = CAST(Parameter(Descriptor::kContext));
1916 56 : TNode<Object> const receiver = args.GetReceiver();
1917 56 : TNode<Object> const mapper_function = args.GetOptionalArgumentValue(0);
1918 :
1919 : // 1. Let O be ? ToObject(this value).
1920 56 : TNode<JSReceiver> const o = ToObject_Inline(context, receiver);
1921 :
1922 : // 2. Let sourceLen be ? ToLength(? Get(O, "length")).
1923 : TNode<Number> const source_length =
1924 112 : ToLength_Inline(context, GetProperty(context, o, LengthStringConstant()));
1925 :
1926 : // 3. If IsCallable(mapperFunction) is false, throw a TypeError exception.
1927 56 : Label if_not_callable(this, Label::kDeferred);
1928 112 : GotoIf(TaggedIsSmi(mapper_function), &if_not_callable);
1929 112 : GotoIfNot(IsCallable(CAST(mapper_function)), &if_not_callable);
1930 :
1931 : // 4. If thisArg is present, let T be thisArg; else let T be undefined.
1932 56 : TNode<Object> const t = args.GetOptionalArgumentValue(1);
1933 :
1934 : // 5. Let A be ? ArraySpeciesCreate(O, 0).
1935 : TNode<JSReceiver> const constructor =
1936 : CAST(CallRuntime(Runtime::kArraySpeciesConstructor, context, o));
1937 56 : TNode<JSReceiver> const a = Construct(context, constructor, SmiConstant(0));
1938 :
1939 : // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T).
1940 : CallBuiltin(Builtins::kFlatMapIntoArray, context, a, o, source_length,
1941 112 : SmiConstant(0), SmiConstant(1), mapper_function, t);
1942 :
1943 : // 7. Return A.
1944 56 : args.PopAndReturn(a);
1945 :
1946 56 : BIND(&if_not_callable);
1947 56 : { ThrowTypeError(context, MessageTemplate::kMapperFunctionNonCallable); }
1948 56 : }
1949 :
1950 112 : TF_BUILTIN(ArrayConstructor, ArrayBuiltinsAssembler) {
1951 : // This is a trampoline to ArrayConstructorImpl which just adds
1952 : // allocation_site parameter value and sets new_target if necessary.
1953 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1954 : TNode<JSFunction> function = CAST(Parameter(Descriptor::kTarget));
1955 : TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget));
1956 : TNode<Int32T> argc =
1957 56 : UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
1958 :
1959 : // If new_target is undefined, then this is the 'Call' case, so set new_target
1960 : // to function.
1961 112 : new_target =
1962 : SelectConstant<Object>(IsUndefined(new_target), function, new_target);
1963 :
1964 : // Run the native code for the Array function called as a normal function.
1965 112 : TNode<Object> no_allocation_site = UndefinedConstant();
1966 56 : TailCallBuiltin(Builtins::kArrayConstructorImpl, context, function,
1967 56 : new_target, argc, no_allocation_site);
1968 56 : }
1969 :
1970 784 : void ArrayBuiltinsAssembler::TailCallArrayConstructorStub(
1971 : const Callable& callable, TNode<Context> context, TNode<JSFunction> target,
1972 : TNode<HeapObject> allocation_site_or_undefined, TNode<Int32T> argc) {
1973 784 : TNode<Code> code = HeapConstant(callable.code());
1974 :
1975 : // We are going to call here ArrayNoArgumentsConstructor or
1976 : // ArraySingleArgumentsConstructor which in addition to the register arguments
1977 : // also expect some number of arguments on the expression stack.
1978 : // Since
1979 : // 1) incoming JS arguments are still on the stack,
1980 : // 2) the ArrayNoArgumentsConstructor, ArraySingleArgumentsConstructor and
1981 : // ArrayNArgumentsConstructor are defined so that the register arguments
1982 : // are passed on the same registers,
1983 : // in order to be able to generate a tail call to those builtins we do the
1984 : // following trick here: we tail call to the constructor builtin using
1985 : // ArrayNArgumentsConstructorDescriptor, so the tail call instruction
1986 : // pops the current frame but leaves all the incoming JS arguments on the
1987 : // expression stack so that the target builtin can still find them where it
1988 : // expects.
1989 784 : TailCallStub(ArrayNArgumentsConstructorDescriptor{}, code, context, target,
1990 : allocation_site_or_undefined, argc);
1991 784 : }
1992 :
1993 112 : void ArrayBuiltinsAssembler::CreateArrayDispatchNoArgument(
1994 : TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc,
1995 : AllocationSiteOverrideMode mode, TNode<AllocationSite> allocation_site) {
1996 112 : if (mode == DISABLE_ALLOCATION_SITES) {
1997 : Callable callable = CodeFactory::ArrayNoArgumentConstructor(
1998 56 : isolate(), GetInitialFastElementsKind(), mode);
1999 :
2000 112 : TailCallArrayConstructorStub(callable, context, target, UndefinedConstant(),
2001 56 : argc);
2002 : } else {
2003 : DCHECK_EQ(mode, DONT_OVERRIDE);
2004 56 : TNode<Int32T> elements_kind = LoadElementsKind(allocation_site);
2005 :
2006 : // TODO(ishell): Compute the builtin index dynamically instead of
2007 : // iterating over all expected elements kinds.
2008 : int last_index =
2009 56 : GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND);
2010 728 : for (int i = 0; i <= last_index; ++i) {
2011 672 : Label next(this);
2012 336 : ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
2013 1008 : GotoIfNot(Word32Equal(elements_kind, Int32Constant(kind)), &next);
2014 :
2015 : Callable callable =
2016 336 : CodeFactory::ArrayNoArgumentConstructor(isolate(), kind, mode);
2017 :
2018 336 : TailCallArrayConstructorStub(callable, context, target, allocation_site,
2019 336 : argc);
2020 :
2021 336 : BIND(&next);
2022 : }
2023 :
2024 : // If we reached this point there is a problem.
2025 56 : Abort(AbortReason::kUnexpectedElementsKindInArrayConstructor);
2026 : }
2027 112 : }
2028 :
2029 112 : void ArrayBuiltinsAssembler::CreateArrayDispatchSingleArgument(
2030 : TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc,
2031 : AllocationSiteOverrideMode mode, TNode<AllocationSite> allocation_site) {
2032 112 : if (mode == DISABLE_ALLOCATION_SITES) {
2033 : ElementsKind initial = GetInitialFastElementsKind();
2034 : ElementsKind holey_initial = GetHoleyElementsKind(initial);
2035 : Callable callable = CodeFactory::ArraySingleArgumentConstructor(
2036 56 : isolate(), holey_initial, mode);
2037 :
2038 112 : TailCallArrayConstructorStub(callable, context, target, UndefinedConstant(),
2039 56 : argc);
2040 : } else {
2041 : DCHECK_EQ(mode, DONT_OVERRIDE);
2042 56 : TNode<Smi> transition_info = LoadTransitionInfo(allocation_site);
2043 :
2044 : // Least significant bit in fast array elements kind means holeyness.
2045 : STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
2046 : STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
2047 : STATIC_ASSERT(PACKED_ELEMENTS == 2);
2048 : STATIC_ASSERT(HOLEY_ELEMENTS == 3);
2049 : STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
2050 : STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
2051 :
2052 112 : Label normal_sequence(this);
2053 112 : TVARIABLE(Int32T, var_elements_kind,
2054 : Signed(DecodeWord32<AllocationSite::ElementsKindBits>(
2055 : SmiToInt32(transition_info))));
2056 : // Is the low bit set? If so, we are holey and that is good.
2057 : int fast_elements_kind_holey_mask =
2058 : AllocationSite::ElementsKindBits::encode(static_cast<ElementsKind>(1));
2059 112 : GotoIf(IsSetSmi(transition_info, fast_elements_kind_holey_mask),
2060 56 : &normal_sequence);
2061 : {
2062 : // Make elements kind holey and update elements kind in the type info.
2063 112 : var_elements_kind =
2064 112 : Signed(Word32Or(var_elements_kind.value(), Int32Constant(1)));
2065 56 : StoreObjectFieldNoWriteBarrier(
2066 : allocation_site, AllocationSite::kTransitionInfoOrBoilerplateOffset,
2067 : SmiOr(transition_info, SmiConstant(fast_elements_kind_holey_mask)));
2068 56 : Goto(&normal_sequence);
2069 : }
2070 56 : BIND(&normal_sequence);
2071 :
2072 : // TODO(ishell): Compute the builtin index dynamically instead of
2073 : // iterating over all expected elements kinds.
2074 : // TODO(ishell): Given that the code above ensures that the elements kind
2075 : // is holey we can skip checking with non-holey elements kinds.
2076 : int last_index =
2077 56 : GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND);
2078 728 : for (int i = 0; i <= last_index; ++i) {
2079 336 : Label next(this);
2080 336 : ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
2081 1008 : GotoIfNot(Word32Equal(var_elements_kind.value(), Int32Constant(kind)),
2082 336 : &next);
2083 :
2084 : Callable callable =
2085 336 : CodeFactory::ArraySingleArgumentConstructor(isolate(), kind, mode);
2086 :
2087 336 : TailCallArrayConstructorStub(callable, context, target, allocation_site,
2088 336 : argc);
2089 :
2090 336 : BIND(&next);
2091 : }
2092 :
2093 : // If we reached this point there is a problem.
2094 56 : Abort(AbortReason::kUnexpectedElementsKindInArrayConstructor);
2095 : }
2096 112 : }
2097 :
2098 112 : void ArrayBuiltinsAssembler::GenerateDispatchToArrayStub(
2099 : TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc,
2100 : AllocationSiteOverrideMode mode, TNode<AllocationSite> allocation_site) {
2101 224 : Label check_one_case(this), fallthrough(this);
2102 336 : GotoIfNot(Word32Equal(argc, Int32Constant(0)), &check_one_case);
2103 112 : CreateArrayDispatchNoArgument(context, target, argc, mode, allocation_site);
2104 :
2105 112 : BIND(&check_one_case);
2106 336 : GotoIfNot(Word32Equal(argc, Int32Constant(1)), &fallthrough);
2107 : CreateArrayDispatchSingleArgument(context, target, argc, mode,
2108 112 : allocation_site);
2109 :
2110 112 : BIND(&fallthrough);
2111 112 : }
2112 :
2113 168 : TF_BUILTIN(ArrayConstructorImpl, ArrayBuiltinsAssembler) {
2114 : TNode<JSFunction> target = CAST(Parameter(Descriptor::kTarget));
2115 : TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget));
2116 : TNode<Int32T> argc =
2117 56 : UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
2118 : TNode<HeapObject> maybe_allocation_site =
2119 : CAST(Parameter(Descriptor::kAllocationSite));
2120 :
2121 : // Initial map for the builtin Array functions should be Map.
2122 : CSA_ASSERT(this, IsMap(CAST(LoadObjectField(
2123 : target, JSFunction::kPrototypeOrInitialMapOffset))));
2124 :
2125 : // We should either have undefined or a valid AllocationSite
2126 : CSA_ASSERT(this, Word32Or(IsUndefined(maybe_allocation_site),
2127 : IsAllocationSite(maybe_allocation_site)));
2128 :
2129 : // "Enter" the context of the Array function.
2130 : TNode<Context> context =
2131 112 : CAST(LoadObjectField(target, JSFunction::kContextOffset));
2132 :
2133 56 : Label runtime(this, Label::kDeferred);
2134 56 : GotoIf(WordNotEqual(target, new_target), &runtime);
2135 :
2136 56 : Label no_info(this);
2137 : // If the feedback vector is the undefined value call an array constructor
2138 : // that doesn't use AllocationSites.
2139 112 : GotoIf(IsUndefined(maybe_allocation_site), &no_info);
2140 :
2141 56 : GenerateDispatchToArrayStub(context, target, argc, DONT_OVERRIDE,
2142 56 : CAST(maybe_allocation_site));
2143 56 : Goto(&runtime);
2144 :
2145 56 : BIND(&no_info);
2146 56 : GenerateDispatchToArrayStub(context, target, argc, DISABLE_ALLOCATION_SITES);
2147 56 : Goto(&runtime);
2148 :
2149 56 : BIND(&runtime);
2150 : GenerateArrayNArgumentsConstructor(context, target, new_target, argc,
2151 56 : maybe_allocation_site);
2152 56 : }
2153 :
2154 448 : void ArrayBuiltinsAssembler::GenerateConstructor(
2155 : Node* context, Node* array_function, Node* array_map, Node* array_size,
2156 : Node* allocation_site, ElementsKind elements_kind,
2157 : AllocationSiteMode mode) {
2158 896 : Label ok(this);
2159 448 : Label smi_size(this);
2160 448 : Label small_smi_size(this);
2161 448 : Label call_runtime(this, Label::kDeferred);
2162 :
2163 896 : Branch(TaggedIsSmi(array_size), &smi_size, &call_runtime);
2164 :
2165 448 : BIND(&smi_size);
2166 :
2167 448 : if (IsFastPackedElementsKind(elements_kind)) {
2168 224 : Label abort(this, Label::kDeferred);
2169 448 : Branch(SmiEqual(CAST(array_size), SmiConstant(0)), &small_smi_size, &abort);
2170 :
2171 224 : BIND(&abort);
2172 : Node* reason = SmiConstant(AbortReason::kAllocatingNonEmptyPackedArray);
2173 224 : TailCallRuntime(Runtime::kAbort, context, reason);
2174 : } else {
2175 : int element_size =
2176 : IsDoubleElementsKind(elements_kind) ? kDoubleSize : kTaggedSize;
2177 : int max_fast_elements =
2178 : (kMaxRegularHeapObjectSize - FixedArray::kHeaderSize - JSArray::kSize -
2179 : AllocationMemento::kSize) /
2180 : element_size;
2181 448 : Branch(SmiAboveOrEqual(CAST(array_size), SmiConstant(max_fast_elements)),
2182 224 : &call_runtime, &small_smi_size);
2183 : }
2184 :
2185 448 : BIND(&small_smi_size);
2186 : {
2187 : TNode<JSArray> array = AllocateJSArray(
2188 : elements_kind, CAST(array_map), array_size, CAST(array_size),
2189 : mode == DONT_TRACK_ALLOCATION_SITE ? nullptr : allocation_site,
2190 448 : CodeStubAssembler::SMI_PARAMETERS);
2191 448 : Return(array);
2192 : }
2193 :
2194 448 : BIND(&call_runtime);
2195 : {
2196 448 : TailCallRuntime(Runtime::kNewArray, context, array_function, array_size,
2197 448 : array_function, allocation_site);
2198 : }
2199 448 : }
2200 :
2201 448 : void ArrayBuiltinsAssembler::GenerateArrayNoArgumentConstructor(
2202 : ElementsKind kind, AllocationSiteOverrideMode mode) {
2203 : typedef ArrayNoArgumentConstructorDescriptor Descriptor;
2204 : Node* native_context = LoadObjectField(Parameter(Descriptor::kFunction),
2205 448 : JSFunction::kContextOffset);
2206 : bool track_allocation_site =
2207 448 : AllocationSite::ShouldTrack(kind) && mode != DISABLE_ALLOCATION_SITES;
2208 : Node* allocation_site =
2209 448 : track_allocation_site ? Parameter(Descriptor::kAllocationSite) : nullptr;
2210 448 : TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
2211 : TNode<JSArray> array = AllocateJSArray(
2212 896 : kind, array_map, IntPtrConstant(JSArray::kPreallocatedArrayElements),
2213 1344 : SmiConstant(0), allocation_site);
2214 448 : Return(array);
2215 448 : }
2216 :
2217 448 : void ArrayBuiltinsAssembler::GenerateArraySingleArgumentConstructor(
2218 : ElementsKind kind, AllocationSiteOverrideMode mode) {
2219 : typedef ArraySingleArgumentConstructorDescriptor Descriptor;
2220 448 : Node* context = Parameter(Descriptor::kContext);
2221 448 : Node* function = Parameter(Descriptor::kFunction);
2222 448 : Node* native_context = LoadObjectField(function, JSFunction::kContextOffset);
2223 896 : Node* array_map = LoadJSArrayElementsMap(kind, native_context);
2224 :
2225 : AllocationSiteMode allocation_site_mode = DONT_TRACK_ALLOCATION_SITE;
2226 448 : if (mode == DONT_OVERRIDE) {
2227 : allocation_site_mode = AllocationSite::ShouldTrack(kind)
2228 : ? TRACK_ALLOCATION_SITE
2229 112 : : DONT_TRACK_ALLOCATION_SITE;
2230 : }
2231 :
2232 448 : Node* array_size = Parameter(Descriptor::kArraySizeSmiParameter);
2233 448 : Node* allocation_site = Parameter(Descriptor::kAllocationSite);
2234 :
2235 : GenerateConstructor(context, function, array_map, array_size, allocation_site,
2236 448 : kind, allocation_site_mode);
2237 448 : }
2238 :
2239 112 : void ArrayBuiltinsAssembler::GenerateArrayNArgumentsConstructor(
2240 : TNode<Context> context, TNode<JSFunction> target, TNode<Object> new_target,
2241 : TNode<Int32T> argc, TNode<HeapObject> maybe_allocation_site) {
2242 : // Replace incoming JS receiver argument with the target.
2243 : // TODO(ishell): Avoid replacing the target on the stack and just add it
2244 : // as another additional parameter for Runtime::kNewArray.
2245 336 : CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
2246 112 : args.SetReceiver(target);
2247 :
2248 : // Adjust arguments count for the runtime call: +1 for implicit receiver
2249 : // and +2 for new_target and maybe_allocation_site.
2250 112 : argc = Int32Add(argc, Int32Constant(3));
2251 : TailCallRuntime(Runtime::kNewArray, argc, context, new_target,
2252 : maybe_allocation_site);
2253 112 : }
2254 :
2255 224 : TF_BUILTIN(ArrayNArgumentsConstructor, ArrayBuiltinsAssembler) {
2256 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2257 : TNode<JSFunction> target = CAST(Parameter(Descriptor::kFunction));
2258 : TNode<Int32T> argc =
2259 56 : UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
2260 : TNode<HeapObject> maybe_allocation_site =
2261 56 : CAST(Parameter(Descriptor::kAllocationSite));
2262 :
2263 112 : GenerateArrayNArgumentsConstructor(context, target, target, argc,
2264 56 : maybe_allocation_site);
2265 56 : }
2266 :
2267 : #define GENERATE_ARRAY_CTOR(name, kind_camel, kind_caps, mode_camel, \
2268 : mode_caps) \
2269 : TF_BUILTIN(Array##name##Constructor_##kind_camel##_##mode_camel, \
2270 : ArrayBuiltinsAssembler) { \
2271 : GenerateArray##name##Constructor(kind_caps, mode_caps); \
2272 : }
2273 :
2274 : // The ArrayNoArgumentConstructor builtin family.
2275 56 : GENERATE_ARRAY_CTOR(NoArgument, PackedSmi, PACKED_SMI_ELEMENTS, DontOverride,
2276 : DONT_OVERRIDE)
2277 56 : GENERATE_ARRAY_CTOR(NoArgument, HoleySmi, HOLEY_SMI_ELEMENTS, DontOverride,
2278 : DONT_OVERRIDE)
2279 56 : GENERATE_ARRAY_CTOR(NoArgument, PackedSmi, PACKED_SMI_ELEMENTS,
2280 : DisableAllocationSites, DISABLE_ALLOCATION_SITES)
2281 56 : GENERATE_ARRAY_CTOR(NoArgument, HoleySmi, HOLEY_SMI_ELEMENTS,
2282 : DisableAllocationSites, DISABLE_ALLOCATION_SITES)
2283 56 : GENERATE_ARRAY_CTOR(NoArgument, Packed, PACKED_ELEMENTS, DisableAllocationSites,
2284 : DISABLE_ALLOCATION_SITES)
2285 56 : GENERATE_ARRAY_CTOR(NoArgument, Holey, HOLEY_ELEMENTS, DisableAllocationSites,
2286 : DISABLE_ALLOCATION_SITES)
2287 56 : GENERATE_ARRAY_CTOR(NoArgument, PackedDouble, PACKED_DOUBLE_ELEMENTS,
2288 : DisableAllocationSites, DISABLE_ALLOCATION_SITES)
2289 56 : GENERATE_ARRAY_CTOR(NoArgument, HoleyDouble, HOLEY_DOUBLE_ELEMENTS,
2290 : DisableAllocationSites, DISABLE_ALLOCATION_SITES)
2291 :
2292 : // The ArraySingleArgumentConstructor builtin family.
2293 56 : GENERATE_ARRAY_CTOR(SingleArgument, PackedSmi, PACKED_SMI_ELEMENTS,
2294 : DontOverride, DONT_OVERRIDE)
2295 56 : GENERATE_ARRAY_CTOR(SingleArgument, HoleySmi, HOLEY_SMI_ELEMENTS, DontOverride,
2296 : DONT_OVERRIDE)
2297 56 : GENERATE_ARRAY_CTOR(SingleArgument, PackedSmi, PACKED_SMI_ELEMENTS,
2298 : DisableAllocationSites, DISABLE_ALLOCATION_SITES)
2299 56 : GENERATE_ARRAY_CTOR(SingleArgument, HoleySmi, HOLEY_SMI_ELEMENTS,
2300 : DisableAllocationSites, DISABLE_ALLOCATION_SITES)
2301 56 : GENERATE_ARRAY_CTOR(SingleArgument, Packed, PACKED_ELEMENTS,
2302 : DisableAllocationSites, DISABLE_ALLOCATION_SITES)
2303 56 : GENERATE_ARRAY_CTOR(SingleArgument, Holey, HOLEY_ELEMENTS,
2304 : DisableAllocationSites, DISABLE_ALLOCATION_SITES)
2305 56 : GENERATE_ARRAY_CTOR(SingleArgument, PackedDouble, PACKED_DOUBLE_ELEMENTS,
2306 : DisableAllocationSites, DISABLE_ALLOCATION_SITES)
2307 56 : GENERATE_ARRAY_CTOR(SingleArgument, HoleyDouble, HOLEY_DOUBLE_ELEMENTS,
2308 : DisableAllocationSites, DISABLE_ALLOCATION_SITES)
2309 :
2310 : #undef GENERATE_ARRAY_CTOR
2311 :
2312 112 : TF_BUILTIN(InternalArrayNoArgumentConstructor_Packed, ArrayBuiltinsAssembler) {
2313 : typedef ArrayNoArgumentConstructorDescriptor Descriptor;
2314 : TNode<Map> array_map =
2315 56 : CAST(LoadObjectField(Parameter(Descriptor::kFunction),
2316 : JSFunction::kPrototypeOrInitialMapOffset));
2317 : TNode<JSArray> array = AllocateJSArray(
2318 : PACKED_ELEMENTS, array_map,
2319 56 : IntPtrConstant(JSArray::kPreallocatedArrayElements), SmiConstant(0));
2320 56 : Return(array);
2321 56 : }
2322 :
2323 : } // namespace internal
2324 59456 : } // namespace v8
|