Source code

Revision control

Copy as Markdown

Other Tools

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