Skip to content

Commit 597ed3e

Browse files
Reapply: Mark drop calls in landing pads cold instead of noinline
This reverts commit 4a56cbe, reversing changes made to 6e5a6ff.
1 parent c5a43b8 commit 597ed3e

File tree

10 files changed

+87
-31
lines changed

10 files changed

+87
-31
lines changed

compiler/rustc_codegen_gcc/src/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1403,7 +1403,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
14031403
self.cx
14041404
}
14051405

1406-
fn do_not_inline(&mut self, _llret: RValue<'gcc>) {
1406+
fn apply_attrs_to_cleanup_callsite(&mut self, _llret: RValue<'gcc>) {
14071407
unimplemented!();
14081408
}
14091409

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
2323
use rustc_span::Span;
2424
use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
2525
use rustc_target::spec::{HasTargetSpec, Target};
26+
use smallvec::SmallVec;
2627
use std::borrow::Cow;
2728
use std::ffi::CStr;
2829
use std::iter;
@@ -1188,9 +1189,19 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
11881189
unsafe { llvm::LLVMBuildZExt(self.llbuilder, val, dest_ty, UNNAMED) }
11891190
}
11901191

1191-
fn do_not_inline(&mut self, llret: &'ll Value) {
1192-
let noinline = llvm::AttributeKind::NoInline.create_attr(self.llcx);
1193-
attributes::apply_to_callsite(llret, llvm::AttributePlace::Function, &[noinline]);
1192+
fn apply_attrs_to_cleanup_callsite(&mut self, llret: &'ll Value) {
1193+
let mut attrs = SmallVec::<[_; 2]>::new();
1194+
1195+
// Cleanup is always the cold path.
1196+
attrs.push(llvm::AttributeKind::Cold.create_attr(self.llcx));
1197+
1198+
// In LLVM versions with deferred inlining (currently, system LLVM < 14),
1199+
// inlining drop glue can lead to exponential size blowup, see #41696 and #92110.
1200+
if !llvm_util::is_rust_llvm() && llvm_util::get_version() < (14, 0, 0) {
1201+
attrs.push(llvm::AttributeKind::NoInline.create_attr(self.llcx));
1202+
}
1203+
1204+
attributes::apply_to_callsite(llret, llvm::AttributePlace::Function, &attrs);
11941205
}
11951206
}
11961207

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,6 +1929,8 @@ extern "C" {
19291929
pub fn LLVMRustVersionMinor() -> u32;
19301930
pub fn LLVMRustVersionPatch() -> u32;
19311931

1932+
pub fn LLVMRustIsRustLLVM() -> bool;
1933+
19321934
/// Add LLVM module flags.
19331935
///
19341936
/// In order for Rust-C LTO to work, module flags must be compatible with Clang. What

compiler/rustc_codegen_llvm/src/llvm_util.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,12 @@ pub fn get_version() -> (u32, u32, u32) {
261261
}
262262
}
263263

264+
/// Returns `true` if this LLVM is Rust's bundled LLVM (and not system LLVM).
265+
pub fn is_rust_llvm() -> bool {
266+
// Can be called without initializing LLVM
267+
unsafe { llvm::LLVMRustIsRustLLVM() }
268+
}
269+
264270
pub fn print_passes() {
265271
// Can be called without initializing LLVM
266272
unsafe {

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
166166
bx.invoke(fn_ty, fn_ptr, &llargs, ret_llbb, unwind_block, self.funclet(fx));
167167
bx.apply_attrs_callsite(&fn_abi, invokeret);
168168
if fx.mir[self.bb].is_cleanup {
169-
bx.do_not_inline(invokeret);
169+
bx.apply_attrs_to_cleanup_callsite(invokeret);
170170
}
171171

172172
if let Some((ret_dest, target)) = destination {
@@ -178,11 +178,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
178178
let llret = bx.call(fn_ty, fn_ptr, &llargs, self.funclet(fx));
179179
bx.apply_attrs_callsite(&fn_abi, llret);
180180
if fx.mir[self.bb].is_cleanup {
181-
// Cleanup is always the cold path. Don't inline
182-
// drop glue. Also, when there is a deeply-nested
183-
// struct, there are "symmetry" issues that cause
184-
// exponential inlining - see issue #41696.
185-
bx.do_not_inline(llret);
181+
bx.apply_attrs_to_cleanup_callsite(llret);
186182
}
187183

188184
if let Some((ret_dest, target)) = destination {
@@ -1448,7 +1444,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
14481444

14491445
let llret = bx.call(fn_ty, fn_ptr, &[], None);
14501446
bx.apply_attrs_callsite(&fn_abi, llret);
1451-
bx.do_not_inline(llret);
1447+
bx.apply_attrs_to_cleanup_callsite(llret);
14521448

14531449
bx.unreachable();
14541450

compiler/rustc_codegen_ssa/src/traits/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,5 +479,5 @@ pub trait BuilderMethods<'a, 'tcx>:
479479
) -> Self::Value;
480480
fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
481481

482-
fn do_not_inline(&mut self, llret: Self::Value);
482+
fn apply_attrs_to_cleanup_callsite(&mut self, llret: Self::Value);
483483
}

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,14 @@ extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; }
663663

664664
extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; }
665665

666+
extern "C" bool LLVMRustIsRustLLVM() {
667+
#ifdef LLVM_RUSTLLVM
668+
return true;
669+
#else
670+
return false;
671+
#endif
672+
}
673+
666674
extern "C" void LLVMRustAddModuleFlag(
667675
LLVMModuleRef M,
668676
Module::ModFlagBehavior MergeBehavior,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// no-system-llvm: needs #92110
2+
// compile-flags: -Cno-prepopulate-passes
3+
#![crate_type = "lib"]
4+
5+
// This test checks that drop calls in unwind landing pads
6+
// get the `cold` attribute.
7+
8+
// CHECK-LABEL: @check_cold
9+
// CHECK: {{(call|invoke) void .+}}drop_in_place{{.+}} [[ATTRIBUTES:#[0-9]+]]
10+
// CHECK: attributes [[ATTRIBUTES]] = { cold }
11+
#[no_mangle]
12+
pub fn check_cold(f: fn(), x: Box<u32>) {
13+
// this may unwind
14+
f();
15+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// no-system-llvm: needs #92110 + patch for Rust alloc/dealloc functions
2+
// compile-flags: -Copt-level=3
3+
#![crate_type = "lib"]
4+
5+
// This test checks that we can inline drop_in_place in
6+
// unwind landing pads.
7+
8+
// Without inlining, the box pointers escape via the call to drop_in_place,
9+
// and LLVM will not optimize out the pointer comparison.
10+
// With inlining, everything should be optimized out.
11+
// See https://github.com/rust-lang/rust/issues/46515
12+
// CHECK-LABEL: @check_no_escape_in_landingpad
13+
// CHECK: start:
14+
// CHECK-NEXT: ret void
15+
#[no_mangle]
16+
pub fn check_no_escape_in_landingpad(f: fn()) {
17+
let x = &*Box::new(0);
18+
let y = &*Box::new(0);
19+
20+
if x as *const _ == y as *const _ {
21+
f();
22+
}
23+
}
24+
25+
// Without inlining, the compiler can't tell that
26+
// dropping an empty string (in a landing pad) does nothing.
27+
// With inlining, the landing pad should be optimized out.
28+
// See https://github.com/rust-lang/rust/issues/87055
29+
// CHECK-LABEL: @check_eliminate_noop_drop
30+
// CHECK: start:
31+
// CHECK-NEXT: call void %g()
32+
// CHECK-NEXT: ret void
33+
#[no_mangle]
34+
pub fn check_eliminate_noop_drop(g: fn()) {
35+
let _var = String::new();
36+
g();
37+
}

src/test/codegen/vec-shrink-panik.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,13 @@ pub fn shrink_to_fit(vec: &mut Vec<u32>) {
1616
// CHECK-LABEL: @issue71861
1717
#[no_mangle]
1818
pub fn issue71861(vec: Vec<u32>) -> Box<[u32]> {
19-
// CHECK-NOT: panic
20-
21-
// Call to panic_no_unwind in case of double-panic is expected,
22-
// but other panics are not.
23-
// CHECK: cleanup
24-
// CHECK-NEXT: ; call core::panicking::panic_no_unwind
25-
// CHECK-NEXT: panic_no_unwind
26-
2719
// CHECK-NOT: panic
2820
vec.into_boxed_slice()
2921
}
3022

3123
// CHECK-LABEL: @issue75636
3224
#[no_mangle]
3325
pub fn issue75636<'a>(iter: &[&'a str]) -> Box<[&'a str]> {
34-
// CHECK-NOT: panic
35-
36-
// Call to panic_no_unwind in case of double-panic is expected,
37-
// but other panics are not.
38-
// CHECK: cleanup
39-
// CHECK-NEXT: ; call core::panicking::panic_no_unwind
40-
// CHECK-NEXT: panic_no_unwind
41-
4226
// CHECK-NOT: panic
4327
iter.iter().copied().collect()
4428
}
45-
46-
// CHECK: ; core::panicking::panic_no_unwind
47-
// CHECK: declare void @{{.*}}panic_no_unwind

0 commit comments

Comments
 (0)