From 018dda44d493078f873b457ea6c0ea368e9c2fbb Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:03:44 +0000 Subject: [PATCH 1/2] Use inline asm rather than llvm intrinsic for panics on wasm This way we don't have to support unwinding llvm intrinsics in the codegen backends. --- library/unwind/src/lib.rs | 2 +- library/unwind/src/wasm.rs | 38 ++++++++------------------- tests/codegen-llvm/wasm_exceptions.rs | 14 +++++----- 3 files changed, 19 insertions(+), 35 deletions(-) diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index e3a0a77f53f08..0510e0e79c31f 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -7,7 +7,7 @@ #![cfg_attr(not(target_env = "msvc"), feature(libc))] #![cfg_attr( all(target_family = "wasm", any(not(target_os = "emscripten"), emscripten_wasm_eh)), - feature(link_llvm_intrinsics, simd_wasm64) + feature(asm_experimental_arch, asm_unwind, simd_wasm64) )] #![allow(internal_features)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/library/unwind/src/wasm.rs b/library/unwind/src/wasm.rs index 2bff306af293f..f1ada8e837a8d 100644 --- a/library/unwind/src/wasm.rs +++ b/library/unwind/src/wasm.rs @@ -47,33 +47,17 @@ pub unsafe fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwi // enabled exceptions via `-Z build-std` with `-C panic=unwind`. cfg_select! { panic = "unwind" => { - // It's important that this intrinsic is defined here rather than in `core`. Since it - // unwinds, invoking it from Rust code compiled with `-C panic=unwind` immediately - // forces `panic_unwind` as the required panic runtime. - // - // We ship unwinding `core` on Emscripten, so making this intrinsic part of `core` would - // prevent linking precompiled `core` into `-C panic=abort` binaries. Unlike `core`, - // this particular module is never precompiled with `-C panic=unwind` because it's only - // used for bare-metal targets, so an error can only arise if the user both manually - // recompiles `std` with `-C panic=unwind` and manually compiles the binary crate with - // `-C panic=abort`, which we don't care to support. - // - // See https://github.com/rust-lang/rust/issues/148246. - unsafe extern "C-unwind" { - /// LLVM lowers this intrinsic to the `throw` instruction. - #[link_name = "llvm.wasm.throw"] - fn wasm_throw(tag: i32, ptr: *mut u8) -> !; - } - - // The wasm `throw` instruction takes a "tag", which differentiates certain types of - // exceptions from others. LLVM currently just identifies these via integers, with 0 - // corresponding to C++ exceptions and 1 to C setjmp()/longjmp(). Ideally, we'd be able - // to choose something unique for Rust, but for now, we pretend to be C++ and implement - // the Itanium exception-handling ABI. - // corresponds with llvm::WebAssembly::Tag::CPP_EXCEPTION - // in llvm-project/llvm/include/llvm/CodeGen/WasmEHFuncInfo.h - const CPP_EXCEPTION_TAG: i32 = 0; - wasm_throw(CPP_EXCEPTION_TAG, exception.cast()) + // LLVM currently only runs cleanup code for exception using the C++ exception tag and + // not those for any other exception tag like the longjmp exception tag. Ideally, we'd + // be able to choose something unique for Rust, but for now, we pretend to be C++ and + // implement the Itanium exception-handling ABI. + // This is using inline asm rather than the llvm.wasm.throw llvm intrinsic as supporting + // unwinding for llvm intrinsics complicates things in the backend. + core::arch::asm!(" + .tagtype __cpp_exception i32 + local.get {exc} + throw __cpp_exception + ", exc = in(local) exception, options(may_unwind, noreturn, nostack)); } _ => { let _ = exception; diff --git a/tests/codegen-llvm/wasm_exceptions.rs b/tests/codegen-llvm/wasm_exceptions.rs index 7f38b669a6f8e..4f807e8cacd67 100644 --- a/tests/codegen-llvm/wasm_exceptions.rs +++ b/tests/codegen-llvm/wasm_exceptions.rs @@ -3,7 +3,7 @@ //@ [WASMEXN] compile-flags: -C panic=unwind -Z emscripten-wasm-eh #![crate_type = "lib"] -#![feature(core_intrinsics, link_llvm_intrinsics)] +#![feature(asm_experimental_arch, asm_unwind, core_intrinsics)] extern "C-unwind" { fn may_panic(); @@ -82,15 +82,15 @@ pub fn test_rtry() { pub fn test_intrinsic() { let _log_on_drop = LogOnDrop; - unsafe extern "C-unwind" { - #[link_name = "llvm.wasm.throw"] - fn wasm_throw(tag: i32, ptr: *mut u8) -> !; - } unsafe { - wasm_throw(0, core::ptr::null_mut()); + std::arch::asm!(" + .tagtype __cpp_exception i32 + local.get {exc} + throw __cpp_exception + ", exc = in(local) core::ptr::null_mut::<()>(), options(may_unwind, noreturn, nostack)); } // WASMEXN-NOT: call - // WASMEXN: invoke void @llvm.wasm.throw(i32 noundef 0, ptr noundef null) + // WASMEXN: invoke void asm sideeffect unwind // WASMEXN: %cleanuppad = cleanuppad within none [] } From 4ba864f9e72d049f2341630a2746481eac69382a Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 20 Nov 2025 16:09:15 +0000 Subject: [PATCH 2/2] Mark __cpp_exception as weak to fix duplicate symbol errors --- library/unwind/src/wasm.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/unwind/src/wasm.rs b/library/unwind/src/wasm.rs index f1ada8e837a8d..af4a91e334b63 100644 --- a/library/unwind/src/wasm.rs +++ b/library/unwind/src/wasm.rs @@ -55,6 +55,8 @@ pub unsafe fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwi // unwinding for llvm intrinsics complicates things in the backend. core::arch::asm!(" .tagtype __cpp_exception i32 + .globl __cpp_exception + .weak __cpp_exception local.get {exc} throw __cpp_exception ", exc = in(local) exception, options(may_unwind, noreturn, nostack));