diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 9863fc397ddb..ee85fc98e526 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -1256,79 +1255,6 @@ static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) { return true; } -static bool ComputeTier(JSContext* cx, const wasm::Code& code, - HandleValue tierSelection, wasm::Tier* tier) { - *tier = code.stableTier(); - if (!tierSelection.isUndefined() && - !ConvertToTier(cx, tierSelection, code, tier)) { - JS_ReportErrorASCII(cx, "invalid tier"); - return false; - } - - if (!code.hasTier(*tier)) { - JS_ReportErrorASCII(cx, "function missing selected tier"); - return false; - } - - return true; -} - -static bool DisassembleIt( - JSContext* cx, bool asString, MutableHandleValue rval, - const std::function& disassembleIt) { - if (asString) { - DisasmBuffer buf(cx); - disasmBuf.set(&buf); - auto onFinish = mozilla::MakeScopeExit([&] { disasmBuf.set(nullptr); }); - disassembleIt(captureDisasmText); - if (buf.oom) { - ReportOutOfMemory(cx); - return false; - } - JSString* sresult = buf.builder.finishString(); - if (!sresult) { - ReportOutOfMemory(cx); - return false; - } - rval.setString(sresult); - return true; - } - - disassembleIt([](const char* text) { fprintf(stderr, "%s\n", text); }); - return true; -} - -static bool WasmDisassembleFunction(JSContext* cx, const HandleFunction& func, - HandleValue tierSelection, bool asString, - MutableHandleValue rval) { - wasm::Instance& instance = wasm::ExportedFunctionToInstance(func); - uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func); - wasm::Tier tier; - - if (!ComputeTier(cx, instance.code(), tierSelection, &tier)) { - return false; - } - - return DisassembleIt( - cx, asString, rval, [&](void (*captureText)(const char*)) { - instance.disassembleExport(cx, funcIndex, tier, captureText); - }); -} - -static bool WasmDisassembleCode(JSContext* cx, const wasm::Code& code, - HandleValue tierSelection, int kindSelection, - bool asString, MutableHandleValue rval) { - wasm::Tier tier; - if (!ComputeTier(cx, code, tierSelection, &tier)) { - return false; - } - - return DisassembleIt(cx, asString, rval, - [&](void (*captureText)(const char*)) { - code.disassemble(cx, tier, kindSelection, captureText); - }); -} - static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) { if (!cx->options().wasm()) { JS_ReportErrorASCII(cx, "wasm support unavailable"); @@ -1344,87 +1270,51 @@ static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) { return false; } - bool asString = false; - RootedValue tierSelection(cx); - int kindSelection = (1 << wasm::CodeRange::Function); - if (args.length() > 1 && args[1].isObject()) { - RootedObject options(cx, &args[1].toObject()); - RootedValue val(cx); - - if (!JS_GetProperty(cx, options, "asString", &val)) { - return false; - } - asString = val.isBoolean() && val.toBoolean(); - - if (!JS_GetProperty(cx, options, "tier", &tierSelection)) { - return false; - } - - if (!JS_GetProperty(cx, options, "kinds", &val)) { - return false; - } - if (val.isString() && val.toString()->hasLatin1Chars()) { - AutoStableStringChars stable(cx); - if (!stable.init(cx, val.toString())) { - return false; - } - const char* p = (const char*)(stable.latin1Chars()); - const char* end = p + val.toString()->length(); - int selection = 0; - for (;;) { - if (strncmp(p, "Function", 8) == 0) { - selection |= (1 << wasm::CodeRange::Function); - p += 8; - } else if (strncmp(p, "InterpEntry", 11) == 0) { - selection |= (1 << wasm::CodeRange::InterpEntry); - p += 11; - } else if (strncmp(p, "JitEntry", 8) == 0) { - selection |= (1 << wasm::CodeRange::JitEntry); - p += 8; - } else if (strncmp(p, "ImportInterpExit", 16) == 0) { - selection |= (1 << wasm::CodeRange::ImportInterpExit); - p += 16; - } else if (strncmp(p, "ImportJitExit", 13) == 0) { - selection |= (1 << wasm::CodeRange::ImportJitExit); - p += 13; - } else if (strncmp(p, "all", 3) == 0) { - selection = ~0; - p += 3; - } else { - break; - } - if (p == end || *p != ',') { - break; - } - p++; - } - if (p == end) { - kindSelection = selection; - } else { - JS_ReportErrorASCII(cx, "argument object has invalid `kinds`"); - return false; - } - } - } - RootedFunction func(cx, args[0].toObject().maybeUnwrapIf()); - if (func && wasm::IsWasmExportedFunction(func)) { - return WasmDisassembleFunction(cx, func, tierSelection, asString, - args.rval()); + + if (!func || !wasm::IsWasmExportedFunction(func)) { + JS_ReportErrorASCII(cx, "argument is not an exported wasm function"); + return false; } - if (args[0].toObject().is()) { - return WasmDisassembleCode( - cx, args[0].toObject().as().module().code(), - tierSelection, kindSelection, asString, args.rval()); + + wasm::Instance& instance = wasm::ExportedFunctionToInstance(func); + uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func); + + wasm::Tier tier = instance.code().stableTier(); + + if (args.length() > 1 && + !ConvertToTier(cx, args[1], instance.code(), &tier)) { + JS_ReportErrorASCII(cx, "invalid tier"); + return false; } - if (args[0].toObject().is()) { - return WasmDisassembleCode( - cx, args[0].toObject().as().instance().code(), - tierSelection, kindSelection, asString, args.rval()); + + if (!instance.code().hasTier(tier)) { + JS_ReportErrorASCII(cx, "function missing selected tier"); + return false; } - JS_ReportErrorASCII( - cx, "argument is not an exported wasm function or a wasm module"); - return false; + + if (args.length() > 2 && args[2].isBoolean() && args[2].toBoolean()) { + DisasmBuffer buf(cx); + disasmBuf.set(&buf); + auto onFinish = mozilla::MakeScopeExit([&] { disasmBuf.set(nullptr); }); + instance.disassembleExport(cx, funcIndex, tier, captureDisasmText); + if (buf.oom) { + ReportOutOfMemory(cx); + return false; + } + JSString* sresult = buf.builder.finishString(); + if (!sresult) { + ReportOutOfMemory(cx); + return false; + } + args.rval().setString(sresult); + return true; + } + + instance.disassembleExport(cx, funcIndex, tier, [](const char* text) { + fprintf(stderr, "%s\n", text); + }); + return true; } enum class Flag { Tier2Complete, Deserialized }; @@ -6989,22 +6879,11 @@ gc::ZealModeHelpText), " until background compilation is complete."), JS_FN_HELP("wasmDis", WasmDisassemble, 1, 0, -"wasmDis(wasmObject[, options])\n", -" Disassembles generated machine code from an exported WebAssembly function,\n" -" or from all the functions defined in the module or instance, exported and not.\n" -" The `options` is an object with the following optional keys:\n" -" asString: boolean - if true, return a string rather than printing on stderr,\n" -" the default is false.\n" -" tier: string - one of 'stable', 'best', 'baseline', or 'ion'; the default is\n" -" 'stable'.\n" -" kinds: string - if set, and the wasmObject is a module or instance, a\n" -" comma-separated list of the following keys, the default is `Function`:\n" -" Function - functions defined in the module\n" -" InterpEntry - C++-to-wasm stubs\n" -" JitEntry - jitted-js-to-wasm stubs\n" -" ImportInterpExit - wasm-to-C++ stubs\n" -" ImportJitExit - wasm-to-jitted-JS stubs\n" -" all - all kinds, including obscure ones\n"), +"wasmDis(function[, tier [, asString]])", +" Disassembles generated machine code from an exported WebAssembly function.\n" +" The tier is a string, 'stable', 'best', 'baseline', or 'ion'; the default is\n" +" 'stable'. If `asString` is present and is the value `true` then the output\n" +" is returned as a string; otherwise it is printed on stderr."), JS_FN_HELP("wasmHasTier2CompilationCompleted", WasmHasTier2CompilationCompleted, 1, 0, "wasmHasTier2CompilationCompleted(module)", diff --git a/js/src/jit-test/lib/codegen-x64-test.js b/js/src/jit-test/lib/codegen-x64-test.js index 4f88653702be..44e2ea8e61c3 100644 --- a/js/src/jit-test/lib/codegen-x64-test.js +++ b/js/src/jit-test/lib/codegen-x64-test.js @@ -147,7 +147,7 @@ function codegenTestX64_adhoc(module_text, export_name, expected, options = {}) let ins = wasmEvalText(module_text, {}, options.features); if (options.instanceBox) options.instanceBox.value = ins; - let output = wasmDis(ins.exports[export_name], {tier:"ion", asString:true}); + let output = wasmDis(ins.exports[export_name], "ion", true); if (!options.no_prefix) expected = x64_prefix + '\n' + expected; if (!options.no_suffix) diff --git a/js/src/jit-test/lib/codegen-x86-test.js b/js/src/jit-test/lib/codegen-x86-test.js index 89c48b3c95e0..ef7cb796e365 100644 --- a/js/src/jit-test/lib/codegen-x86-test.js +++ b/js/src/jit-test/lib/codegen-x86-test.js @@ -49,7 +49,7 @@ function codegenTestX86_adhoc(module_text, export_name, expected, options = {}) assertEq(hasDisassembler(), true); let ins = wasmEvalText(module_text); - let output = wasmDis(ins.exports[export_name], {tier:"ion", asString:true}); + let output = wasmDis(ins.exports[export_name], "ion", true); if (!options.no_prefix) expected = x86_prefix + '\n' + expected; if (!options.no_suffix) diff --git a/js/src/jit-test/tests/wasm/disasm.js b/js/src/jit-test/tests/wasm/disasm.js deleted file mode 100644 index 9e9a3f211651..000000000000 --- a/js/src/jit-test/tests/wasm/disasm.js +++ /dev/null @@ -1,43 +0,0 @@ -// |jit-test| skip-if: !hasDisassembler() - -// Test that the disassembler is reasonably sane. - -var mod = new WebAssembly.Module(wasmTextToBinary(` -(module - (func $hum (import "m" "hum") (param i32) (result f64)) - (memory 1) - (func $hi (export "f") (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32) - (i32.add (i32.load (local.get 5)) (i32.load (local.get 6)))) - (func $ho (param i32) (result i32) (i32.const 37)) -) -`)); - -// The following capture the disassembly as a string. We can't really check -// that no other output is produced. - -var s = wasmDis(mod, {tier:'best', asString:true}); -assertEq(typeof s, "string") -assertEq(s.match(/Kind = Function/g).length, 3) - -var ins = new WebAssembly.Instance(mod, {m:{hum:(x) => x+0.5}}); -var s = wasmDis(ins, {tier:'best', asString:true}); -assertEq(typeof s, "string") -assertEq(s.match(/Kind = Function/g).length, 3) - -var s = wasmDis(ins.exports.f, {tier:'best', asString:true}) -assertEq(typeof s, "string") - -var s = wasmDis(ins, {asString:true, kinds:"InterpEntry,ImportInterpExit,Function"}) -assertEq(typeof s, "string") -assertEq(s.match(/Kind = Function/g).length, 3) -assertEq(s.match(/Kind = InterpEntry/g).length, 1) -assertEq(s.match(/Kind = ImportInterpExit/g).length, 1) -assertEq(s.match(/name = hi/g).length, 2) -assertEq(s.match(/name = ho/g).length, 1) -assertEq(s.match(/name = hum/g).length, 2) - -// This one prints to stderr, we can't check the output but we can check that a -// string is not returned. - -var s = wasmDis(ins, {tier:'best'}) -assertEq(typeof s, "undefined") diff --git a/js/src/jit-test/tests/wasm/simd/const-arm64-vixl-codegen.js b/js/src/jit-test/tests/wasm/simd/const-arm64-vixl-codegen.js index 9657bf3a7919..3bbc5041c46f 100644 --- a/js/src/jit-test/tests/wasm/simd/const-arm64-vixl-codegen.js +++ b/js/src/jit-test/tests/wasm/simd/const-arm64-vixl-codegen.js @@ -86,7 +86,7 @@ ${suffix} (v128.store (i32.const 0) (call $f))) (func $f (export "f") (result v128) (v128.const ${bits})))`); - let output = wasmDis(ins.exports.f, {tier:"baseline", asString:true}); + let output = wasmDis(ins.exports.f, "baseline", true); assertEq(output.match(new RegExp(expected)) != null, true); let mem = new Int8Array(ins.exports.mem.buffer); set(mem, 0, iota(16).map(x => -1-x)); diff --git a/js/src/jit-test/tests/wasm/spectre-mask.js b/js/src/jit-test/tests/wasm/spectre-mask.js index 28205a7ca7e2..76e91d778e14 100644 --- a/js/src/jit-test/tests/wasm/spectre-mask.js +++ b/js/src/jit-test/tests/wasm/spectre-mask.js @@ -23,16 +23,16 @@ var ins = wasmEvalText(` switch (wasmCompileMode()) { case "ion": - assertEq(wasmDis(ins.exports.wasm2wasm, {tier:'stable', asString:true}).match(/call.*\n.*mov %eax, %eax/).length, 1); - assertEq(wasmDis(ins.exports.wasm2import, {tier:'stable', asString:true}).match(/call.*\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); - assertEq(wasmDis(ins.exports.wasmIndirect, {tier:'stable', asString:true}).match(/call.*\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); - assertEq(wasmDis(ins.exports.instanceCall, {tier:'stable', asString:true}).match(/call.*\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); + assertEq(wasmDis(ins.exports.wasm2wasm, 'stable', true).match(/call.*\n.*mov %eax, %eax/).length, 1); + assertEq(wasmDis(ins.exports.wasm2import, 'stable', true).match(/call.*\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); + assertEq(wasmDis(ins.exports.wasmIndirect, 'stable', true).match(/call.*\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); + assertEq(wasmDis(ins.exports.instanceCall, 'stable', true).match(/call.*\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); break; case "baseline": - assertEq(wasmDis(ins.exports.wasm2wasm, {tier:'stable', asString:true}).match(/call.*\n.*add.*%rsp\n.*mov %eax, %eax/).length, 1); - assertEq(wasmDis(ins.exports.wasm2import, {tier:'stable', asString:true}).match(/call.*\n.*add.*%rsp\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); - assertEq(wasmDis(ins.exports.wasmIndirect, {tier:'stable', asString:true}).match(/call.*\n.*add.*%rsp\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); - assertEq(wasmDis(ins.exports.instanceCall, {tier:'stable', asString:true}).match(/call.*\n.*add.*%rsp\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); + assertEq(wasmDis(ins.exports.wasm2wasm, 'stable', true).match(/call.*\n.*add.*%rsp\n.*mov %eax, %eax/).length, 1); + assertEq(wasmDis(ins.exports.wasm2import, 'stable', true).match(/call.*\n.*add.*%rsp\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); + assertEq(wasmDis(ins.exports.wasmIndirect, 'stable', true).match(/call.*\n.*add.*%rsp\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); + assertEq(wasmDis(ins.exports.instanceCall, 'stable', true).match(/call.*\n.*add.*%rsp\n(?:.*movq.*\n)*.*mov %eax, %eax/).length, 1); break; default: throw "Unexpected compile mode"; diff --git a/js/src/wasm/WasmCode.cpp b/js/src/wasm/WasmCode.cpp index 7514cbbbf33f..58b4f99a597b 100644 --- a/js/src/wasm/WasmCode.cpp +++ b/js/src/wasm/WasmCode.cpp @@ -25,7 +25,6 @@ #include "jsnum.h" -#include "jit/Disassemble.h" #include "jit/ExecutableAllocator.h" #ifdef JS_ION_PERF # include "jit/PerfSpewer.h" @@ -1537,65 +1536,6 @@ uint8_t* Code::serialize(uint8_t* cursor, const LinkData& linkData) const { return cursor; } -void Code::disassemble(JSContext* cx, Tier tier, int kindSelection, - PrintCallback printString) const { - const MetadataTier& metadataTier = metadata(tier); - const CodeTier& codeTier = this->codeTier(tier); - const ModuleSegment& segment = codeTier.segment(); - - for (const CodeRange& range : metadataTier.codeRanges) { - if (kindSelection & (1 << range.kind())) { - MOZ_ASSERT(range.begin() < segment.length()); - MOZ_ASSERT(range.end() < segment.length()); - - const char* kind; - char kindbuf[128]; - switch (range.kind()) { - case CodeRange::Function: - kind = "Function"; - break; - case CodeRange::InterpEntry: - kind = "InterpEntry"; - break; - case CodeRange::JitEntry: - kind = "JitEntry"; - break; - case CodeRange::ImportInterpExit: - kind = "ImportInterpExit"; - break; - case CodeRange::ImportJitExit: - kind = "ImportJitExit"; - break; - default: - SprintfLiteral(kindbuf, "CodeRange::Kind(%d)", range.kind()); - kind = kindbuf; - break; - } - const char* separator = - "\n--------------------------------------------------\n"; - // The buffer is quite large in order to accomodate mangled C++ names; - // lengths over 3500 have been observed in the wild. - char buf[4096]; - if (range.hasFuncIndex()) { - const char* funcName = "(unknown)"; - UTF8Bytes namebuf; - if (metadata().getFuncNameStandalone(range.funcIndex(), &namebuf) && - namebuf.append('\0')) { - funcName = namebuf.begin(); - } - SprintfLiteral(buf, "%sKind = %s, index = %d, name = %s:\n", separator, - kind, range.funcIndex(), funcName); - } else { - SprintfLiteral(buf, "%sKind = %s\n", separator, kind); - } - printString(buf); - - uint8_t* theCode = segment.base() + range.begin(); - jit::Disassemble(theCode, range.end() - range.begin(), printString); - } - } -} - void wasm::PatchDebugSymbolicAccesses(uint8_t* codeBase, MacroAssembler& masm) { #ifdef WASM_CODEGEN_DEBUG for (auto& access : masm.symbolicAccesses()) { diff --git a/js/src/wasm/WasmCode.h b/js/src/wasm/WasmCode.h index 935802a7afed..a04011f9e1f3 100644 --- a/js/src/wasm/WasmCode.h +++ b/js/src/wasm/WasmCode.h @@ -748,11 +748,6 @@ class Code : public ShareableBase { void ensureProfilingLabels(bool profilingEnabled) const; const char* profilingLabel(uint32_t funcIndex) const; - // Wasm disassembly support - - void disassemble(JSContext* cx, Tier tier, int kindSelection, - PrintCallback printString) const; - // about:memory reporting: void addSizeOfMiscIfNotSeen(MallocSizeOf mallocSizeOf, diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp index 6b1b63ee3efd..e31be8bd74a6 100644 --- a/js/src/wasm/WasmInstance.cpp +++ b/js/src/wasm/WasmInstance.cpp @@ -2123,7 +2123,7 @@ void Instance::destroyBreakpointSite(JSFreeOp* fop, uint32_t offset) { } void Instance::disassembleExport(JSContext* cx, uint32_t funcIndex, Tier tier, - PrintCallback printString) const { + PrintCallback callback) const { const MetadataTier& metadataTier = metadata(tier); const FuncExport& funcExport = metadataTier.lookupFuncExport(funcIndex); const CodeRange& range = metadataTier.codeRange(funcExport); @@ -2134,7 +2134,7 @@ void Instance::disassembleExport(JSContext* cx, uint32_t funcIndex, Tier tier, MOZ_ASSERT(range.end() < segment.length()); uint8_t* functionCode = segment.base() + range.begin(); - jit::Disassemble(functionCode, range.end() - range.begin(), printString); + jit::Disassemble(functionCode, range.end() - range.begin(), callback); } void Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf, diff --git a/js/src/wasm/WasmInstance.h b/js/src/wasm/WasmInstance.h index 6e2518da2ffb..377b225cb135 100644 --- a/js/src/wasm/WasmInstance.h +++ b/js/src/wasm/WasmInstance.h @@ -175,7 +175,7 @@ class Instance { // Wasm disassembly support void disassembleExport(JSContext* cx, uint32_t funcIndex, Tier tier, - PrintCallback printString) const; + PrintCallback callback) const; public: // Functions to be called directly from wasm code.