From ebef9d7f6387fb0541a0dab48d1cd1f86556df04 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 8 Aug 2025 12:39:30 +0200 Subject: [PATCH] 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. --- compiler/rustc_codegen_llvm/src/abi.rs | 12 ++++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 7 +++++ tests/codegen-llvm/addr-of-mutate.rs | 6 ++-- tests/codegen-llvm/dead_on_return.rs | 31 +++++++++++++++++++ tests/codegen-llvm/function-arguments.rs | 2 +- .../loongarch-abi/loongarch64-lp64d-abi.rs | 2 +- 7 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 tests/codegen-llvm/dead_on_return.rs diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 009e7e2487b6..043123fcab2c 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -24,6 +24,7 @@ use crate::attributes::{self, llfn_attrs_from_instance}; use crate::builder::Builder; use crate::context::CodegenCx; use crate::llvm::{self, Attribute, AttributePlace}; +use crate::llvm_util; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; @@ -500,7 +501,16 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } } PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => { - apply(attrs); + let i = apply(attrs); + if cx.sess().opts.optimize != config::OptLevel::No + && llvm_util::get_version() >= (21, 0, 0) + { + attributes::apply_to_llfn( + llfn, + llvm::AttributePlace::Argument(i), + &[llvm::AttributeKind::DeadOnReturn.create_attr(cx.llcx)], + ); + } } PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => { assert!(!on_stack); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 75d3d27f74e1..ad3c3d5932ee 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -249,6 +249,7 @@ pub(crate) enum AttributeKind { FnRetThunkExtern = 41, Writable = 42, DeadOnUnwind = 43, + DeadOnReturn = 44, } /// LLVMIntPredicate diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 588d867bbbf4..cd4f80f808c6 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -277,6 +277,7 @@ enum class LLVMRustAttributeKind { FnRetThunkExtern = 41, Writable = 42, DeadOnUnwind = 43, + DeadOnReturn = 44, }; static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) { @@ -369,6 +370,12 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) { return Attribute::Writable; case LLVMRustAttributeKind::DeadOnUnwind: return Attribute::DeadOnUnwind; + case LLVMRustAttributeKind::DeadOnReturn: +#if LLVM_VERSION_GE(21, 0) + return Attribute::DeadOnReturn; +#else + report_fatal_error("DeadOnReturn attribute requires LLVM 21 or later"); +#endif } report_fatal_error("bad LLVMRustAttributeKind"); } diff --git a/tests/codegen-llvm/addr-of-mutate.rs b/tests/codegen-llvm/addr-of-mutate.rs index 14bc4b8ab28c..716690652893 100644 --- a/tests/codegen-llvm/addr-of-mutate.rs +++ b/tests/codegen-llvm/addr-of-mutate.rs @@ -5,7 +5,7 @@ // Test for the absence of `readonly` on the argument when it is mutated via `&raw const`. // See . -// CHECK: i8 @foo(ptr noalias{{( nocapture)?}} noundef align 1{{( captures\(none\))?}} dereferenceable(128) %x) +// CHECK: i8 @foo(ptr{{( dead_on_return)?}} noalias{{( nocapture)?}} noundef align 1{{( captures\(none\))?}} dereferenceable(128) %x) #[no_mangle] pub fn foo(x: [u8; 128]) -> u8 { let ptr = core::ptr::addr_of!(x).cast_mut(); @@ -15,7 +15,7 @@ pub fn foo(x: [u8; 128]) -> u8 { x[0] } -// CHECK: i1 @second(ptr noalias{{( nocapture)?}} noundef align {{[0-9]+}}{{( captures\(none\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b) +// CHECK: i1 @second(ptr{{( dead_on_return)?}} noalias{{( nocapture)?}} noundef align {{[0-9]+}}{{( captures\(none\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b) #[no_mangle] pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool { 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 { } // If going through a deref (and there are no other mutating accesses), then `readonly` is fine. -// CHECK: i1 @third(ptr noalias{{( nocapture)?}} noundef readonly align {{[0-9]+}}{{( captures\(none\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b) +// CHECK: i1 @third(ptr{{( dead_on_return)?}} noalias{{( nocapture)?}} noundef readonly align {{[0-9]+}}{{( captures\(none\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b) #[no_mangle] pub unsafe fn third(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool { let b_bool_ptr = core::ptr::addr_of!((*a_ptr_and_b.0).1).cast_mut(); diff --git a/tests/codegen-llvm/dead_on_return.rs b/tests/codegen-llvm/dead_on_return.rs new file mode 100644 index 000000000000..3c1940d6ba7c --- /dev/null +++ b/tests/codegen-llvm/dead_on_return.rs @@ -0,0 +1,31 @@ +//@ compile-flags: -C opt-level=3 +//@ min-llvm-version: 21 + +#![crate_type = "lib"] +#![allow(unused_assignments, unused_variables)] + +// Check that the old string is deallocated, but a new one is not initialized. +#[unsafe(no_mangle)] +pub fn test_str_new(mut s: String) { + // CHECK-LABEL: @test_str_new + // CHECK: __rust_dealloc + // CHECK-NOT: store + s = String::new(); +} + +#[unsafe(no_mangle)] +pub fn test_str_take(mut x: String) -> String { + // CHECK-LABEL: @test_str_take + // CHECK-NEXT: {{.*}}: + // CHECK-NEXT: call void @llvm.memcpy + // CHECK-NEXT: ret + core::mem::take(&mut x) +} + +#[unsafe(no_mangle)] +pub fn test_array_store(mut x: [u32; 100]) { + // CHECK-LABEL: @test_array_store + // CHECK-NEXT: {{.*}}: + // CHECK-NEXT: ret + x[0] = 1; +} diff --git a/tests/codegen-llvm/function-arguments.rs b/tests/codegen-llvm/function-arguments.rs index c8cd8526ae51..a3fafbe6f821 100644 --- a/tests/codegen-llvm/function-arguments.rs +++ b/tests/codegen-llvm/function-arguments.rs @@ -134,7 +134,7 @@ pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {} #[no_mangle] pub fn notunpin_borrow(_: &NotUnpin) {} -// CHECK: @indirect_struct(ptr noalias{{( nocapture)?}} noundef readonly align 4{{( captures\(none\))?}} dereferenceable(32) %_1) +// CHECK: @indirect_struct(ptr{{( dead_on_return)?}} noalias{{( nocapture)?}} noundef readonly align 4{{( captures\(none\))?}} dereferenceable(32) %_1) #[no_mangle] pub fn indirect_struct(_: S) {} diff --git a/tests/codegen-llvm/loongarch-abi/loongarch64-lp64d-abi.rs b/tests/codegen-llvm/loongarch-abi/loongarch64-lp64d-abi.rs index 93c8d60930b7..4666342a16a3 100644 --- a/tests/codegen-llvm/loongarch-abi/loongarch64-lp64d-abi.rs +++ b/tests/codegen-llvm/loongarch-abi/loongarch64-lp64d-abi.rs @@ -256,7 +256,7 @@ pub struct IntDoubleInt { c: i32, } -// CHECK: define void @f_int_double_int_s_arg(ptr noalias{{( nocapture)?}} noundef align 8{{( captures\(none\))?}} dereferenceable(24) %a) +// CHECK: define void @f_int_double_int_s_arg(ptr{{( dead_on_return)?}} noalias{{( nocapture)?}} noundef align 8{{( captures\(none\))?}} dereferenceable(24) %a) #[no_mangle] pub extern "C" fn f_int_double_int_s_arg(a: IntDoubleInt) {}