519c82ce36
When a function is invoked by prototype.apply, it may undergo following transformation in the JSCallReducer: receiver.apply(this, args) -> this.receiver(...args) Since the new target (also the receiver of apply()) is not collected to the feedback slot, further speculative optimization on the new target is not available if the new target is not a heapconstant. With this CL, the receiver will be collected to the feedback instead of the target if the target is a prototype.apply. It may improve the performance of the following usecase by ~80%. function reduceArray(func, arr, r) { for (var i = 0, len = arr.length; i < len; i++) { r = func.apply(null, r, arr[i]); } return r; } var a = 0; for (var i = 0; i < 10000000; i++) { a += reduceArray(Math.imul, [5,6,2,3,7,6,8,3,7,9,2,5,], 1); } console.log(a); This CL also improves the runTime score of JetStream2/richards-wasm by ~45% in default, ~60% with --turbo-inline-js-wasm-calls. Change-Id: I542eb8d3fcb592f4e0993af93ba1af70e89c3982 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2639813 Commit-Queue: Fanchen Kong <fanchen.kong@intel.com> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#74413}
296 lines
8.8 KiB
JavaScript
296 lines
8.8 KiB
JavaScript
// Copyright 2019 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 --opt --no-always-opt
|
|
|
|
"use strict";
|
|
const mathAbs = Math.abs;
|
|
const mathImul = Math.imul;
|
|
|
|
|
|
// Testing: FunctionPrototypeApply
|
|
function TestFunctionPrototypeApplyHelper() {
|
|
return mathAbs.apply(undefined, arguments);
|
|
}
|
|
|
|
function TestFunctionPrototypeApply(x) {
|
|
return TestFunctionPrototypeApplyHelper(x);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestFunctionPrototypeApplyHelper);
|
|
%PrepareFunctionForOptimization(TestFunctionPrototypeApply);
|
|
assertEquals(TestFunctionPrototypeApply(-13), 13);
|
|
assertEquals(TestFunctionPrototypeApply(42), 42);
|
|
%OptimizeFunctionOnNextCall(TestFunctionPrototypeApply);
|
|
assertEquals(TestFunctionPrototypeApply(-13), 13);
|
|
assertOptimized(TestFunctionPrototypeApply);
|
|
TestFunctionPrototypeApply("abc");
|
|
assertUnoptimized(TestFunctionPrototypeApply);
|
|
|
|
// Testing: FunctionPrototypeApply with non-HeapConstant Receiver
|
|
var MathMin = (function() { return Math.min.apply(null, arguments); })
|
|
|
|
function TestFunctionPrototypeApplyReceiver(func, x, y) {
|
|
return func(x, y);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(MathMin);
|
|
%PrepareFunctionForOptimization(TestFunctionPrototypeApplyReceiver);
|
|
assertEquals(-13, TestFunctionPrototypeApplyReceiver(MathMin, -13, 42));
|
|
assertEquals(-4, TestFunctionPrototypeApplyReceiver(MathMin, 3, -4));
|
|
%OptimizeFunctionOnNextCall(TestFunctionPrototypeApplyReceiver);
|
|
assertEquals(7, TestFunctionPrototypeApplyReceiver(MathMin, 7, 9));
|
|
assertOptimized(TestFunctionPrototypeApplyReceiver);
|
|
TestFunctionPrototypeApplyReceiver(MathMin, "abc");
|
|
assertUnoptimized(TestFunctionPrototypeApplyReceiver);
|
|
|
|
// Testing: FunctionPrototypeApply with non-HeapConstant Receiver won't cause
|
|
// deopt loop
|
|
(function() {
|
|
|
|
var F;
|
|
function foo() {
|
|
return F.apply(null, arguments);
|
|
}
|
|
function test(x, y) {
|
|
return foo(x, y);
|
|
}
|
|
F = Math.min;
|
|
%PrepareFunctionForOptimization(foo);
|
|
%PrepareFunctionForOptimization(test);
|
|
assertEquals(-13, test(-13, 42));
|
|
%OptimizeFunctionOnNextCall(test);
|
|
assertEquals(-13, test(-13, 42));
|
|
assertOptimized(test);
|
|
%PrepareFunctionForOptimization(test);
|
|
F = Math.max;
|
|
assertEquals(42, test(-13, 42));
|
|
assertUnoptimized(test);
|
|
%OptimizeFunctionOnNextCall(test);
|
|
assertEquals(42, test(-13, 42));
|
|
F = Math.min;
|
|
assertEquals(-13, test(-13, 42));
|
|
assertOptimized(test);
|
|
})();
|
|
|
|
// Testing: FunctionPrototypeCall
|
|
function TestFunctionPrototypeCall(x) {
|
|
return mathAbs.call(undefined, x);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestFunctionPrototypeCall);
|
|
TestFunctionPrototypeCall(42);
|
|
TestFunctionPrototypeCall(52);
|
|
%OptimizeFunctionOnNextCall(TestFunctionPrototypeCall);
|
|
TestFunctionPrototypeCall(12);
|
|
assertOptimized(TestFunctionPrototypeCall);
|
|
TestFunctionPrototypeCall("abc");
|
|
assertUnoptimized(TestFunctionPrototypeCall);
|
|
|
|
|
|
// Testing: ArrayForEach
|
|
function TestArrayForEach(x) {
|
|
x.forEach(mathAbs);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestArrayForEach);
|
|
TestArrayForEach([1, 3, -4]);
|
|
TestArrayForEach([-9, 9, 0]);
|
|
%OptimizeFunctionOnNextCall(TestArrayForEach);
|
|
TestArrayForEach([1, 3, -4]);
|
|
assertOptimized(TestArrayForEach);
|
|
TestArrayForEach(["abc", "xy"]);
|
|
assertUnoptimized(TestArrayForEach);
|
|
|
|
|
|
// Testing: ArrayReduce
|
|
function TestArrayReduce(x) {
|
|
return x.reduce(mathImul);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestArrayReduce);
|
|
assertEquals(TestArrayReduce([1, 2, -3, 4]), -24);
|
|
assertEquals(TestArrayReduce([3, 5, 7]), 105);
|
|
%OptimizeFunctionOnNextCall(TestArrayReduce);
|
|
assertEquals(TestArrayReduce([1, 2, -3, 4]), -24);
|
|
assertOptimized(TestArrayReduce);
|
|
TestArrayReduce(["abc", "xy"]);
|
|
assertUnoptimized(TestArrayReduce);
|
|
|
|
|
|
// Testing: ArrayReduceRight
|
|
function TestArrayReduceRight(x) {
|
|
return x.reduceRight(mathImul);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestArrayReduceRight);
|
|
assertEquals(TestArrayReduceRight([1, 2, -3, 4]), -24);
|
|
assertEquals(TestArrayReduceRight([3, 5, 7]), 105);
|
|
%OptimizeFunctionOnNextCall(TestArrayReduceRight);
|
|
assertEquals(TestArrayReduceRight([1, 2, -3, 4]), -24);
|
|
assertOptimized(TestArrayReduceRight);
|
|
TestArrayReduceRight(["abc", "xy"]);
|
|
assertUnoptimized(TestArrayReduceRight);
|
|
|
|
|
|
// Testing: ArrayMap
|
|
function TestArrayMap(x) {
|
|
return x.map(mathAbs);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestArrayMap);
|
|
assertEquals(TestArrayMap([1, -2, -3, 4]), [1, 2, 3, 4]);
|
|
assertEquals(TestArrayMap([5, -5, 5, -5]), [5, 5, 5, 5]);
|
|
%OptimizeFunctionOnNextCall(TestArrayMap);
|
|
assertEquals(TestArrayMap([1, -2, 3, -4]), [1, 2, 3, 4]);
|
|
assertOptimized(TestArrayMap);
|
|
TestArrayMap(["abc", "xy"]);
|
|
assertUnoptimized(TestArrayMap);
|
|
|
|
|
|
// Testing: ArrayFilter
|
|
function TestArrayFilter(x) {
|
|
return x.filter(mathAbs);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestArrayFilter);
|
|
assertEquals(TestArrayFilter([-2, 0, 3, -4]), [-2, 3, -4]);
|
|
assertEquals(TestArrayFilter([0, 1, 1, 0]), [1, 1]);
|
|
%OptimizeFunctionOnNextCall(TestArrayFilter);
|
|
assertEquals(TestArrayFilter([-2, 0, 3, -4]), [-2, 3, -4]);
|
|
assertOptimized(TestArrayFilter);
|
|
TestArrayFilter(["abc", "xy"]);
|
|
assertUnoptimized(TestArrayFilter);
|
|
|
|
|
|
// Testing: ArrayFind
|
|
function TestArrayFind(x) {
|
|
return x.find(mathAbs);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestArrayFind);
|
|
assertEquals(TestArrayFind([0, 0, -3, 12]), -3);
|
|
assertEquals(TestArrayFind([0, -18]), -18);
|
|
%OptimizeFunctionOnNextCall(TestArrayFind);
|
|
assertEquals(TestArrayFind([0, 0, -3, 12]), -3);
|
|
assertOptimized(TestArrayFind);
|
|
TestArrayFind(["", "abc", "xy"]);
|
|
assertUnoptimized(TestArrayFind);
|
|
|
|
|
|
// Testing: ArrayFindIndex
|
|
function TestArrayFindIndex(x) {
|
|
return x.findIndex(mathAbs);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestArrayFindIndex);
|
|
assertEquals(TestArrayFindIndex([0, 0, -3, 12]), 2);
|
|
assertEquals(TestArrayFindIndex([0, -18]), 1);
|
|
%OptimizeFunctionOnNextCall(TestArrayFindIndex);
|
|
assertEquals(TestArrayFindIndex([0, 0, -3, 12]), 2);
|
|
assertOptimized(TestArrayFindIndex);
|
|
TestArrayFindIndex(["", "abc", "xy"]);
|
|
assertUnoptimized(TestArrayFindIndex);
|
|
|
|
|
|
// Testing: ArrayEvery
|
|
function TestArrayEvery(x) {
|
|
return x.every(mathAbs);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestArrayEvery);
|
|
assertEquals(TestArrayEvery([3, 0, -9]), false);
|
|
assertEquals(TestArrayEvery([2, 12, -1]), true);
|
|
%OptimizeFunctionOnNextCall(TestArrayEvery);
|
|
assertEquals(TestArrayEvery([3, 0, -9]), false);
|
|
assertOptimized(TestArrayEvery);
|
|
TestArrayEvery(["abc", "xy"]);
|
|
assertUnoptimized(TestArrayEvery);
|
|
|
|
|
|
// Testing: ArraySome
|
|
function TestArraySome(x) {
|
|
return x.some(mathAbs);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestArraySome);
|
|
assertEquals(TestArraySome([3, 0, -9]), true);
|
|
assertEquals(TestArraySome([0, 0]), false);
|
|
%OptimizeFunctionOnNextCall(TestArraySome);
|
|
assertEquals(TestArraySome([3, 0, -9]), true);
|
|
assertOptimized(TestArraySome);
|
|
TestArraySome(["abc", "xy"]);
|
|
assertUnoptimized(TestArraySome);
|
|
|
|
|
|
// Testing: JSCall (JSFunction)
|
|
const boundMathImul = mathImul.bind(undefined, -3);
|
|
function TestJSCallWithJSFunction(x) {
|
|
return boundMathImul(x);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestJSCallWithJSFunction);
|
|
assertEquals(TestJSCallWithJSFunction(-14), 42);
|
|
assertEquals(TestJSCallWithJSFunction(14), -42);
|
|
%OptimizeFunctionOnNextCall(TestJSCallWithJSFunction);
|
|
assertEquals(TestJSCallWithJSFunction(-14), 42);
|
|
assertOptimized(TestJSCallWithJSFunction);
|
|
TestJSCallWithJSFunction("abc");
|
|
assertUnoptimized(TestJSCallWithJSFunction);
|
|
|
|
|
|
// Testing: JSCall (JSBoundFunction)
|
|
function TestJSCallWithJSBoundFunction(x) {
|
|
return mathImul.bind(undefined, -3)(x);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestJSCallWithJSBoundFunction);
|
|
assertEquals(TestJSCallWithJSBoundFunction(-14), 42);
|
|
assertEquals(TestJSCallWithJSBoundFunction(14), -42);
|
|
%OptimizeFunctionOnNextCall(TestJSCallWithJSBoundFunction);
|
|
assertEquals(TestJSCallWithJSBoundFunction(-14), 42);
|
|
assertOptimized(TestJSCallWithJSBoundFunction);
|
|
TestJSCallWithJSBoundFunction("abc");
|
|
assertUnoptimized(TestJSCallWithJSBoundFunction);
|
|
|
|
|
|
// Testing: ReflectApply
|
|
function TestReflectApplyHelper() {
|
|
return Reflect.apply(mathAbs, undefined, arguments);
|
|
}
|
|
|
|
function TestReflectApply(x) {
|
|
return TestReflectApplyHelper(x);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestReflectApplyHelper);
|
|
%PrepareFunctionForOptimization(TestReflectApply);
|
|
assertEquals(TestReflectApply(-9), 9);
|
|
assertEquals(TestReflectApply(7), 7);
|
|
%OptimizeFunctionOnNextCall(TestReflectApply);
|
|
assertEquals(TestReflectApply(-9), 9);
|
|
assertOptimized(TestReflectApply);
|
|
TestReflectApply("abc");
|
|
assertUnoptimized(TestReflectApply);
|
|
|
|
|
|
// Testing: CallWithSpread
|
|
function TestCallWithSpreadHelper() {
|
|
return mathImul(...arguments);
|
|
}
|
|
|
|
function TestCallWithSpread(x) {
|
|
return TestCallWithSpreadHelper(x, x);
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(TestCallWithSpreadHelper);
|
|
%PrepareFunctionForOptimization(TestCallWithSpread);
|
|
assertEquals(TestCallWithSpread(-13), 169);
|
|
assertEquals(TestCallWithSpread(7), 49);
|
|
%OptimizeFunctionOnNextCall(TestCallWithSpread);
|
|
assertEquals(TestCallWithSpread(-13), 169);
|
|
assertOptimized(TestCallWithSpread);
|
|
TestCallWithSpread("abc");
|
|
assertUnoptimized(TestCallWithSpread);
|