v8/test/mjsunit/compiler/array-multiple-receiver-maps.js
Mythri A 3e90eee96b [turbofan] Allow polymorphic inlining for Array push / pop / shift
Array push / pop / shift were inlined if the elements kind of the
receiver maps is the same. This cl extends it by inlining these
builtins even when the receiver maps have different elements kinds.
It still limits it to only fast elements kinds. This is required to
prevent regressions in deltablue when lazy feedback allocation is
enabled. With lazy feedback allocation we may see polymorphic
feedback more often, since we don't have allocation site feedback
till the feedback vectors are allocated.

Bug: v8:9078
Change-Id: Id4a7b84be6305b125913b6ce0fb4f3eb3e3b15ec
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1632239
Commit-Queue: Mythri Alle <mythria@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61949}
2019-05-31 14:35:08 +00:00

189 lines
7.1 KiB
JavaScript

// Copyright 2018 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
// Flags: --no-stress-background-compile --trace-opt --trace-deopt
let id = 0;
function runTest(f, message, mkICTraining, deoptArg, speculationCheck) {
function test(f, message, ictraining, deoptArg, speculationCheck) {
// Train the call ic to the maps.
let t = ictraining;
// We put the training data into local variables
// to ensure their maps are kepts alive. If the
// maps die, gc *may* deoptimize {f}, which makes
// the test flaky.
let t1 = t();
let t2 = t();
let t3 = t();
%PrepareFunctionForOptimization(f);
for (let a of t1) {
f(a.arr, () => a.el);
}
for (let a of t2) {
f(a.arr, () => a.el);
}
%OptimizeFunctionOnNextCall(f);
message += " trained with" + JSON.stringify(t());
if (deoptArg == undefined) {
// Make sure the optimized function can handle
// all trained maps without deopt.
for (let a of t3) {
message += " for args " + JSON.stringify(a) + " should have been optimized";
f(a.arr, () => a.el);
assertOptimized(f, undefined, message);
}
} else {
// Trigger deopt, causing no-speculation bit to be set.
let a1 = deoptArg;
let a2 = deoptArg;
let a3 = deoptArg;
message += " for args " + JSON.stringify(a1);
message_unoptimized = message + " should have been unoptimized"
message_optimized = message + " should have been optimized"
f(a1.darr, () => a1.del);
assertUnoptimized(f, undefined, message_unoptimized);
if (speculationCheck) {
%PrepareFunctionForOptimization(f);
%OptimizeFunctionOnNextCall(f);
f(a2.darr, () => a2.del);
assertUnoptimized(f, undefined, message_unoptimized);
}
%PrepareFunctionForOptimization(f);
%OptimizeFunctionOnNextCall(f);
// No speculation should protect against further deopts.
f(a3.darr, () => a3.del);
assertOptimized(f, undefined, message_optimized);
}
}
// Get function as a string.
var testString = test.toString();
// Remove the function header..
testString = testString.replace(new RegExp("[^\n]*"), "let f = " + f.toString() + ";");
// ..and trailing '}'.
testString = testString.replace(new RegExp("[^\n]*$"), "");
// Substitute parameters.
testString = testString.replace(new RegExp("ictraining", 'g'), mkICTraining.toString());
testString = testString.replace(new RegExp("deoptArg", 'g'),
deoptArg ? JSON.stringify(deoptArg).replace(/"/g,'') : "undefined");
testString = testString.replace(new RegExp("speculationCheck", 'g'),
speculationCheck ? JSON.stringify(deoptArg).replace(/"/g,'') : "undefined");
// Make field names unique to avoid learning of types.
id = id + 1;
testString = testString.replace(/[.]el/g, '.el' + id);
testString = testString.replace(/el:/g, 'el' + id + ':');
testString = testString.replace(/[.]arr/g, '.arr' + id);
testString = testString.replace(/arr:/g, 'arr' + id + ':');
testString = testString.replace(/[.]del/g, '.del' + id);
testString = testString.replace(/[.]darr/g, '.darr' + id);
var modTest = new Function("message", testString);
modTest(message);
}
let checks = {
smiReceiver:
{ mkTrainingArguments : () => [{arr:[1], el:3}],
deoptingArguments : [{darr:[0.1], del:1}, {darr:[{}], del:1}]
},
objectReceiver:
{ mkTrainingArguments : () => [{arr:[{}], el:0.1}],
deoptingArguments : []
},
multipleSmiReceivers:
{ mkTrainingArguments : () => { let b = [1]; b.x=3; return [{arr:[1], el:3}, {arr:b, el:3}] },
deoptingArguments : [{darr:[0.1], del:1}, {darr:[{}], del:1}]
},
multipleSmiReceiversPackedUnpacked:
{ mkTrainingArguments : () => { let b = [1]; b[100] = 3; return [{arr:[1], el:3}, {arr:b, el:3}] },
deoptingArguments : [{darr:[0.1], del:1}, {darr:[{}], del:1}]
},
multipleDoubleReceivers:
{ mkTrainingArguments : () => { let b = [0.1]; b.x=0.3; return [{arr:[0.1], el:0.3}, {arr:b, el:0.3}] },
deoptingArguments : [{darr:[{}], del:true}, {darr:[1], del: 1}]
},
multipleDoubleReceiversPackedUnpacked:
{ mkTrainingArguments : () => { let b = [0.1]; b[100] = 0.3; return [{arr:[0.1], el:0.3}, {arr:b, el:0.3}] },
deoptingArguments : [{darr:[{}], del:true}, {darr:[1], del: 1}]
},
multipleMixedReceivers:
{ mkTrainingArguments : () => { let b = [0.1]; b.x=0.3; return [{arr:[1], el:1}, {arr:[{}], el:true}, {arr:b, el:0.3}] },
deoptingArguments : []
},
multipleMixedReceiversPackedUnpacked:
{ mkTrainingArguments : () => { let b = [0.1]; b[100] = 0.3; return [{arr:[1], el:1}, {arr:[{}], el:true}, {arr:b, el:0.3}] },
deoptingArguments : []
},
};
let no_speculation_checks = {
smiReceiver:
{ mkTrainingArguments : () => [{arr:[1], el:3}],
deoptingArguments : [{darr:[0.1], del:true}]
},
multipleSmiReceivers:
{ mkTrainingArguments : () => { let b = [1]; b.x=3; return [{arr:[1], el:3}, {arr:[1], el:3}] },
deoptingArguments : [{darr:[0.1], del:true}]
},
multipleSmiReceiversPackedUnpacked:
{ mkTrainingArguments : () => { let b = [1]; b[100] = 3; return [{arr:[1], el:3}, {arr:b, el:3}] },
deoptingArguments : [{darr:[0.1], del:true}]
},
multipleDoubleReceivers:
{ mkTrainingArguments : () => { let b = [0.1]; b.x=0.3; return [{arr:[0.1], el:0.3}, {arr:b, el:0.3}] },
deoptingArguments : [{darr:[1], del:true}]
},
multipleDoubleReceiversPackedUnpacked:
{ mkTrainingArguments : () => { let b = [0.1]; b[100] = 0.3; return [{arr:[0.1], el:0.3}, {arr:b, el:0.3}] },
deoptingArguments : [{darr:[1], del:true}]
},
};
const functions = {
push_reliable: (a,g) => { let b = g(); return a.push(2, b); },
push_unreliable: (a,g) => { return a.push(2, g()); },
pop_reliable: (a,g) => { let b = g(); return a.pop(2, b); },
pop_unreliable: (a,g) => { return a.pop(2, g()); },
shift_reliable: (a,g) => { let b = g(); return a.shift(2, b); },
shift_unreliable: (a,g) => { return a.shift(2, g()); }
}
const push_functions = {
push_reliable: (a,g) => { let b = g(); return a.push(2, b); },
push_unreliable: (a,g) => { return a.push(2, g()); },
}
Object.keys(checks).forEach(
key => {
let check = checks[key];
for (fnc in functions) {
runTest(functions[fnc], "test-" + fnc + "-" + key, check.mkTrainingArguments);
// Test each deopting arg separately.
for (let deoptArg of check.deoptingArguments) {
runTest(functions[fnc], "testDeopt-" + fnc + "-" + key, check.mkTrainingArguments, deoptArg);
}
}
}
);
Object.keys(no_speculation_checks).forEach(
key => {
let check = no_speculation_checks[key];
for (fnc in push_functions) {
runTest(functions[fnc], "test-spec-check-" + fnc + "-" + key, check.mkTrainingArguments);
// Test each deopting arg separately.
for (let deoptArg of check.deoptingArguments) {
runTest(functions[fnc], "testDeopt-spec-check-" + fnc + "-" + key, check.mkTrainingArguments, deoptArg, true);
}
}
}
);