[builtins] Implement Array.prototype.reduceRight in the CSA
BUG=v8:1956 Review-Url: https://codereview.chromium.org/2776433003 Cr-Commit-Position: refs/heads/master@{#44105}
This commit is contained in:
parent
8b8dfda8cf
commit
6141f6e216
@ -4197,6 +4197,10 @@ bool Genesis::InstallNatives(GlobalContextType context_type) {
|
||||
|
||||
// Install Array.prototype.reduce
|
||||
InstallArrayBuiltinFunction(proto, "reduce", Builtins::kArrayReduce, 2);
|
||||
|
||||
// Install Array.prototype.reduceRight
|
||||
InstallArrayBuiltinFunction(proto, "reduceRight",
|
||||
Builtins::kArrayReduceRight, 2);
|
||||
}
|
||||
|
||||
// Install InternalArray.prototype.concat
|
||||
|
@ -194,7 +194,8 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
|
||||
void GenerateIteratingArrayBuiltinBody(
|
||||
const char* name, const BuiltinResultGenerator& generator,
|
||||
const CallResultProcessor& processor, const PostLoopAction& action,
|
||||
const Callable& slow_case_continuation) {
|
||||
const Callable& slow_case_continuation,
|
||||
ForEachDirection direction = ForEachDirection::kForward) {
|
||||
Label non_array(this), slow(this, {&k_, &a_, &to_}),
|
||||
array_changes(this, {&k_, &a_, &to_});
|
||||
|
||||
@ -256,12 +257,17 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
|
||||
// 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
// [Already done by the arguments adapter]
|
||||
|
||||
// 7. Let k be 0.
|
||||
// [Already done in code assembler initialization]
|
||||
if (direction == ForEachDirection::kForward) {
|
||||
// 7. Let k be 0.
|
||||
k_.Bind(SmiConstant(0));
|
||||
} else {
|
||||
k_.Bind(len());
|
||||
k_.Bind(NumberDec(k_.value()));
|
||||
}
|
||||
|
||||
a_.Bind(generator(this));
|
||||
|
||||
HandleFastElements(processor, action, &slow);
|
||||
HandleFastElements(processor, action, &slow, direction);
|
||||
|
||||
Bind(&slow);
|
||||
|
||||
@ -290,14 +296,21 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
|
||||
}
|
||||
|
||||
void GenerateIteratingArrayBuiltinLoopContinuation(
|
||||
const CallResultProcessor& processor, const PostLoopAction& action) {
|
||||
// 8. Repeat, while k < len
|
||||
const CallResultProcessor& processor, const PostLoopAction& action,
|
||||
ForEachDirection direction = ForEachDirection::kForward) {
|
||||
Label loop(this, {&k_, &a_, &to_});
|
||||
Label after_loop(this);
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
GotoUnlessNumberLessThan(k(), len_, &after_loop);
|
||||
if (direction == ForEachDirection::kForward) {
|
||||
// 8. Repeat, while k < len
|
||||
GotoUnlessNumberLessThan(k(), len_, &after_loop);
|
||||
} else {
|
||||
// OR
|
||||
// 10. Repeat, while k >= 0
|
||||
GotoUnlessNumberLessThan(SmiConstant(-1), k(), &after_loop);
|
||||
}
|
||||
|
||||
Label done_element(this, &to_);
|
||||
// a. Let Pk be ToString(k).
|
||||
@ -321,8 +334,13 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
|
||||
|
||||
Bind(&done_element);
|
||||
|
||||
// e. Increase k by 1.
|
||||
k_.Bind(NumberInc(k_.value()));
|
||||
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);
|
||||
@ -334,13 +352,20 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
|
||||
private:
|
||||
void VisitAllFastElementsOneKind(ElementsKind kind,
|
||||
const CallResultProcessor& processor,
|
||||
Label* array_changed, ParameterMode mode) {
|
||||
Label* array_changed, ParameterMode mode,
|
||||
ForEachDirection direction) {
|
||||
Comment("begin VisitAllFastElementsOneKind");
|
||||
Variable original_map(this, MachineRepresentation::kTagged);
|
||||
original_map.Bind(LoadMap(o()));
|
||||
VariableList list({&original_map, &a_, &k_, &to_}, zone());
|
||||
Node* start = IntPtrOrSmiConstant(0, mode);
|
||||
Node* end = TaggedToParameter(len(), mode);
|
||||
IndexAdvanceMode advance_mode = direction == ForEachDirection::kReverse
|
||||
? IndexAdvanceMode::kPre
|
||||
: IndexAdvanceMode::kPost;
|
||||
if (direction == ForEachDirection::kReverse) std::swap(start, end);
|
||||
BuildFastLoop(
|
||||
list, IntPtrOrSmiConstant(0, mode), TaggedToParameter(len(), mode),
|
||||
list, start, end,
|
||||
[=, &original_map](Node* index) {
|
||||
k_.Bind(ParameterToTagged(index, mode));
|
||||
Label one_element_done(this), hole_element(this);
|
||||
@ -386,12 +411,13 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
|
||||
|
||||
Bind(&one_element_done);
|
||||
},
|
||||
1, mode, IndexAdvanceMode::kPost);
|
||||
1, mode, advance_mode);
|
||||
Comment("end VisitAllFastElementsOneKind");
|
||||
}
|
||||
|
||||
void HandleFastElements(const CallResultProcessor& processor,
|
||||
const PostLoopAction& action, Label* slow) {
|
||||
const PostLoopAction& action, Label* slow,
|
||||
ForEachDirection direction) {
|
||||
Label switch_on_elements_kind(this), fast_elements(this),
|
||||
maybe_double_elements(this), fast_double_elements(this);
|
||||
|
||||
@ -414,7 +440,8 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
|
||||
ParameterMode mode = OptimalParameterMode();
|
||||
Bind(&fast_elements);
|
||||
{
|
||||
VisitAllFastElementsOneKind(FAST_ELEMENTS, processor, slow, mode);
|
||||
VisitAllFastElementsOneKind(FAST_ELEMENTS, processor, slow, mode,
|
||||
direction);
|
||||
|
||||
action(this);
|
||||
|
||||
@ -428,7 +455,8 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
|
||||
|
||||
Bind(&fast_double_elements);
|
||||
{
|
||||
VisitAllFastElementsOneKind(FAST_DOUBLE_ELEMENTS, processor, slow, mode);
|
||||
VisitAllFastElementsOneKind(FAST_DOUBLE_ELEMENTS, processor, slow, mode,
|
||||
direction);
|
||||
|
||||
action(this);
|
||||
|
||||
@ -757,6 +785,46 @@ TF_BUILTIN(ArrayReduce, ArrayBuiltinCodeStubAssembler) {
|
||||
CodeFactory::ArrayReduceLoopContinuation(isolate()));
|
||||
}
|
||||
|
||||
TF_BUILTIN(ArrayReduceRightLoopContinuation, ArrayBuiltinCodeStubAssembler) {
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
Node* receiver = Parameter(Descriptor::kReceiver);
|
||||
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
|
||||
Node* this_arg = Parameter(Descriptor::kThisArg);
|
||||
Node* accumulator = Parameter(Descriptor::kAccumulator);
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
Node* initial_k = Parameter(Descriptor::kInitialK);
|
||||
Node* len = Parameter(Descriptor::kLength);
|
||||
Node* to = Parameter(Descriptor::kTo);
|
||||
|
||||
InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
|
||||
this_arg, accumulator, object,
|
||||
initial_k, len, to);
|
||||
|
||||
GenerateIteratingArrayBuiltinLoopContinuation(
|
||||
&ArrayBuiltinCodeStubAssembler::ReduceProcessor,
|
||||
&ArrayBuiltinCodeStubAssembler::ReducePostLoopAction,
|
||||
ForEachDirection::kReverse);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value,
|
||||
new_target);
|
||||
|
||||
GenerateIteratingArrayBuiltinBody(
|
||||
"Array.prototype.reduceRight",
|
||||
&ArrayBuiltinCodeStubAssembler::ReduceResultGenerator,
|
||||
&ArrayBuiltinCodeStubAssembler::ReduceProcessor,
|
||||
&ArrayBuiltinCodeStubAssembler::ReducePostLoopAction,
|
||||
CodeFactory::ArrayReduceRightLoopContinuation(isolate()),
|
||||
ForEachDirection::kReverse);
|
||||
}
|
||||
|
||||
TF_BUILTIN(ArrayFilterLoopContinuation, ArrayBuiltinCodeStubAssembler) {
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
Node* receiver = Parameter(Descriptor::kReceiver);
|
||||
|
@ -308,6 +308,10 @@ class Isolate;
|
||||
TFJ(ArrayReduceLoopContinuation, 7, kCallbackFn, kThisArg, kAccumulator, \
|
||||
kObject, kInitialK, kLength, kTo) \
|
||||
TFJ(ArrayReduce, 2, kCallbackFn, kInitialValue) \
|
||||
/* ES6 #sec-array.prototype.reduceRight */ \
|
||||
TFJ(ArrayReduceRightLoopContinuation, 7, kCallbackFn, kThisArg, \
|
||||
kAccumulator, kObject, kInitialK, kLength, kTo) \
|
||||
TFJ(ArrayReduceRight, 2, kCallbackFn, kInitialValue) \
|
||||
/* ES6 #sec-array.prototype.entries */ \
|
||||
TFJ(ArrayPrototypeEntries, 0) \
|
||||
/* ES6 #sec-array.prototype.keys */ \
|
||||
|
@ -543,6 +543,12 @@ Callable CodeFactory::ArrayReduceLoopContinuation(Isolate* isolate) {
|
||||
IteratingArrayBuiltinLoopContinuationDescriptor(isolate));
|
||||
}
|
||||
|
||||
// static
|
||||
Callable CodeFactory::ArrayReduceRightLoopContinuation(Isolate* isolate) {
|
||||
return Callable(isolate->builtins()->ArrayReduceRightLoopContinuation(),
|
||||
IteratingArrayBuiltinLoopContinuationDescriptor(isolate));
|
||||
}
|
||||
|
||||
// static
|
||||
Callable CodeFactory::FunctionPrototypeBind(Isolate* isolate) {
|
||||
return Callable(isolate->builtins()->FunctionPrototypeBind(),
|
||||
|
@ -190,6 +190,7 @@ class V8_EXPORT_PRIVATE CodeFactory final {
|
||||
static Callable ArraySomeLoopContinuation(Isolate* isolate);
|
||||
static Callable ArrayEveryLoopContinuation(Isolate* isolate);
|
||||
static Callable ArrayReduceLoopContinuation(Isolate* isolate);
|
||||
static Callable ArrayReduceRightLoopContinuation(Isolate* isolate);
|
||||
static Callable FunctionPrototypeBind(Isolate* isolate);
|
||||
static Callable PromiseHandleReject(Isolate* isolate);
|
||||
};
|
||||
|
@ -7860,6 +7860,58 @@ Node* CodeStubAssembler::NumberInc(Node* value) {
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::NumberDec(Node* value) {
|
||||
Variable var_result(this, MachineRepresentation::kTagged),
|
||||
var_fdec_value(this, MachineRepresentation::kFloat64);
|
||||
Label if_issmi(this), if_isnotsmi(this), do_fdec(this), end(this);
|
||||
Branch(TaggedIsSmi(value), &if_issmi, &if_isnotsmi);
|
||||
|
||||
Bind(&if_issmi);
|
||||
{
|
||||
// Try fast Smi addition first.
|
||||
Node* one = SmiConstant(Smi::FromInt(1));
|
||||
Node* pair = IntPtrSubWithOverflow(BitcastTaggedToWord(value),
|
||||
BitcastTaggedToWord(one));
|
||||
Node* overflow = Projection(1, pair);
|
||||
|
||||
// Check if the Smi addition overflowed.
|
||||
Label if_overflow(this), if_notoverflow(this);
|
||||
Branch(overflow, &if_overflow, &if_notoverflow);
|
||||
|
||||
Bind(&if_notoverflow);
|
||||
var_result.Bind(BitcastWordToTaggedSigned(Projection(0, pair)));
|
||||
Goto(&end);
|
||||
|
||||
Bind(&if_overflow);
|
||||
{
|
||||
var_fdec_value.Bind(SmiToFloat64(value));
|
||||
Goto(&do_fdec);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&if_isnotsmi);
|
||||
{
|
||||
// Check if the value is a HeapNumber.
|
||||
CSA_ASSERT(this, IsHeapNumberMap(LoadMap(value)));
|
||||
|
||||
// Load the HeapNumber value.
|
||||
var_fdec_value.Bind(LoadHeapNumberValue(value));
|
||||
Goto(&do_fdec);
|
||||
}
|
||||
|
||||
Bind(&do_fdec);
|
||||
{
|
||||
Node* fdec_value = var_fdec_value.value();
|
||||
Node* minus_one = Float64Constant(-1.0);
|
||||
Node* fdec_result = Float64Add(fdec_value, minus_one);
|
||||
var_result.Bind(AllocateHeapNumberWithValue(fdec_result));
|
||||
Goto(&end);
|
||||
}
|
||||
|
||||
Bind(&end);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
void CodeStubAssembler::GotoIfNotNumber(Node* input, Label* is_not_number) {
|
||||
Label is_number(this);
|
||||
GotoIf(TaggedIsSmi(input), &is_number);
|
||||
|
@ -240,6 +240,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
|
||||
// Smi | HeapNumber operations.
|
||||
Node* NumberInc(Node* value);
|
||||
Node* NumberDec(Node* value);
|
||||
void GotoIfNotNumber(Node* value, Label* is_not_number);
|
||||
void GotoIfNumber(Node* value, Label* is_number);
|
||||
|
||||
|
@ -442,6 +442,7 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
|
||||
case Builtins::kArrayEvery:
|
||||
case Builtins::kArraySome:
|
||||
case Builtins::kArrayReduce:
|
||||
case Builtins::kArrayReduceRight:
|
||||
// Boolean bulitins.
|
||||
case Builtins::kBooleanConstructor:
|
||||
case Builtins::kBooleanPrototypeToString:
|
||||
|
@ -1106,45 +1106,6 @@ function ArrayLastIndexOf(element, index) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
function InnerArrayReduceRight(callback, current, array, length,
|
||||
argumentsLength) {
|
||||
if (!IS_CALLABLE(callback)) {
|
||||
throw %make_type_error(kCalledNonCallable, callback);
|
||||
}
|
||||
|
||||
var i = length - 1;
|
||||
find_initial: if (argumentsLength < 2) {
|
||||
for (; i >= 0; i--) {
|
||||
if (i in array) {
|
||||
current = array[i--];
|
||||
break find_initial;
|
||||
}
|
||||
}
|
||||
throw %make_type_error(kReduceNoInitial);
|
||||
}
|
||||
|
||||
for (; i >= 0; i--) {
|
||||
if (i in array) {
|
||||
var element = array[i];
|
||||
current = callback(current, element, i, array);
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
function ArrayReduceRight(callback, current) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight");
|
||||
|
||||
// Pull out the length so that side effects are visible before the
|
||||
// callback function is checked.
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_LENGTH(array.length);
|
||||
return InnerArrayReduceRight(callback, current, array, length,
|
||||
arguments.length);
|
||||
}
|
||||
|
||||
|
||||
// ES#sec-array.prototype.copywithin
|
||||
// (Array.prototype.copyWithin ( target, start [ , end ] )
|
||||
function ArrayCopyWithin(target, start, end) {
|
||||
@ -1425,7 +1386,6 @@ utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
|
||||
"map", getFunction("map", ArrayMap, 1),
|
||||
"indexOf", getFunction("indexOf", null, 1),
|
||||
"lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
|
||||
"reduceRight", getFunction("reduceRight", ArrayReduceRight, 1),
|
||||
"copyWithin", getFunction("copyWithin", ArrayCopyWithin, 2),
|
||||
"find", getFunction("find", ArrayFind, 1),
|
||||
"findIndex", getFunction("findIndex", ArrayFindIndex, 1),
|
||||
@ -1484,7 +1444,6 @@ utils.Export(function(to) {
|
||||
to.InnerArrayFind = InnerArrayFind;
|
||||
to.InnerArrayFindIndex = InnerArrayFindIndex;
|
||||
to.InnerArrayJoin = InnerArrayJoin;
|
||||
to.InnerArrayReduceRight = InnerArrayReduceRight;
|
||||
to.InnerArraySort = InnerArraySort;
|
||||
to.InnerArrayToLocaleString = InnerArrayToLocaleString;
|
||||
to.PackedArrayReverse = PackedArrayReverse;
|
||||
|
@ -24,7 +24,6 @@ var InnerArrayFilter;
|
||||
var InnerArrayFind;
|
||||
var InnerArrayFindIndex;
|
||||
var InnerArrayJoin;
|
||||
var InnerArrayReduceRight;
|
||||
var InnerArraySort;
|
||||
var InnerArrayToLocaleString;
|
||||
var InternalArray = utils.InternalArray;
|
||||
@ -66,7 +65,6 @@ utils.Import(function(from) {
|
||||
InnerArrayFind = from.InnerArrayFind;
|
||||
InnerArrayFindIndex = from.InnerArrayFindIndex;
|
||||
InnerArrayJoin = from.InnerArrayJoin;
|
||||
InnerArrayReduceRight = from.InnerArrayReduceRight;
|
||||
InnerArraySort = from.InnerArraySort;
|
||||
InnerArrayToLocaleString = from.InnerArrayToLocaleString;
|
||||
MaxSimple = from.MaxSimple;
|
||||
@ -572,6 +570,31 @@ function TypedArrayReduce(callback, current) {
|
||||
}
|
||||
%FunctionSetLength(TypedArrayReduce, 1);
|
||||
|
||||
function InnerArrayReduceRight(callback, current, array, length,
|
||||
argumentsLength) {
|
||||
if (!IS_CALLABLE(callback)) {
|
||||
throw %make_type_error(kCalledNonCallable, callback);
|
||||
}
|
||||
|
||||
var i = length - 1;
|
||||
find_initial: if (argumentsLength < 2) {
|
||||
for (; i >= 0; i--) {
|
||||
if (i in array) {
|
||||
current = array[i--];
|
||||
break find_initial;
|
||||
}
|
||||
}
|
||||
throw %make_type_error(kReduceNoInitial);
|
||||
}
|
||||
|
||||
for (; i >= 0; i--) {
|
||||
if (i in array) {
|
||||
var element = array[i];
|
||||
current = callback(current, element, i, array);
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
// ES6 draft 07-15-13, section 22.2.3.19
|
||||
function TypedArrayReduceRight(callback, current) {
|
||||
|
Loading…
Reference in New Issue
Block a user