Skip to content

Commit 7a46a0b

Browse files
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.
1 parent acb6690 commit 7a46a0b

File tree

8 files changed

+61
-77
lines changed

8 files changed

+61
-77
lines changed

src/libpanic_abort/lib.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,11 @@
1717
#![feature(panic_runtime)]
1818
#![feature(staged_api)]
1919
#![feature(rustc_attrs)]
20+
#![feature(raw)]
2021

21-
// Rust's "try" function, but if we're aborting on panics we just call the
22-
// function as there's nothing else we need to do here.
2322
#[rustc_std_internal_symbol]
24-
pub unsafe extern "C" fn __rust_maybe_catch_panic(
25-
f: fn(*mut u8),
26-
data: *mut u8,
27-
_data_ptr: *mut usize,
28-
_vtable_ptr: *mut usize,
29-
) -> u32 {
30-
f(data);
31-
0
23+
pub unsafe extern "C" fn __rust_cleanup(_: *mut u8) -> core::raw::TraitObject {
24+
unreachable!()
3225
}
3326

3427
// "Leak" the payload and shim to the relevant abort on the platform in

src/libpanic_unwind/dummy.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ use alloc::boxed::Box;
66
use core::any::Any;
77
use core::intrinsics;
88

9-
pub fn payload() -> *mut u8 {
10-
core::ptr::null_mut()
11-
}
9+
pub type Payload = *mut u8;
1210

1311
pub unsafe fn cleanup(_ptr: *mut u8) -> Box<dyn Any + Send> {
1412
intrinsics::abort()

src/libpanic_unwind/emcc.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,7 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo {
4848
name: b"rust_panic\0".as_ptr(),
4949
};
5050

51-
pub fn payload() -> *mut u8 {
52-
ptr::null_mut()
53-
}
51+
pub type Payload = *mut u8;
5452

5553
pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
5654
assert!(!ptr.is_null());

src/libpanic_unwind/gcc.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848

4949
use alloc::boxed::Box;
5050
use core::any::Any;
51-
use core::ptr;
5251

5352
use crate::dwarf::eh::{self, EHAction, EHContext};
5453
use libc::{c_int, uintptr_t};
@@ -82,9 +81,7 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
8281
}
8382
}
8483

85-
pub fn payload() -> *mut u8 {
86-
ptr::null_mut()
87-
}
84+
pub type Payload = *mut u8;
8885

8986
pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
9087
let my_ep = ptr as *mut Exception;

src/libpanic_unwind/hermit.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ use alloc::boxed::Box;
66
use core::any::Any;
77
use core::ptr;
88

9-
pub fn payload() -> *mut u8 {
10-
ptr::null_mut()
11-
}
9+
pub type Payload = *mut u8;
1210

1311
pub unsafe fn cleanup(_ptr: *mut u8) -> Box<dyn Any + Send> {
1412
extern "C" {

src/libpanic_unwind/lib.rs

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,19 @@
2222
#![feature(libc)]
2323
#![feature(nll)]
2424
#![feature(panic_unwind)]
25-
#![feature(raw)]
2625
#![feature(staged_api)]
2726
#![feature(std_internals)]
2827
#![feature(unwind_attributes)]
28+
#![feature(rustc_attrs)]
29+
#![feature(raw)]
2930
#![panic_runtime]
3031
#![feature(panic_runtime)]
3132

3233
use alloc::boxed::Box;
33-
use core::intrinsics;
34-
use core::mem;
3534
use core::panic::BoxMeUp;
36-
use core::raw;
3735

36+
// If adding to this list, you should also look at libstd::panicking's identical
37+
// list of Payload types and likely add to there as well.
3838
cfg_if::cfg_if! {
3939
if #[cfg(target_os = "emscripten")] {
4040
#[path = "emcc.rs"]
@@ -62,28 +62,11 @@ cfg_if::cfg_if! {
6262

6363
mod dwarf;
6464

65-
// Entry point for catching an exception, implemented using the `try` intrinsic
66-
// in the compiler.
67-
//
68-
// The interaction between the `payload` function and the compiler is pretty
69-
// hairy and tightly coupled, for more information see the compiler's
70-
// implementation of this.
7165
#[no_mangle]
72-
pub unsafe extern "C" fn __rust_maybe_catch_panic(
73-
f: fn(*mut u8),
74-
data: *mut u8,
75-
data_ptr: *mut usize,
76-
vtable_ptr: *mut usize,
77-
) -> u32 {
78-
let mut payload = imp::payload();
79-
if intrinsics::r#try(f, data, &mut payload as *mut _ as *mut _) == 0 {
80-
0
81-
} else {
82-
let obj = mem::transmute::<_, raw::TraitObject>(imp::cleanup(payload));
83-
*data_ptr = obj.data as usize;
84-
*vtable_ptr = obj.vtable as usize;
85-
1
86-
}
66+
pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> core::raw::TraitObject {
67+
let payload = payload as *mut imp::Payload;
68+
let payload = *(payload);
69+
core::mem::transmute(imp::cleanup(payload))
8770
}
8871

8972
// Entry point for raising an exception, just delegates to the platform-specific

src/libpanic_unwind/seh.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,9 +264,7 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
264264
_CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
265265
}
266266

267-
pub fn payload() -> [u64; 2] {
268-
[0; 2]
269-
}
267+
pub type Payload = [u64; 2];
270268

271269
pub unsafe fn cleanup(payload: [u64; 2]) -> Box<dyn Any + Send> {
272270
mem::transmute(raw::TraitObject { data: payload[0] as *mut _, vtable: payload[1] as *mut _ })

src/libstd/panicking.rs

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ use core::panic::{BoxMeUp, Location, PanicInfo};
1212
use crate::any::Any;
1313
use crate::fmt;
1414
use crate::intrinsics;
15-
use crate::mem::{self, ManuallyDrop};
15+
use crate::mem::{self, ManuallyDrop, MaybeUninit};
1616
use crate::process;
17-
use crate::raw;
1817
use crate::sync::atomic::{AtomicBool, Ordering};
1918
use crate::sys::stdio::panic_output;
2019
use crate::sys_common::backtrace::{self, RustBacktrace};
@@ -29,6 +28,31 @@ use crate::io::set_panic;
2928
#[cfg(test)]
3029
use realstd::io::set_panic;
3130

31+
// This must be kept in sync with the implementations in libpanic_unwind.
32+
//
33+
// This is *not* checked in anyway; the compiler does not allow us to use a
34+
// type/macro/anything from panic_unwind, since we're then linking in the
35+
// panic_unwind runtime even during -Cpanic=abort.
36+
//
37+
// Essentially this must be the type of `imp::Payload` in libpanic_unwind.
38+
cfg_if::cfg_if! {
39+
if #[cfg(not(feature = "panic_unwind"))] {
40+
type Payload = ();
41+
} else if #[cfg(target_os = "emscripten")] {
42+
type Payload = *mut u8;
43+
} else if #[cfg(target_arch = "wasm32")] {
44+
type Payload = *mut u8;
45+
} else if #[cfg(target_os = "hermit")] {
46+
type Payload = *mut u8;
47+
} else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] {
48+
type Payload = *mut u8;
49+
} else if #[cfg(target_env = "msvc")] {
50+
type Payload = [u64; 2];
51+
} else {
52+
type Payload = *mut u8;
53+
}
54+
}
55+
3256
// Binary interface to the panic runtime that the standard library depends on.
3357
//
3458
// The standard library is tagged with `#![needs_panic_runtime]` (introduced in
@@ -41,12 +65,10 @@ use realstd::io::set_panic;
4165
// hook up these functions, but it is not this day!
4266
#[allow(improper_ctypes)]
4367
extern "C" {
44-
fn __rust_maybe_catch_panic(
45-
f: fn(*mut u8),
46-
data: *mut u8,
47-
data_ptr: *mut usize,
48-
vtable_ptr: *mut usize,
49-
) -> u32;
68+
/// The payload ptr here is actually the same as the payload ptr for the try
69+
/// intrinsic (i.e., is really `*mut [u64; 2]` or `*mut *mut u8`).
70+
#[unwind(allowed)]
71+
fn __rust_panic_cleanup(payload: *mut u8) -> core::raw::TraitObject;
5072

5173
/// `payload` is actually a `*mut &mut dyn BoxMeUp` but that would cause FFI warnings.
5274
/// It cannot be `Box<dyn BoxMeUp>` because the other end of this call does not depend
@@ -241,9 +263,9 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
241263
}
242264

243265
// We do some sketchy operations with ownership here for the sake of
244-
// performance. We can only pass pointers down to
245-
// `__rust_maybe_catch_panic` (can't pass objects by value), so we do all
246-
// the ownership tracking here manually using a union.
266+
// performance. We can only pass pointers down to `do_call` (can't pass
267+
// objects by value), so we do all the ownership tracking here manually
268+
// using a union.
247269
//
248270
// We go through a transition where:
249271
//
@@ -254,7 +276,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
254276
// * If the closure successfully returns, we write the return value into the
255277
// data's return slot. Note that `ptr::write` is used as it's overwriting
256278
// uninitialized data.
257-
// * Finally, when we come back out of the `__rust_maybe_catch_panic` we're
279+
// * Finally, when we come back out of the `try` intrinsic we're
258280
// in one of two states:
259281
//
260282
// 1. The closure didn't panic, in which case the return value was
@@ -265,28 +287,25 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
265287
//
266288
// Once we stack all that together we should have the "most efficient'
267289
// method of calling a catch panic whilst juggling ownership.
268-
let mut any_data = 0;
269-
let mut any_vtable = 0;
270290
let mut data = Data { f: ManuallyDrop::new(f) };
271291

272-
let r = __rust_maybe_catch_panic(
273-
do_call::<F, R>,
274-
&mut data as *mut _ as *mut u8,
275-
&mut any_data,
276-
&mut any_vtable,
277-
);
292+
let mut payload: MaybeUninit<Payload> = MaybeUninit::uninit();
278293

279-
return if r == 0 {
294+
let data_ptr = &mut data as *mut _ as *mut u8;
295+
let payload_ptr = payload.as_mut_ptr() as *mut _;
296+
return if intrinsics::r#try(do_call::<F, R>, data_ptr, payload_ptr) == 0 {
280297
debug_assert!(update_panic_count(0) == 0);
281298
Ok(ManuallyDrop::into_inner(data.r))
282299
} else {
300+
Err(cleanup(payload.assume_init()))
301+
};
302+
303+
unsafe fn cleanup(mut payload: Payload) -> Box<dyn Any + Send + 'static> {
304+
let obj = crate::mem::transmute(__rust_panic_cleanup(&mut payload as *mut _ as *mut u8));
283305
update_panic_count(-1);
284306
debug_assert!(update_panic_count(0) == 0);
285-
Err(mem::transmute(raw::TraitObject {
286-
data: any_data as *mut _,
287-
vtable: any_vtable as *mut _,
288-
}))
289-
};
307+
obj
308+
}
290309

291310
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
292311
unsafe {

0 commit comments

Comments
 (0)