Skip to content

Commit 28316f2

Browse files
committed
Add panic_unreachable_unchecked feature flag to the standard library
This is similar to `panic_immediate_abort` except that all panics are considered immediate UB and can therefore be optimized away as unreachable by the compiler. This has been tested on [regalloc3](https://github.com/Amanieu/regalloc3) where it resulted in a 10% speedup compared to using a normal standard library, mainly due to the elimination of bounds checks. While it may seem that this feature merely to satisfy those with a reckless thirst for performance at any cost, it is also useful for saner heads as a profiling tool to investigate the impact of unnecessary safety check and find places where unsafe code could be used to avoid them.
1 parent 0b45675 commit 28316f2

File tree

8 files changed

+45
-6
lines changed

8 files changed

+45
-6
lines changed

library/alloc/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ compiler-builtins-no-f16-f128 = ["compiler_builtins/no-f16-f128"]
2626
compiler-builtins-mangled-names = ["compiler_builtins/mangled-names"]
2727
# Make panics and failed asserts immediately abort without formatting any message
2828
panic_immediate_abort = ["core/panic_immediate_abort"]
29+
# Make the optimizer assume panics are unreachable.
30+
panic_unreachable_unchecked = [
31+
"panic_immediate_abort",
32+
"core/panic_unreachable_unchecked",
33+
]
2934
# Choose algorithms that are optimized for binary size instead of runtime performance
3035
optimize_for_size = ["core/optimize_for_size"]
3136

library/alloc/src/alloc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ pub const fn handle_alloc_error(layout: Layout) -> ! {
400400
}
401401

402402
#[inline]
403+
#[cfg(not(feature = "panic_immediate_abort"))]
403404
fn rt_error(layout: Layout) -> ! {
404405
unsafe {
405406
__rust_alloc_error_handler(layout.size(), layout.align());

library/core/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ bench = false
1818
[features]
1919
# Make panics and failed asserts immediately abort without formatting any message
2020
panic_immediate_abort = []
21+
# Make the optimizer assume panics are unreachable.
22+
panic_unreachable_unchecked = ["panic_immediate_abort"]
2123
# Choose algorithms that are optimized for binary size instead of runtime performance
2224
optimize_for_size = []
2325
# Make `RefCell` store additional debugging information, which is printed out when

library/core/src/panicking.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ const _: () = assert!(cfg!(panic = "abort"), "panic_immediate_abort requires -C
5353
#[rustc_do_not_const_check] // hooked by const-eval
5454
#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable
5555
pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
56+
if cfg!(feature = "panic_unreachable_unchecked") {
57+
// SAFETY: it's not...
58+
unsafe { super::intrinsics::unreachable() }
59+
}
5660
if cfg!(feature = "panic_immediate_abort") {
5761
super::intrinsics::abort()
5862
}
@@ -94,6 +98,10 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo
9498
// We don't unwind anyway at compile-time so we can call the regular `panic_fmt`.
9599
panic_fmt(fmt)
96100
} else #[track_caller] {
101+
if cfg!(feature = "panic_unreachable_unchecked") {
102+
// SAFETY: it's not...
103+
unsafe { super::intrinsics::unreachable() }
104+
}
97105
if cfg!(feature = "panic_immediate_abort") {
98106
super::intrinsics::abort()
99107
}
@@ -266,6 +274,10 @@ pub const fn panic_display<T: fmt::Display>(x: &T) -> ! {
266274
#[track_caller]
267275
#[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access
268276
fn panic_bounds_check(index: usize, len: usize) -> ! {
277+
if cfg!(feature = "panic_unreachable_unchecked") {
278+
// SAFETY: it's not...
279+
unsafe { super::intrinsics::unreachable() }
280+
}
269281
if cfg!(feature = "panic_immediate_abort") {
270282
super::intrinsics::abort()
271283
}
@@ -279,6 +291,10 @@ fn panic_bounds_check(index: usize, len: usize) -> ! {
279291
#[lang = "panic_misaligned_pointer_dereference"] // needed by codegen for panic on misaligned pointer deref
280292
#[rustc_nounwind] // `CheckAlignment` MIR pass requires this function to never unwind
281293
fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! {
294+
if cfg!(feature = "panic_unreachable_unchecked") {
295+
// SAFETY: it's not...
296+
unsafe { super::intrinsics::unreachable() }
297+
}
282298
if cfg!(feature = "panic_immediate_abort") {
283299
super::intrinsics::abort()
284300
}
@@ -297,6 +313,10 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! {
297313
#[lang = "panic_null_pointer_dereference"] // needed by codegen for panic on null pointer deref
298314
#[rustc_nounwind] // `CheckNull` MIR pass requires this function to never unwind
299315
fn panic_null_pointer_dereference() -> ! {
316+
if cfg!(feature = "panic_unreachable_unchecked") {
317+
// SAFETY: it's not...
318+
unsafe { super::intrinsics::unreachable() }
319+
}
300320
if cfg!(feature = "panic_immediate_abort") {
301321
super::intrinsics::abort()
302322
}

library/core/src/str/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const fn slice_error_fail_ct(_: &str, _: usize, _: usize) -> ! {
7878
panic!("failed to slice string");
7979
}
8080

81+
#[cfg(not(feature = "panic_immediate_abort"))]
8182
#[track_caller]
8283
fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! {
8384
const MAX_DISPLAY_LENGTH: usize = 256;

library/std/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ panic_immediate_abort = [
107107
"core/panic_immediate_abort",
108108
"alloc/panic_immediate_abort",
109109
]
110+
# Make the optimizer assume panics are unreachable.
111+
panic_unreachable_unchecked = [
112+
"panic_immediate_abort",
113+
"core/panic_unreachable_unchecked",
114+
"alloc/panic_unreachable_unchecked",
115+
]
110116
# Choose algorithms that are optimized for binary size instead of runtime performance
111117
optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"]
112118

library/std/src/panicking.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ use realstd::io::try_set_output_capture;
1919
use crate::any::Any;
2020
#[cfg(not(test))]
2121
use crate::io::try_set_output_capture;
22-
use crate::mem::{self, ManuallyDrop};
22+
#[cfg(not(feature = "panic_immediate_abort"))]
23+
use crate::mem::ManuallyDrop;
2324
use crate::panic::{BacktraceStyle, PanicHookInfo};
2425
use crate::sync::atomic::{AtomicBool, Ordering};
2526
use crate::sync::{PoisonError, RwLock};
2627
use crate::sys::backtrace;
2728
use crate::sys::stdio::panic_output;
28-
use crate::{fmt, intrinsics, process, thread};
29+
use crate::{fmt, intrinsics, mem, process, thread};
2930

3031
// This forces codegen of the function called by panic!() inside the std crate, rather than in
3132
// downstream crates. Primarily this is useful for rustc's codegen tests, which rely on noticing
@@ -341,7 +342,7 @@ pub mod panic_count {
341342
}
342343

343344
#[inline]
344-
pub fn increase(run_panic_hook: bool) -> Option<MustAbort> {
345+
pub fn increase(_run_panic_hook: bool) -> Option<MustAbort> {
345346
None
346347
}
347348

@@ -726,6 +727,10 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
726727
#[track_caller]
727728
#[rustc_do_not_const_check] // hooked by const-eval
728729
pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
730+
if cfg!(feature = "panic_unreachable_unchecked") {
731+
// SAFETY: it's not...
732+
unsafe { super::intrinsics::unreachable() }
733+
}
729734
if cfg!(feature = "panic_immediate_abort") {
730735
intrinsics::abort()
731736
}
@@ -898,7 +903,5 @@ fn rust_panic(msg: &mut dyn PanicPayload) -> ! {
898903
#[cfg_attr(not(test), rustc_std_internal_symbol)]
899904
#[cfg(feature = "panic_immediate_abort")]
900905
fn rust_panic(_: &mut dyn PanicPayload) -> ! {
901-
unsafe {
902-
crate::intrinsics::abort();
903-
}
906+
crate::intrinsics::abort();
904907
}

library/sysroot/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ system-llvm-libunwind = ["std/system-llvm-libunwind"]
2828
optimize_for_size = ["std/optimize_for_size"]
2929
panic-unwind = ["std/panic_unwind"]
3030
panic_immediate_abort = ["std/panic_immediate_abort"]
31+
panic_unreachable_unchecked = ["std/panic_unreachable_unchecked"]
3132
profiler = ["dep:profiler_builtins"]
3233
std_detect_file_io = ["std/std_detect_file_io"]
3334
std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"]

0 commit comments

Comments
 (0)