Commit b3d84990 authored by Mike Stanton's avatar Mike Stanton Committed by Commit Bot

[Turbofan] Reland Array.prototype.filter inlining.

Support inlining of Array.prototype.filter in TurboFan.

Bug: v8:1956
Change-Id: If50e230d14461063d378c0591dc27dea43371afa
Reviewed-on: https://chromium-review.googlesource.com/733089
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48846}
parent f0d3b954
......@@ -1605,6 +1605,66 @@ TF_BUILTIN(ArrayFilterLoopContinuation, ArrayBuiltinCodeStubAssembler) {
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction);
}
TF_BUILTIN(ArrayFilterLoopEagerDeoptContinuation,
ArrayBuiltinCodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* array = Parameter(Descriptor::kArray);
Node* initial_k = Parameter(Descriptor::kInitialK);
Node* len = Parameter(Descriptor::kLength);
Node* to = Parameter(Descriptor::kTo);
Callable stub(
Builtins::CallableFor(isolate(), Builtins::kArrayFilterLoopContinuation));
Return(CallStub(stub, context, receiver, callbackfn, this_arg, array,
receiver, initial_k, len, to));
}
TF_BUILTIN(ArrayFilterLoopLazyDeoptContinuation,
ArrayBuiltinCodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* array = Parameter(Descriptor::kArray);
Node* initial_k = Parameter(Descriptor::kInitialK);
Node* len = Parameter(Descriptor::kLength);
Node* value_k = Parameter(Descriptor::kValueK);
Node* result = Parameter(Descriptor::kResult);
VARIABLE(to, MachineRepresentation::kTagged, Parameter(Descriptor::kTo));
// This custom lazy deopt point is right after the callback. filter() needs
// to pick up at the next step, which is setting the callback result in
// the output array. After incrementing k and to, we can glide into the loop
// continuation builtin.
Label true_continue(this, &to), false_continue(this);
// iii. If selected is true, then...
BranchIfToBooleanIsTrue(result, &true_continue, &false_continue);
BIND(&true_continue);
{
// 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue).
CallRuntime(Runtime::kCreateDataProperty, context, array, to.value(),
value_k);
// 2. Increase to by 1.
to.Bind(NumberInc(to.value()));
Goto(&false_continue);
}
BIND(&false_continue);
// Increment k.
initial_k = NumberInc(initial_k);
Callable stub(
Builtins::CallableFor(isolate(), Builtins::kArrayFilterLoopContinuation));
Return(CallStub(stub, context, receiver, callbackfn, this_arg, array,
receiver, initial_k, len, to.value()));
}
TF_BUILTIN(ArrayFilter, ArrayBuiltinCodeStubAssembler) {
Node* argc =
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
......
......@@ -288,6 +288,10 @@ namespace internal {
TFS(ArrayFilterLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
kObject, kInitialK, kLength, kTo) \
TFJ(ArrayFilter, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
TFJ(ArrayFilterLoopEagerDeoptContinuation, 6, kCallbackFn, kThisArg, kArray, \
kInitialK, kLength, kTo) \
TFJ(ArrayFilterLoopLazyDeoptContinuation, 8, kCallbackFn, kThisArg, kArray, \
kInitialK, kLength, kValueK, kTo, kResult) \
/* ES6 #sec-array.prototype.foreach */ \
TFS(ArrayMapLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
kObject, kInitialK, kLength, kTo) \
......
......@@ -197,6 +197,16 @@ Callable Builtins::CallableFor(Isolate* isolate, Name name) {
BUILTIN_CODE(isolate, ArrayMapLoopLazyDeoptContinuation);
return Callable(code, BuiltinDescriptor(isolate));
}
case kArrayFilterLoopEagerDeoptContinuation: {
Handle<Code> code =
BUILTIN_CODE(isolate, ArrayFilterLoopEagerDeoptContinuation);
return Callable(code, BuiltinDescriptor(isolate));
}
case kArrayFilterLoopLazyDeoptContinuation: {
Handle<Code> code =
BUILTIN_CODE(isolate, ArrayFilterLoopLazyDeoptContinuation);
return Callable(code, BuiltinDescriptor(isolate));
}
default:
UNREACHABLE();
}
......@@ -235,6 +245,8 @@ bool Builtins::IsLazy(int index) {
case kArrayForEachLoopLazyDeoptContinuation: // https://crbug.com/v8/6786.
case kArrayMapLoopEagerDeoptContinuation: // https://crbug.com/v8/6786.
case kArrayMapLoopLazyDeoptContinuation: // https://crbug.com/v8/6786.
case kArrayFilterLoopEagerDeoptContinuation: // https://crbug.com/v8/6786.
case kArrayFilterLoopLazyDeoptContinuation: // https://crbug.com/v8/6786.
case kCheckOptimizationMarker:
case kCompileLazy:
case kDeserializeLazy:
......
This diff is collapsed.
......@@ -74,6 +74,7 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceReflectHas(Node* node);
Reduction ReduceArrayForEach(Handle<JSFunction> function, Node* node);
Reduction ReduceArrayMap(Handle<JSFunction> function, Node* node);
Reduction ReduceArrayFilter(Handle<JSFunction> function, Node* node);
Reduction ReduceCallOrConstructWithArrayLikeOrSpread(
Node* node, int arity, CallFrequency const& frequency,
VectorSlotPair const& feedback);
......@@ -87,6 +88,12 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceSoftDeoptimize(Node* node, DeoptimizeReason reason);
// Returns the updated {to} node, and updates control and effect along the
// way.
Node* DoFilterPostCallbackWork(ElementsKind kind, Node** control,
Node** effect, Node* a, Node* to,
Node* element, Node* callback_value);
// If {fncallback} is not callable, throw a TypeError.
// {control} is altered, and new nodes {check_fail} and {check_throw} are
// returned. {check_fail} is the control branch where IsCallable failed,
......
......@@ -1568,6 +1568,7 @@ void Deoptimizer::DoComputeBuiltinContinuation(
BailoutId bailout_id = translated_frame->node_id();
Builtins::Name builtin_name = Builtins::GetBuiltinFromBailoutId(bailout_id);
DCHECK(!Builtins::IsLazy(builtin_name));
Code* builtin = isolate()->builtins()->builtin(builtin_name);
Callable continuation_callable =
Builtins::CallableFor(isolate(), builtin_name);
......
......@@ -67,70 +67,78 @@ function assertNotHoley(obj, name_opt) {
assertEquals(false, isHoley(obj), name_opt);
}
var a;
// Packed literal arrays.
obj = [1, 2, 3];
a = obj.filter(x => false);
assertKind(elements_kind.fast_smi_only, a);
assertNotHoley(a);
obj = [true, true, false];
a = obj.filter(x => false);
assertKind(elements_kind.fast, a);
assertNotHoley(a);
obj = [1.0, 1.5, 3.5];
a = obj.filter(x => false);
assertKind(elements_kind.fast_double, a);
assertNotHoley(a);
// Holey literal arrays.
obj = [1,, 3]; obj[1] = 2;
a = obj.filter(x => false);
assertKind(elements_kind.fast_smi_only, a);
assertHoley(a);
obj = [true,, false]; obj[1] = true;
a = obj.filter(x => false);
assertKind(elements_kind.fast, a);
assertHoley(a);
obj = [1.0,, 3.5]; obj[1] = 1.5;
a = obj.filter(x => false);
assertKind(elements_kind.fast_double, a);
assertHoley(a);
// Packed constructed arrays.
obj = new Array(1, 2, 3);
a = obj.filter(x => false);
assertKind(elements_kind.fast_smi_only, a);
assertNotHoley(a);
obj = new Array(true, true, false);
a = obj.filter(x => false);
assertKind(elements_kind.fast, a);
assertNotHoley(a);
obj = new Array(1.0, 1.5, 3.5);
a = obj.filter(x => false);
assertKind(elements_kind.fast_double, a);
assertNotHoley(a);
// Holey constructed arrays.
obj = new Array(3);
obj[0] = 1; obj[1] = 2; obj[2] = 3;
a = obj.filter(x => false);
assertKind(elements_kind.fast_smi_only, a);
assertHoley(a);
obj = new Array(3);
obj[0] = true; obj[1] = true; obj[2] = false;
a = obj.filter(x => false);
assertKind(elements_kind.fast, a);
assertHoley(a);
obj = new Array(3);
obj[0] = 1.0; obj[1] = 1.5; obj[2] = 3.5;
a = obj.filter(x => false);
assertKind(elements_kind.fast_double, a);
assertHoley(a);
// Create a new closure that inlines Array.prototype.filter().
function create(a) {
return function() {
return a.filter(x => false);
}
}
function runTest(test, kind, holey_predicate) {
// Verify built-in implementation produces correct results.
let a = test();
assertKind(kind, a);
holey_predicate(a);
test();
test();
%OptimizeFunctionOnNextCall(test);
// Now for optimized code.
a = test();
assertKind(kind, a);
holey_predicate(a);
}
function chooseHoleyPredicate(a) {
return isHoley(a) ? assertHoley : assertNotHoley;
}
(function() {
let data = [];
// Packed literal arrays.
data.push(() => [1, 2, 3]);
data.push(() => [true, true, false]);
data.push(() => [1.0, 1.5, 3.5]);
// Holey literal arrays.
data.push(() => { let obj = [1,, 3]; obj[1] = 2; return obj; });
data.push(() => { let obj = [true,, false]; obj[1] = true; return obj; });
data.push(() => { let obj = [1.0,, 3.5]; obj[1] = 1.5; return obj; });
// Packed constructed arrays.
data.push(() => new Array(1, 2, 3));
data.push(() => new Array(true, true, false));
data.push(() => new Array(1.0, 1.5, 3.5));
// Holey constructed arrays.
data.push(() => {
let obj = new Array(3);
obj[0] = 1;
obj[1] = 2;
obj[2] = 3;
return obj;
});
data.push(() => {
let obj = new Array(3);
obj[0] = true;
obj[1] = true;
obj[2] = false;
return obj;
});
data.push(() => {
let obj = new Array(3);
obj[0] = 1.0;
obj[1] = 1.5;
obj[2] = 3.5;
return obj;
});
for (datum of data) {
let a = datum();
// runTest(create(a), getKind(a), chooseHoleyPredicate(a));
let f = function() { return a.filter(x => false); }
runTest(f, getKind(a), chooseHoleyPredicate(a));
}
})();
This diff is collapsed.
......@@ -35,3 +35,19 @@
%OptimizeFunctionOnNextCall(foo);
assertInstanceof(foo(), TypeError);
})();
(function TestNonCallableFilter() {
function foo() { [].filter(undefined); }
assertThrows(foo, TypeError);
assertThrows(foo, TypeError);
%OptimizeFunctionOnNextCall(foo);
assertThrows(foo, TypeError);
})();
(function TestNonCallableFilterCaught() {
function foo() { try { [].filter(undefined) } catch(e) { return e } }
assertInstanceof(foo(), TypeError);
assertInstanceof(foo(), TypeError);
%OptimizeFunctionOnNextCall(foo);
assertInstanceof(foo(), TypeError);
})();
// Copyright 2017 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.
// Flags: --allow-natives-syntax
function classOf() {; }
function PrettyPrint(value) { return ""; }
function fail() { }
function deepEquals(a, b) { if (a === b) { if (a === 0)1 / b; return true; } if (typeof a != typeof b) return false; if (typeof a == "number") return isNaN(); if (typeof a !== "object" && typeof a !== "function") return false; var objectClass = classOf(); if (b) return false; if (objectClass === "RegExp") {; } if (objectClass === "Function") return false; if (objectClass === "Array") { var elementCount = 0; if (a.length != b.length) { return false; } for (var i = 0; i < a.length; i++) { if (a[i][i]) return false; } return true; } if (objectClass == "String" || objectClass == "Number" || objectClass == "Boolean" || objectClass == "Date") { if (a.valueOf()) return false; }; }
assertSame = function assertSame() { if (found === expected) { if (1 / found) return; } else if ((expected !== expected) && (found !== found)) { return; }; }; assertEquals = function assertEquals(expected, found, name_opt) { if (!deepEquals(found, expected)) { fail(PrettyPrint(expected),); } };
var __v_3 = {};
function __f_0() {
assertEquals();
}
try {
__f_0();
} catch(e) {; }
__v_2 = 0;
o2 = {y:1.5};
o2.y = 0;
o3 = o2.y;
function __f_1() {
for (var __v_1 = 0; __v_1 < 10; __v_1++) {
__v_2 += __v_3.x + o3.foo;
[ 3].filter(__f_9);
}
}
__f_1();
%OptimizeFunctionOnNextCall(__f_1);
__f_1();
function __f_9(){ "use __f_9"; assertEquals( this); }
// Copyright 2017 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.
// Flags: --enable-slow-asserts --expose-gc --allow-natives-syntax
function __getProperties(obj) {
let properties = [];
for (let name of Object.getOwnPropertyNames(obj)) {
properties.push(name);
}
return properties;
}
function __getRandomProperty(obj, seed) {
let properties = __getProperties(obj);
return properties[seed % properties.length];
}
(function() {
var __v_59904 = [12, 13, 14, 16, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25];
var __v_59906 = function(__v_59908) {
var __v_59909 = function(__v_59910, __v_59911) {
if (__v_59911 == 13 && __v_59908) {
__v_59904.abc = 25;
}
return true;
};
return __v_59904.filter(__v_59909);
};
print(__v_59906());
__v_59904[__getRandomProperty(__v_59904, 366855)] = this, gc();
print(__v_59906());
%OptimizeFunctionOnNextCall(__v_59906);
var __v_59907 = __v_59906(true);
print(__v_59907);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment