blob: da2de78aaee2ecd1389be46e2d0ffd2addd5e071 [file] [edit]
import { instantiate } from "../wabt-wrapper.js";
import * as assert from "../assert.js";
let watExpressionStackBug = `
(module
(import "env" "thrower" (func $thrower))
(import "env" "val1" (global $val1 i32))
(import "env" "val2" (global $val2 f64))
(import "env" "val3" (global $val3 i32))
(import "env" "tag" (tag $exc))
(func (export "test") (result i32)
(local $outerlocal i32)
(local $innerlocal i32)
global.get $val3 ;; 50
local.set $outerlocal
try $outer
call $thrower
catch $exc
global.get $val1 ;; 100 (i32)
global.get $val2 ;; 200.0 (f64)
;; Enter inner try with those values on the stack
try $inner
i32.const 25
local.set $innerlocal
call $thrower
catch $exc
;; Inner catch completes - values 100 (i32), 200.0 (f64) should still be on stack
;; And innerlocal should still be 25
end
;; Convert f64 to i32 and add: i32.trunc_f64_s(200.0) + 100 = 200 + 100 = 300
i32.trunc_f64_s
i32.add
;; Add the local values: 300 + 50 + 25 = 375
local.get $outerlocal
i32.add
local.get $innerlocal
i32.add
return
end
;; Should not reach here
i32.const 999
)
)
`;
let watExpressionStackBugWithOSR = `
(module
(import "env" "thrower" (func $thrower))
(import "env" "val1" (global $val1 i32))
(import "env" "val2" (global $val2 f64))
(import "env" "val3" (global $val3 i32))
(import "env" "tag" (tag $exc))
(func (export "test") (param $loopCount i32) (result i32)
(local $outerlocal i32)
(local $innerlocal i32)
(local $counter i32)
global.get $val3 ;; 50
local.set $outerlocal
try $outer
call $thrower
catch $exc
global.get $val1 ;; 100 (i32)
global.get $val2 ;; 200.0 (f64)
try $inner
i32.const 25
local.set $innerlocal
call $thrower
catch $exc
;; Inner catch - do a hot loop to trigger OSR tier-up
;; This tests that expression stack and locals are preserved across OSR
i32.const 0
local.set $counter
(block $break
(loop $continue
;; Increment counter
local.get $counter
i32.const 1
i32.add
local.set $counter
local.get $counter
local.get $loopCount
i32.lt_u
br_if $continue
)
)
;; After OSR, values 100 (i32), 200.0 (f64) should still be on stack
;; And innerlocal should still be 25
end
;; Convert f64 to i32 and add: i32.trunc_f64_s(200.0) + 100 = 200 + 100 = 300
i32.trunc_f64_s
i32.add
;; Add the local values: 300 + 50 + 25 = 375
local.get $outerlocal
i32.add
local.get $innerlocal
i32.add
return
end
;; Should not reach here
i32.const 999
)
)
`;
async function testExpressionStack() {
const tag = new WebAssembly.Tag({ parameters: [] });
const importObject = {
env: {
tag: tag,
thrower: () => { throw new WebAssembly.Exception(tag, []); },
val1: new WebAssembly.Global({ value: 'i32', mutable: false }, 100),
val2: new WebAssembly.Global({ value: 'f64', mutable: false }, 200.0),
val3: new WebAssembly.Global({ value: 'i32', mutable: false }, 50)
}
};
const instance = await instantiate(watExpressionStackBug, importObject, { exceptions: true });
for (let i = 0; i < wasmTestLoopCount; i++) {
let result = instance.exports.test();
// Expected: i32.trunc_f64_s(200.0) + 100 + 50 + 25 = 200 + 100 + 50 + 25 = 375
assert.eq(result, 375);
}
}
async function testExpressionStackWithOSR() {
const tag = new WebAssembly.Tag({ parameters: [] });
const importObject = {
env: {
tag: tag,
thrower: () => { throw new WebAssembly.Exception(tag, []); },
val1: new WebAssembly.Global({ value: 'i32', mutable: false }, 100),
val2: new WebAssembly.Global({ value: 'f64', mutable: false }, 200.0),
val3: new WebAssembly.Global({ value: 'i32', mutable: false }, 50)
}
};
const instance = await instantiate(watExpressionStackBugWithOSR, importObject, { exceptions: true });
// Use a large loop count to ensure OSR tier-up happens
const loopCount = 10000;
for (let i = 0; i < wasmTestLoopCount; i++) {
let result = instance.exports.test(loopCount);
// Expected: i32.trunc_f64_s(200.0) + 100 + 50 + 25 = 200 + 100 + 50 + 25 = 375
assert.eq(result, 375);
}
}
assert.asyncTest(testExpressionStack());
assert.asyncTest(testExpressionStackWithOSR());