about summary refs log tree commit diff
path: root/library/panic_unwind/src
diff options
context:
space:
mode:
authorNoa <coolreader18@gmail.com>2024-02-21 17:32:58 -0600
committerNoa <coolreader18@gmail.com>2024-02-22 16:45:26 -0600
commit3908a935ef821c2828a7d825eac859f8ff54702a (patch)
treedd62444f5a135976635b66ee483fe4bae0a79101 /library/panic_unwind/src
parentf8131a48a46ac3bc8a3d0fe0477055b132cffdc3 (diff)
downloadrust-3908a935ef821c2828a7d825eac859f8ff54702a.tar.gz
rust-3908a935ef821c2828a7d825eac859f8ff54702a.zip
std support for wasm32 panic=unwind
Diffstat (limited to 'library/panic_unwind/src')
-rw-r--r--library/panic_unwind/src/lib.rs13
-rw-r--r--library/panic_unwind/src/wasm.rs32
2 files changed, 44 insertions, 1 deletions
diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs
index 7a0bae34642..bc3c9363d8a 100644
--- a/library/panic_unwind/src/lib.rs
+++ b/library/panic_unwind/src/lib.rs
@@ -16,6 +16,10 @@
 #![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")]
 #![feature(core_intrinsics)]
 #![feature(lang_items)]
+#![cfg_attr(
+    all(target_family = "wasm", not(target_os = "emscripten")),
+    feature(link_llvm_intrinsics)
+)]
 #![feature(panic_unwind)]
 #![feature(staged_api)]
 #![feature(std_internals)]
@@ -56,9 +60,16 @@ cfg_if::cfg_if! {
     ))] {
         #[path = "gcc.rs"]
         mod real_imp;
+    } else if #[cfg(all(target_family = "wasm", panic = "unwind"))] {
+        // for now, PanicStrategy::Unwind is not the default for wasm targets,
+        // so we need the panic = "unwind" in the cfg above. to use llvm.wasm.throw,
+        // we need to pass -wasm-enable-eh to LLVM, but that only happens if rustc
+        // is compiling with -C panic=unwind. So, this lets us -Zbuild-std with
+        // panic=unwind, while keeping the default panic=abort working.
+        #[path = "wasm.rs"]
+        mod real_imp;
     } else {
         // Targets that don't support unwinding.
-        // - family=wasm
         // - os=none ("bare metal" targets)
         // - os=uefi
         // - os=espidf
diff --git a/library/panic_unwind/src/wasm.rs b/library/panic_unwind/src/wasm.rs
new file mode 100644
index 00000000000..b11fb912b63
--- /dev/null
+++ b/library/panic_unwind/src/wasm.rs
@@ -0,0 +1,32 @@
+//! Unwinding panics for wasm32.
+use alloc::boxed::Box;
+use core::any::Any;
+
+// The type of the exception payload that the wasm engine propagates
+// through unwinding for us. LLVM requires that it be a thin pointer.
+type Payload = Box<Box<dyn Any + Send>>;
+
+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<dyn Any + Send>) -> 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<Box<dyn Any>>`, 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<dyn Any + Send> {
+    // Recover the underlying `Box`.
+    let payload_box: Payload = Box::from_raw(payload_box as *mut _);
+    *payload_box
+}