summaryrefslogtreecommitdiff
path: root/deps/v8/src/builtins/builtins-array-gen.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/builtins/builtins-array-gen.cc')
-rw-r--r--deps/v8/src/builtins/builtins-array-gen.cc1465
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(&not_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), &not_smi);
search_num.Bind(SmiToFloat64(search_element));
Goto(&heap_num_loop);
BIND(&not_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), &not_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, &not_nan_loop);
+ Label* nan_handling =
+ variant == kIncludes ? &nan_loop : &return_not_found;
+ BranchIfFloat64IsNaN(search_num.value(), nan_handling, &not_nan_loop);
BIND(&not_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), &not_smi);
Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)),
- &return_true, &continue_loop);
+ &return_found, &continue_loop);
BIND(&not_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(&not_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(&not_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, &not_nan_loop);
+ Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
+ BranchIfFloat64IsNaN(search_num.value(), nan_handling, &not_nan_loop);
- // Search for HeapNumber
BIND(&not_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(&not_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(&not_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, &not_nan_loop);
+ Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
+ BranchIfFloat64IsNaN(search_num.value(), nan_handling, &not_nan_loop);
- // Search for HeapNumber
BIND(&not_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(&not_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), &not_smi);
- search_num.Bind(SmiToFloat64(search_element));
- Goto(&heap_num_loop);
-
- BIND(&not_smi);
- Node* map = LoadMap(search_element);
- GotoIfNot(IsHeapNumberMap(map), &not_heap_num);
- search_num.Bind(LoadHeapNumberValue(search_element));
- Goto(&heap_num_loop);
-
- BIND(&not_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,
- &not_nan_loop);
-
- BIND(&not_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), &not_smi);
- Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)),
- &return_found, &continue_loop);
-
- BIND(&not_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(&not_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(&not_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, &not_nan_loop);
-
- // Search for HeapNumber
- BIND(&not_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(&not_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(&not_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, &not_nan_loop);
-
- // Search for HeapNumber
- BIND(&not_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(&not_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);
}