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