[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:
danno 2017-03-24 06:35:56 -07:00 committed by Commit bot
parent 8b8dfda8cf
commit 6141f6e216
10 changed files with 177 additions and 58 deletions

View File

@ -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

View File

@ -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]
if (direction == ForEachDirection::kForward) {
// 7. Let k be 0.
// [Already done in code assembler initialization]
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);
{
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);
if (direction == ForEachDirection::kForward) {
// e. Increase k by 1.
k_.Bind(NumberInc(k_.value()));
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);

View File

@ -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 */ \

View File

@ -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(),

View File

@ -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);
};

View File

@ -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);

View File

@ -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);

View File

@ -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:

View File

@ -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;

View File

@ -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) {