diff options
Diffstat (limited to 'deps/v8/src/builtins/builtins-array-gen.cc')
-rw-r--r-- | deps/v8/src/builtins/builtins-array-gen.cc | 1465 |
1 files changed, 873 insertions, 592 deletions
diff --git a/deps/v8/src/builtins/builtins-array-gen.cc b/deps/v8/src/builtins/builtins-array-gen.cc index aad31db8a1..8c95007622 100644 --- a/deps/v8/src/builtins/builtins-array-gen.cc +++ b/deps/v8/src/builtins/builtins-array-gen.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "src/builtins/builtins-string-gen.h" #include "src/builtins/builtins-utils-gen.h" #include "src/builtins/builtins.h" #include "src/code-stub-assembler.h" @@ -15,13 +16,11 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { : CodeStubAssembler(state), k_(this, MachineRepresentation::kTagged), a_(this, MachineRepresentation::kTagged), - to_(this, MachineRepresentation::kTagged, SmiConstant(0)) {} - - typedef std::function<Node*(ArrayBuiltinCodeStubAssembler* masm)> - BuiltinResultGenerator; + to_(this, MachineRepresentation::kTagged, SmiConstant(0)), + fully_spec_compliant_(this, {&k_, &a_, &to_}) {} typedef std::function<void(ArrayBuiltinCodeStubAssembler* masm)> - BuiltinResultIndexInitializer; + BuiltinResultGenerator; typedef std::function<Node*(ArrayBuiltinCodeStubAssembler* masm, Node* k_value, Node* k)> @@ -30,7 +29,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { typedef std::function<void(ArrayBuiltinCodeStubAssembler* masm)> PostLoopAction; - Node* ForEachResultGenerator() { return UndefinedConstant(); } + void ForEachResultGenerator() { a_.Bind(UndefinedConstant()); } Node* ForEachProcessor(Node* k_value, Node* k) { CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), this_arg(), @@ -38,7 +37,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { return a(); } - Node* SomeResultGenerator() { return FalseConstant(); } + void SomeResultGenerator() { a_.Bind(FalseConstant()); } Node* SomeProcessor(Node* k_value, Node* k) { Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), @@ -46,12 +45,12 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { Label false_continue(this), return_true(this); BranchIfToBooleanIsTrue(value, &return_true, &false_continue); BIND(&return_true); - Return(TrueConstant()); + ReturnFromBuiltin(TrueConstant()); BIND(&false_continue); return a(); } - Node* EveryResultGenerator() { return TrueConstant(); } + void EveryResultGenerator() { a_.Bind(TrueConstant()); } Node* EveryProcessor(Node* k_value, Node* k) { Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), @@ -59,44 +58,12 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { Label true_continue(this), return_false(this); BranchIfToBooleanIsTrue(value, &true_continue, &return_false); BIND(&return_false); - Return(FalseConstant()); + ReturnFromBuiltin(FalseConstant()); BIND(&true_continue); return a(); } - Node* ReduceResultGenerator() { - VARIABLE(a, MachineRepresentation::kTagged, UndefinedConstant()); - Label no_initial_value(this), has_initial_value(this), done(this, {&a}); - - // 8. If initialValue is present, then - Node* parent_frame_ptr = LoadParentFramePointer(); - Node* marker_or_function = LoadBufferObject( - parent_frame_ptr, CommonFrameConstants::kContextOrFrameTypeOffset); - GotoIf( - MarkerIsNotFrameType(marker_or_function, StackFrame::ARGUMENTS_ADAPTOR), - &has_initial_value); - - // Has arguments adapter, check count. - Node* adapted_parameter_count = LoadBufferObject( - parent_frame_ptr, ArgumentsAdaptorFrameConstants::kLengthOffset); - Branch(SmiLessThan(adapted_parameter_count, - SmiConstant(IteratingArrayBuiltinDescriptor::kThisArg)), - &no_initial_value, &has_initial_value); - - // a. Set accumulator to initialValue. - BIND(&has_initial_value); - a.Bind(this_arg()); - Goto(&done); - - // 9. Else initialValue is not present, - BIND(&no_initial_value); - - // a. Let kPresent be false. - a.Bind(TheHoleConstant()); - Goto(&done); - BIND(&done); - return a.value(); - } + void ReduceResultGenerator() { return a_.Bind(this_arg()); } Node* ReduceProcessor(Node* k_value, Node* k) { VARIABLE(result, MachineRepresentation::kTagged); @@ -123,9 +90,10 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { BIND(&ok); } - Node* FilterResultGenerator() { + void FilterResultGenerator() { // 7. Let A be ArraySpeciesCreate(O, 0). - return ArraySpeciesCreate(context(), o(), SmiConstant(0)); + Node* len = SmiConstant(0); + ArraySpeciesCreate(len); } Node* FilterProcessor(Node* k_value, Node* k) { @@ -137,25 +105,82 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { BIND(&true_continue); // iii. If selected is true, then... { - // 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue). - CallRuntime(Runtime::kCreateDataProperty, context(), a(), to_.value(), - k_value); + Label after_work(this, &to_); + Node* kind = nullptr; + + // If a() is a JSArray, we can have a fast path. + Label fast(this); + Label runtime(this); + Label object_push_pre(this), object_push(this), double_push(this); + BranchIfFastJSArray(a(), context(), FastJSArrayAccessMode::ANY_ACCESS, + &fast, &runtime); + + BIND(&fast); + { + kind = EnsureArrayPushable(a(), &runtime); + GotoIf(IsElementsKindGreaterThan(kind, FAST_HOLEY_SMI_ELEMENTS), + &object_push_pre); + + BuildAppendJSArray(FAST_SMI_ELEMENTS, a(), k_value, &runtime); + Goto(&after_work); + } + + BIND(&object_push_pre); + { + Branch(IsElementsKindGreaterThan(kind, FAST_HOLEY_ELEMENTS), + &double_push, &object_push); + } + + BIND(&object_push); + { + BuildAppendJSArray(FAST_ELEMENTS, a(), k_value, &runtime); + Goto(&after_work); + } + + BIND(&double_push); + { + BuildAppendJSArray(FAST_DOUBLE_ELEMENTS, a(), k_value, &runtime); + Goto(&after_work); + } + + BIND(&runtime); + { + // 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue). + CallRuntime(Runtime::kCreateDataProperty, context(), a(), to_.value(), + k_value); + Goto(&after_work); + } - // 2. Increase to by 1. - to_.Bind(NumberInc(to_.value())); - Goto(&false_continue); + BIND(&after_work); + { + // 2. Increase to by 1. + to_.Bind(NumberInc(to_.value())); + Goto(&false_continue); + } } BIND(&false_continue); return a(); } - Node* MapResultGenerator() { - // 5. Let A be ? ArraySpeciesCreate(O, len). - return ArraySpeciesCreate(context(), o(), len_); + void MapResultGenerator() { ArraySpeciesCreate(len_); } + + void TypedArrayMapResultGenerator() { + // 6. Let A be ? TypedArraySpeciesCreate(O, len). + Node* a = TypedArraySpeciesCreateByLength(context(), o(), len_); + // In the Spec and our current implementation, the length check is already + // performed in TypedArraySpeciesCreate. Repeating the check here to + // keep this invariant local. + // TODO(tebbi): Change this to a release mode check. + CSA_ASSERT( + this, WordEqual(len_, LoadObjectField(a, JSTypedArray::kLengthOffset))); + fast_typed_array_target_ = Word32Equal(LoadInstanceType(LoadElements(o_)), + LoadInstanceType(LoadElements(a))); + a_.Bind(a); } - Node* MapProcessor(Node* k_value, Node* k) { - // i. Let kValue be ? Get(O, Pk). Performed by the caller of MapProcessor. + Node* SpecCompliantMapProcessor(Node* k_value, Node* k) { + // i. Let kValue be ? Get(O, Pk). Performed by the caller of + // SpecCompliantMapProcessor. // ii. Let mappedValue be ? Call(callbackfn, T, kValue, k, O). Node* mappedValue = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), this_arg(), k_value, k, o()); @@ -165,12 +190,117 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { return a(); } + Node* FastMapProcessor(Node* k_value, Node* k) { + // i. Let kValue be ? Get(O, Pk). Performed by the caller of + // FastMapProcessor. + // ii. Let mappedValue be ? Call(callbackfn, T, kValue, k, O). + Node* mappedValue = CallJS(CodeFactory::Call(isolate()), context(), + callbackfn(), this_arg(), k_value, k, o()); + + Label finished(this); + Node* kind = nullptr; + Node* elements = nullptr; + + // If a() is a JSArray, we can have a fast path. + // mode is SMI_PARAMETERS because k has tagged representation. + ParameterMode mode = SMI_PARAMETERS; + Label fast(this); + Label runtime(this); + Label object_push_pre(this), object_push(this), double_push(this); + BranchIfFastJSArray(a(), context(), FastJSArrayAccessMode::ANY_ACCESS, + &fast, &runtime); + + BIND(&fast); + { + kind = EnsureArrayPushable(a(), &runtime); + elements = LoadElements(a()); + GotoIf(IsElementsKindGreaterThan(kind, FAST_HOLEY_SMI_ELEMENTS), + &object_push_pre); + TryStoreArrayElement(FAST_SMI_ELEMENTS, mode, &runtime, elements, k, + mappedValue); + Goto(&finished); + } + + BIND(&object_push_pre); + { + Branch(IsElementsKindGreaterThan(kind, FAST_HOLEY_ELEMENTS), &double_push, + &object_push); + } + + BIND(&object_push); + { + TryStoreArrayElement(FAST_ELEMENTS, mode, &runtime, elements, k, + mappedValue); + Goto(&finished); + } + + BIND(&double_push); + { + TryStoreArrayElement(FAST_DOUBLE_ELEMENTS, mode, &runtime, elements, k, + mappedValue); + Goto(&finished); + } + + BIND(&runtime); + { + // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). + CallRuntime(Runtime::kCreateDataProperty, context(), a(), k, mappedValue); + Goto(&finished); + } + + BIND(&finished); + return a(); + } + + // See tc39.github.io/ecma262/#sec-%typedarray%.prototype.map. + Node* TypedArrayMapProcessor(Node* k_value, Node* k) { + // 8. c. Let mappedValue be ? Call(callbackfn, T, « kValue, k, O »). + Node* mappedValue = CallJS(CodeFactory::Call(isolate()), context(), + callbackfn(), this_arg(), k_value, k, o()); + Label fast(this), slow(this), done(this), detached(this, Label::kDeferred); + + // 8. d. Perform ? Set(A, Pk, mappedValue, true). + // Since we know that A is a TypedArray, this always ends up in + // #sec-integer-indexed-exotic-objects-set-p-v-receiver and then + // tc39.github.io/ecma262/#sec-integerindexedelementset . + Branch(fast_typed_array_target_, &fast, &slow); + + BIND(&fast); + // #sec-integerindexedelementset 3. Let numValue be ? ToNumber(value). + Node* num_value = ToNumber(context(), mappedValue); + // The only way how this can bailout is because of a detached buffer. + EmitElementStore( + a(), k, num_value, false, source_elements_kind_, + KeyedAccessStoreMode::STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS, + &detached); + Goto(&done); + + BIND(&slow); + CallRuntime(Runtime::kSetProperty, context(), a(), k, mappedValue, + SmiConstant(STRICT)); + Goto(&done); + + BIND(&detached); + { + // tc39.github.io/ecma262/#sec-integerindexedelementset + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + CallRuntime(Runtime::kThrowTypeError, context_, + SmiConstant(MessageTemplate::kDetachedOperation), + name_string_); + Unreachable(); + } + + BIND(&done); + return a(); + } + void NullPostLoopAction() {} protected: Node* context() { return context_; } Node* receiver() { return receiver_; } Node* new_target() { return new_target_; } + Node* argc() { return argc_; } Node* o() { return o_; } Node* len() { return len_; } Node* callbackfn() { return callbackfn_; } @@ -178,14 +308,25 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { Node* k() { return k_.value(); } Node* a() { return a_.value(); } + void ReturnFromBuiltin(Node* value) { + if (argc_ == nullptr) { + Return(value); + } else { + // argc_ doesn't include the receiver, so it has to be added back in + // manually. + PopAndReturn(IntPtrAdd(argc_, IntPtrConstant(1)), value); + } + } + void InitIteratingArrayBuiltinBody(Node* context, Node* receiver, Node* callbackfn, Node* this_arg, - Node* new_target) { + Node* new_target, Node* argc) { context_ = context; receiver_ = receiver; new_target_ = new_target; callbackfn_ = callbackfn; this_arg_ = this_arg; + argc_ = argc; } void GenerateIteratingArrayBuiltinBody( @@ -193,8 +334,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { const CallResultProcessor& processor, const PostLoopAction& action, const Callable& slow_case_continuation, ForEachDirection direction = ForEachDirection::kForward) { - Label non_array(this), slow(this, {&k_, &a_, &to_}), - array_changes(this, {&k_, &a_, &to_}); + Label non_array(this), array_changes(this, {&k_, &a_, &to_}); // TODO(danno): Seriously? Do we really need to throw the exact error // message on null and undefined so that the webkit tests pass? @@ -220,8 +360,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { BIND(¬_js_array); Node* len_property = GetProperty(context(), o(), isolate()->factory()->length_string()); - merged_length.Bind( - CallStub(CodeFactory::ToLength(isolate()), context(), len_property)); + merged_length.Bind(ToLength_Inline(context(), len_property)); Goto(&has_length); BIND(&has_length); len_ = merged_length.value(); @@ -261,19 +400,16 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { k_.Bind(NumberDec(len())); } - a_.Bind(generator(this)); + generator(this); - HandleFastElements(processor, action, &slow, direction); + HandleFastElements(processor, action, &fully_spec_compliant_, direction); - BIND(&slow); + BIND(&fully_spec_compliant_); - Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset, - MachineType::TaggedPointer()); - TailCallStub( - slow_case_continuation, context(), target, new_target(), - Int32Constant(IteratingArrayBuiltinLoopContinuationDescriptor::kArity), - receiver(), callbackfn(), this_arg(), a_.value(), o(), k_.value(), - len(), to_.value()); + Node* result = + CallStub(slow_case_continuation, context(), receiver(), callbackfn(), + this_arg(), a_.value(), o(), k_.value(), len(), to_.value()); + ReturnFromBuiltin(result); } void InitIteratingArrayBuiltinLoopContinuation(Node* context, Node* receiver, @@ -284,6 +420,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { context_ = context; this_arg_ = this_arg; callbackfn_ = callbackfn; + argc_ = nullptr; a_.Bind(a); k_.Bind(initial_k); o_ = o; @@ -295,7 +432,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { const char* name, const BuiltinResultGenerator& generator, const CallResultProcessor& processor, const PostLoopAction& action, ForEachDirection direction = ForEachDirection::kForward) { - Node* name_string = + name_string_ = HeapConstant(isolate()->factory()->NewStringFromAsciiChecked(name)); // ValidateTypedArray: tc39.github.io/ecma262/#sec-validatetypedarray @@ -330,7 +467,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { { CallRuntime(Runtime::kThrowTypeError, context_, SmiConstant(MessageTemplate::kDetachedOperation), - name_string); + name_string_); Unreachable(); } @@ -367,25 +504,25 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { } else { k_.Bind(NumberDec(len())); } - a_.Bind(generator(this)); - Node* elements_type = LoadInstanceType(LoadElements(o_)); - Switch(elements_type, &unexpected_instance_type, instance_types.data(), + Node* instance_type = LoadInstanceType(LoadElements(o_)); + Switch(instance_type, &unexpected_instance_type, instance_types.data(), label_ptrs.data(), labels.size()); for (size_t i = 0; i < labels.size(); ++i) { BIND(&labels[i]); Label done(this); + source_elements_kind_ = ElementsKindForInstanceType( + static_cast<InstanceType>(instance_types[i])); + generator(this); // TODO(tebbi): Silently cancelling the loop on buffer detachment is a - // spec violation. Should go to &detached and throw a TypeError instead. - VisitAllTypedArrayElements( - ElementsKindForInstanceType( - static_cast<InstanceType>(instance_types[i])), - array_buffer, processor, &done, direction); + // spec violation. Should go to &throw_detached and throw a TypeError + // instead. + VisitAllTypedArrayElements(array_buffer, processor, &done, direction); Goto(&done); // No exception, return success BIND(&done); action(this); - Return(a_.value()); + ReturnFromBuiltin(a_.value()); } } @@ -459,7 +596,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { } } - void VisitAllTypedArrayElements(ElementsKind kind, Node* array_buffer, + void VisitAllTypedArrayElements(Node* array_buffer, const CallResultProcessor& processor, Label* detached, ForEachDirection direction) { VariableList list({&a_, &k_, &to_}, zone()); @@ -473,8 +610,8 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset, MachineType::Pointer()); Node* data_ptr = IntPtrAdd(BitcastTaggedToWord(base_ptr), external_ptr); - Node* value = LoadFixedTypedArrayElementAsTagged(data_ptr, index, kind, - SMI_PARAMETERS); + Node* value = LoadFixedTypedArrayElementAsTagged( + data_ptr, index, source_elements_kind_, SMI_PARAMETERS); k_.Bind(index); a_.Bind(processor(this, value, index)); }; @@ -575,7 +712,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { Node* o_map = LoadMap(o()); Node* bit_field2 = LoadMapBitField2(o_map); Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2); - Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_ELEMENTS)), + Branch(IsElementsKindGreaterThan(kind, FAST_HOLEY_ELEMENTS), &maybe_double_elements, &fast_elements); ParameterMode mode = OptimalParameterMode(); @@ -587,12 +724,12 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { action(this); // No exception, return success - Return(a_.value()); + ReturnFromBuiltin(a_.value()); } BIND(&maybe_double_elements); - Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_DOUBLE_ELEMENTS)), - slow, &fast_double_elements); + Branch(IsElementsKindGreaterThan(kind, FAST_HOLEY_DOUBLE_ELEMENTS), slow, + &fast_double_elements); BIND(&fast_double_elements); { @@ -602,10 +739,55 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { action(this); // No exception, return success - Return(a_.value()); + ReturnFromBuiltin(a_.value()); } } + // Perform ArraySpeciesCreate (ES6 #sec-arrayspeciescreate). + void ArraySpeciesCreate(Node* len) { + Label runtime(this, Label::kDeferred), done(this); + + Node* const original_map = LoadMap(o()); + GotoIf(Word32NotEqual(LoadMapInstanceType(original_map), + Int32Constant(JS_ARRAY_TYPE)), + &runtime); + + Node* const native_context = LoadNativeContext(context()); + Node* const initial_array_prototype = LoadContextElement( + native_context, Context::INITIAL_ARRAY_PROTOTYPE_INDEX); + Node* proto = LoadMapPrototype(original_map); + GotoIf(WordNotEqual(proto, initial_array_prototype), &runtime); + + Node* species_protector = SpeciesProtectorConstant(); + Node* value = LoadObjectField(species_protector, Cell::kValueOffset); + Node* const protector_invalid = SmiConstant(Isolate::kProtectorInvalid); + GotoIf(WordEqual(value, protector_invalid), &runtime); + + GotoIfNot(TaggedIsPositiveSmi(len), &runtime); + GotoIf(SmiAbove(len, SmiConstant(JSArray::kInitialMaxFastElementArray)), + &runtime); + + const ElementsKind elements_kind = + GetHoleyElementsKind(GetInitialFastElementsKind()); + Node* array_map = LoadJSArrayElementsMap(elements_kind, native_context); + a_.Bind(AllocateJSArray(FAST_SMI_ELEMENTS, array_map, len, len, nullptr, + CodeStubAssembler::SMI_PARAMETERS)); + + Goto(&done); + + BIND(&runtime); + { + // 5. Let A be ? ArraySpeciesCreate(O, len). + Node* constructor = + CallRuntime(Runtime::kArraySpeciesConstructor, context(), o()); + a_.Bind(ConstructJS(CodeFactory::Construct(isolate()), context(), + constructor, len)); + Goto(&fully_spec_compliant_); + } + + BIND(&done); + } + Node* callbackfn_ = nullptr; Node* o_ = nullptr; Node* this_arg_ = nullptr; @@ -613,11 +795,117 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler { Node* context_ = nullptr; Node* receiver_ = nullptr; Node* new_target_ = nullptr; + Node* argc_ = nullptr; + Node* fast_typed_array_target_ = nullptr; + Node* name_string_ = nullptr; Variable k_; Variable a_; Variable to_; + Label fully_spec_compliant_; + ElementsKind source_elements_kind_ = ElementsKind::NO_ELEMENTS; }; +TF_BUILTIN(FastArrayPop, CodeStubAssembler) { + Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount); + Node* context = Parameter(BuiltinDescriptor::kContext); + CSA_ASSERT(this, WordEqual(Parameter(BuiltinDescriptor::kNewTarget), + UndefinedConstant())); + + CodeStubArguments args(this, ChangeInt32ToIntPtr(argc)); + Node* receiver = args.GetReceiver(); + + Label runtime(this, Label::kDeferred); + Label fast(this); + + // Only pop in this stub if + // 1) the array has fast elements + // 2) the length is writable, + // 3) the elements backing store isn't copy-on-write, + // 4) we aren't supposed to shrink the backing store. + + // 1) Check that the array has fast elements. + BranchIfFastJSArray(receiver, context, FastJSArrayAccessMode::INBOUNDS_READ, + &fast, &runtime); + + BIND(&fast); + { + CSA_ASSERT(this, TaggedIsPositiveSmi( + LoadObjectField(receiver, JSArray::kLengthOffset))); + Node* length = LoadAndUntagObjectField(receiver, JSArray::kLengthOffset); + Label return_undefined(this), fast_elements(this); + GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &return_undefined); + + // 2) Ensure that the length is writable. + EnsureArrayLengthWritable(LoadMap(receiver), &runtime); + + // 3) Check that the elements backing store isn't copy-on-write. + Node* elements = LoadElements(receiver); + GotoIf(WordEqual(LoadMap(elements), + LoadRoot(Heap::kFixedCOWArrayMapRootIndex)), + &runtime); + + Node* new_length = IntPtrSub(length, IntPtrConstant(1)); + + // 4) Check that we're not supposed to shrink the backing store, as + // implemented in elements.cc:ElementsAccessorBase::SetLengthImpl. + Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements)); + GotoIf(IntPtrLessThan( + IntPtrAdd(IntPtrAdd(new_length, new_length), + IntPtrConstant(JSObject::kMinAddedElementsCapacity)), + capacity), + &runtime); + + StoreObjectFieldNoWriteBarrier(receiver, JSArray::kLengthOffset, + SmiTag(new_length)); + + Node* elements_kind = LoadMapElementsKind(LoadMap(receiver)); + GotoIf(Int32LessThanOrEqual(elements_kind, + Int32Constant(TERMINAL_FAST_ELEMENTS_KIND)), + &fast_elements); + + Node* value = LoadFixedDoubleArrayElement( + elements, new_length, MachineType::Float64(), 0, INTPTR_PARAMETERS, + &return_undefined); + + int32_t header_size = FixedDoubleArray::kHeaderSize - kHeapObjectTag; + Node* offset = ElementOffsetFromIndex( + new_length, FAST_HOLEY_DOUBLE_ELEMENTS, INTPTR_PARAMETERS, header_size); + if (Is64()) { + Node* double_hole = Int64Constant(kHoleNanInt64); + StoreNoWriteBarrier(MachineRepresentation::kWord64, elements, offset, + double_hole); + } else { + STATIC_ASSERT(kHoleNanLower32 == kHoleNanUpper32); + Node* double_hole = Int32Constant(kHoleNanLower32); + StoreNoWriteBarrier(MachineRepresentation::kWord32, elements, offset, + double_hole); + StoreNoWriteBarrier(MachineRepresentation::kWord32, elements, + IntPtrAdd(offset, IntPtrConstant(kPointerSize)), + double_hole); + } + args.PopAndReturn(AllocateHeapNumberWithValue(value)); + + BIND(&fast_elements); + { + Node* value = LoadFixedArrayElement(elements, new_length); + StoreFixedArrayElement(elements, new_length, TheHoleConstant()); + GotoIf(WordEqual(value, TheHoleConstant()), &return_undefined); + args.PopAndReturn(value); + } + + BIND(&return_undefined); + { args.PopAndReturn(UndefinedConstant()); } + } + + BIND(&runtime); + { + Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset, + MachineType::TaggedPointer()); + TailCallStub(CodeFactory::ArrayPop(isolate()), context, target, + UndefinedConstant(), argc); + } +} + TF_BUILTIN(FastArrayPush, CodeStubAssembler) { VARIABLE(arg_index, MachineType::PointerRepresentation()); Label default_label(this, &arg_index); @@ -632,7 +920,8 @@ TF_BUILTIN(FastArrayPush, CodeStubAssembler) { // arguments are reordered. Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount); Node* context = Parameter(BuiltinDescriptor::kContext); - Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + CSA_ASSERT(this, WordEqual(Parameter(BuiltinDescriptor::kNewTarget), + UndefinedConstant())); CodeStubArguments args(this, ChangeInt32ToIntPtr(argc)); Node* receiver = args.GetReceiver(); @@ -644,39 +933,13 @@ TF_BUILTIN(FastArrayPush, CodeStubAssembler) { BIND(&fast); { - // Disallow pushing onto prototypes. It might be the JSArray prototype. - // Disallow pushing onto non-extensible objects. - Comment("Disallow pushing onto prototypes"); - Node* map = LoadMap(receiver); - Node* bit_field2 = LoadMapBitField2(map); - int mask = static_cast<int>(Map::IsPrototypeMapBits::kMask) | - (1 << Map::kIsExtensible); - Node* test = Word32And(bit_field2, Int32Constant(mask)); - GotoIf(Word32NotEqual(test, Int32Constant(1 << Map::kIsExtensible)), - &runtime); - - // Disallow pushing onto arrays in dictionary named property mode. We need - // to figure out whether the length property is still writable. - Comment("Disallow pushing onto arrays in dictionary named property mode"); - GotoIf(IsDictionaryMap(map), &runtime); - - // Check whether the length property is writable. The length property is the - // only default named property on arrays. It's nonconfigurable, hence is - // guaranteed to stay the first property. - Node* descriptors = LoadMapDescriptors(map); - Node* details = - LoadFixedArrayElement(descriptors, DescriptorArray::ToDetailsIndex(0)); - GotoIf(IsSetSmi(details, PropertyDetails::kAttributesReadOnlyMask), - &runtime); - arg_index.Bind(IntPtrConstant(0)); - kind = DecodeWord32<Map::ElementsKindBits>(bit_field2); - - GotoIf(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_SMI_ELEMENTS)), + kind = EnsureArrayPushable(receiver, &runtime); + GotoIf(IsElementsKindGreaterThan(kind, FAST_HOLEY_SMI_ELEMENTS), &object_push_pre); - Node* new_length = BuildAppendJSArray(FAST_SMI_ELEMENTS, context, receiver, - args, arg_index, &smi_transition); + Node* new_length = BuildAppendJSArray(FAST_SMI_ELEMENTS, receiver, args, + arg_index, &smi_transition); args.PopAndReturn(new_length); } @@ -708,22 +971,21 @@ TF_BUILTIN(FastArrayPush, CodeStubAssembler) { BIND(&object_push_pre); { - Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_ELEMENTS)), - &double_push, &object_push); + Branch(IsElementsKindGreaterThan(kind, FAST_HOLEY_ELEMENTS), &double_push, + &object_push); } BIND(&object_push); { - Node* new_length = BuildAppendJSArray(FAST_ELEMENTS, context, receiver, - args, arg_index, &default_label); + Node* new_length = BuildAppendJSArray(FAST_ELEMENTS, receiver, args, + arg_index, &default_label); args.PopAndReturn(new_length); } BIND(&double_push); { - Node* new_length = - BuildAppendJSArray(FAST_DOUBLE_ELEMENTS, context, receiver, args, - arg_index, &double_transition); + Node* new_length = BuildAppendJSArray(FAST_DOUBLE_ELEMENTS, receiver, args, + arg_index, &double_transition); args.PopAndReturn(new_length); } @@ -769,8 +1031,156 @@ TF_BUILTIN(FastArrayPush, CodeStubAssembler) { { Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset, MachineType::TaggedPointer()); - TailCallStub(CodeFactory::ArrayPush(isolate()), context, target, new_target, - argc); + TailCallStub(CodeFactory::ArrayPush(isolate()), context, target, + UndefinedConstant(), argc); + } +} + +TF_BUILTIN(FastArrayShift, CodeStubAssembler) { + Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount); + Node* context = Parameter(BuiltinDescriptor::kContext); + CSA_ASSERT(this, WordEqual(Parameter(BuiltinDescriptor::kNewTarget), + UndefinedConstant())); + + CodeStubArguments args(this, ChangeInt32ToIntPtr(argc)); + Node* receiver = args.GetReceiver(); + + Label runtime(this, Label::kDeferred); + Label fast(this); + + // Only shift in this stub if + // 1) the array has fast elements + // 2) the length is writable, + // 3) the elements backing store isn't copy-on-write, + // 4) we aren't supposed to shrink the backing store, + // 5) we aren't supposed to left-trim the backing store. + + // 1) Check that the array has fast elements. + BranchIfFastJSArray(receiver, context, FastJSArrayAccessMode::INBOUNDS_READ, + &fast, &runtime); + + BIND(&fast); + { + CSA_ASSERT(this, TaggedIsPositiveSmi( + LoadObjectField(receiver, JSArray::kLengthOffset))); + Node* length = LoadAndUntagObjectField(receiver, JSArray::kLengthOffset); + Label return_undefined(this), fast_elements_tagged(this), + fast_elements_untagged(this); + GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &return_undefined); + + // 2) Ensure that the length is writable. + EnsureArrayLengthWritable(LoadMap(receiver), &runtime); + + // 3) Check that the elements backing store isn't copy-on-write. + Node* elements = LoadElements(receiver); + GotoIf(WordEqual(LoadMap(elements), + LoadRoot(Heap::kFixedCOWArrayMapRootIndex)), + &runtime); + + Node* new_length = IntPtrSub(length, IntPtrConstant(1)); + + // 4) Check that we're not supposed to right-trim the backing store, as + // implemented in elements.cc:ElementsAccessorBase::SetLengthImpl. + Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements)); + GotoIf(IntPtrLessThan( + IntPtrAdd(IntPtrAdd(new_length, new_length), + IntPtrConstant(JSObject::kMinAddedElementsCapacity)), + capacity), + &runtime); + + // 5) Check that we're not supposed to left-trim the backing store, as + // implemented in elements.cc:FastElementsAccessor::MoveElements. + GotoIf(IntPtrGreaterThan(new_length, + IntPtrConstant(JSArray::kMaxCopyElements)), + &runtime); + + StoreObjectFieldNoWriteBarrier(receiver, JSArray::kLengthOffset, + SmiTag(new_length)); + + Node* elements_kind = LoadMapElementsKind(LoadMap(receiver)); + GotoIf(Int32LessThanOrEqual(elements_kind, + Int32Constant(FAST_HOLEY_SMI_ELEMENTS)), + &fast_elements_untagged); + GotoIf(Int32LessThanOrEqual(elements_kind, + Int32Constant(TERMINAL_FAST_ELEMENTS_KIND)), + &fast_elements_tagged); + Node* value = LoadFixedDoubleArrayElement( + elements, IntPtrConstant(0), MachineType::Float64(), 0, + INTPTR_PARAMETERS, &return_undefined); + + int32_t header_size = FixedDoubleArray::kHeaderSize - kHeapObjectTag; + Node* memmove = + ExternalConstant(ExternalReference::libc_memmove_function(isolate())); + Node* start = IntPtrAdd( + BitcastTaggedToWord(elements), + ElementOffsetFromIndex(IntPtrConstant(0), FAST_HOLEY_DOUBLE_ELEMENTS, + INTPTR_PARAMETERS, header_size)); + CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(), + MachineType::Pointer(), MachineType::UintPtr(), memmove, + start, IntPtrAdd(start, IntPtrConstant(kDoubleSize)), + IntPtrMul(new_length, IntPtrConstant(kDoubleSize))); + Node* offset = ElementOffsetFromIndex( + new_length, FAST_HOLEY_DOUBLE_ELEMENTS, INTPTR_PARAMETERS, header_size); + if (Is64()) { + Node* double_hole = Int64Constant(kHoleNanInt64); + StoreNoWriteBarrier(MachineRepresentation::kWord64, elements, offset, + double_hole); + } else { + STATIC_ASSERT(kHoleNanLower32 == kHoleNanUpper32); + Node* double_hole = Int32Constant(kHoleNanLower32); + StoreNoWriteBarrier(MachineRepresentation::kWord32, elements, offset, + double_hole); + StoreNoWriteBarrier(MachineRepresentation::kWord32, elements, + IntPtrAdd(offset, IntPtrConstant(kPointerSize)), + double_hole); + } + args.PopAndReturn(AllocateHeapNumberWithValue(value)); + + BIND(&fast_elements_tagged); + { + Node* value = LoadFixedArrayElement(elements, 0); + BuildFastLoop(IntPtrConstant(0), new_length, + [&](Node* index) { + StoreFixedArrayElement( + elements, index, + LoadFixedArrayElement( + elements, IntPtrAdd(index, IntPtrConstant(1)))); + }, + 1, ParameterMode::INTPTR_PARAMETERS, + IndexAdvanceMode::kPost); + StoreFixedArrayElement(elements, new_length, TheHoleConstant()); + GotoIf(WordEqual(value, TheHoleConstant()), &return_undefined); + args.PopAndReturn(value); + } + + BIND(&fast_elements_untagged); + { + Node* value = LoadFixedArrayElement(elements, 0); + Node* memmove = + ExternalConstant(ExternalReference::libc_memmove_function(isolate())); + Node* start = IntPtrAdd( + BitcastTaggedToWord(elements), + ElementOffsetFromIndex(IntPtrConstant(0), FAST_HOLEY_SMI_ELEMENTS, + INTPTR_PARAMETERS, header_size)); + CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(), + MachineType::Pointer(), MachineType::UintPtr(), memmove, + start, IntPtrAdd(start, IntPtrConstant(kPointerSize)), + IntPtrMul(new_length, IntPtrConstant(kPointerSize))); + StoreFixedArrayElement(elements, new_length, TheHoleConstant()); + GotoIf(WordEqual(value, TheHoleConstant()), &return_undefined); + args.PopAndReturn(value); + } + + BIND(&return_undefined); + { args.PopAndReturn(UndefinedConstant()); } + } + + BIND(&runtime); + { + Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset, + MachineType::TaggedPointer()); + TailCallStub(CodeFactory::ArrayShift(isolate()), context, target, + UndefinedConstant(), argc); } } @@ -795,21 +1205,25 @@ TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinCodeStubAssembler) { } TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) { - Node* context = Parameter(Descriptor::kContext); - Node* receiver = Parameter(Descriptor::kReceiver); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* this_arg = Parameter(Descriptor::kThisArg); - Node* new_target = Parameter(Descriptor::kNewTarget); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* this_arg = args.GetOptionalArgumentValue(1, UndefinedConstant()); InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, - new_target); + new_target, argc); GenerateIteratingArrayBuiltinBody( "Array.prototype.forEach", &ArrayBuiltinCodeStubAssembler::ForEachResultGenerator, &ArrayBuiltinCodeStubAssembler::ForEachProcessor, &ArrayBuiltinCodeStubAssembler::NullPostLoopAction, - CodeFactory::ArrayForEachLoopContinuation(isolate())); + Builtins::CallableFor(isolate(), + Builtins::kArrayForEachLoopContinuation)); } TF_BUILTIN(ArraySomeLoopContinuation, ArrayBuiltinCodeStubAssembler) { @@ -833,32 +1247,38 @@ TF_BUILTIN(ArraySomeLoopContinuation, ArrayBuiltinCodeStubAssembler) { } TF_BUILTIN(ArraySome, ArrayBuiltinCodeStubAssembler) { - Node* context = Parameter(Descriptor::kContext); - Node* receiver = Parameter(Descriptor::kReceiver); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* this_arg = Parameter(Descriptor::kThisArg); - Node* new_target = Parameter(Descriptor::kNewTarget); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* this_arg = args.GetOptionalArgumentValue(1, UndefinedConstant()); InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, - new_target); + new_target, argc); GenerateIteratingArrayBuiltinBody( "Array.prototype.some", &ArrayBuiltinCodeStubAssembler::SomeResultGenerator, &ArrayBuiltinCodeStubAssembler::SomeProcessor, &ArrayBuiltinCodeStubAssembler::NullPostLoopAction, - CodeFactory::ArraySomeLoopContinuation(isolate())); + Builtins::CallableFor(isolate(), Builtins::kArraySomeLoopContinuation)); } TF_BUILTIN(TypedArrayPrototypeSome, ArrayBuiltinCodeStubAssembler) { - Node* context = Parameter(Descriptor::kContext); - Node* receiver = Parameter(Descriptor::kReceiver); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* this_arg = Parameter(Descriptor::kThisArg); - Node* new_target = Parameter(Descriptor::kNewTarget); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* this_arg = args.GetOptionalArgumentValue(1, UndefinedConstant()); InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, - new_target); + new_target, argc); GenerateIteratingTypedArrayBuiltinBody( "%TypedArray%.prototype.some", @@ -888,32 +1308,38 @@ TF_BUILTIN(ArrayEveryLoopContinuation, ArrayBuiltinCodeStubAssembler) { } TF_BUILTIN(ArrayEvery, ArrayBuiltinCodeStubAssembler) { - Node* context = Parameter(Descriptor::kContext); - Node* receiver = Parameter(Descriptor::kReceiver); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* this_arg = Parameter(Descriptor::kThisArg); - Node* new_target = Parameter(Descriptor::kNewTarget); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* this_arg = args.GetOptionalArgumentValue(1, UndefinedConstant()); InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, - new_target); + new_target, argc); GenerateIteratingArrayBuiltinBody( "Array.prototype.every", &ArrayBuiltinCodeStubAssembler::EveryResultGenerator, &ArrayBuiltinCodeStubAssembler::EveryProcessor, &ArrayBuiltinCodeStubAssembler::NullPostLoopAction, - CodeFactory::ArrayEveryLoopContinuation(isolate())); + Builtins::CallableFor(isolate(), Builtins::kArrayEveryLoopContinuation)); } TF_BUILTIN(TypedArrayPrototypeEvery, ArrayBuiltinCodeStubAssembler) { - Node* context = Parameter(Descriptor::kContext); - Node* receiver = Parameter(Descriptor::kReceiver); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* this_arg = Parameter(Descriptor::kThisArg); - Node* new_target = Parameter(Descriptor::kNewTarget); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* this_arg = args.GetOptionalArgumentValue(1, UndefinedConstant()); InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, - new_target); + new_target, argc); GenerateIteratingTypedArrayBuiltinBody( "%TypedArray%.prototype.every", @@ -943,32 +1369,38 @@ TF_BUILTIN(ArrayReduceLoopContinuation, ArrayBuiltinCodeStubAssembler) { } TF_BUILTIN(ArrayReduce, ArrayBuiltinCodeStubAssembler) { - Node* context = Parameter(Descriptor::kContext); - Node* receiver = Parameter(Descriptor::kReceiver); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* initial_value = Parameter(Descriptor::kInitialValue); - Node* new_target = Parameter(Descriptor::kNewTarget); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant()); InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value, - new_target); + new_target, argc); GenerateIteratingArrayBuiltinBody( "Array.prototype.reduce", &ArrayBuiltinCodeStubAssembler::ReduceResultGenerator, &ArrayBuiltinCodeStubAssembler::ReduceProcessor, &ArrayBuiltinCodeStubAssembler::ReducePostLoopAction, - CodeFactory::ArrayReduceLoopContinuation(isolate())); + Builtins::CallableFor(isolate(), Builtins::kArrayReduceLoopContinuation)); } TF_BUILTIN(TypedArrayPrototypeReduce, ArrayBuiltinCodeStubAssembler) { - Node* context = Parameter(Descriptor::kContext); - Node* receiver = Parameter(Descriptor::kReceiver); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* initial_value = Parameter(Descriptor::kInitialValue); - Node* new_target = Parameter(Descriptor::kNewTarget); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant()); InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value, - new_target); + new_target, argc); GenerateIteratingTypedArrayBuiltinBody( "%TypedArray%.prototype.reduce", @@ -999,33 +1431,40 @@ TF_BUILTIN(ArrayReduceRightLoopContinuation, ArrayBuiltinCodeStubAssembler) { } TF_BUILTIN(ArrayReduceRight, ArrayBuiltinCodeStubAssembler) { - Node* context = Parameter(Descriptor::kContext); - Node* receiver = Parameter(Descriptor::kReceiver); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* initial_value = Parameter(Descriptor::kInitialValue); - Node* new_target = Parameter(Descriptor::kNewTarget); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant()); InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value, - new_target); + new_target, argc); GenerateIteratingArrayBuiltinBody( "Array.prototype.reduceRight", &ArrayBuiltinCodeStubAssembler::ReduceResultGenerator, &ArrayBuiltinCodeStubAssembler::ReduceProcessor, &ArrayBuiltinCodeStubAssembler::ReducePostLoopAction, - CodeFactory::ArrayReduceRightLoopContinuation(isolate()), + Builtins::CallableFor(isolate(), + Builtins::kArrayReduceRightLoopContinuation), ForEachDirection::kReverse); } TF_BUILTIN(TypedArrayPrototypeReduceRight, ArrayBuiltinCodeStubAssembler) { - Node* context = Parameter(Descriptor::kContext); - Node* receiver = Parameter(Descriptor::kReceiver); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* initial_value = Parameter(Descriptor::kInitialValue); - Node* new_target = Parameter(Descriptor::kNewTarget); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant()); InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value, - new_target); + new_target, argc); GenerateIteratingTypedArrayBuiltinBody( "%TypedArray%.prototype.reduceRight", @@ -1056,21 +1495,24 @@ TF_BUILTIN(ArrayFilterLoopContinuation, ArrayBuiltinCodeStubAssembler) { } TF_BUILTIN(ArrayFilter, ArrayBuiltinCodeStubAssembler) { - Node* context = Parameter(Descriptor::kContext); - Node* receiver = Parameter(Descriptor::kReceiver); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* this_arg = Parameter(Descriptor::kThisArg); - Node* new_target = Parameter(Descriptor::kNewTarget); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* this_arg = args.GetOptionalArgumentValue(1, UndefinedConstant()); InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, - new_target); + new_target, argc); GenerateIteratingArrayBuiltinBody( "Array.prototype.filter", &ArrayBuiltinCodeStubAssembler::FilterResultGenerator, &ArrayBuiltinCodeStubAssembler::FilterProcessor, &ArrayBuiltinCodeStubAssembler::NullPostLoopAction, - CodeFactory::ArrayFilterLoopContinuation(isolate())); + Builtins::CallableFor(isolate(), Builtins::kArrayFilterLoopContinuation)); } TF_BUILTIN(ArrayMapLoopContinuation, ArrayBuiltinCodeStubAssembler) { @@ -1089,25 +1531,48 @@ TF_BUILTIN(ArrayMapLoopContinuation, ArrayBuiltinCodeStubAssembler) { len, to); GenerateIteratingArrayBuiltinLoopContinuation( - &ArrayBuiltinCodeStubAssembler::MapProcessor, + &ArrayBuiltinCodeStubAssembler::SpecCompliantMapProcessor, &ArrayBuiltinCodeStubAssembler::NullPostLoopAction); } TF_BUILTIN(ArrayMap, ArrayBuiltinCodeStubAssembler) { - Node* context = Parameter(Descriptor::kContext); - Node* receiver = Parameter(Descriptor::kReceiver); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* this_arg = Parameter(Descriptor::kThisArg); - Node* new_target = Parameter(Descriptor::kNewTarget); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* this_arg = args.GetOptionalArgumentValue(1, UndefinedConstant()); InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, - new_target); + new_target, argc); GenerateIteratingArrayBuiltinBody( "Array.prototype.map", &ArrayBuiltinCodeStubAssembler::MapResultGenerator, - &ArrayBuiltinCodeStubAssembler::MapProcessor, + &ArrayBuiltinCodeStubAssembler::FastMapProcessor, &ArrayBuiltinCodeStubAssembler::NullPostLoopAction, - CodeFactory::ArrayMapLoopContinuation(isolate())); + Builtins::CallableFor(isolate(), Builtins::kArrayMapLoopContinuation)); +} + +TF_BUILTIN(TypedArrayPrototypeMap, ArrayBuiltinCodeStubAssembler) { + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* context = Parameter(BuiltinDescriptor::kContext); + Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); + Node* receiver = args.GetReceiver(); + Node* callbackfn = args.GetOptionalArgumentValue(0, UndefinedConstant()); + Node* this_arg = args.GetOptionalArgumentValue(1, UndefinedConstant()); + + InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, + new_target, argc); + + GenerateIteratingTypedArrayBuiltinBody( + "%TypedArray%.prototype.map", + &ArrayBuiltinCodeStubAssembler::TypedArrayMapResultGenerator, + &ArrayBuiltinCodeStubAssembler::TypedArrayMapProcessor, + &ArrayBuiltinCodeStubAssembler::NullPostLoopAction); } TF_BUILTIN(ArrayIsArray, CodeStubAssembler) { @@ -1136,79 +1601,117 @@ TF_BUILTIN(ArrayIsArray, CodeStubAssembler) { Return(CallRuntime(Runtime::kArrayIsArray, context, object)); } -TF_BUILTIN(ArrayIncludes, CodeStubAssembler) { - Node* const array = Parameter(Descriptor::kReceiver); - Node* const search_element = Parameter(Descriptor::kSearchElement); - Node* const start_from = Parameter(Descriptor::kFromIndex); - Node* const context = Parameter(Descriptor::kContext); +class ArrayIncludesIndexofAssembler : public CodeStubAssembler { + public: + explicit ArrayIncludesIndexofAssembler(compiler::CodeAssemblerState* state) + : CodeStubAssembler(state) {} + + enum SearchVariant { kIncludes, kIndexOf }; + + void Generate(SearchVariant variant); +}; - VARIABLE(index_var, MachineType::PointerRepresentation()); +void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant) { + const int kSearchElementArg = 0; + const int kFromIndexArg = 1; - Label init_k(this), return_true(this), return_false(this), call_runtime(this); - Label init_len(this), select_loop(this); + Node* argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); - index_var.Bind(IntPtrConstant(0)); + Node* array = args.GetReceiver(); + Node* search_element = + args.GetOptionalArgumentValue(kSearchElementArg, UndefinedConstant()); + Node* context = Parameter(BuiltinDescriptor::kContext); + + Node* intptr_zero = IntPtrConstant(0); + + Label init_index(this), return_found(this), return_not_found(this), + call_runtime(this); // Take slow path if not a JSArray, if retrieving elements requires // traversing prototype, or if access checks are required. BranchIfFastJSArray(array, context, FastJSArrayAccessMode::INBOUNDS_READ, - &init_len, &call_runtime); - - BIND(&init_len); - // JSArray length is always an Smi for fast arrays. - CSA_ASSERT(this, TaggedIsSmi(LoadObjectField(array, JSArray::kLengthOffset))); - Node* const len = LoadAndUntagObjectField(array, JSArray::kLengthOffset); - - GotoIf(IsUndefined(start_from), &select_loop); - - // Bailout to slow path if startIndex is not an Smi. - Branch(TaggedIsSmi(start_from), &init_k, &call_runtime); - - BIND(&init_k); - CSA_ASSERT(this, TaggedIsSmi(start_from)); - Node* const untagged_start_from = SmiToWord(start_from); - index_var.Bind( - Select(IntPtrGreaterThanOrEqual(untagged_start_from, IntPtrConstant(0)), - [=]() { return untagged_start_from; }, - [=]() { - Node* const index = IntPtrAdd(len, untagged_start_from); - return SelectConstant(IntPtrLessThan(index, IntPtrConstant(0)), - IntPtrConstant(0), index, - MachineType::PointerRepresentation()); - }, - MachineType::PointerRepresentation())); - - Goto(&select_loop); - BIND(&select_loop); - static int32_t kElementsKind[] = { - FAST_SMI_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, FAST_ELEMENTS, - FAST_HOLEY_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_DOUBLE_ELEMENTS, - }; + &init_index, &call_runtime); + + BIND(&init_index); + VARIABLE(index_var, MachineType::PointerRepresentation(), intptr_zero); + + // JSArray length is always a positive Smi for fast arrays. + CSA_ASSERT(this, TaggedIsPositiveSmi(LoadJSArrayLength(array))); + Node* array_length = SmiUntag(LoadJSArrayLength(array)); + + { + // Initialize fromIndex. + Label is_smi(this), is_nonsmi(this), done(this); + + // If no fromIndex was passed, default to 0. + GotoIf(IntPtrLessThanOrEqual(argc, IntPtrConstant(kFromIndexArg)), &done); + + Node* start_from = args.AtIndex(kFromIndexArg); + // Handle Smis and undefined here and everything else in runtime. + // We must be very careful with side effects from the ToInteger conversion, + // as the side effects might render previously checked assumptions about + // the receiver being a fast JSArray and its length invalid. + Branch(TaggedIsSmi(start_from), &is_smi, &is_nonsmi); + + BIND(&is_nonsmi); + { + GotoIfNot(IsUndefined(start_from), &call_runtime); + Goto(&done); + } + BIND(&is_smi); + { + Node* intptr_start_from = SmiUntag(start_from); + index_var.Bind(intptr_start_from); + + GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done); + // The fromIndex is negative: add it to the array's length. + index_var.Bind(IntPtrAdd(array_length, index_var.value())); + // Clamp negative results at zero. + GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done); + index_var.Bind(intptr_zero); + Goto(&done); + } + BIND(&done); + } + + // Fail early if startIndex >= array.length. + GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), array_length), + &return_not_found); Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this); - Label* element_kind_handlers[] = {&if_smiorobjects, &if_smiorobjects, - &if_smiorobjects, &if_smiorobjects, - &if_packed_doubles, &if_holey_doubles}; - Node* map = LoadMap(array); - Node* elements_kind = LoadMapElementsKind(map); + Node* elements_kind = LoadMapElementsKind(LoadMap(array)); Node* elements = LoadElements(array); - Switch(elements_kind, &return_false, kElementsKind, element_kind_handlers, - arraysize(kElementsKind)); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); + GotoIf( + Uint32LessThanOrEqual(elements_kind, Int32Constant(FAST_HOLEY_ELEMENTS)), + &if_smiorobjects); + GotoIf(Word32Equal(elements_kind, Int32Constant(FAST_DOUBLE_ELEMENTS)), + &if_packed_doubles); + GotoIf(Word32Equal(elements_kind, Int32Constant(FAST_HOLEY_DOUBLE_ELEMENTS)), + &if_holey_doubles); + Goto(&return_not_found); BIND(&if_smiorobjects); { VARIABLE(search_num, MachineRepresentation::kFloat64); Label ident_loop(this, &index_var), heap_num_loop(this, &search_num), - string_loop(this, &index_var), undef_loop(this, &index_var), - not_smi(this), not_heap_num(this); + string_loop(this), undef_loop(this, &index_var), not_smi(this), + not_heap_num(this); GotoIfNot(TaggedIsSmi(search_element), ¬_smi); search_num.Bind(SmiToFloat64(search_element)); Goto(&heap_num_loop); BIND(¬_smi); - GotoIf(WordEqual(search_element, UndefinedConstant()), &undef_loop); + if (variant == kIncludes) { + GotoIf(IsUndefined(search_element), &undef_loop); + } Node* map = LoadMap(search_element); GotoIfNot(IsHeapNumberMap(map), ¬_heap_num); search_num.Bind(LoadHeapNumberValue(search_element)); @@ -1221,83 +1724,105 @@ TF_BUILTIN(ArrayIncludes, CodeStubAssembler) { BIND(&ident_loop); { - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + &return_not_found); Node* element_k = LoadFixedArrayElement(elements, index_var.value()); - GotoIf(WordEqual(element_k, search_element), &return_true); + GotoIf(WordEqual(element_k, search_element), &return_found); - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); + Increment(index_var); Goto(&ident_loop); } - BIND(&undef_loop); - { - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); + if (variant == kIncludes) { + BIND(&undef_loop); + + GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + &return_not_found); Node* element_k = LoadFixedArrayElement(elements, index_var.value()); - GotoIf(WordEqual(element_k, UndefinedConstant()), &return_true); - GotoIf(WordEqual(element_k, TheHoleConstant()), &return_true); + GotoIf(IsUndefined(element_k), &return_found); + GotoIf(IsTheHole(element_k), &return_found); - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); + Increment(index_var); Goto(&undef_loop); } BIND(&heap_num_loop); { Label nan_loop(this, &index_var), not_nan_loop(this, &index_var); - BranchIfFloat64IsNaN(search_num.value(), &nan_loop, ¬_nan_loop); + Label* nan_handling = + variant == kIncludes ? &nan_loop : &return_not_found; + BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop); BIND(¬_nan_loop); { Label continue_loop(this), not_smi(this); - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + &return_not_found); Node* element_k = LoadFixedArrayElement(elements, index_var.value()); GotoIfNot(TaggedIsSmi(element_k), ¬_smi); Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)), - &return_true, &continue_loop); + &return_found, &continue_loop); BIND(¬_smi); GotoIfNot(IsHeapNumber(element_k), &continue_loop); Branch(Float64Equal(search_num.value(), LoadHeapNumberValue(element_k)), - &return_true, &continue_loop); + &return_found, &continue_loop); BIND(&continue_loop); - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); + Increment(index_var); Goto(¬_nan_loop); } - BIND(&nan_loop); - { + // Array.p.includes uses SameValueZero comparisons, where NaN == NaN. + if (variant == kIncludes) { + BIND(&nan_loop); Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + &return_not_found); Node* element_k = LoadFixedArrayElement(elements, index_var.value()); GotoIf(TaggedIsSmi(element_k), &continue_loop); GotoIfNot(IsHeapNumber(element_k), &continue_loop); - BranchIfFloat64IsNaN(LoadHeapNumberValue(element_k), &return_true, + BranchIfFloat64IsNaN(LoadHeapNumberValue(element_k), &return_found, &continue_loop); BIND(&continue_loop); - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); + Increment(index_var); Goto(&nan_loop); } } BIND(&string_loop); { - Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); + CSA_ASSERT(this, IsString(search_element)); + Label continue_loop(this), next_iteration(this, &index_var), + slow_compare(this), runtime(this, Label::kDeferred); + Node* search_length = LoadStringLength(search_element); + Goto(&next_iteration); + BIND(&next_iteration); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + &return_not_found); Node* element_k = LoadFixedArrayElement(elements, index_var.value()); GotoIf(TaggedIsSmi(element_k), &continue_loop); - GotoIfNot(IsStringInstanceType(LoadInstanceType(element_k)), - &continue_loop); - - // TODO(bmeurer): Consider inlining the StringEqual logic here. - Node* result = CallStub(CodeFactory::StringEqual(isolate()), context, - search_element, element_k); - Branch(WordEqual(BooleanConstant(true), result), &return_true, + GotoIf(WordEqual(search_element, element_k), &return_found); + Node* element_k_type = LoadInstanceType(element_k); + GotoIfNot(IsStringInstanceType(element_k_type), &continue_loop); + Branch(WordEqual(search_length, LoadStringLength(element_k)), + &slow_compare, &continue_loop); + + BIND(&slow_compare); + StringBuiltinsAssembler string_asm(state()); + string_asm.StringEqual_Core(context, search_element, search_type, + search_length, element_k, element_k_type, + &return_found, &continue_loop, &runtime); + BIND(&runtime); + Node* result = CallRuntime(Runtime::kStringEqual, context, search_element, + element_k); + Branch(WordEqual(BooleanConstant(true), result), &return_found, &continue_loop); BIND(&continue_loop); - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); - Goto(&string_loop); + Increment(index_var); + Goto(&next_iteration); } } @@ -1312,36 +1837,38 @@ TF_BUILTIN(ArrayIncludes, CodeStubAssembler) { Goto(¬_nan_loop); BIND(&search_notnan); - GotoIfNot(IsHeapNumber(search_element), &return_false); + GotoIfNot(IsHeapNumber(search_element), &return_not_found); search_num.Bind(LoadHeapNumberValue(search_element)); - BranchIfFloat64IsNaN(search_num.value(), &nan_loop, ¬_nan_loop); + Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found; + BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop); - // Search for HeapNumber BIND(¬_nan_loop); { Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + &return_not_found); Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), MachineType::Float64()); - Branch(Float64Equal(element_k, search_num.value()), &return_true, + Branch(Float64Equal(element_k, search_num.value()), &return_found, &continue_loop); BIND(&continue_loop); - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); + Increment(index_var); Goto(¬_nan_loop); } - // Search for NaN - BIND(&nan_loop); - { + // Array.p.includes uses SameValueZero comparisons, where NaN == NaN. + if (variant == kIncludes) { + BIND(&nan_loop); Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + &return_not_found); Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), MachineType::Float64()); - BranchIfFloat64IsNaN(element_k, &return_true, &continue_loop); + BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop); BIND(&continue_loop); - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); + Increment(index_var); Goto(&nan_loop); } } @@ -1357,339 +1884,94 @@ TF_BUILTIN(ArrayIncludes, CodeStubAssembler) { Goto(¬_nan_loop); BIND(&search_notnan); - GotoIf(WordEqual(search_element, UndefinedConstant()), &hole_loop); - GotoIfNot(IsHeapNumber(search_element), &return_false); + if (variant == kIncludes) { + GotoIf(IsUndefined(search_element), &hole_loop); + } + GotoIfNot(IsHeapNumber(search_element), &return_not_found); search_num.Bind(LoadHeapNumberValue(search_element)); - BranchIfFloat64IsNaN(search_num.value(), &nan_loop, ¬_nan_loop); + Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found; + BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop); - // Search for HeapNumber BIND(¬_nan_loop); { Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + &return_not_found); - // Load double value or continue if it contains a double hole. - Node* element_k = LoadFixedDoubleArrayElement( - elements, index_var.value(), MachineType::Float64(), 0, - INTPTR_PARAMETERS, &continue_loop); + // No need for hole checking here; the following Float64Equal will + // return 'not equal' for holes anyway. + Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), + MachineType::Float64()); - Branch(Float64Equal(element_k, search_num.value()), &return_true, + Branch(Float64Equal(element_k, search_num.value()), &return_found, &continue_loop); BIND(&continue_loop); - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); + Increment(index_var); Goto(¬_nan_loop); } - // Search for NaN - BIND(&nan_loop); - { + // Array.p.includes uses SameValueZero comparisons, where NaN == NaN. + if (variant == kIncludes) { + BIND(&nan_loop); Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + &return_not_found); - // Load double value or continue if it contains a double hole. + // Load double value or continue if it's the hole NaN. Node* element_k = LoadFixedDoubleArrayElement( elements, index_var.value(), MachineType::Float64(), 0, INTPTR_PARAMETERS, &continue_loop); - BranchIfFloat64IsNaN(element_k, &return_true, &continue_loop); + BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop); BIND(&continue_loop); - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); + Increment(index_var); Goto(&nan_loop); } - // Search for the Hole - BIND(&hole_loop); - { - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false); + // Array.p.includes treats the hole as undefined. + if (variant == kIncludes) { + BIND(&hole_loop); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + &return_not_found); // Check if the element is a double hole, but don't load it. LoadFixedDoubleArrayElement(elements, index_var.value(), MachineType::None(), 0, INTPTR_PARAMETERS, - &return_true); + &return_found); - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1))); + Increment(index_var); Goto(&hole_loop); } } - BIND(&return_true); - Return(TrueConstant()); + BIND(&return_found); + args.PopAndReturn(variant == kIncludes ? TrueConstant() + : SmiTag(index_var.value())); - BIND(&return_false); - Return(FalseConstant()); + BIND(&return_not_found); + args.PopAndReturn(variant == kIncludes ? FalseConstant() + : NumberConstant(-1)); BIND(&call_runtime); - Return(CallRuntime(Runtime::kArrayIncludes_Slow, context, array, - search_element, start_from)); -} - -TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) { - Node* array = Parameter(Descriptor::kReceiver); - Node* search_element = Parameter(Descriptor::kSearchElement); - Node* start_from = Parameter(Descriptor::kFromIndex); - Node* context = Parameter(Descriptor::kContext); - - Node* intptr_zero = IntPtrConstant(0); - Node* intptr_one = IntPtrConstant(1); - - VARIABLE(len_var, MachineType::PointerRepresentation()); - VARIABLE(index_var, MachineType::PointerRepresentation()); - VARIABLE(start_from_var, MachineType::PointerRepresentation()); - - Label init_k(this), return_found(this), return_not_found(this), - call_runtime(this); - - Label init_len(this); - - index_var.Bind(intptr_zero); - len_var.Bind(intptr_zero); - - // Take slow path if not a JSArray, if retrieving elements requires - // traversing prototype, or if access checks are required. - BranchIfFastJSArray(array, context, FastJSArrayAccessMode::INBOUNDS_READ, - &init_len, &call_runtime); - - BIND(&init_len); - { - // JSArray length is always an Smi for fast arrays. - CSA_ASSERT(this, - TaggedIsSmi(LoadObjectField(array, JSArray::kLengthOffset))); - Node* len = LoadAndUntagObjectField(array, JSArray::kLengthOffset); - - len_var.Bind(len); - Branch(WordEqual(len_var.value(), intptr_zero), &return_not_found, &init_k); - } - - BIND(&init_k); - { - // For now only deal with undefined and Smis here; we must be really careful - // with side-effects from the ToInteger conversion as the side-effects might - // render our assumptions about the receiver being a fast JSArray and the - // length invalid. - Label done(this), init_k_smi(this), init_k_other(this), init_k_zero(this), - init_k_n(this); - Branch(TaggedIsSmi(start_from), &init_k_smi, &init_k_other); - - BIND(&init_k_smi); - { - // The fromIndex is a Smi. - start_from_var.Bind(SmiUntag(start_from)); - Goto(&init_k_n); - } - - BIND(&init_k_other); - { - // The fromIndex must be undefined then, otherwise bailout and let the - // runtime deal with the full ToInteger conversion. - GotoIfNot(IsUndefined(start_from), &call_runtime); - start_from_var.Bind(intptr_zero); - Goto(&init_k_n); - } - - BIND(&init_k_n); - { - Label if_positive(this), if_negative(this), done(this); - Branch(IntPtrLessThan(start_from_var.value(), intptr_zero), &if_negative, - &if_positive); - - BIND(&if_positive); - { - index_var.Bind(start_from_var.value()); - Goto(&done); - } - - BIND(&if_negative); - { - index_var.Bind(IntPtrAdd(len_var.value(), start_from_var.value())); - Branch(IntPtrLessThan(index_var.value(), intptr_zero), &init_k_zero, - &done); - } - - BIND(&init_k_zero); - { - index_var.Bind(intptr_zero); - Goto(&done); - } - - BIND(&done); - } - } - - static int32_t kElementsKind[] = { - FAST_SMI_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, FAST_ELEMENTS, - FAST_HOLEY_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_DOUBLE_ELEMENTS, - }; - - Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this); - Label* element_kind_handlers[] = {&if_smiorobjects, &if_smiorobjects, - &if_smiorobjects, &if_smiorobjects, - &if_packed_doubles, &if_holey_doubles}; - - Node* map = LoadMap(array); - Node* elements_kind = LoadMapElementsKind(map); - Node* elements = LoadElements(array); - Switch(elements_kind, &return_not_found, kElementsKind, element_kind_handlers, - arraysize(kElementsKind)); - - BIND(&if_smiorobjects); - { - VARIABLE(search_num, MachineRepresentation::kFloat64); - Label ident_loop(this, &index_var), heap_num_loop(this, &search_num), - string_loop(this, &index_var), not_smi(this), not_heap_num(this); - - GotoIfNot(TaggedIsSmi(search_element), ¬_smi); - search_num.Bind(SmiToFloat64(search_element)); - Goto(&heap_num_loop); - - BIND(¬_smi); - Node* map = LoadMap(search_element); - GotoIfNot(IsHeapNumberMap(map), ¬_heap_num); - search_num.Bind(LoadHeapNumberValue(search_element)); - Goto(&heap_num_loop); - - BIND(¬_heap_num); - Node* search_type = LoadMapInstanceType(map); - GotoIf(IsStringInstanceType(search_type), &string_loop); - Goto(&ident_loop); - - BIND(&ident_loop); - { - GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()), - &return_not_found); - Node* element_k = LoadFixedArrayElement(elements, index_var.value()); - GotoIf(WordEqual(element_k, search_element), &return_found); - - index_var.Bind(IntPtrAdd(index_var.value(), intptr_one)); - Goto(&ident_loop); - } - - BIND(&heap_num_loop); - { - Label not_nan_loop(this, &index_var); - BranchIfFloat64IsNaN(search_num.value(), &return_not_found, - ¬_nan_loop); - - BIND(¬_nan_loop); - { - Label continue_loop(this), not_smi(this); - GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()), - &return_not_found); - Node* element_k = LoadFixedArrayElement(elements, index_var.value()); - GotoIfNot(TaggedIsSmi(element_k), ¬_smi); - Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)), - &return_found, &continue_loop); - - BIND(¬_smi); - GotoIfNot(IsHeapNumber(element_k), &continue_loop); - Branch(Float64Equal(search_num.value(), LoadHeapNumberValue(element_k)), - &return_found, &continue_loop); - - BIND(&continue_loop); - index_var.Bind(IntPtrAdd(index_var.value(), intptr_one)); - Goto(¬_nan_loop); - } - } - - BIND(&string_loop); - { - Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()), - &return_not_found); - Node* element_k = LoadFixedArrayElement(elements, index_var.value()); - GotoIf(TaggedIsSmi(element_k), &continue_loop); - GotoIfNot(IsString(element_k), &continue_loop); - - // TODO(bmeurer): Consider inlining the StringEqual logic here. - Callable callable = CodeFactory::StringEqual(isolate()); - Node* result = CallStub(callable, context, search_element, element_k); - Branch(WordEqual(BooleanConstant(true), result), &return_found, - &continue_loop); - - BIND(&continue_loop); - index_var.Bind(IntPtrAdd(index_var.value(), intptr_one)); - Goto(&string_loop); - } - } - - BIND(&if_packed_doubles); { - Label not_nan_loop(this, &index_var), search_notnan(this); - VARIABLE(search_num, MachineRepresentation::kFloat64); - - GotoIfNot(TaggedIsSmi(search_element), &search_notnan); - search_num.Bind(SmiToFloat64(search_element)); - Goto(¬_nan_loop); - - BIND(&search_notnan); - GotoIfNot(IsHeapNumber(search_element), &return_not_found); - - search_num.Bind(LoadHeapNumberValue(search_element)); - - BranchIfFloat64IsNaN(search_num.value(), &return_not_found, ¬_nan_loop); - - // Search for HeapNumber - BIND(¬_nan_loop); - { - GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()), - &return_not_found); - Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), - MachineType::Float64()); - GotoIf(Float64Equal(element_k, search_num.value()), &return_found); - - index_var.Bind(IntPtrAdd(index_var.value(), intptr_one)); - Goto(¬_nan_loop); - } - } - - BIND(&if_holey_doubles); - { - Label not_nan_loop(this, &index_var), search_notnan(this); - VARIABLE(search_num, MachineRepresentation::kFloat64); - - GotoIfNot(TaggedIsSmi(search_element), &search_notnan); - search_num.Bind(SmiToFloat64(search_element)); - Goto(¬_nan_loop); - - BIND(&search_notnan); - GotoIfNot(IsHeapNumber(search_element), &return_not_found); - - search_num.Bind(LoadHeapNumberValue(search_element)); - - BranchIfFloat64IsNaN(search_num.value(), &return_not_found, ¬_nan_loop); - - // Search for HeapNumber - BIND(¬_nan_loop); - { - Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()), - &return_not_found); - - // Load double value or continue if it contains a double hole. - Node* element_k = LoadFixedDoubleArrayElement( - elements, index_var.value(), MachineType::Float64(), 0, - INTPTR_PARAMETERS, &continue_loop); - - Branch(Float64Equal(element_k, search_num.value()), &return_found, - &continue_loop); - BIND(&continue_loop); - index_var.Bind(IntPtrAdd(index_var.value(), intptr_one)); - Goto(¬_nan_loop); - } + Node* start_from = + args.GetOptionalArgumentValue(kFromIndexArg, UndefinedConstant()); + Runtime::FunctionId function = variant == kIncludes + ? Runtime::kArrayIncludes_Slow + : Runtime::kArrayIndexOf; + args.PopAndReturn( + CallRuntime(function, context, array, search_element, start_from)); } +} - BIND(&return_found); - Return(SmiTag(index_var.value())); - - BIND(&return_not_found); - Return(NumberConstant(-1)); - - BIND(&call_runtime); - Return(CallRuntime(Runtime::kArrayIndexOf, context, array, search_element, - start_from)); +TF_BUILTIN(ArrayIncludes, ArrayIncludesIndexofAssembler) { + Generate(kIncludes); } +TF_BUILTIN(ArrayIndexOf, ArrayIncludesIndexofAssembler) { Generate(kIndexOf); } + class ArrayPrototypeIterationAssembler : public CodeStubAssembler { public: explicit ArrayPrototypeIterationAssembler(compiler::CodeAssemblerState* state) @@ -1945,8 +2227,7 @@ TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) { { Node* length = GetProperty(context, array, factory()->length_string()); - Callable to_length = CodeFactory::ToLength(isolate()); - var_length.Bind(CallStub(to_length, context, length)); + var_length.Bind(ToLength_Inline(context, length)); Goto(&done); } |