2018-01-03 13:25:25 +00:00
|
|
|
// 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.
|
|
|
|
|
2018-01-10 14:12:34 +00:00
|
|
|
// Flags: --allow-natives-syntax --opt --no-always-opt
|
2019-01-14 15:04:54 +00:00
|
|
|
// Flags: --no-stress-background-compile --trace-opt --trace-deopt
|
2018-01-03 13:25:25 +00:00
|
|
|
|
2018-04-10 16:10:28 +00:00
|
|
|
let id = 0;
|
|
|
|
|
2019-05-31 13:56:31 +00:00
|
|
|
function runTest(f, message, mkICTraining, deoptArg, speculationCheck) {
|
|
|
|
function test(f, message, ictraining, deoptArg, speculationCheck) {
|
2018-01-10 14:12:34 +00:00
|
|
|
// Train the call ic to the maps.
|
|
|
|
let t = ictraining;
|
2018-01-03 13:25:25 +00:00
|
|
|
|
2018-01-10 14:12:34 +00:00
|
|
|
// 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();
|
2018-01-03 13:25:25 +00:00
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(f);
|
2018-01-10 14:12:34 +00:00
|
|
|
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) {
|
2018-01-23 14:32:13 +00:00
|
|
|
message += " for args " + JSON.stringify(a) + " should have been optimized";
|
2018-01-10 14:12:34 +00:00
|
|
|
f(a.arr, () => a.el);
|
2018-01-23 14:32:13 +00:00
|
|
|
assertOptimized(f, undefined, message);
|
2018-01-10 14:12:34 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Trigger deopt, causing no-speculation bit to be set.
|
|
|
|
let a1 = deoptArg;
|
|
|
|
let a2 = deoptArg;
|
2019-05-31 13:56:31 +00:00
|
|
|
let a3 = deoptArg;
|
2018-01-10 14:12:34 +00:00
|
|
|
message += " for args " + JSON.stringify(a1);
|
2018-01-23 14:32:13 +00:00
|
|
|
message_unoptimized = message + " should have been unoptimized"
|
2019-05-31 13:56:31 +00:00
|
|
|
message_optimized = message + " should have been optimized"
|
|
|
|
f(a1.darr, () => a1.del);
|
2018-01-23 14:32:13 +00:00
|
|
|
assertUnoptimized(f, undefined, message_unoptimized);
|
2019-05-31 13:56:31 +00:00
|
|
|
if (speculationCheck) {
|
|
|
|
%PrepareFunctionForOptimization(f);
|
|
|
|
%OptimizeFunctionOnNextCall(f);
|
|
|
|
f(a2.darr, () => a2.del);
|
|
|
|
assertUnoptimized(f, undefined, message_unoptimized);
|
|
|
|
}
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(f);
|
2018-01-10 14:12:34 +00:00
|
|
|
%OptimizeFunctionOnNextCall(f);
|
|
|
|
// No speculation should protect against further deopts.
|
2019-05-31 13:56:31 +00:00
|
|
|
f(a3.darr, () => a3.del);
|
2018-01-23 14:32:13 +00:00
|
|
|
assertOptimized(f, undefined, message_optimized);
|
2018-01-10 14:12:34 +00:00
|
|
|
}
|
2018-01-09 16:51:15 +00:00
|
|
|
}
|
|
|
|
|
2018-01-10 14:12:34 +00:00
|
|
|
// 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'),
|
2018-04-10 16:10:28 +00:00
|
|
|
deoptArg ? JSON.stringify(deoptArg).replace(/"/g,'') : "undefined");
|
2019-05-31 13:56:31 +00:00
|
|
|
testString = testString.replace(new RegExp("speculationCheck", 'g'),
|
|
|
|
speculationCheck ? JSON.stringify(deoptArg).replace(/"/g,'') : "undefined");
|
2018-04-10 16:10:28 +00:00
|
|
|
|
|
|
|
// 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 + ':');
|
2019-05-31 13:56:31 +00:00
|
|
|
testString = testString.replace(/[.]del/g, '.del' + id);
|
|
|
|
testString = testString.replace(/[.]darr/g, '.darr' + id);
|
2018-01-03 13:25:25 +00:00
|
|
|
|
2018-01-10 14:12:34 +00:00
|
|
|
var modTest = new Function("message", testString);
|
|
|
|
modTest(message);
|
|
|
|
}
|
2018-01-03 13:25:25 +00:00
|
|
|
|
2018-01-10 14:12:34 +00:00
|
|
|
let checks = {
|
|
|
|
smiReceiver:
|
|
|
|
{ mkTrainingArguments : () => [{arr:[1], el:3}],
|
2019-05-31 13:56:31 +00:00
|
|
|
deoptingArguments : [{darr:[0.1], del:1}, {darr:[{}], del:1}]
|
2018-01-10 14:12:34 +00:00
|
|
|
},
|
|
|
|
objectReceiver:
|
|
|
|
{ mkTrainingArguments : () => [{arr:[{}], el:0.1}],
|
|
|
|
deoptingArguments : []
|
|
|
|
},
|
|
|
|
multipleSmiReceivers:
|
|
|
|
{ mkTrainingArguments : () => { let b = [1]; b.x=3; return [{arr:[1], el:3}, {arr:b, el:3}] },
|
2019-05-31 13:56:31 +00:00
|
|
|
deoptingArguments : [{darr:[0.1], del:1}, {darr:[{}], del:1}]
|
2018-01-10 14:12:34 +00:00
|
|
|
},
|
|
|
|
multipleSmiReceiversPackedUnpacked:
|
|
|
|
{ mkTrainingArguments : () => { let b = [1]; b[100] = 3; return [{arr:[1], el:3}, {arr:b, el:3}] },
|
2019-05-31 13:56:31 +00:00
|
|
|
deoptingArguments : [{darr:[0.1], del:1}, {darr:[{}], del:1}]
|
2018-01-10 14:12:34 +00:00
|
|
|
},
|
|
|
|
multipleDoubleReceivers:
|
|
|
|
{ mkTrainingArguments : () => { let b = [0.1]; b.x=0.3; return [{arr:[0.1], el:0.3}, {arr:b, el:0.3}] },
|
2019-05-31 13:56:31 +00:00
|
|
|
deoptingArguments : [{darr:[{}], del:true}, {darr:[1], del: 1}]
|
2018-01-10 14:12:34 +00:00
|
|
|
},
|
|
|
|
multipleDoubleReceiversPackedUnpacked:
|
|
|
|
{ mkTrainingArguments : () => { let b = [0.1]; b[100] = 0.3; return [{arr:[0.1], el:0.3}, {arr:b, el:0.3}] },
|
2019-05-31 13:56:31 +00:00
|
|
|
deoptingArguments : [{darr:[{}], del:true}, {darr:[1], del: 1}]
|
2018-01-10 14:12:34 +00:00
|
|
|
},
|
|
|
|
multipleMixedReceivers:
|
2019-05-31 13:56:31 +00:00
|
|
|
{ mkTrainingArguments : () => { let b = [0.1]; b.x=0.3; return [{arr:[1], el:1}, {arr:[{}], el:true}, {arr:b, el:0.3}] },
|
2018-01-10 14:12:34 +00:00
|
|
|
deoptingArguments : []
|
|
|
|
},
|
|
|
|
multipleMixedReceiversPackedUnpacked:
|
2019-05-31 13:56:31 +00:00
|
|
|
{ mkTrainingArguments : () => { let b = [0.1]; b[100] = 0.3; return [{arr:[1], el:1}, {arr:[{}], el:true}, {arr:b, el:0.3}] },
|
2018-01-10 14:12:34 +00:00
|
|
|
deoptingArguments : []
|
|
|
|
},
|
|
|
|
};
|
2018-01-09 16:51:15 +00:00
|
|
|
|
2019-05-31 13:56:31 +00:00
|
|
|
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}]
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2018-01-10 14:12:34 +00:00
|
|
|
const functions = {
|
|
|
|
push_reliable: (a,g) => { let b = g(); return a.push(2, b); },
|
|
|
|
push_unreliable: (a,g) => { return a.push(2, g()); },
|
2018-01-10 17:31:26 +00:00
|
|
|
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()); }
|
2018-01-10 14:12:34 +00:00
|
|
|
}
|
2018-01-03 13:25:25 +00:00
|
|
|
|
2019-05-31 13:56:31 +00:00
|
|
|
const push_functions = {
|
|
|
|
push_reliable: (a,g) => { let b = g(); return a.push(2, b); },
|
|
|
|
push_unreliable: (a,g) => { return a.push(2, g()); },
|
|
|
|
}
|
|
|
|
|
2018-01-10 14:12:34 +00:00
|
|
|
Object.keys(checks).forEach(
|
|
|
|
key => {
|
|
|
|
let check = checks[key];
|
2018-01-03 13:25:25 +00:00
|
|
|
|
2018-01-10 14:12:34 +00:00
|
|
|
for (fnc in functions) {
|
2018-04-10 16:10:28 +00:00
|
|
|
runTest(functions[fnc], "test-" + fnc + "-" + key, check.mkTrainingArguments);
|
2018-01-10 14:12:34 +00:00
|
|
|
// Test each deopting arg separately.
|
|
|
|
for (let deoptArg of check.deoptingArguments) {
|
2018-04-10 16:10:28 +00:00
|
|
|
runTest(functions[fnc], "testDeopt-" + fnc + "-" + key, check.mkTrainingArguments, deoptArg);
|
2018-01-10 14:12:34 +00:00
|
|
|
}
|
|
|
|
}
|
2018-01-03 13:25:25 +00:00
|
|
|
}
|
2018-01-10 14:12:34 +00:00
|
|
|
);
|
2019-05-31 13:56:31 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|