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), ¬_smi); 774 search_num = SmiToFloat64(CAST(search_element)); 775 Goto(&heap_num_loop); 776 777 BIND(¬_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), ¬_heap_num); 783 search_num = LoadHeapNumberValue(CAST(search_element)); 784 Goto(&heap_num_loop); 785 786 BIND(¬_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, ¬_nan_loop); 823 824 BIND(¬_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(¬_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(¬_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, ¬_nan_loop); 957 958 BIND(¬_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(¬_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(¬_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, ¬_nan_loop); 1028 1029 BIND(¬_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(¬_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