| // 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); | |
| } | |
| } | |
| } | |
| ); |