]> git.lizzy.rs Git - rust.git/commitdiff
Inline catching panics into std::catch_unwind
authorMark Rousskov <mark.simulacrum@gmail.com>
Thu, 26 Dec 2019 15:02:21 +0000 (10:02 -0500)
committerAmanieu d'Antras <amanieu@gmail.com>
Mon, 2 Mar 2020 11:43:06 +0000 (11:43 +0000)
This allows LLVM to inline the happy path, such that catching unwinding is
zero-cost when no panic occurs. This also allows us to match the code generated
by C++ try/catch.

src/libpanic_abort/lib.rs
src/libpanic_unwind/dummy.rs
src/libpanic_unwind/emcc.rs
src/libpanic_unwind/gcc.rs
src/libpanic_unwind/hermit.rs
src/libpanic_unwind/lib.rs
src/libpanic_unwind/seh.rs
src/libstd/panicking.rs

index db7c250e21157810f35b357b6a2d072a924a042b..ebc57860b9d4a47f2231c9a69f7506ff49396633 100644 (file)
 #![feature(panic_runtime)]
 #![feature(staged_api)]
 #![feature(rustc_attrs)]
+#![feature(raw)]
 
-// Rust's "try" function, but if we're aborting on panics we just call the
-// function as there's nothing else we need to do here.
 #[rustc_std_internal_symbol]
-pub unsafe extern "C" fn __rust_maybe_catch_panic(
-    f: fn(*mut u8),
-    data: *mut u8,
-    _data_ptr: *mut usize,
-    _vtable_ptr: *mut usize,
-) -> u32 {
-    f(data);
-    0
+pub unsafe extern "C" fn __rust_cleanup(_: *mut u8) -> core::raw::TraitObject {
+    unreachable!()
 }
 
 // "Leak" the payload and shim to the relevant abort on the platform in
index 8675632638712aa2593f77f1b1bcba0621316680..30593d3b88af9bcea9ce6010c31ff75e178e98e6 100644 (file)
@@ -6,9 +6,7 @@
 use core::any::Any;
 use core::intrinsics;
 
-pub fn payload() -> *mut u8 {
-    core::ptr::null_mut()
-}
+pub type Payload = *mut u8;
 
 pub unsafe fn cleanup(_ptr: *mut u8) -> Box<dyn Any + Send> {
     intrinsics::abort()
index 9161d49959cf5cc1577264dcdb1502cde151115a..873135414bd9d46b46cb4397386a3fb3795891a5 100644 (file)
@@ -48,9 +48,7 @@ unsafe impl Sync for TypeInfo {}
     name: b"rust_panic\0".as_ptr(),
 };
 
-pub fn payload() -> *mut u8 {
-    ptr::null_mut()
-}
+pub type Payload = *mut u8;
 
 struct Exception {
     // This needs to be an Option because the object's lifetime follows C++
index 591ff9d7fdcaa3c24f7097b22b177d29063a0dcf..dd84a814f48b16a7afe76cf89ef8bd2466e2bcd4 100644 (file)
@@ -48,7 +48,6 @@
 
 use alloc::boxed::Box;
 use core::any::Any;
-use core::ptr;
 
 use crate::dwarf::eh::{self, EHAction, EHContext};
 use libc::{c_int, uintptr_t};
@@ -83,9 +82,7 @@ extern "C" fn exception_cleanup(
     }
 }
 
-pub fn payload() -> *mut u8 {
-    ptr::null_mut()
-}
+pub type Payload = *mut u8;
 
 pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
     let exception = Box::from_raw(ptr as *mut Exception);
index 2f53df2861d4436bcf84ce88b4ddf7cdc87594d6..8ffb4bcd3df23697d0fe691d5f7c55527f8dbfa1 100644 (file)
@@ -6,9 +6,7 @@
 use core::any::Any;
 use core::ptr;
 
-pub fn payload() -> *mut u8 {
-    ptr::null_mut()
-}
+pub type Payload = *mut u8;
 
 pub unsafe fn cleanup(_ptr: *mut u8) -> Box<dyn Any + Send> {
     extern "C" {
index 6383ae39fb6dba0c6795de05873964bc528341bb..60ddf70cea52f2ec57590096d1abf349bfdeb7fe 100644 (file)
 #![feature(libc)]
 #![feature(nll)]
 #![feature(panic_unwind)]
-#![feature(raw)]
 #![feature(staged_api)]
 #![feature(std_internals)]
 #![feature(unwind_attributes)]
 #![feature(abi_thiscall)]
+#![feature(rustc_attrs)]
+#![feature(raw)]
 #![panic_runtime]
 #![feature(panic_runtime)]
 
 use alloc::boxed::Box;
-use core::intrinsics;
-use core::mem;
 use core::panic::BoxMeUp;
-use core::raw;
 
+// If adding to this list, you should also look at libstd::panicking's identical
+// list of Payload types and likely add to there as well.
 cfg_if::cfg_if! {
     if #[cfg(target_os = "emscripten")] {
         #[path = "emcc.rs"]
 
 mod dwarf;
 
-// Entry point for catching an exception, implemented using the `try` intrinsic
-// in the compiler.
-//
-// The interaction between the `payload` function and the compiler is pretty
-// hairy and tightly coupled, for more information see the compiler's
-// implementation of this.
 #[no_mangle]
-pub unsafe extern "C" fn __rust_maybe_catch_panic(
-    f: fn(*mut u8),
-    data: *mut u8,
-    data_ptr: *mut usize,
-    vtable_ptr: *mut usize,
-) -> u32 {
-    let mut payload = imp::payload();
-    if intrinsics::r#try(f, data, &mut payload as *mut _ as *mut _) == 0 {
-        0
-    } else {
-        let obj = mem::transmute::<_, raw::TraitObject>(imp::cleanup(payload));
-        *data_ptr = obj.data as usize;
-        *vtable_ptr = obj.vtable as usize;
-        1
-    }
+pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> core::raw::TraitObject {
+    let payload = payload as *mut imp::Payload;
+    let payload = *(payload);
+    core::mem::transmute(imp::cleanup(payload))
 }
 
 // Entry point for raising an exception, just delegates to the platform-specific
index 6f507e85e742c59f6de1982c2fa76a5c23f4b3ff..6f464c1ab680e5550f398aef809167147a66d333 100644 (file)
@@ -308,9 +308,7 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
     _CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
 }
 
-pub fn payload() -> [u64; 2] {
-    [0; 2]
-}
+pub type Payload = [u64; 2];
 
 pub unsafe fn cleanup(payload: [u64; 2]) -> Box<dyn Any + Send> {
     mem::transmute(raw::TraitObject { data: payload[0] as *mut _, vtable: payload[1] as *mut _ })
index 8b12aaaa7e2fd25c6cdf69b9a9d06ffbe122d8c4..f71849fae34fa3afe1bcdfb2d556da45e19f34e9 100644 (file)
@@ -12,9 +12,8 @@
 use crate::any::Any;
 use crate::fmt;
 use crate::intrinsics;
-use crate::mem::{self, ManuallyDrop};
+use crate::mem::{self, ManuallyDrop, MaybeUninit};
 use crate::process;
-use crate::raw;
 use crate::sync::atomic::{AtomicBool, Ordering};
 use crate::sys::stdio::panic_output;
 use crate::sys_common::backtrace::{self, RustBacktrace};
 #[cfg(test)]
 use realstd::io::set_panic;
 
+// This must be kept in sync with the implementations in libpanic_unwind.
+//
+// This is *not* checked in anyway; the compiler does not allow us to use a
+// type/macro/anything from panic_unwind, since we're then linking in the
+// panic_unwind runtime even during -Cpanic=abort.
+//
+// Essentially this must be the type of `imp::Payload` in libpanic_unwind.
+cfg_if::cfg_if! {
+    if #[cfg(not(feature = "panic_unwind"))] {
+        type Payload = ();
+    } else if #[cfg(target_os = "emscripten")] {
+        type Payload = *mut u8;
+    } else if #[cfg(target_arch = "wasm32")] {
+        type Payload = *mut u8;
+    } else if #[cfg(target_os = "hermit")] {
+        type Payload = *mut u8;
+    } else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] {
+        type Payload = *mut u8;
+    } else if #[cfg(target_env = "msvc")] {
+        type Payload = [u64; 2];
+    } else {
+        type Payload = *mut u8;
+    }
+}
+
 // Binary interface to the panic runtime that the standard library depends on.
 //
 // The standard library is tagged with `#![needs_panic_runtime]` (introduced in
 // hook up these functions, but it is not this day!
 #[allow(improper_ctypes)]
 extern "C" {
-    fn __rust_maybe_catch_panic(
-        f: fn(*mut u8),
-        data: *mut u8,
-        data_ptr: *mut usize,
-        vtable_ptr: *mut usize,
-    ) -> u32;
+    /// The payload ptr here is actually the same as the payload ptr for the try
+    /// intrinsic (i.e., is really `*mut [u64; 2]` or `*mut *mut u8`).
+    fn __rust_panic_cleanup(payload: *mut u8) -> core::raw::TraitObject;
 
     /// `payload` is actually a `*mut &mut dyn BoxMeUp` but that would cause FFI warnings.
     /// It cannot be `Box<dyn BoxMeUp>` because the other end of this call does not depend
@@ -250,9 +271,9 @@ union Data<F, R> {
     }
 
     // We do some sketchy operations with ownership here for the sake of
-    // performance. We can only  pass pointers down to
-    // `__rust_maybe_catch_panic` (can't pass objects by value), so we do all
-    // the ownership tracking here manually using a union.
+    // performance. We can only pass pointers down to `do_call` (can't pass
+    // objects by value), so we do all the ownership tracking here manually
+    // using a union.
     //
     // We go through a transition where:
     //
@@ -263,7 +284,7 @@ union Data<F, R> {
     // * If the closure successfully returns, we write the return value into the
     //   data's return slot. Note that `ptr::write` is used as it's overwriting
     //   uninitialized data.
-    // * Finally, when we come back out of the `__rust_maybe_catch_panic` we're
+    // * Finally, when we come back out of the `try` intrinsic we're
     //   in one of two states:
     //
     //      1. The closure didn't panic, in which case the return value was
@@ -274,27 +295,24 @@ union Data<F, R> {
     //
     // Once we stack all that together we should have the "most efficient'
     // method of calling a catch panic whilst juggling ownership.
-    let mut any_data = 0;
-    let mut any_vtable = 0;
     let mut data = Data { f: ManuallyDrop::new(f) };
 
-    let r = __rust_maybe_catch_panic(
-        do_call::<F, R>,
-        &mut data as *mut _ as *mut u8,
-        &mut any_data,
-        &mut any_vtable,
-    );
+    let mut payload: MaybeUninit<Payload> = MaybeUninit::uninit();
 
-    return if r == 0 {
+    let data_ptr = &mut data as *mut _ as *mut u8;
+    let payload_ptr = payload.as_mut_ptr() as *mut _;
+    return if intrinsics::r#try(do_call::<F, R>, data_ptr, payload_ptr) == 0 {
         Ok(ManuallyDrop::into_inner(data.r))
     } else {
-        update_panic_count(-1);
-        Err(mem::transmute(raw::TraitObject {
-            data: any_data as *mut _,
-            vtable: any_vtable as *mut _,
-        }))
+        Err(cleanup(payload.assume_init()))
     };
 
+    unsafe fn cleanup(mut payload: Payload) -> Box<dyn Any + Send + 'static> {
+        let obj = crate::mem::transmute(__rust_panic_cleanup(&mut payload as *mut _ as *mut u8));
+        update_panic_count(-1);
+        obj
+    }
+
     fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
         unsafe {
             let data = data as *mut Data<F, R>;