cranelift-interpreter: Propagate traps across calls (#6156)
* cranelift-interpreter: Propagate traps from call's * cranelift-interpreter: Make `unwrap_return` only available in tests This is a footgun for normal use in the interpreter (#6156) but it still has uses in the tests, so enable it only there.
This commit is contained in:
@@ -118,17 +118,29 @@ impl<'a> Interpreter<'a> {
|
|||||||
maybe_inst = layout.first_inst(block)
|
maybe_inst = layout.first_inst(block)
|
||||||
}
|
}
|
||||||
ControlFlow::Call(called_function, arguments) => {
|
ControlFlow::Call(called_function, arguments) => {
|
||||||
let returned_arguments =
|
match self.call(called_function, &arguments)? {
|
||||||
self.call(called_function, &arguments)?.unwrap_return();
|
ControlFlow::Return(rets) => {
|
||||||
self.state
|
self.state
|
||||||
.current_frame_mut()
|
.current_frame_mut()
|
||||||
.set_all(function.dfg.inst_results(inst), returned_arguments);
|
.set_all(function.dfg.inst_results(inst), rets.to_vec());
|
||||||
maybe_inst = layout.next_inst(inst)
|
maybe_inst = layout.next_inst(inst)
|
||||||
}
|
}
|
||||||
|
ControlFlow::Trap(trap) => return Ok(ControlFlow::Trap(trap)),
|
||||||
|
cf => {
|
||||||
|
panic!("invalid control flow after call: {:?}", cf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ControlFlow::ReturnCall(callee, args) => {
|
ControlFlow::ReturnCall(callee, args) => {
|
||||||
self.state.pop_frame();
|
self.state.pop_frame();
|
||||||
let rets = self.call(callee, &args)?.unwrap_return();
|
|
||||||
return Ok(ControlFlow::Return(rets.into()));
|
return match self.call(callee, &args)? {
|
||||||
|
ControlFlow::Return(rets) => Ok(ControlFlow::Return(rets)),
|
||||||
|
ControlFlow::Trap(trap) => Ok(ControlFlow::Trap(trap)),
|
||||||
|
cf => {
|
||||||
|
panic!("invalid control flow after return_call: {:?}", cf)
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
ControlFlow::Return(returned_values) => {
|
ControlFlow::Return(returned_values) => {
|
||||||
self.state.pop_frame();
|
self.state.pop_frame();
|
||||||
@@ -1026,4 +1038,48 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(trap, CraneliftTrap::User(TrapCode::HeapMisaligned));
|
assert_eq!(trap, CraneliftTrap::User(TrapCode::HeapMisaligned));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When a trap occurs in a function called by another function, the trap was not being propagated
|
||||||
|
// correctly. Instead the interpterer panicked with a invalid control flow state.
|
||||||
|
// See this issue for more details: https://github.com/bytecodealliance/wasmtime/issues/6155
|
||||||
|
#[test]
|
||||||
|
fn trap_across_call_propagates_correctly() {
|
||||||
|
let code = "
|
||||||
|
function %u2() -> f32 system_v {
|
||||||
|
ss0 = explicit_slot 69
|
||||||
|
ss1 = explicit_slot 69
|
||||||
|
ss2 = explicit_slot 69
|
||||||
|
|
||||||
|
block0:
|
||||||
|
v0 = f32const -0x1.434342p-60
|
||||||
|
v1 = stack_addr.i64 ss2+24
|
||||||
|
store notrap aligned v0, v1
|
||||||
|
return v0
|
||||||
|
}
|
||||||
|
|
||||||
|
function %u1() -> f32 system_v {
|
||||||
|
sig0 = () -> f32 system_v
|
||||||
|
fn0 = colocated %u2 sig0
|
||||||
|
|
||||||
|
block0:
|
||||||
|
v57 = call fn0()
|
||||||
|
return v57
|
||||||
|
}";
|
||||||
|
|
||||||
|
let mut env = FunctionStore::default();
|
||||||
|
|
||||||
|
let funcs = parse_functions(code).unwrap();
|
||||||
|
for func in &funcs {
|
||||||
|
env.add(func.name.to_string(), func);
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = InterpreterState::default().with_function_store(env);
|
||||||
|
let trap = Interpreter::new(state)
|
||||||
|
.call_by_name("%u1", &[])
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_trap();
|
||||||
|
|
||||||
|
// Ensure that the correct trap was propagated.
|
||||||
|
assert_eq!(trap, CraneliftTrap::User(TrapCode::HeapMisaligned));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1397,6 +1397,7 @@ pub enum ControlFlow<'a, V> {
|
|||||||
impl<'a, V> ControlFlow<'a, V> {
|
impl<'a, V> ControlFlow<'a, V> {
|
||||||
/// For convenience, we can unwrap the [ControlFlow] state assuming that it is a
|
/// For convenience, we can unwrap the [ControlFlow] state assuming that it is a
|
||||||
/// [ControlFlow::Return], panicking otherwise.
|
/// [ControlFlow::Return], panicking otherwise.
|
||||||
|
#[cfg(test)]
|
||||||
pub fn unwrap_return(self) -> Vec<V> {
|
pub fn unwrap_return(self) -> Vec<V> {
|
||||||
if let ControlFlow::Return(values) = self {
|
if let ControlFlow::Return(values) = self {
|
||||||
values.into_vec()
|
values.into_vec()
|
||||||
@@ -1407,6 +1408,7 @@ impl<'a, V> ControlFlow<'a, V> {
|
|||||||
|
|
||||||
/// For convenience, we can unwrap the [ControlFlow] state assuming that it is a
|
/// For convenience, we can unwrap the [ControlFlow] state assuming that it is a
|
||||||
/// [ControlFlow::Trap], panicking otherwise.
|
/// [ControlFlow::Trap], panicking otherwise.
|
||||||
|
#[cfg(test)]
|
||||||
pub fn unwrap_trap(self) -> CraneliftTrap {
|
pub fn unwrap_trap(self) -> CraneliftTrap {
|
||||||
if let ControlFlow::Trap(trap) = self {
|
if let ControlFlow::Trap(trap) = self {
|
||||||
trap
|
trap
|
||||||
|
|||||||
Reference in New Issue
Block a user