From 6141f6e2169f26d0606bee34e2ff9d680eac5d65 Mon Sep 17 00:00:00 2001 From: danno Date: Fri, 24 Mar 2017 06:35:56 -0700 Subject: [PATCH] [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} --- src/bootstrapper.cc | 4 ++ src/builtins/builtins-array-gen.cc | 98 +++++++++++++++++++++++++----- src/builtins/builtins.h | 4 ++ src/code-factory.cc | 6 ++ src/code-factory.h | 1 + src/code-stub-assembler.cc | 52 ++++++++++++++++ src/code-stub-assembler.h | 1 + src/debug/debug-evaluate.cc | 1 + src/js/array.js | 41 ------------- src/js/typedarray.js | 27 +++++++- 10 files changed, 177 insertions(+), 58 deletions(-) diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 3dc58eb901..81a3e86161 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -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 diff --git a/src/builtins/builtins-array-gen.cc b/src/builtins/builtins-array-gen.cc index 4764d2341b..f8163ff345 100644 --- a/src/builtins/builtins-array-gen.cc +++ b/src/builtins/builtins-array-gen.cc @@ -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); diff --git a/src/builtins/builtins.h b/src/builtins/builtins.h index 31ae3e2bc4..c137b0a824 100644 --- a/src/builtins/builtins.h +++ b/src/builtins/builtins.h @@ -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 */ \ diff --git a/src/code-factory.cc b/src/code-factory.cc index a69d441427..f5818b2782 100644 --- a/src/code-factory.cc +++ b/src/code-factory.cc @@ -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(), diff --git a/src/code-factory.h b/src/code-factory.h index 6d2b8b8ec7..193304474b 100644 --- a/src/code-factory.h +++ b/src/code-factory.h @@ -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); }; diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc index 183a044953..043bc41454 100644 --- a/src/code-stub-assembler.cc +++ b/src/code-stub-assembler.cc @@ -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); diff --git a/src/code-stub-assembler.h b/src/code-stub-assembler.h index 360e1fa4e7..5172537267 100644 --- a/src/code-stub-assembler.h +++ b/src/code-stub-assembler.h @@ -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); diff --git a/src/debug/debug-evaluate.cc b/src/debug/debug-evaluate.cc index 8937c89ab2..ae0d77c6e3 100644 --- a/src/debug/debug-evaluate.cc +++ b/src/debug/debug-evaluate.cc @@ -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: diff --git a/src/js/array.js b/src/js/array.js index 68831d33e4..cd239ba3f2 100644 --- a/src/js/array.js +++ b/src/js/array.js @@ -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; diff --git a/src/js/typedarray.js b/src/js/typedarray.js index ac8609a826..1f11e897e4 100644 --- a/src/js/typedarray.js +++ b/src/js/typedarray.js @@ -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) {