[Torque] Port Array.prototype.find and findIndex to Torque

Happily, with the port of Array.prototype.find and findIndex, we can
remove a large set of library functions from array-builtins-gen.cc.

BUG: v8:7672
Change-Id: I74e07fe00162b34b2246c868386d4551ba4dc032
Reviewed-on: https://chromium-review.googlesource.com/c/1484296
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59902}
This commit is contained in:
Mike Stanton 2019-02-27 14:26:01 +01:00 committed by Commit Bot
parent 1937e2b128
commit ada192fe21
7 changed files with 332 additions and 527 deletions

View File

@ -902,6 +902,8 @@ torque_files = [
"src/builtins/array-copywithin.tq",
"src/builtins/array-every.tq",
"src/builtins/array-filter.tq",
"src/builtins/array-find.tq",
"src/builtins/array-findindex.tq",
"src/builtins/array-foreach.tq",
"src/builtins/array-join.tq",
"src/builtins/array-lastindexof.tq",
@ -933,6 +935,8 @@ torque_namespaces = [
"array",
"array-copywithin",
"array-filter",
"array-find",
"array-findindex",
"array-foreach",
"array-join",
"array-map",

158
src/builtins/array-find.tq Normal file
View File

@ -0,0 +1,158 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
namespace array_find {
transitioning javascript builtin
ArrayFindLoopEagerDeoptContinuation(implicit context: Context)(
receiver: Object, callback: Object, thisArg: Object, initialK: Object,
length: Object): Object {
// All continuation points in the optimized find implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires Object type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArrayFindLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
}
transitioning javascript builtin
ArrayFindLoopLazyDeoptContinuation(implicit context: Context)(
receiver: Object, callback: Object, thisArg: Object, initialK: Object,
length: Object, result: Object): Object {
// This deopt continuation point is never actually called, it just
// exists to make stack traces correct from a ThrowTypeError if the
// callback was found to be non-callable.
unreachable;
}
// Continuation that is called after a lazy deoptimization from TF that
// happens right after the callback and it's returned value must be handled
// before iteration continues.
transitioning javascript builtin
ArrayFindLoopAfterCallbackLazyDeoptContinuation(implicit context: Context)(
receiver: Object, callback: Object, thisArg: Object, initialK: Object,
length: Object, foundValue: Object, isFound: Object): Object {
// All continuation points in the optimized find implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// This custom lazy deopt point is right after the callback. find() needs
// to pick up at the next step, which is returning the element if the
// callback value is truthy. Otherwise, continue the search by calling the
// continuation.
if (ToBoolean(isFound)) {
return foundValue;
}
return ArrayFindLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
}
transitioning builtin ArrayFindLoopContinuation(implicit context: Context)(
receiver: JSReceiver, callbackfn: Callable, thisArg: Object,
o: JSReceiver, initialK: Number, length: Number): Object {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 6b. i. Let kValue be ? Get(O, Pk).
const value: Object = GetProperty(o, k);
// 6c. Let testResult be ToBoolean(? Call(predicate, T, <<kValue, k,
// O>>)).
const testResult: Object =
Call(context, callbackfn, thisArg, value, k, o);
// 6d. If testResult is true, return kValue.
if (ToBoolean(testResult)) {
return value;
}
// 6e. Increase k by 1. (done by the loop).
}
return Undefined;
}
transitioning macro FastArrayFind(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: Object): Object
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = FastJSArrayWitness{fastO};
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
const value: Object = fastOW.LoadElementOrUndefined(k);
const testResult: Object =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
if (ToBoolean(testResult)) {
return value;
}
}
return Undefined;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.find
transitioning javascript builtin
ArrayPrototypeFind(implicit context: Context)(receiver: Object, ...arguments):
Object {
try {
if (IsNullOrUndefined(receiver)) {
goto NullOrUndefinedError;
}
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NotCallableError;
}
const callbackfn =
Cast<Callable>(arguments[0]) otherwise NotCallableError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: Object = arguments.length > 1 ? arguments[1] : Undefined;
// Special cases.
try {
return FastArrayFind(o, len, callbackfn, thisArg)
otherwise Bailout;
}
label Bailout(k: Smi) deferred {
return ArrayFindLoopContinuation(o, callbackfn, thisArg, o, k, len);
}
}
label NotCallableError deferred {
ThrowTypeError(kCalledNonCallable, arguments[0]);
}
label NullOrUndefinedError deferred {
ThrowTypeError(kCalledOnNullOrUndefined, 'Array.prototype.find');
}
}
}

View File

@ -0,0 +1,161 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
namespace array_findindex {
transitioning javascript builtin
ArrayFindIndexLoopEagerDeoptContinuation(implicit context: Context)(
receiver: Object, callback: Object, thisArg: Object, initialK: Object,
length: Object): Object {
// All continuation points in the optimized findIndex implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
//
// Also, this great mass of casts is necessary because the signature
// of Torque javascript builtins requires Object type for all parameters
// other than {context}.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
return ArrayFindIndexLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
}
transitioning javascript builtin
ArrayFindIndexLoopLazyDeoptContinuation(implicit context: Context)(
receiver: Object, callback: Object, thisArg: Object, initialK: Object,
length: Object, result: Object): Object {
// This deopt continuation point is never actually called, it just
// exists to make stack traces correct from a ThrowTypeError if the
// callback was found to be non-callable.
unreachable;
}
// Continuation that is called after a lazy deoptimization from TF that
// happens right after the callback and it's returned value must be handled
// before iteration continues.
transitioning javascript builtin
ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation(implicit context:
Context)(
receiver: Object, callback: Object, thisArg: Object, initialK: Object,
length: Object, foundValue: Object, isFound: Object): Object {
// All continuation points in the optimized findIndex implementation are
// after the ToObject(O) call that ensures we are dealing with a
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// This custom lazy deopt point is right after the callback. find() needs
// to pick up at the next step, which is returning the element if the
// callback value is truthy. Otherwise, continue the search by calling the
// continuation.
if (ToBoolean(isFound)) {
return foundValue;
}
return ArrayFindIndexLoopContinuation(
jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength);
}
transitioning builtin ArrayFindIndexLoopContinuation(implicit context:
Context)(
receiver: JSReceiver, callbackfn: Callable, thisArg: Object,
o: JSReceiver, initialK: Number, length: Number): Number {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Number = initialK; k < length; k++) {
// 6a. Let Pk be ! ToString(k).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 6b. i. Let kValue be ? Get(O, Pk).
const value: Object = GetProperty(o, k);
// 6c. Let testResult be ToBoolean(? Call(predicate, T, <<kValue, k,
// O>>)).
const testResult: Object =
Call(context, callbackfn, thisArg, value, k, o);
// 6d. If testResult is true, return k.
if (ToBoolean(testResult)) {
return k;
}
// 6e. Increase k by 1. (done by the loop).
}
return Convert<Smi>(-1);
}
transitioning macro FastArrayFindIndex(implicit context: Context)(
o: JSReceiver, len: Number, callbackfn: Callable, thisArg: Object): Number
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = FastJSArrayWitness{fastO};
// Build a fast loop over the smi array.
for (; k < smiLen; k++) {
fastOW.Recheck() otherwise goto Bailout(k);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
const value: Object = fastOW.LoadElementOrUndefined(k);
const testResult: Object =
Call(context, callbackfn, thisArg, value, k, fastOW.Get());
if (ToBoolean(testResult)) {
return k;
}
}
return -1;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
transitioning javascript builtin
ArrayPrototypeFindIndex(implicit context:
Context)(receiver: Object, ...arguments): Object {
try {
if (IsNullOrUndefined(receiver)) {
goto NullOrUndefinedError;
}
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
const len: Number = GetLengthProperty(o);
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NotCallableError;
}
const callbackfn =
Cast<Callable>(arguments[0]) otherwise NotCallableError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
const thisArg: Object = arguments.length > 1 ? arguments[1] : Undefined;
// Special cases.
try {
return FastArrayFindIndex(o, len, callbackfn, thisArg)
otherwise Bailout;
}
label Bailout(k: Smi) deferred {
return ArrayFindIndexLoopContinuation(
o, callbackfn, thisArg, o, k, len);
}
}
label NotCallableError deferred {
ThrowTypeError(kCalledNonCallable, arguments[0]);
}
label NullOrUndefinedError deferred {
ThrowTypeError(kCalledOnNullOrUndefined, 'Array.prototype.findIndex');
}
}
}

View File

@ -1526,6 +1526,15 @@ struct FastJSArrayWitness {
}
}
LoadElementOrUndefined(implicit context: Context)(k: Smi): Object {
try {
return this.LoadElementNoHole(k) otherwise FoundHole;
}
label FoundHole {
return Undefined;
}
}
stable: JSArray;
unstable: FastJSArray;
map: Map;

View File

@ -233,97 +233,6 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) {
argc_ = argc;
}
void ArrayBuiltinsAssembler::GenerateIteratingArrayBuiltinBody(
const char* name, const BuiltinResultGenerator& generator,
const CallResultProcessor& processor, const PostLoopAction& action,
const Callable& slow_case_continuation,
MissingPropertyMode missing_property_mode, ForEachDirection direction) {
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?
Label throw_null_undefined_exception(this, Label::kDeferred);
GotoIf(IsNullOrUndefined(receiver()), &throw_null_undefined_exception);
// By the book: taken directly from the ECMAScript 2015 specification
// 1. Let O be ToObject(this value).
// 2. ReturnIfAbrupt(O)
o_ = ToObject_Inline(context(), receiver());
// 3. Let len be ToLength(Get(O, "length")).
// 4. ReturnIfAbrupt(len).
TVARIABLE(Number, merged_length);
Label has_length(this, &merged_length), not_js_array(this);
GotoIf(DoesntHaveInstanceType(o(), JS_ARRAY_TYPE), &not_js_array);
merged_length = LoadJSArrayLength(CAST(o()));
Goto(&has_length);
BIND(&not_js_array);
{
Node* len_property =
GetProperty(context(), o(), isolate()->factory()->length_string());
merged_length = ToLength_Inline(context(), len_property);
Goto(&has_length);
}
BIND(&has_length);
{
len_ = merged_length.value();
// 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
Label type_exception(this, Label::kDeferred);
Label done(this);
GotoIf(TaggedIsSmi(callbackfn()), &type_exception);
Branch(IsCallableMap(LoadMap(callbackfn())), &done, &type_exception);
BIND(&throw_null_undefined_exception);
ThrowTypeError(context(), MessageTemplate::kCalledOnNullOrUndefined,
name);
BIND(&type_exception);
ThrowTypeError(context(), MessageTemplate::kCalledNonCallable,
callbackfn());
BIND(&done);
}
// 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
// [Already done by the arguments adapter]
if (direction == ForEachDirection::kForward) {
// 7. Let k be 0.
k_.Bind(SmiConstant(0));
} else {
k_.Bind(NumberDec(len()));
}
generator(this);
HandleFastElements(processor, action, &fully_spec_compliant_, direction,
missing_property_mode);
BIND(&fully_spec_compliant_);
Node* result =
CallStub(slow_case_continuation, context(), receiver(), callbackfn(),
this_arg(), a_.value(), o(), k_.value(), len(), to_.value());
ReturnFromBuiltin(result);
}
void ArrayBuiltinsAssembler::InitIteratingArrayBuiltinLoopContinuation(
TNode<Context> context, TNode<Object> receiver, Node* callbackfn,
Node* this_arg, Node* a, TNode<JSReceiver> o, Node* initial_k,
TNode<Number> len, Node* to) {
context_ = context;
this_arg_ = this_arg;
callbackfn_ = callbackfn;
a_.Bind(a);
k_.Bind(initial_k);
o_ = o;
len_ = len;
to_.Bind(to);
}
void ArrayBuiltinsAssembler::GenerateIteratingTypedArrayBuiltinBody(
const char* name, const BuiltinResultGenerator& generator,
const CallResultProcessor& processor, const PostLoopAction& action,
@ -410,65 +319,6 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) {
}
}
void ArrayBuiltinsAssembler::GenerateIteratingArrayBuiltinLoopContinuation(
const CallResultProcessor& processor, const PostLoopAction& action,
MissingPropertyMode missing_property_mode, ForEachDirection direction) {
Label loop(this, {&k_, &a_, &to_});
Label after_loop(this);
Goto(&loop);
BIND(&loop);
{
if (direction == ForEachDirection::kForward) {
// 8. Repeat, while k < len
GotoIfNumberGreaterThanOrEqual(k(), len_, &after_loop);
} else {
// OR
// 10. Repeat, while k >= 0
GotoIfNumberGreaterThanOrEqual(SmiConstant(-1), k(), &after_loop);
}
Label done_element(this, &to_);
// a. Let Pk be ToString(k).
// k() is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
CSA_ASSERT(this, IsSafeInteger(k()));
if (missing_property_mode == MissingPropertyMode::kSkip) {
// b. Let kPresent be HasProperty(O, Pk).
// c. ReturnIfAbrupt(kPresent).
TNode<Oddball> k_present =
HasProperty(context(), o(), k(), kHasProperty);
// d. If kPresent is true, then
GotoIf(IsFalse(k_present), &done_element);
}
// i. Let kValue be Get(O, Pk).
// ii. ReturnIfAbrupt(kValue).
Node* k_value = GetProperty(context(), o(), k());
// iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»).
// iv. ReturnIfAbrupt(funcResult).
a_.Bind(processor(this, k_value, k()));
Goto(&done_element);
BIND(&done_element);
if (direction == ForEachDirection::kForward) {
// e. Increase k by 1.
k_.Bind(NumberInc(k()));
} else {
// e. Decrease k by 1.
k_.Bind(NumberDec(k()));
}
Goto(&loop);
}
BIND(&after_loop);
action(this);
Return(a_.value());
}
ElementsKind ArrayBuiltinsAssembler::ElementsKindForInstanceType(
InstanceType type) {
switch (type) {
@ -516,133 +366,6 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) {
advance_mode);
}
void ArrayBuiltinsAssembler::VisitAllFastElementsOneKind(
ElementsKind kind, const CallResultProcessor& processor,
Label* array_changed, ParameterMode mode, ForEachDirection direction,
MissingPropertyMode missing_property_mode, TNode<Smi> length) {
Comment("begin VisitAllFastElementsOneKind");
// We only use this kind of processing if the no-elements protector is
// in place at the start. We'll continue checking during array iteration.
CSA_ASSERT(this, Word32BinaryNot(IsNoElementsProtectorCellInvalid()));
VARIABLE(original_map, MachineRepresentation::kTagged);
original_map.Bind(LoadMap(o()));
VariableList list({&original_map, &a_, &k_, &to_}, zone());
Node* start = IntPtrOrSmiConstant(0, mode);
Node* end = TaggedToParameter(length, mode);
IndexAdvanceMode advance_mode = direction == ForEachDirection::kReverse
? IndexAdvanceMode::kPre
: IndexAdvanceMode::kPost;
if (direction == ForEachDirection::kReverse) std::swap(start, end);
BuildFastLoop(
list, start, end,
[=, &original_map](Node* index) {
k_.Bind(ParameterToTagged(index, mode));
Label one_element_done(this), hole_element(this),
process_element(this);
// Check if o's map has changed during the callback. If so, we have to
// fall back to the slower spec implementation for the rest of the
// iteration.
Node* o_map = LoadMap(o());
GotoIf(WordNotEqual(o_map, original_map.value()), array_changed);
TNode<JSArray> o_array = CAST(o());
// Check if o's length has changed during the callback and if the
// index is now out of range of the new length.
GotoIf(SmiGreaterThanOrEqual(CAST(k_.value()),
CAST(LoadJSArrayLength(o_array))),
array_changed);
// Re-load the elements array. If may have been resized.
Node* elements = LoadElements(o_array);
// Fast case: load the element directly from the elements FixedArray
// and call the callback if the element is not the hole.
DCHECK(kind == PACKED_ELEMENTS || kind == PACKED_DOUBLE_ELEMENTS);
int base_size = kind == PACKED_ELEMENTS
? FixedArray::kHeaderSize
: (FixedArray::kHeaderSize - kHeapObjectTag);
Node* offset = ElementOffsetFromIndex(index, kind, mode, base_size);
VARIABLE(value, MachineRepresentation::kTagged);
if (kind == PACKED_ELEMENTS) {
value.Bind(LoadObjectField(elements, offset));
GotoIf(WordEqual(value.value(), TheHoleConstant()), &hole_element);
} else {
Node* double_value =
LoadDoubleWithHoleCheck(elements, offset, &hole_element);
value.Bind(AllocateHeapNumberWithValue(double_value));
}
Goto(&process_element);
BIND(&hole_element);
if (missing_property_mode == MissingPropertyMode::kSkip) {
// The NoElementsProtectorCell could go invalid during callbacks.
Branch(IsNoElementsProtectorCellInvalid(), array_changed,
&one_element_done);
} else {
value.Bind(UndefinedConstant());
Goto(&process_element);
}
BIND(&process_element);
{
a_.Bind(processor(this, value.value(), k()));
Goto(&one_element_done);
}
BIND(&one_element_done);
},
1, mode, advance_mode);
Comment("end VisitAllFastElementsOneKind");
}
void ArrayBuiltinsAssembler::HandleFastElements(
const CallResultProcessor& processor, const PostLoopAction& action,
Label* slow, ForEachDirection direction,
MissingPropertyMode missing_property_mode) {
Label switch_on_elements_kind(this), fast_elements(this),
maybe_double_elements(this), fast_double_elements(this);
Comment("begin HandleFastElements");
// Non-smi lengths must use the slow path.
GotoIf(TaggedIsNotSmi(len()), slow);
BranchIfFastJSArray(o(), context(),
&switch_on_elements_kind, slow);
BIND(&switch_on_elements_kind);
TNode<Smi> smi_len = CAST(len());
// Select by ElementsKind
Node* kind = LoadElementsKind(o());
Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS),
&maybe_double_elements, &fast_elements);
ParameterMode mode = OptimalParameterMode();
BIND(&fast_elements);
{
VisitAllFastElementsOneKind(PACKED_ELEMENTS, processor, slow, mode,
direction, missing_property_mode, smi_len);
action(this);
// No exception, return success
ReturnFromBuiltin(a_.value());
}
BIND(&maybe_double_elements);
Branch(IsElementsKindGreaterThan(kind, HOLEY_DOUBLE_ELEMENTS), slow,
&fast_double_elements);
BIND(&fast_double_elements);
{
VisitAllFastElementsOneKind(PACKED_DOUBLE_ELEMENTS, processor, slow, mode,
direction, missing_property_mode, smi_len);
action(this);
// No exception, return success
ReturnFromBuiltin(a_.value());
}
}
// Perform ArraySpeciesCreate (ES6 #sec-arrayspeciescreate).
void ArrayBuiltinsAssembler::GenerateArraySpeciesCreate(TNode<Number> len) {
Label runtime(this, Label::kDeferred), done(this);
@ -1085,199 +808,6 @@ TF_BUILTIN(CloneFastJSArrayFillingHoles, ArrayBuiltinsAssembler) {
HoleConversionMode::kConvertToUndefined));
}
TF_BUILTIN(ArrayFindLoopContinuation, ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* array = Parameter(Descriptor::kArray);
TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
Node* initial_k = Parameter(Descriptor::kInitialK);
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
Node* to = Parameter(Descriptor::kTo);
InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
this_arg, array, object, initial_k,
len, to);
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinsAssembler::FindProcessor,
&ArrayBuiltinsAssembler::NullPostLoopAction,
MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
}
// Continuation that is called after an eager deoptimization from TF (ex. the
// array changes during iteration).
TF_BUILTIN(ArrayFindLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* initial_k = Parameter(Descriptor::kInitialK);
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
Return(CallBuiltin(Builtins::kArrayFindLoopContinuation, context, receiver,
callbackfn, this_arg, UndefinedConstant(), receiver,
initial_k, len, UndefinedConstant()));
}
// Continuation that is called after a lazy deoptimization from TF (ex. the
// callback function is no longer callable).
TF_BUILTIN(ArrayFindLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* initial_k = Parameter(Descriptor::kInitialK);
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
Return(CallBuiltin(Builtins::kArrayFindLoopContinuation, context, receiver,
callbackfn, this_arg, UndefinedConstant(), receiver,
initial_k, len, UndefinedConstant()));
}
// Continuation that is called after a lazy deoptimization from TF that happens
// right after the callback and it's returned value must be handled before
// iteration continues.
TF_BUILTIN(ArrayFindLoopAfterCallbackLazyDeoptContinuation,
ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* initial_k = Parameter(Descriptor::kInitialK);
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
Node* found_value = Parameter(Descriptor::kFoundValue);
Node* is_found = Parameter(Descriptor::kIsFound);
// This custom lazy deopt point is right after the callback. find() needs
// to pick up at the next step, which is returning the element if the callback
// value is truthy. Otherwise, continue the search by calling the
// continuation.
Label if_true(this), if_false(this);
BranchIfToBooleanIsTrue(is_found, &if_true, &if_false);
BIND(&if_true);
Return(found_value);
BIND(&if_false);
Return(CallBuiltin(Builtins::kArrayFindLoopContinuation, context, receiver,
callbackfn, this_arg, UndefinedConstant(), receiver,
initial_k, len, UndefinedConstant()));
}
// ES #sec-get-%typedarray%.prototype.find
TF_BUILTIN(ArrayPrototypeFind, ArrayBuiltinsAssembler) {
TNode<IntPtrT> argc =
ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
CodeStubArguments args(this, argc);
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = args.GetReceiver();
Node* callbackfn = args.GetOptionalArgumentValue(0);
Node* this_arg = args.GetOptionalArgumentValue(1);
InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
GenerateIteratingArrayBuiltinBody(
"Array.prototype.find", &ArrayBuiltinsAssembler::FindResultGenerator,
&ArrayBuiltinsAssembler::FindProcessor,
&ArrayBuiltinsAssembler::NullPostLoopAction,
Builtins::CallableFor(isolate(), Builtins::kArrayFindLoopContinuation),
MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
}
TF_BUILTIN(ArrayFindIndexLoopContinuation, ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* array = Parameter(Descriptor::kArray);
TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
Node* initial_k = Parameter(Descriptor::kInitialK);
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
Node* to = Parameter(Descriptor::kTo);
InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
this_arg, array, object, initial_k,
len, to);
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinsAssembler::FindIndexProcessor,
&ArrayBuiltinsAssembler::NullPostLoopAction,
MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
}
TF_BUILTIN(ArrayFindIndexLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* initial_k = Parameter(Descriptor::kInitialK);
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
Return(CallBuiltin(Builtins::kArrayFindIndexLoopContinuation, context,
receiver, callbackfn, this_arg, SmiConstant(-1), receiver,
initial_k, len, UndefinedConstant()));
}
TF_BUILTIN(ArrayFindIndexLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* initial_k = Parameter(Descriptor::kInitialK);
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
Return(CallBuiltin(Builtins::kArrayFindIndexLoopContinuation, context,
receiver, callbackfn, this_arg, SmiConstant(-1), receiver,
initial_k, len, UndefinedConstant()));
}
TF_BUILTIN(ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation,
ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* initial_k = Parameter(Descriptor::kInitialK);
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
Node* found_value = Parameter(Descriptor::kFoundValue);
Node* is_found = Parameter(Descriptor::kIsFound);
// This custom lazy deopt point is right after the callback. find() needs
// to pick up at the next step, which is returning the element if the callback
// value is truthy. Otherwise, continue the search by calling the
// continuation.
Label if_true(this), if_false(this);
BranchIfToBooleanIsTrue(is_found, &if_true, &if_false);
BIND(&if_true);
Return(found_value);
BIND(&if_false);
Return(CallBuiltin(Builtins::kArrayFindIndexLoopContinuation, context,
receiver, callbackfn, this_arg, SmiConstant(-1), receiver,
initial_k, len, UndefinedConstant()));
}
// ES #sec-get-%typedarray%.prototype.findIndex
TF_BUILTIN(ArrayPrototypeFindIndex, ArrayBuiltinsAssembler) {
TNode<IntPtrT> argc =
ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
CodeStubArguments args(this, argc);
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = args.GetReceiver();
Node* callbackfn = args.GetOptionalArgumentValue(0);
Node* this_arg = args.GetOptionalArgumentValue(1);
InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
GenerateIteratingArrayBuiltinBody(
"Array.prototype.findIndex",
&ArrayBuiltinsAssembler::FindIndexResultGenerator,
&ArrayBuiltinsAssembler::FindIndexProcessor,
&ArrayBuiltinsAssembler::NullPostLoopAction,
Builtins::CallableFor(isolate(),
Builtins::kArrayFindIndexLoopContinuation),
MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
}
class ArrayPopulatorAssembler : public CodeStubAssembler {
public:
explicit ArrayPopulatorAssembler(compiler::CodeAssemblerState* state)

View File

@ -23,8 +23,6 @@ class ArrayBuiltinsAssembler : public CodeStubAssembler {
typedef std::function<void(ArrayBuiltinsAssembler* masm)> PostLoopAction;
enum class MissingPropertyMode { kSkip, kUseUndefined };
void FindResultGenerator();
Node* FindProcessor(Node* k_value, Node* k);
@ -51,12 +49,6 @@ class ArrayBuiltinsAssembler : public CodeStubAssembler {
void ReducePostLoopAction();
void FilterResultGenerator();
Node* FilterProcessor(Node* k_value, Node* k);
void MapResultGenerator();
void TypedArrayMapResultGenerator();
Node* SpecCompliantMapProcessor(Node* k_value, Node* k);
@ -106,27 +98,11 @@ class ArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<Object> receiver, Node* callbackfn,
Node* this_arg, TNode<IntPtrT> argc);
void GenerateIteratingArrayBuiltinBody(
const char* name, const BuiltinResultGenerator& generator,
const CallResultProcessor& processor, const PostLoopAction& action,
const Callable& slow_case_continuation,
MissingPropertyMode missing_property_mode,
ForEachDirection direction = ForEachDirection::kForward);
void InitIteratingArrayBuiltinLoopContinuation(
TNode<Context> context, TNode<Object> receiver, Node* callbackfn,
Node* this_arg, Node* a, TNode<JSReceiver> o, Node* initial_k,
TNode<Number> len, Node* to);
void GenerateIteratingTypedArrayBuiltinBody(
const char* name, const BuiltinResultGenerator& generator,
const CallResultProcessor& processor, const PostLoopAction& action,
ForEachDirection direction = ForEachDirection::kForward);
void GenerateIteratingArrayBuiltinLoopContinuation(
const CallResultProcessor& processor, const PostLoopAction& action,
MissingPropertyMode missing_property_mode,
ForEachDirection direction = ForEachDirection::kForward);
void TailCallArrayConstructorStub(
const Callable& callable, TNode<Context> context,
TNode<JSFunction> target, TNode<HeapObject> allocation_site_or_undefined,
@ -167,18 +143,6 @@ class ArrayBuiltinsAssembler : public CodeStubAssembler {
Label* detached, ForEachDirection direction,
TNode<JSTypedArray> typed_array);
void VisitAllFastElementsOneKind(ElementsKind kind,
const CallResultProcessor& processor,
Label* array_changed, ParameterMode mode,
ForEachDirection direction,
MissingPropertyMode missing_property_mode,
TNode<Smi> length);
void HandleFastElements(const CallResultProcessor& processor,
const PostLoopAction& action, Label* slow,
ForEachDirection direction,
MissingPropertyMode missing_property_mode);
// Perform ArraySpeciesCreate (ES6 #sec-arrayspeciescreate).
// This version is specialized to create a zero length array
// of the elements kind of the input array.

View File

@ -352,27 +352,6 @@ namespace internal {
TFS(ExtractFastJSArray, kSource, kBegin, kCount) \
/* ES6 #sec-array.prototype.entries */ \
TFJ(ArrayPrototypeEntries, 0, kReceiver) \
/* ES6 #sec-array.prototype.find */ \
TFS(ArrayFindLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
kObject, kInitialK, kLength, kTo) \
TFJ(ArrayFindLoopEagerDeoptContinuation, 4, kReceiver, kCallbackFn, \
kThisArg, kInitialK, kLength) \
TFJ(ArrayFindLoopLazyDeoptContinuation, 5, kReceiver, kCallbackFn, kThisArg, \
kInitialK, kLength, kResult) \
TFJ(ArrayFindLoopAfterCallbackLazyDeoptContinuation, 6, kReceiver, \
kCallbackFn, kThisArg, kInitialK, kLength, kFoundValue, kIsFound) \
TFJ(ArrayPrototypeFind, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.findIndex */ \
TFS(ArrayFindIndexLoopContinuation, kReceiver, kCallbackFn, kThisArg, \
kArray, kObject, kInitialK, kLength, kTo) \
TFJ(ArrayFindIndexLoopEagerDeoptContinuation, 4, kReceiver, kCallbackFn, \
kThisArg, kInitialK, kLength) \
TFJ(ArrayFindIndexLoopLazyDeoptContinuation, 5, kReceiver, kCallbackFn, \
kThisArg, kInitialK, kLength, kResult) \
TFJ(ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation, 6, kReceiver, \
kCallbackFn, kThisArg, kInitialK, kLength, kFoundValue, kIsFound) \
TFJ(ArrayPrototypeFindIndex, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.keys */ \
TFJ(ArrayPrototypeKeys, 0, kReceiver) \
/* ES6 #sec-array.prototype.values */ \