Skip to content

Commit e9f9594

Browse files
committed
Auto merge of #121114 - Nilstrieb:no-inline!, r=saethlin
Add `#[rustc_no_mir_inline]` for standard library UB checks should help with #121110 and also with #120848 Because the MIR inliner cannot know whether the checks are enabled or not, so inlining is an unnecessary compile time pessimization when debug assertions are disabled. LLVM knows whether they are enabled or not, so it can optimize accordingly without wasting time. r? `@saethlin`
2 parents 89d8e31 + 81d7069 commit e9f9594

9 files changed

+107
-3
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

+4
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
792792
rustc_intrinsic, Normal, template!(Word), ErrorFollowing,
793793
"the `#[rustc_intrinsic]` attribute is used to declare intrinsics with function bodies",
794794
),
795+
rustc_attr!(
796+
rustc_no_mir_inline, Normal, template!(Word), WarnFollowing,
797+
"#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen"
798+
),
795799

796800
// ==========================================================================
797801
// Internal attributes, Testing:

compiler/rustc_mir_transform/src/inline.rs

+4
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,10 @@ impl<'tcx> Inliner<'tcx> {
421421
callee_attrs: &CodegenFnAttrs,
422422
cross_crate_inlinable: bool,
423423
) -> Result<(), &'static str> {
424+
if self.tcx.has_attr(callsite.callee.def_id(), sym::rustc_no_mir_inline) {
425+
return Err("#[rustc_no_mir_inline]");
426+
}
427+
424428
if let InlineAttr::Never = callee_attrs.inline {
425429
return Err("never inline hint");
426430
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1441,6 +1441,7 @@ symbols! {
14411441
rustc_mir,
14421442
rustc_must_implement_one_of,
14431443
rustc_never_returns_null_ptr,
1444+
rustc_no_mir_inline,
14441445
rustc_nonnull_optimization_guaranteed,
14451446
rustc_nounwind,
14461447
rustc_object_lifetime_default,

library/core/src/intrinsics.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -2706,13 +2706,25 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize)
27062706
macro_rules! assert_unsafe_precondition {
27072707
($message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => {
27082708
{
2709+
// #[cfg(bootstrap)] (this comment)
27092710
// When the standard library is compiled with debug assertions, we want the check to inline for better performance.
27102711
// This is important when working on the compiler, which is compiled with debug assertions locally.
27112712
// When not compiled with debug assertions (so the precompiled std) we outline the check to minimize the compile
27122713
// time impact when debug assertions are disabled.
2713-
// It is not clear whether that is the best solution, see #120848.
2714-
#[cfg_attr(debug_assertions, inline(always))]
2715-
#[cfg_attr(not(debug_assertions), inline(never))]
2714+
// The proper solution to this is the `#[rustc_no_mir_inline]` below, but we still want decent performance for cfg(bootstrap).
2715+
#[cfg_attr(all(debug_assertions, bootstrap), inline(always))]
2716+
#[cfg_attr(all(not(debug_assertions), bootstrap), inline(never))]
2717+
2718+
// This check is inlineable, but not by the MIR inliner.
2719+
// The reason for this is that the MIR inliner is in an exceptionally bad position
2720+
// to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`,
2721+
// which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and
2722+
// would be bad for compile times.
2723+
//
2724+
// LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without
2725+
// inlining the check. If it's `true`, it can inline it and get significantly better performance.
2726+
#[cfg_attr(not(bootstrap), rustc_no_mir_inline)]
2727+
#[cfg_attr(not(bootstrap), inline)]
27162728
#[rustc_nounwind]
27172729
fn precondition_check($($name:$ty),*) {
27182730
if !$e {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
- // MIR for `caller` before Inline
2+
+ // MIR for `caller` after Inline
3+
4+
fn caller() -> () {
5+
let mut _0: ();
6+
let _1: ();
7+
8+
bb0: {
9+
StorageLive(_1);
10+
_1 = callee() -> [return: bb1, unwind unreachable];
11+
}
12+
13+
bb1: {
14+
StorageDead(_1);
15+
_0 = const ();
16+
return;
17+
}
18+
}
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
- // MIR for `caller` before Inline
2+
+ // MIR for `caller` after Inline
3+
4+
fn caller() -> () {
5+
let mut _0: ();
6+
let _1: ();
7+
8+
bb0: {
9+
StorageLive(_1);
10+
_1 = callee() -> [return: bb1, unwind continue];
11+
}
12+
13+
bb1: {
14+
StorageDead(_1);
15+
_0 = const ();
16+
return;
17+
}
18+
}
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// MIR for `caller` after PreCodegen
2+
3+
fn caller() -> () {
4+
let mut _0: ();
5+
let _1: ();
6+
7+
bb0: {
8+
_1 = callee() -> [return: bb1, unwind unreachable];
9+
}
10+
11+
bb1: {
12+
return;
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// MIR for `caller` after PreCodegen
2+
3+
fn caller() -> () {
4+
let mut _0: ();
5+
let _1: ();
6+
7+
bb0: {
8+
_1 = callee() -> [return: bb1, unwind continue];
9+
}
10+
11+
bb1: {
12+
return;
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
2+
#![crate_type = "lib"]
3+
#![feature(rustc_attrs)]
4+
5+
//@ compile-flags: -Zmir-opt-level=2 -Zinline-mir
6+
7+
#[inline]
8+
#[rustc_no_mir_inline]
9+
pub fn callee() {}
10+
11+
// EMIT_MIR rustc_no_mir_inline.caller.Inline.diff
12+
// EMIT_MIR rustc_no_mir_inline.caller.PreCodegen.after.mir
13+
pub fn caller() {
14+
// CHECK-LABEL: fn caller(
15+
// CHECK: callee()
16+
callee();
17+
}

0 commit comments

Comments
 (0)