Skip to content

Commit 21f6a33

Browse files
committed
Auto merge of #145093 - nikic:dead-on-return, r=nnethercote
Set dead_on_return attribute for indirect arguments Set the dead_on_return attribute (added in LLVM 21) for arguments that are passed indirectly, but not byval. This indicates that the value of the argument on return does not matter, enabling additional dead store elimination. From LangRef: > This attribute indicates that the memory pointed to by the argument is dead upon function return, both upon normal return and if the calls unwinds, meaning that the caller will not depend on its contents. Stores that would be observable either on the return path or on the unwind path may be elided. > > Specifically, the behavior is as-if any memory written through the pointer during the execution of the function is overwritten with a poison value upon function return. The caller may access the memory, but any load not preceded by a store will return poison. > > This attribute does not imply aliasing properties. For pointer arguments that do not alias other memory locations, noalias attribute may be used in conjunction. Conversely, this attribute always implies dead_on_unwind. > > This attribute cannot be applied to return values. This fixes parts of #96497.
2 parents a153133 + ebef9d7 commit 21f6a33

File tree

7 files changed

+55
-6
lines changed

7 files changed

+55
-6
lines changed

compiler/rustc_codegen_llvm/src/abi.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use crate::attributes::{self, llfn_attrs_from_instance};
2424
use crate::builder::Builder;
2525
use crate::context::CodegenCx;
2626
use crate::llvm::{self, Attribute, AttributePlace};
27+
use crate::llvm_util;
2728
use crate::type_::Type;
2829
use crate::type_of::LayoutLlvmExt;
2930
use crate::value::Value;
@@ -500,7 +501,16 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
500501
}
501502
}
502503
PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => {
503-
apply(attrs);
504+
let i = apply(attrs);
505+
if cx.sess().opts.optimize != config::OptLevel::No
506+
&& llvm_util::get_version() >= (21, 0, 0)
507+
{
508+
attributes::apply_to_llfn(
509+
llfn,
510+
llvm::AttributePlace::Argument(i),
511+
&[llvm::AttributeKind::DeadOnReturn.create_attr(cx.llcx)],
512+
);
513+
}
504514
}
505515
PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => {
506516
assert!(!on_stack);

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ pub(crate) enum AttributeKind {
249249
FnRetThunkExtern = 41,
250250
Writable = 42,
251251
DeadOnUnwind = 43,
252+
DeadOnReturn = 44,
252253
}
253254

254255
/// LLVMIntPredicate

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ enum class LLVMRustAttributeKind {
277277
FnRetThunkExtern = 41,
278278
Writable = 42,
279279
DeadOnUnwind = 43,
280+
DeadOnReturn = 44,
280281
};
281282

282283
static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
@@ -369,6 +370,12 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
369370
return Attribute::Writable;
370371
case LLVMRustAttributeKind::DeadOnUnwind:
371372
return Attribute::DeadOnUnwind;
373+
case LLVMRustAttributeKind::DeadOnReturn:
374+
#if LLVM_VERSION_GE(21, 0)
375+
return Attribute::DeadOnReturn;
376+
#else
377+
report_fatal_error("DeadOnReturn attribute requires LLVM 21 or later");
378+
#endif
372379
}
373380
report_fatal_error("bad LLVMRustAttributeKind");
374381
}

tests/codegen-llvm/addr-of-mutate.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Test for the absence of `readonly` on the argument when it is mutated via `&raw const`.
66
// See <https://github.com/rust-lang/rust/issues/111502>.
77

8-
// CHECK: i8 @foo(ptr noalias{{( nocapture)?}} noundef align 1{{( captures\(none\))?}} dereferenceable(128) %x)
8+
// CHECK: i8 @foo(ptr{{( dead_on_return)?}} noalias{{( nocapture)?}} noundef align 1{{( captures\(none\))?}} dereferenceable(128) %x)
99
#[no_mangle]
1010
pub fn foo(x: [u8; 128]) -> u8 {
1111
let ptr = core::ptr::addr_of!(x).cast_mut();
@@ -15,7 +15,7 @@ pub fn foo(x: [u8; 128]) -> u8 {
1515
x[0]
1616
}
1717

18-
// CHECK: i1 @second(ptr noalias{{( nocapture)?}} noundef align {{[0-9]+}}{{( captures\(none\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
18+
// CHECK: i1 @second(ptr{{( dead_on_return)?}} noalias{{( nocapture)?}} noundef align {{[0-9]+}}{{( captures\(none\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
1919
#[no_mangle]
2020
pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
2121
let b_bool_ptr = core::ptr::addr_of!(a_ptr_and_b.1.1).cast_mut();
@@ -24,7 +24,7 @@ pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
2424
}
2525

2626
// If going through a deref (and there are no other mutating accesses), then `readonly` is fine.
27-
// CHECK: i1 @third(ptr noalias{{( nocapture)?}} noundef readonly align {{[0-9]+}}{{( captures\(none\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
27+
// CHECK: i1 @third(ptr{{( dead_on_return)?}} noalias{{( nocapture)?}} noundef readonly align {{[0-9]+}}{{( captures\(none\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
2828
#[no_mangle]
2929
pub unsafe fn third(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
3030
let b_bool_ptr = core::ptr::addr_of!((*a_ptr_and_b.0).1).cast_mut();

tests/codegen-llvm/dead_on_return.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ compile-flags: -C opt-level=3
2+
//@ min-llvm-version: 21
3+
4+
#![crate_type = "lib"]
5+
#![allow(unused_assignments, unused_variables)]
6+
7+
// Check that the old string is deallocated, but a new one is not initialized.
8+
#[unsafe(no_mangle)]
9+
pub fn test_str_new(mut s: String) {
10+
// CHECK-LABEL: @test_str_new
11+
// CHECK: __rust_dealloc
12+
// CHECK-NOT: store
13+
s = String::new();
14+
}
15+
16+
#[unsafe(no_mangle)]
17+
pub fn test_str_take(mut x: String) -> String {
18+
// CHECK-LABEL: @test_str_take
19+
// CHECK-NEXT: {{.*}}:
20+
// CHECK-NEXT: call void @llvm.memcpy
21+
// CHECK-NEXT: ret
22+
core::mem::take(&mut x)
23+
}
24+
25+
#[unsafe(no_mangle)]
26+
pub fn test_array_store(mut x: [u32; 100]) {
27+
// CHECK-LABEL: @test_array_store
28+
// CHECK-NEXT: {{.*}}:
29+
// CHECK-NEXT: ret
30+
x[0] = 1;
31+
}

tests/codegen-llvm/function-arguments.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {}
134134
#[no_mangle]
135135
pub fn notunpin_borrow(_: &NotUnpin) {}
136136

137-
// CHECK: @indirect_struct(ptr noalias{{( nocapture)?}} noundef readonly align 4{{( captures\(none\))?}} dereferenceable(32) %_1)
137+
// CHECK: @indirect_struct(ptr{{( dead_on_return)?}} noalias{{( nocapture)?}} noundef readonly align 4{{( captures\(none\))?}} dereferenceable(32) %_1)
138138
#[no_mangle]
139139
pub fn indirect_struct(_: S) {}
140140

tests/codegen-llvm/loongarch-abi/loongarch64-lp64d-abi.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ pub struct IntDoubleInt {
256256
c: i32,
257257
}
258258

259-
// CHECK: define void @f_int_double_int_s_arg(ptr noalias{{( nocapture)?}} noundef align 8{{( captures\(none\))?}} dereferenceable(24) %a)
259+
// CHECK: define void @f_int_double_int_s_arg(ptr{{( dead_on_return)?}} noalias{{( nocapture)?}} noundef align 8{{( captures\(none\))?}} dereferenceable(24) %a)
260260
#[no_mangle]
261261
pub extern "C" fn f_int_double_int_s_arg(a: IntDoubleInt) {}
262262

0 commit comments

Comments
 (0)