summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/profiling.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/profiling.js')
-rw-r--r--js/src/jit-test/tests/wasm/profiling.js416
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();
+})();