From 3908a935ef821c2828a7d825eac859f8ff54702a Mon Sep 17 00:00:00 2001 From: Noa Date: Wed, 21 Feb 2024 17:32:58 -0600 Subject: [PATCH] std support for wasm32 panic=unwind --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 13 ++++++-- library/panic_unwind/src/lib.rs | 13 +++++++- library/panic_unwind/src/wasm.rs | 32 ++++++++++++++++++++ library/std/src/sys/personality/mod.rs | 8 ++--- 4 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 library/panic_unwind/src/wasm.rs diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 54e8ed85e32..e383673e1d4 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -5,6 +5,7 @@ }; use crate::llvm; use libc::c_int; +use rustc_codegen_ssa::base::wants_wasm_eh; use rustc_codegen_ssa::traits::PrintBackendInfo; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::small_c_str::SmallCStr; @@ -98,8 +99,12 @@ fn llvm_arg_to_arg_name(full_arg: &str) -> &str { } } - if sess.target.os == "emscripten" && sess.panic_strategy() == PanicStrategy::Unwind { - add("-enable-emscripten-cxx-exceptions", false); + if sess.panic_strategy() == PanicStrategy::Unwind { + if sess.target.os == "emscripten" { + add("-enable-emscripten-cxx-exceptions", false); + } else if wants_wasm_eh(sess) { + add("-wasm-enable-eh", false); + } } // HACK(eddyb) LLVM inserts `llvm.assume` calls to preserve align attributes @@ -520,6 +525,10 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec>; + +extern "C" { + /// LLVM lowers this intrinsic to the `throw` instruction. + #[link_name = "llvm.wasm.throw"] + fn wasm_throw(tag: i32, ptr: *mut u8) -> !; +} + +pub unsafe fn panic(payload: Box) -> u32 { + // The payload we pass to `wasm_throw` will be exactly the argument we get + // in `cleanup` below. So we just box it up once, to get something pointer-sized. + let payload_box: Payload = Box::new(payload); + // 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, such that we + // don't try to treat a C++ exception payload as a `Box>`, but + // otherwise, pretending to be C++ works for now. + wasm_throw(0, Box::into_raw(payload_box) as *mut u8) +} + +pub unsafe fn cleanup(payload_box: *mut u8) -> Box { + // Recover the underlying `Box`. + let payload_box: Payload = Box::from_raw(payload_box as *mut _); + *payload_box +} diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index d37b8ce6346..1a6ea1dafcb 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -16,11 +16,12 @@ cfg_if::cfg_if! { if #[cfg(target_os = "emscripten")] { mod emcc; - } else if #[cfg(target_env = "msvc")] { + } else if #[cfg(any(target_env = "msvc", target_family = "wasm"))] { // This is required by the compiler to exist (e.g., it's a lang item), // but it's never actually called by the compiler because - // _CxxFrameHandler3 is the personality function that is always used. - // Hence this is just an aborting stub. + // __CxxFrameHandler3 (msvc) / __gxx_wasm_personality_v0 (wasm) is the + // personality function that is always used. Hence this is just an + // aborting stub. #[lang = "eh_personality"] fn rust_eh_personality() { core::intrinsics::abort() @@ -36,7 +37,6 @@ fn rust_eh_personality() { mod gcc; } else { // Targets that don't support unwinding. - // - family=wasm // - os=none ("bare metal" targets) // - os=uefi // - os=espidf