Source code

Revision control

Copy as Markdown

Other Tools

// A stress test for stackmap creation as it relates to params and locals.
function Stuff(n) {
this.n = n;
}
function allocate(n) {
const res = new Stuff(n);
// The webassembly loop below will provide us with 254 as an arg after not
// very long.
if (n == 254) {
gc();
}
return res;
}
function visit(aStuff) {
return aStuff.n;
}
let bin = wasmTextToBinary(
`
(module
(import "" "allocate" (func $allocate (param i32) (result externref)))
(import "" "visit" (func $visit (param externref) (result i32)))
;; A function with many params and locals, most of which are ref-typed.
;; The purpose of having so many is to defeat any reasonable attempt at
;; allocating them all in registers. The asymmetrically-placed i32s are
;; an attempt to expose any misalignment or inversion of the stack layout
;; vs what the stackmap claims the layout to be.
(func $manyParamsAndLocals (export "manyParamsAndLocals")
(param $p1 externref) (param $p2 i32) (param $p3 externref)
(param $p4 externref) (param $p5 externref) (param $p6 externref)
(param $p7 externref) (param $p8 externref) (param $p9 i32)
(result i32)
(local $l1 externref) (local $l2 externref) (local $l3 externref)
(local $l4 i32) (local $l5 externref) (local $l6 i32)
(local $l7 externref) (local $l8 externref) (local $l9 externref)
(local $i i32)
(local $runningTotal i32)
;; Bind some objects to l1 .. l9. The JS harness will already
;; have done the same for p1 .. p9.
(local.set $l1 (call $allocate (i32.const 1)))
(local.set $l2 (call $allocate (i32.const 3)))
(local.set $l3 (call $allocate (i32.const 5)))
(local.set $l4 (i32.const 7))
(local.set $l5 (call $allocate (i32.const 9)))
(local.set $l6 (i32.const 11))
(local.set $l7 (call $allocate (i32.const 13)))
(local.set $l8 (call $allocate (i32.const 15)))
(local.set $l9 (call $allocate (i32.const 17)))
;; Now loop, allocating as we go, and forcing GC every 256 iterations.
;; Also in each iteration, visit all the locals and params, in the hope
;; of exposing any cases where they are not held live across GC.
(loop $CONT
;; Allocate, and hold on to the resulting value, so that Ion can't
;; delete the allocation.
(local.set $l9 (call $allocate (i32.and (local.get $i) (i32.const 255))))
;; Visit all params and locals
local.get $runningTotal
(call $visit (local.get $p1))
i32.add
local.get $p2
i32.add
(call $visit (local.get $p3))
i32.add
(call $visit (local.get $p4))
i32.add
(call $visit (local.get $p5))
i32.add
(call $visit (local.get $p6))
i32.add
(call $visit (local.get $p7))
i32.add
(call $visit (local.get $p8))
i32.add
local.get $p9
i32.add
(call $visit (local.get $l1))
i32.add
(call $visit (local.get $l2))
i32.add
(call $visit (local.get $l3))
i32.add
local.get $l4
i32.add
(call $visit (local.get $l5))
i32.add
local.get $l6
i32.add
(call $visit (local.get $l7))
i32.add
(call $visit (local.get $l8))
i32.add
(call $visit (local.get $l9))
i32.add
local.set $runningTotal
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(br_if $CONT (i32.lt_s (local.get $i) (i32.const 10000)))
) ;; loop
local.get $runningTotal
) ;; func
)
`);
let mod = new WebAssembly.Module(bin);
let ins = new WebAssembly.Instance(mod, {"":{allocate, visit}});
let p1 = allocate(97);
let p2 = 93;
let p3 = allocate(91);
let p4 = allocate(83);
let p5 = allocate(79);
let p6 = allocate(73);
let p7 = allocate(71);
let p8 = allocate(67);
let p9 = 61;
let res = ins.exports.manyParamsAndLocals(p1, p2, p3, p4, p5, p6, p7, p8, p9);
// Check that GC didn't mess up p1 .. p9
res += visit(p1);
res += p2;
res += visit(p3);
res += visit(p4);
res += visit(p5);
res += visit(p6);
res += visit(p7);
res += visit(p8);
res += p9;
assertEq(res, 9063795);