Inline catching panics into std::catch_unwind
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.
This commit is contained in:
parent
6b2983af1a
commit
d45ce5aed6
8 changed files with 60 additions and 77 deletions
|
|
@ -17,18 +17,11 @@
|
|||
#![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
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ use alloc::boxed::Box;
|
|||
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()
|
||||
|
|
|
|||
|
|
@ -48,9 +48,7 @@ static EXCEPTION_TYPE_INFO: TypeInfo = 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++
|
||||
|
|
|
|||
|
|
@ -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 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ use alloc::boxed::Box;
|
|||
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" {
|
||||
|
|
|
|||
|
|
@ -22,20 +22,20 @@
|
|||
#![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"]
|
||||
|
|
@ -69,28 +69,11 @@ extern "C" {
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 _ })
|
||||
|
|
|
|||
|
|
@ -12,9 +12,8 @@ use core::panic::{BoxMeUp, Location, PanicInfo};
|
|||
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};
|
||||
|
|
@ -29,6 +28,31 @@ use crate::io::set_panic;
|
|||
#[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
|
||||
|
|
@ -41,12 +65,9 @@ use realstd::io::set_panic;
|
|||
// 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 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
|
|||
}
|
||||
|
||||
// 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 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
|
|||
// * 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 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
|
|||
//
|
||||
// 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>;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue