diff options
Diffstat (limited to 'js/src/jit-test/tests/wasm/profiling.js')
-rw-r--r-- | js/src/jit-test/tests/wasm/profiling.js | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/profiling.js b/js/src/jit-test/tests/wasm/profiling.js new file mode 100644 index 0000000000..06d1fb676c --- /dev/null +++ b/js/src/jit-test/tests/wasm/profiling.js @@ -0,0 +1,416 @@ +// |jit-test| skip-if: !WasmHelpers.isSingleStepPropfilingEnabled + +const Module = WebAssembly.Module; +const Instance = WebAssembly.Instance; +const Table = WebAssembly.Table; + +const { + assertEqImpreciseStacks, + assertEqPreciseStacks, + startProfiling, + endProfiling +} = WasmHelpers; + +function test(code, importObj, expectedStacks) +{ + enableGeckoProfiling(); + + var f = wasmEvalText(code, importObj).exports[""]; + startProfiling(); + f(); + assertEqImpreciseStacks(endProfiling(), expectedStacks); + + disableGeckoProfiling(); +} + +test( +`(module + (func (result i32) (i32.const 42)) + (export "" (func 0)) +)`, +{}, +["", ">", "0,>", ">", ""]); + +test( +`(module + (func (result i32) (i32.add (call 1) (i32.const 1))) + (func (result i32) (i32.const 42)) + (export "" (func 0)) +)`, +{}, +["", ">", "0,>", "1,0,>", "0,>", ">", ""]); + +test( +`(module + (func $foo (call_indirect (type 0) (i32.const 0))) + (func $bar) + (table funcref (elem $bar)) + (export "" (func $foo)) +)`, +{}, +["", ">", "0,>", "1,0,>", "0,>", ">", ""]); + +test( +`(module + (import $foo "" "foo") + (table funcref (elem $foo)) + (func $bar (call_indirect (type 0) (i32.const 0))) + (export "" (func $bar)) +)`, +{"":{foo:()=>{}}}, +["", ">", "1,>", "0,1,>", "<,0,1,>", "0,1,>", "1,>", ">", ""]); + +test(`(module + (import $f32 "Math" "sin" (param f32) (result f32)) + (func (export "") (param f32) (result f32) + local.get 0 + call $f32 + ) +)`, +this, +["", ">", "1,>", "<,1,>", "1,>", ">", ""]); + +if (getBuildConfiguration()["arm-simulator"]) { + // On ARM, some int64 operations are calls to C++. + for (let op of ['div_s', 'rem_s', 'div_u', 'rem_u']) { + test(`(module + (func (export "") (param i32) (result i32) + local.get 0 + i64.extend_s/i32 + i64.const 0x1a2b3c4d5e6f + i64.${op} + i32.wrap/i64 + ) + )`, + this, + ["", ">", "0,>", "<,0,>", `i64.${op},0,>`, "<,0,>", "0,>", ">", ""], + ); + } +} + +// memory.size is a callout. +test(`(module + (memory 1) + (func (export "") (result i32) + memory.size + ) +)`, +this, +["", ">", "0,>", "<,0,>", "memory.size,0,>", "<,0,>", "0,>", ">", ""], +); + +// memory.grow is a callout. +test(`(module + (memory 1) + (func (export "") (result i32) + i32.const 1 + memory.grow + ) +)`, +this, +["", ">", "0,>", "<,0,>", "memory.grow,0,>", "<,0,>", "0,>", ">", ""], +); + +// A few math builtins. +for (let type of ['f32', 'f64']) { + for (let func of ['ceil', 'floor', 'nearest', 'trunc']) { + test(`(module + (func (export "") (param ${type}) (result ${type}) + local.get 0 + ${type}.${func} + ) + )`, + this, + ["", ">", "0,>", "<,0,>", `${type}.${func},0,>`, "<,0,>", "0,>", ">", ""]); + } +} + +(function() { + // Error handling. + function testError(code, error, expect) + { + enableGeckoProfiling(); + var f = wasmEvalText(code).exports[""]; + enableSingleStepProfiling(); + assertThrowsInstanceOf(f, error); + assertEqImpreciseStacks(disableSingleStepProfiling(), expect); + disableGeckoProfiling(); + } + + testError( + `(module + (func $foo (unreachable)) + (func (export "") (call $foo)) + )`, + WebAssembly.RuntimeError, + ["", ">", "1,>", "0,1,>", "1,>", "", ">", ""]); + + testError( + `(module + (type $good (func)) + (type $bad (func (param i32))) + (func $foo (call_indirect (type $bad) (i32.const 1) (i32.const 0))) + (func $bar (type $good)) + (table funcref (elem $bar)) + (export "" (func $foo)) + )`, + WebAssembly.RuntimeError, + ["", ">", "0,>", "1,0,>", ">", "", ">", ""]); +})(); + +(function() { + // Tables fun. + var e = wasmEvalText(` + (module + (func $foo (result i32) (i32.const 42)) + (export "foo" (func $foo)) + (func $bar (result i32) (i32.const 13)) + (table 10 funcref) + (elem (i32.const 0) $foo $bar) + (export "tbl" (table 0)) + )`).exports; + assertEq(e.foo(), 42); + assertEq(e.tbl.get(0)(), 42); + assertEq(e.tbl.get(1)(), 13); + + enableGeckoProfiling(); + enableSingleStepProfiling(); + assertEq(e.tbl.get(0)(), 42); + assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "0,>", ">", ""]); + disableGeckoProfiling(); + + assertEq(e.foo(), 42); + assertEq(e.tbl.get(0)(), 42); + assertEq(e.tbl.get(1)(), 13); + + enableGeckoProfiling(); + enableSingleStepProfiling(); + assertEq(e.tbl.get(1)(), 13); + assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "1,>", ">", ""]); + disableGeckoProfiling(); + + assertEq(e.tbl.get(0)(), 42); + assertEq(e.tbl.get(1)(), 13); + assertEq(e.foo(), 42); + + enableGeckoProfiling(); + enableSingleStepProfiling(); + assertEq(e.foo(), 42); + assertEq(e.tbl.get(1)(), 13); + assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "0,>", ">", "", ">", "1,>", ">", ""]); + disableGeckoProfiling(); + + var e2 = wasmEvalText(` + (module + (type $v2i (func (result i32))) + (import "a" "b" (table 10 funcref)) + (elem (i32.const 2) $bar) + (func $bar (result i32) (i32.const 99)) + (func $baz (param $i i32) (result i32) (call_indirect (type $v2i) (local.get $i))) + (export "baz" (func $baz)) + )`, {a:{b:e.tbl}}).exports; + + enableGeckoProfiling(); + enableSingleStepProfiling(); + assertEq(e2.baz(0), 42); + assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "1,>", "0,1,>", "1,>", ">", ""]); + disableGeckoProfiling(); + + enableGeckoProfiling(); + enableSingleStepProfiling(); + assertEq(e2.baz(1), 13); + assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "1,>", "1,1,>", "1,>", ">", ""]); + disableGeckoProfiling(); + + enableGeckoProfiling(); + enableSingleStepProfiling(); + assertEq(e2.baz(2), 99); + assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "1,>", "0,1,>", "1,>", ">", ""]); + disableGeckoProfiling(); +})(); + +(function() { + // Optimized wasm->wasm import. + var m1 = new Module(wasmTextToBinary(`(module + (func $foo (result i32) (i32.const 42)) + (export "foo" (func $foo)) + )`)); + var m2 = new Module(wasmTextToBinary(`(module + (import $foo "a" "foo" (result i32)) + (func $bar (result i32) (call $foo)) + (export "bar" (func $bar)) + )`)); + + // Instantiate while not active: + var e1 = new Instance(m1).exports; + var e2 = new Instance(m2, {a:e1}).exports; + enableGeckoProfiling(); + enableSingleStepProfiling(); + assertEq(e2.bar(), 42); + assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "1,>", "0,1,>", "1,>", ">", ""]); + disableGeckoProfiling(); + assertEq(e2.bar(), 42); + + // Instantiate while active: + enableGeckoProfiling(); + var e3 = new Instance(m1).exports; + var e4 = new Instance(m2, {a:e3}).exports; + enableSingleStepProfiling(); + assertEq(e4.bar(), 42); + assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "1,>", "0,1,>", "1,>", ">", ""]); + disableGeckoProfiling(); + assertEq(e4.bar(), 42); +})(); + +(function() { + // FFIs test. + let prevOptions = getJitCompilerOptions(); + + // Skip tests if baseline isn't enabled, since the stacks might differ by + // a few instructions. + if (prevOptions['baseline.enable'] === 0) + return; + + setJitCompilerOption("baseline.warmup.trigger", 10); + + enableGeckoProfiling(); + + var m = new Module(wasmTextToBinary(`(module + (import $ffi "a" "ffi" (param i32) (result i32)) + + (import $missingOneArg "a" "sumTwo" (param i32) (result i32)) + + (func (export "foo") (param i32) (result i32) + local.get 0 + call $ffi) + + (func (export "id") (param i32) (result i32) + local.get 0 + call $missingOneArg + ) + )`)); + + var valueToConvert = 0; + function ffi(n) { + new Error().stack; // enter VM to clobber FP register. + if (n == 1337) { return valueToConvert }; + return 42; + } + + function sumTwo(a, b) { + return (a|0)+(b|0)|0; + } + + // Baseline compile ffi. + for (var i = 20; i --> 0;) { + ffi(i); + sumTwo(i-1, i+1); + } + + var imports = { + a: { + ffi, + sumTwo + } + }; + + var i = new Instance(m, imports).exports; + + // Enable the jit exit. + assertEq(i.foo(0), 42); + assertEq(i.id(13), 13); + + // Test normal conditions. + enableSingleStepProfiling(); + assertEq(i.foo(0), 42); + assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "2,>", "<,2,>", + // Losing stack information while the JIT func prologue sets profiler + // virtual FP. + "", + // Callee time. + "<,2,>", + // Losing stack information while we're exiting JIT func epilogue and + // recovering wasm FP. + "", + // Back into the jit exit (frame info has been recovered). + "<,2,>", + // Normal unwinding. + "2,>", ">", ""]); + + // Test rectifier frame. + enableSingleStepProfiling(); + assertEq(i.id(100), 100); + assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "3,>", "<,3,>", + // Rectifier frame time is spent here (lastProfilingFrame has not been + // set). + "", + "<,3,>", + // Rectifier frame unwinding time is spent here. + "", + "<,3,>", + "3,>", ">", ""]); + + // Test OOL coercion path. + valueToConvert = 2**31; + + enableSingleStepProfiling(); + assertEq(i.foo(1337), -(2**31)); + assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "2,>", "<,2,>", "", "<,2,>", "", + // Back into the jit exit (frame info has been recovered). + // Inline conversion fails, we skip to the OOL path, call from there + // and get back to the jit exit. + "<,2,>", + // Normal unwinding. + "2,>", ">", ""]); + + disableGeckoProfiling(); + setJitCompilerOption("baseline.warmup.trigger", prevOptions["baseline.warmup.trigger"]); +})(); + +// Make sure it's possible to single-step through call through debug-enabled code. +(function() { + enableGeckoProfiling(); + + let g = newGlobal(''); + let dbg = new Debugger(g); + dbg.onEnterFrame = () => {}; + enableSingleStepProfiling(); + g.eval(` + var code = wasmTextToBinary('(module (func (export "run") (result i32) i32.const 42))'); + var i = new WebAssembly.Instance(new WebAssembly.Module(code)); + assertEq(i.exports.run(), 42); + `); + + disableSingleStepProfiling(); + disableGeckoProfiling(); +})(); + +// Ion->wasm calls. +let func = wasmEvalText(`(module + (func $inner (result i32) (param i32) (param i32) + local.get 0 + local.get 1 + i32.add + ) + (func (export "add") (result i32) (param i32) (param i32) + local.get 0 + local.get 1 + call $inner + ) +)`).exports.add; + +(function() { + enableGeckoProfiling(); + // 10 is enough in ion eager mode. + for (let i = 0; i < 10; i++) { + enableSingleStepProfiling(); + let res = func(i - 1, i + 1); + assertEqPreciseStacks(disableSingleStepProfiling(), [ + ['', '>', '1,>', '0,1,>' , '1,>', '>', ''], // slow entry + ['', '!>', '1,!>', '0,1,!>' , '1,!>', '!>', ''], // fast entry + ['', '1', '0,1' , '1', ''], // inlined jit call + ]); + assertEq(res, i+i); + } + disableGeckoProfiling(); +})(); |