79b5f0b560
This mjsunittest assumed specific internal types (i.e. Smi) for certain fields; it generates some dozens of variants of the test using new Function, but used the same property names in all of them. This causes V8 to sometimes learn more general types for fields (i.e. unboxed double), which the test did not expect. This commit uses unique field names for each of the test variants. Change-Id: Ib1ecb3ae33a57c8a1293a29a2233dad4e16a39fb Reviewed-on: https://chromium-review.googlesource.com/1004897 Commit-Queue: Sigurd Schneider <sigurds@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#52528}
134 lines
4.9 KiB
JavaScript
134 lines
4.9 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
|
|
|
|
let id = 0;
|
|
|
|
function runTest(f, message, mkICTraining, deoptArg) {
|
|
function test(f, message, ictraining, deoptArg) {
|
|
// 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();
|
|
|
|
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;
|
|
message += " for args " + JSON.stringify(a1);
|
|
message_unoptimized = message + " should have been unoptimized"
|
|
message_optimized = message + " should have been unoptimized"
|
|
f(a1.arr, () => a1.el);
|
|
assertUnoptimized(f, undefined, message_unoptimized);
|
|
%OptimizeFunctionOnNextCall(f);
|
|
// No speculation should protect against further deopts.
|
|
f(a2.arr, () => a2.el);
|
|
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");
|
|
|
|
// 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 + ':');
|
|
|
|
var modTest = new Function("message", testString);
|
|
//print(modTest);
|
|
modTest(message);
|
|
}
|
|
|
|
let checks = {
|
|
smiReceiver:
|
|
{ mkTrainingArguments : () => [{arr:[1], el:3}],
|
|
deoptingArguments : [{arr:[0.1], el:1}, {arr:[{}], el: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 : [{arr:[0.1], el:1}, {arr:[{}], el:1}]
|
|
},
|
|
multipleSmiReceiversPackedUnpacked:
|
|
{ mkTrainingArguments : () => { let b = [1]; b[100] = 3; return [{arr:[1], el:3}, {arr:b, el:3}] },
|
|
deoptingArguments : [{arr:[0.1], el:1}, {arr:[{}], el:1}]
|
|
},
|
|
multipleDoubleReceivers:
|
|
{ mkTrainingArguments : () => { let b = [0.1]; b.x=0.3; return [{arr:[0.1], el:0.3}, {arr:b, el:0.3}] },
|
|
deoptingArguments : [{arr:[{}], el:true}, {arr:[1], el:true}]
|
|
},
|
|
multipleDoubleReceiversPackedUnpacked:
|
|
{ mkTrainingArguments : () => { let b = [0.1]; b[100] = 0.3; return [{arr:[0.1], el:0.3}, {arr:b, el:0.3}] },
|
|
deoptingArguments : [{arr:[{}], el:true}, {arr:[1], el:true}]
|
|
},
|
|
multipleMixedReceivers:
|
|
{ mkTrainingArguments : () => { let b = [0.1]; b.x=0.3; return [{arr:[1], el:0.3}, {arr:[{}], el:true}, {arr:b, el:0.3}] },
|
|
deoptingArguments : []
|
|
},
|
|
multipleMixedReceiversPackedUnpacked:
|
|
{ mkTrainingArguments : () => { let b = [0.1]; b[100] = 0.3; return [{arr:[1], el:0.3}, {arr:[{}], el:true}, {arr:b, el:0.3}] },
|
|
deoptingArguments : []
|
|
},
|
|
};
|
|
|
|
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()); }
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
);
|