Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cranelift/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ pub mod write;
pub use crate::entity::packed_option;
pub use crate::machinst::buffer::{
ExceptionContextLoc, FinalizedMachCallSite, FinalizedMachExceptionHandler, FinalizedMachReloc,
FinalizedRelocTarget, MachCallSite, MachSrcLoc, MachTextSectionBuilder, MachTrap,
OpenPatchRegion, PatchRegion,
FinalizedRelocTarget, MachCallSite, MachExceptionHandler, MachSrcLoc, MachTextSectionBuilder,
MachTrap, OpenPatchRegion, PatchRegion,
};
pub use crate::machinst::{
CallInfo, CompiledCode, Final, FrameLayout, MachBuffer, MachBufferDebugTagList,
Expand Down
3 changes: 0 additions & 3 deletions crates/test-util/src/wast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,9 +567,6 @@ impl WastTest {
"misc_testsuite/externref-table-dropped-segment-issue-8281.wast",
"misc_testsuite/many_table_gets_lead_to_gc.wast",
"misc_testsuite/no-panic.wast",
// Winch does not implement exception handlers yet.
"misc_testsuite/traps-skip-catch-all.wast",
"spec_testsuite/throw.wast",
];

if unsupported.iter().any(|part| self.path.ends_with(part)) {
Expand Down
174 changes: 162 additions & 12 deletions tests/all/exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
use wasmtime::*;
use wasmtime_test_macros::wasmtime_test;

// Winch does not implement catches yet. Re-enable after catch is implemented.
#[wasmtime_test(strategies(not(Winch)), wasm_features(exceptions))]
#[wasmtime_test(wasm_features(exceptions))]
#[cfg_attr(miri, ignore)]
fn basic_throw(config: &mut Config) -> Result<()> {
let engine = Engine::new(config)?;
Expand Down Expand Up @@ -40,8 +39,7 @@ fn basic_throw(config: &mut Config) -> Result<()> {
Ok(())
}

// Winch does not implement catches yet. Re-enable after catch is implemented.
#[wasmtime_test(strategies(not(Winch)), wasm_features(exceptions))]
#[wasmtime_test(wasm_features(exceptions))]
#[cfg_attr(miri, ignore)]
fn dynamic_tags(config: &mut Config) -> Result<()> {
let engine = Engine::new(config)?;
Expand Down Expand Up @@ -101,6 +99,58 @@ fn dynamic_tags(config: &mut Config) -> Result<()> {
Ok(())
}

#[wasmtime_test(wasm_features(exceptions))]
#[cfg_attr(miri, ignore)]
fn nested_handler_scopes(config: &mut Config) -> Result<()> {
let engine = Engine::new(config)?;
let mut store = Store::new(&engine, ());

let module = Module::new(
&engine,
r#"
(module
(tag $outer)
(tag $inner)

(func $throw_outer
(throw $outer))

(func $throw_inner
(throw $inner))

;; While both handlers are active, the inner tag does not match and
;; lookup continues to the outer handler.
(func (export "nested") (result i32)
(block $outer_handler
(try_table (catch $outer $outer_handler)
(block $inner_handler
(try_table (catch $inner $inner_handler)
(call $throw_outer)))))
(i32.const 1))

;; After the inner try_table ends, its handler is no longer active.
;; The outer catch_all handles the throw instead.
(func (export "after") (result i32)
(block $stale_inner_handler
(block $outer_handler
(try_table (catch_all $outer_handler)
(try_table (catch $inner $stale_inner_handler)
(nop))
(call $throw_inner)))
(return (i32.const 1)))
(i32.const 2)))
"#,
)?;

let instance = Instance::new(&mut store, &module, &[])?;
let nested = instance.get_typed_func::<(), i32>(&mut store, "nested")?;
let after = instance.get_typed_func::<(), i32>(&mut store, "after")?;
assert_eq!(nested.call(&mut store, ())?, 1);
assert_eq!(after.call(&mut store, ())?, 1);

Ok(())
}

#[wasmtime_test(wasm_features(exceptions))]
#[cfg_attr(miri, ignore)]
fn exception_escape_to_host(config: &mut Config) -> Result<()> {
Expand Down Expand Up @@ -193,10 +243,57 @@ fn funcref_exception_payload_escape_to_host(config: &mut Config) -> Result<()> {
Ok(())
}

#[wasmtime_test(wasm_features(exceptions, reference_types))]
#[cfg_attr(miri, ignore)]
fn caught_funcref_payload(config: &mut Config) -> Result<()> {
let engine = Engine::new(config)?;
let mut store = Store::new(&engine, ());

let module = Module::new(
&engine,
r#"
(module
(tag $e (param funcref i32))

(func $throw (param funcref)
(throw $e (local.get 0) (i32.const 42)))

(func (export "catch") (param funcref) (result funcref i32)
(block $handler (result funcref i32)
(try_table (result funcref i32) (catch $e $handler)
(call $throw (local.get 0))
(ref.null func)
(i32.const 0)))))
"#,
)?;

let instance = Instance::new(&mut store, &module, &[])?;
let catch = instance.get_func(&mut store, "catch").unwrap();
let expected = Func::wrap(&mut store, || 126_i32);
let mut results = [Val::null_func_ref(), Val::I32(0)];
catch.call(&mut store, &[Val::FuncRef(Some(expected))], &mut results)?;

let actual = results[0].unwrap_funcref().unwrap();
let actual = actual.typed::<(), i32>(&store)?;
assert_eq!(actual.call(&mut store, ())?, 126);
assert_eq!(results[1].unwrap_i32(), 42);

Ok(())
}

#[wasmtime_test(wasm_features(exceptions, reference_types))]
#[cfg_attr(miri, ignore)]
fn thrown_externref_payload_survives_gc(config: &mut Config) -> Result<()> {
config.collector(Collector::DeferredReferenceCounting);
for collector in [Collector::Copying, Collector::DeferredReferenceCounting] {
println!("Using GC collector: {collector:?}");
config.collector(collector);
run_thrown_externref_payload_survives_gc(config)?;
}

Ok(())
}

fn run_thrown_externref_payload_survives_gc(config: &Config) -> Result<()> {
let engine = Engine::new(config)?;
let mut store = Store::new(&engine, ());

Expand Down Expand Up @@ -234,6 +331,62 @@ fn thrown_externref_payload_survives_gc(config: &mut Config) -> Result<()> {
Ok(())
}

#[wasmtime_test(wasm_features(exceptions, reference_types))]
#[cfg_attr(miri, ignore)]
fn caught_externref_payload_survives_gc(config: &mut Config) -> Result<()> {
for collector in [Collector::Copying, Collector::DeferredReferenceCounting] {
println!("Using GC collector: {collector:?}");
config.collector(collector);
run_caught_externref_payload_survives_gc(config)?;
}

Ok(())
}

fn run_caught_externref_payload_survives_gc(config: &Config) -> Result<()> {
let engine = Engine::new(config)?;
let mut store = Store::new(&engine, ());

let module = Module::new(
&engine,
r#"
(module
(tag $e (param externref i32))

(func $throw (param externref)
(throw $e (local.get 0) (i32.const 42)))

(func (export "catch") (param externref) (result externref i32)
(block $handler (result externref i32)
(try_table (result externref i32) (catch $e $handler)
(call $throw (local.get 0))
(ref.null extern)
(i32.const 0)))))
"#,
)?;

let instance = Instance::new(&mut store, &module, &[])?;
let catch = instance
.get_typed_func::<Option<Rooted<ExternRef>>, (Option<Rooted<ExternRef>>, i32)>(
&mut store, "catch",
)?;
let dropped = Arc::new(AtomicBool::new(false));

let caught = {
let mut scope = RootScope::new(&mut store);
let payload = ExternRef::new(&mut scope, SetFlagOnDrop(dropped.clone()))?;
let (caught, value) = catch.call(&mut scope, Some(payload))?;
assert_eq!(value, 42);
caught.unwrap().to_owned_rooted(&mut scope)?
};

store.gc(None)?;
assert!(!dropped.load(Relaxed));
assert!(caught.data(&store)?.is_some());

Ok(())
}

#[wasmtime_test(wasm_features(exceptions))]
#[cfg_attr(miri, ignore)]
fn throw_with_null_collector(config: &mut Config) -> Result<()> {
Expand Down Expand Up @@ -261,8 +414,7 @@ fn throw_with_null_collector(config: &mut Config) -> Result<()> {
Ok(())
}

// Winch does not implement catches yet. Re-enable after catch is implemented.
#[wasmtime_test(strategies(not(Winch)), wasm_features(exceptions))]
#[wasmtime_test(wasm_features(exceptions))]
#[cfg_attr(miri, ignore)]
fn exception_from_host(config: &mut Config) -> Result<()> {
let engine = Engine::new(config)?;
Expand Down Expand Up @@ -402,8 +554,7 @@ fn thrown_exception_without_throwing(config: &mut Config) -> Result<()> {
Ok(())
}

// Winch does not implement catches yet. Re-enable after catch is implemented.
#[wasmtime_test(strategies(not(Winch)), wasm_features(exceptions))]
#[wasmtime_test(wasm_features(exceptions))]
#[cfg_attr(miri, ignore)]
fn wasm_exceptions_have_backtraces(config: &mut Config) -> Result<()> {
let engine = Engine::new(config)?;
Expand All @@ -429,8 +580,7 @@ fn wasm_exceptions_have_backtraces(config: &mut Config) -> Result<()> {
Ok(())
}

// Winch does not implement catches yet. Re-enable after catch is implemented.
#[wasmtime_test(strategies(not(Winch)), wasm_features(exceptions))]
#[wasmtime_test(wasm_features(exceptions))]
#[cfg_attr(miri, ignore)]
fn store_pending_exnref_is_cloned(config: &mut Config) -> wasmtime::Result<()> {
config.collector(Collector::DeferredReferenceCounting);
Expand Down Expand Up @@ -485,7 +635,7 @@ fn store_pending_exnref_is_cloned(config: &mut Config) -> wasmtime::Result<()> {
Ok(())
}

// Winch does not implement catches yet. Re-enable after catch is implemented.
// Winch does not implement `catch_ref` yet.
#[wasmtime_test(strategies(not(Winch)), wasm_features(exceptions, reference_types))]
#[cfg_attr(miri, ignore)]
fn store_pending_exnref_is_exposed(config: &mut Config) -> wasmtime::Result<()> {
Expand Down
37 changes: 30 additions & 7 deletions tests/disas/winch/aarch64/exceptions/throw-drc.wat
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
;;! test = "winch"
;;! flags = "-W exceptions -C collector=drc"

;; `throw` builds an exception that escapes to the host, while `try_table`
;; still compiles as a plain block.
;; Calls made while the `try_table` handler is active carry exception metadata.
;; Its landing pad loads the exception's payload and branches to `$h`.
(module
(tag $e (param i32))
(func (result i32)
Expand All @@ -21,14 +21,16 @@
;; movk x17, #0x20
;; add x16, x16, x17
;; cmp sp, x16
;; b.lo #0x120
;; b.lo #0x160
;; 2c: mov x9, x0
;; sub x28, x28, #0x10
;; mov sp, x28
;; stur x0, [x28, #8]
;; stur x1, [x28]
;; mov x0, x9
;; bl #0x25c
;; bl #0x2a0
;; ├─╼ exception frame offset: SP = FP - 0x20
;; ╰─╼ exception handler: tag=0, context at [SP+0x8], handler=0x108
;; 48: ldur x9, [x28, #8]
;; ldur x1, [x9, #0x28]
;; ldur w1, [x1, #8]
Expand All @@ -45,7 +47,9 @@
;; ldur w2, [x28, #8]
;; mov x3, #0x28
;; mov x4, #8
;; bl #0x20c
;; bl #0x250
;; ├─╼ exception frame offset: SP = FP - 0x30
;; ╰─╼ exception handler: tag=0, context at [SP+0x18], handler=0x108
;; 8c: add x28, x28, #8
;; mov sp, x28
;; add x28, x28, #4
Expand All @@ -71,16 +75,35 @@
;; mov sp, x28
;; mov x0, x9
;; ldur w1, [x28, #0xc]
;; bl #0x28c
;; bl #0x2d0
;; ├─╼ exception frame offset: SP = FP - 0x30
;; ╰─╼ exception handler: tag=0, context at [SP+0x18], handler=0x108
;; f4: add x28, x28, #0xc
;; mov sp, x28
;; add x28, x28, #4
;; mov sp, x28
;; ldur x9, [x28, #8]
;; mov x28, x29
;; sub x28, x28, #0x10
;; mov sp, x28
;; sub x28, x28, #0x10
;; mov sp, x28
;; ldur x9, [x28, #8]
;; ldur x1, [x9, #8]
;; ldur x2, [x1, #0x28]
;; ldur x1, [x1, #0x20]
;; mov x16, x0
;; add x16, x16, #0x28
;; cmp x16, x2, uxtx
;; b.hi #0x164
;; 13c: mov x2, x1
;; add x2, x2, x0, uxtx
;; ldur w0, [x2, #0x20]
;; add x28, x28, #0x10
;; mov sp, x28
;; mov sp, x28
;; ldr x28, [sp], #0x10
;; ldp x29, x30, [sp], #0x10
;; ret
;; 120: udf #0xc11f
;; 160: udf #0xc11f
;; 164: udf #0xc11f
Loading
Loading