mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
Bug 1910194 - wasm: Add testing functions and improve speculative inlining test. r=jseward
Adds testing function for getting the best tier of a function. Adds prefs for configuring our inlining heuristics. Improves test of speculative inlining. Differential Revision: https://phabricator.services.mozilla.com/D217881
This commit is contained in:
parent
a9f56370e0
commit
5bde5d48e3
@ -2051,6 +2051,38 @@ static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool WasmFunctionTier(JSContext* cx, unsigned argc, Value* vp) {
|
||||
if (!wasm::HasSupport(cx)) {
|
||||
JS_ReportErrorASCII(cx, "wasm support unavailable");
|
||||
return false;
|
||||
}
|
||||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
args.rval().set(UndefinedValue());
|
||||
|
||||
if (!args.get(0).isObject()) {
|
||||
JS_ReportErrorASCII(cx, "argument is not an object");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
|
||||
if (func && wasm::IsWasmExportedFunction(func)) {
|
||||
uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func);
|
||||
wasm::Instance& instance = wasm::ExportedFunctionToInstance(func);
|
||||
wasm::Tier tier = instance.code().funcTier(funcIndex);
|
||||
RootedString tierString(cx, JS_NewStringCopyZ(cx, wasm::ToString(tier)));
|
||||
if (!tierString) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
args.rval().set(StringValue(tierString));
|
||||
return true;
|
||||
}
|
||||
JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ToIonDumpContents(JSContext* cx, HandleValue value,
|
||||
wasm::IonDumpContents* contents) {
|
||||
RootedString option(cx, JS::ToString(cx, value));
|
||||
@ -10057,6 +10089,10 @@ JS_FOR_WASM_FEATURES(WASM_FEATURE)
|
||||
" ImportJitExit - wasm-to-jitted-JS stubs\n"
|
||||
" all - all kinds, including obscure ones\n"),
|
||||
|
||||
JS_FN_HELP("wasmFunctionTier", WasmFunctionTier, 1, 0,
|
||||
"wasmFunctionTier(wasmFunc)\n",
|
||||
" Returns the best compiled tier for a function. Either 'baseline' or 'optimized'."),
|
||||
|
||||
JS_FN_HELP("wasmHasTier2CompilationCompleted", WasmHasTier2CompilationCompleted, 1, 0,
|
||||
"wasmHasTier2CompilationCompleted(module)",
|
||||
" Returns a boolean indicating whether a given module has finished compiled code for tier2. \n"
|
||||
|
@ -1,19 +1,66 @@
|
||||
// |jit-test| skip-if: !wasmGcEnabled(); test-also=-P wasm_experimental_compile_pipeline;
|
||||
// |jit-test| skip-if: !wasmGcEnabled() || !wasmExperimentalCompilePipelineEnabled(); test-also=-P wasm_experimental_compile_pipeline;
|
||||
|
||||
// Basic test that call_ref profiling information works. Will be expanded in
|
||||
// a future commit.
|
||||
let {test} = wasmEvalText(`
|
||||
(module
|
||||
(type $refType (func (result i32)))
|
||||
(func $ref (export "ref") (result i32)
|
||||
i32.const 1
|
||||
)
|
||||
(func (export "test") (result i32)
|
||||
ref.func $ref
|
||||
call_ref $refType
|
||||
)
|
||||
)`).exports;
|
||||
const tierUpThreshold = 1;
|
||||
let {importFunc} = wasmEvalText(`
|
||||
(module (func (export "importFunc") (result i32) i32.const 2))
|
||||
`).exports;
|
||||
let testFuncs = [
|
||||
[importFunc, 2],
|
||||
["trueFunc", 1],
|
||||
["falseFunc", 0],
|
||||
["trapFunc", WebAssembly.RuntimeError],
|
||||
[null, WebAssembly.RuntimeError],
|
||||
];
|
||||
function invokeTestWith(exports, exportThing, expected) {
|
||||
let targetFunc;
|
||||
if (exportThing instanceof WebAssembly.Function) {
|
||||
targetFunc = exportThing;
|
||||
} else if (exportThing === null) {
|
||||
targetFunc = null;
|
||||
} else {
|
||||
targetFunc = exports[exportThing];
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
assertEq(test(), 1);
|
||||
if (expected === WebAssembly.RuntimeError) {
|
||||
assertErrorMessage(() => exports.test(targetFunc), WebAssembly.RuntimeError, /./);
|
||||
} else {
|
||||
assertEq(exports.test(targetFunc), expected);
|
||||
}
|
||||
}
|
||||
|
||||
for ([funcToInline, funcToInlineExpected] of testFuncs) {
|
||||
let exports = wasmEvalText(`
|
||||
(module
|
||||
(type $booleanFunc (func (result i32)))
|
||||
|
||||
(func $importFunc (import "" "importFunc") (result i32))
|
||||
(func $trueFunc (export "trueFunc") (result i32)
|
||||
i32.const 1
|
||||
)
|
||||
(func $falseFunc (export "falseFunc") (result i32)
|
||||
i32.const 0
|
||||
)
|
||||
(func $trapFunc (export "trapFunc") (result i32)
|
||||
unreachable
|
||||
)
|
||||
|
||||
(func (export "test") (param (ref null $booleanFunc)) (result i32)
|
||||
local.get 0
|
||||
call_ref $booleanFunc
|
||||
)
|
||||
)`, {"": {importFunc}}).exports;
|
||||
let test = exports["test"];
|
||||
|
||||
// Force a tier-up of the function, calling the same function every time
|
||||
assertEq(wasmFunctionTier(test), "baseline");
|
||||
for (let i = 0; i <= tierUpThreshold; i++) {
|
||||
invokeTestWith(exports, funcToInline, funcToInlineExpected);
|
||||
}
|
||||
assertEq(wasmFunctionTier(test), "optimized");
|
||||
|
||||
// Now that we've inlined something, try calling it with every test function
|
||||
// and double check we get the expected behavior
|
||||
for ([testFunc, testFuncExpected] of testFuncs) {
|
||||
invokeTestWith(exports, testFunc, testFuncExpected);
|
||||
}
|
||||
}
|
||||
|
@ -1128,6 +1128,9 @@ class Code : public ShareableBase<Code> {
|
||||
bool funcHasTier(uint32_t funcIndex, Tier tier) const {
|
||||
return funcCodeBlock(funcIndex).tier() == tier;
|
||||
}
|
||||
Tier funcTier(uint32_t funcIndex) const {
|
||||
return funcCodeBlock(funcIndex).tier();
|
||||
}
|
||||
|
||||
const LinkData* codeBlockLinkData(const CodeBlock& block) const;
|
||||
void clearLinkData() const;
|
||||
@ -1179,6 +1182,8 @@ class Code : public ShareableBase<Code> {
|
||||
}
|
||||
return block->lookupUnwindInfo(pc);
|
||||
}
|
||||
// Search through this code to find which tier a code range is from. Returns
|
||||
// false if this code range was not found.
|
||||
bool lookupFunctionTier(const CodeRange* codeRange, Tier* tier) const;
|
||||
|
||||
// To save memory, profilingLabels_ are generated lazily when profiling mode
|
||||
|
@ -46,6 +46,17 @@ enum class Tier {
|
||||
Serialized = Optimized
|
||||
};
|
||||
|
||||
static constexpr const char* ToString(Tier tier) {
|
||||
switch (tier) {
|
||||
case wasm::Tier::Baseline:
|
||||
return "baseline";
|
||||
case wasm::Tier::Optimized:
|
||||
return "optimized";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator over tiers present in a tiered data structure.
|
||||
|
||||
class Tiers {
|
||||
|
@ -2309,9 +2309,11 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports,
|
||||
|
||||
// Initialize the hotness counters, if relevant
|
||||
if (code().mode() == CompileMode::LazyTiering) {
|
||||
// Set every function so that the first time it executes, it will trip the
|
||||
// hotness counter to negative and force a tier-up.
|
||||
for (uint32_t funcIndex = codeMeta().numFuncImports;
|
||||
funcIndex < codeMeta().numFuncs(); funcIndex++) {
|
||||
funcDefInstanceData(funcIndex)->hotnessCounter = 1;
|
||||
funcDefInstanceData(funcIndex)->hotnessCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2700,12 +2702,14 @@ void Instance::resetHotnessCounter(uint32_t funcIndex) {
|
||||
}
|
||||
|
||||
void Instance::submitCallRefHints(uint32_t funcIndex) {
|
||||
CallRefMetricsRange funcRange = codeMeta().getFuncDefCallRefs(funcIndex);
|
||||
for (uint32_t callRefIndex = funcRange.begin;
|
||||
callRefIndex < funcRange.begin + funcRange.length; callRefIndex++) {
|
||||
uint32_t callCountThreshold =
|
||||
JS::Prefs::wasm_experimental_inline_call_ref_threshold();
|
||||
CallRefMetricsRange range = codeMeta().getFuncDefCallRefs(funcIndex);
|
||||
for (uint32_t callRefIndex = range.begin;
|
||||
callRefIndex < range.begin + range.length; callRefIndex++) {
|
||||
CallRefMetrics& metrics = callRefMetrics_[callRefIndex];
|
||||
if (metrics.state == CallRefMetrics::State::Monomorphic &&
|
||||
metrics.callCount >= 1) {
|
||||
metrics.callCount >= callCountThreshold) {
|
||||
uint32_t funcIndex =
|
||||
wasm::ExportedFunctionToFuncIndex(metrics.monomorphicTarget);
|
||||
codeMeta().setCallRefHint(callRefIndex,
|
||||
|
@ -8270,6 +8270,12 @@
|
||||
mirror: always
|
||||
set_spidermonkey_pref: always
|
||||
|
||||
- name: javascript.options.wasm_experimental_inline_call_ref_threshold
|
||||
type: uint32_t
|
||||
value: 1
|
||||
mirror: always
|
||||
set_spidermonkey_pref: always
|
||||
|
||||
# Support for pretenuring allocations based on their allocation site.
|
||||
- name: javascript.options.site_based_pretenuring
|
||||
type: bool
|
||||
|
Loading…
Reference in New Issue
Block a user