Add a MachBuffer::defer_trap method (#6011)
* Add a `MachBuffer::defer_trap` method This commit adds a new method to `MachBuffer` to defer trap opcodes to the end of a function in a similar manner to how constants are deferred to the end of the function. This is useful for backends which frequently use `TrapIf`-style opcodes. Currently a jump is emitted which skips the next instruction, a trap, and then execution continues normally. While there isn't any pressing problem with this construction the trap opcode is in the middle of the instruction stream as opposed to "off on the side" despite rarely being taken. With this method in place all the backends (except riscv64 since I couldn't figure it out easily enough) have a new lowering of their `TrapIf` opcode. Now a trap is deferred, which returns a label, and then that label is jumped to when executing the trap. A fixup is then recorded in `MachBuffer` to get patched later on during emission, or at the end of the function. Subsequently all `TrapIf` instructions translate to a single branch plus a single trap at the end of the function. I've additionally further updated some more lowerings in the x64 backend which were explicitly using traps to instead use `TrapIf` where applicable to avoid jumping over traps mid-function. Other backends didn't appear to have many jump-over-the-next-trap patterns. Lots of tests have had their expectations updated here which should reflect all the traps being sunk to the end of functions. * Print trap code on all platforms * Emit traps before constants * Preserve source location information for traps * Fix test expectations * Attempt to fix s390x The MachBuffer was registering trap codes with the first byte of the trap, but the SIGILL handler was expecting it to be registered with the last byte of the trap. Exploit that SIGILL is always represented with a 2-byte instruction and always march 2-backwards for SIGILL, continuing to march backwards 1 byte for SIGFPE-generating instructions. * Back out s390x changes * Back out more s390x bits * Review comments
This commit is contained in:
@@ -641,10 +641,15 @@ fn assert_trap_code(wat: &str, code: wasmtime::Trap) {
|
||||
};
|
||||
let trap = err.downcast_ref::<Trap>().unwrap();
|
||||
assert_eq!(*trap, code);
|
||||
|
||||
let trace = err.downcast_ref::<WasmBacktrace>().unwrap().frames();
|
||||
assert!(trace.len() > 0);
|
||||
assert_eq!(trace[0].func_index(), 0);
|
||||
assert!(trace[0].func_offset().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn heap_out_of_bounds_trap() {
|
||||
fn trap_codes() {
|
||||
assert_trap_code(
|
||||
r#"
|
||||
(module
|
||||
@@ -666,6 +671,46 @@ fn heap_out_of_bounds_trap() {
|
||||
"#,
|
||||
Trap::MemoryOutOfBounds,
|
||||
);
|
||||
|
||||
for (ty, min) in [("i32", i32::MIN as u32 as u64), ("i64", i64::MIN as u64)] {
|
||||
for op in ["rem", "div"] {
|
||||
for sign in ["u", "s"] {
|
||||
println!("testing {ty}.{op}_{sign}");
|
||||
assert_trap_code(
|
||||
&format!(
|
||||
r#"
|
||||
(module
|
||||
(func $div (param {ty} {ty}) (result {ty})
|
||||
local.get 0
|
||||
local.get 1
|
||||
{ty}.{op}_{sign})
|
||||
(func $start (drop (call $div ({ty}.const 1) ({ty}.const 0))))
|
||||
(start $start)
|
||||
)
|
||||
"#
|
||||
),
|
||||
Trap::IntegerDivisionByZero,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
println!("testing {ty}.div_s INT_MIN/-1");
|
||||
assert_trap_code(
|
||||
&format!(
|
||||
r#"
|
||||
(module
|
||||
(func $div (param {ty} {ty}) (result {ty})
|
||||
local.get 0
|
||||
local.get 1
|
||||
{ty}.div_s)
|
||||
(func $start (drop (call $div ({ty}.const {min}) ({ty}.const -1))))
|
||||
(start $start)
|
||||
)
|
||||
"#
|
||||
),
|
||||
Trap::IntegerOverflow,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn rustc(src: &str) -> Vec<u8> {
|
||||
|
||||
Reference in New Issue
Block a user