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