Auto merge of #121298 - nikic:writable, r=cuviper

Set writable and dead_on_unwind attributes for sret arguments

Set the `writable` and `dead_on_unwind` attributes for `sret` arguments. This allows call slot optimization to remove more memcpy's.

See https://llvm.org/docs/LangRef.html#parameter-attributes for the specification of these attributes. In short, the statement we're making here is that:

 * The return slot is writable.
 * The return slot will not be read if the function unwinds.

Fixes https://github.com/rust-lang/rust/issues/90595.
This commit is contained in:
bors 2024-04-25 04:31:56 +00:00
commit 284f94f9c0
7 changed files with 33 additions and 4 deletions

View file

@ -2,6 +2,7 @@
use crate::builder::Builder; use crate::builder::Builder;
use crate::context::CodegenCx; use crate::context::CodegenCx;
use crate::llvm::{self, Attribute, AttributePlace}; use crate::llvm::{self, Attribute, AttributePlace};
use crate::llvm_util;
use crate::type_::Type; use crate::type_::Type;
use crate::type_of::LayoutLlvmExt; use crate::type_of::LayoutLlvmExt;
use crate::value::Value; use crate::value::Value;
@ -425,6 +426,18 @@ fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value) {
cx.type_array(cx.type_i8(), self.ret.layout.size.bytes()), cx.type_array(cx.type_i8(), self.ret.layout.size.bytes()),
); );
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[sret]); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[sret]);
if cx.sess().opts.optimize != config::OptLevel::No
&& llvm_util::get_version() >= (18, 0, 0)
{
attributes::apply_to_llfn(
llfn,
llvm::AttributePlace::Argument(i),
&[
llvm::AttributeKind::Writable.create_attr(cx.llcx),
llvm::AttributeKind::DeadOnUnwind.create_attr(cx.llcx),
],
);
}
} }
PassMode::Cast { cast, pad_i32: _ } => { PassMode::Cast { cast, pad_i32: _ } => {
cast.attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn); cast.attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn);

View file

@ -200,6 +200,8 @@ pub enum AttributeKind {
AllocAlign = 39, AllocAlign = 39,
SanitizeSafeStack = 40, SanitizeSafeStack = 40,
FnRetThunkExtern = 41, FnRetThunkExtern = 41,
Writable = 42,
DeadOnUnwind = 43,
} }
/// LLVMIntPredicate /// LLVMIntPredicate

View file

@ -91,6 +91,8 @@ enum LLVMRustAttribute {
AllocAlign = 39, AllocAlign = 39,
SanitizeSafeStack = 40, SanitizeSafeStack = 40,
FnRetThunkExtern = 41, FnRetThunkExtern = 41,
Writable = 42,
DeadOnUnwind = 43,
}; };
typedef struct OpaqueRustString *RustStringRef; typedef struct OpaqueRustString *RustStringRef;

View file

@ -312,6 +312,16 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
return Attribute::SafeStack; return Attribute::SafeStack;
case FnRetThunkExtern: case FnRetThunkExtern:
return Attribute::FnRetThunkExtern; return Attribute::FnRetThunkExtern;
#if LLVM_VERSION_GE(18, 0)
case Writable:
return Attribute::Writable;
case DeadOnUnwind:
return Attribute::DeadOnUnwind;
#else
case Writable:
case DeadOnUnwind:
report_fatal_error("Not supported on this LLVM version");
#endif
} }
report_fatal_error("bad AttributeKind"); report_fatal_error("bad AttributeKind");
} }

View file

@ -197,7 +197,7 @@ pub fn notunpin_box(x: Box<NotUnpin>) -> Box<NotUnpin> {
x x
} }
// CHECK: @struct_return(ptr noalias nocapture noundef sret([32 x i8]) align 4 dereferenceable(32){{( %_0)?}}) // CHECK: @struct_return(ptr{{( dead_on_unwind)?}} noalias nocapture noundef{{( writable)?}} sret([32 x i8]) align 4 dereferenceable(32){{( %_0)?}})
#[no_mangle] #[no_mangle]
pub fn struct_return() -> S { pub fn struct_return() -> S {
S { S {

View file

@ -260,7 +260,7 @@ pub struct IntDoubleInt {
#[no_mangle] #[no_mangle]
pub extern "C" fn f_int_double_int_s_arg(a: IntDoubleInt) {} pub extern "C" fn f_int_double_int_s_arg(a: IntDoubleInt) {}
// CHECK: define void @f_ret_int_double_int_s(ptr noalias nocapture noundef sret([24 x i8]) align 8 dereferenceable(24) %_0) // CHECK: define void @f_ret_int_double_int_s(ptr{{( dead_on_unwind)?}} noalias nocapture noundef{{( writable)?}} sret([24 x i8]) align 8 dereferenceable(24) %_0)
#[no_mangle] #[no_mangle]
pub extern "C" fn f_ret_int_double_int_s() -> IntDoubleInt { pub extern "C" fn f_ret_int_double_int_s() -> IntDoubleInt {
IntDoubleInt { a: 1, b: 2., c: 3 } IntDoubleInt { a: 1, b: 2., c: 3 }

View file

@ -1,4 +1,6 @@
//@ compile-flags: -O //@ compile-flags: -O
//@ needs-unwind
//@ min-llvm-version: 18
#![feature(c_unwind)] #![feature(c_unwind)]
#![crate_type = "lib"] #![crate_type = "lib"]
@ -23,8 +25,8 @@ pub fn new_from_uninit() -> Foo {
} }
pub fn new_from_uninit_unwind() -> Foo { pub fn new_from_uninit_unwind() -> Foo {
// CHECK-LABEL: new_from_uninit // CHECK-LABEL: new_from_uninit_unwind
// CHECK: call void @llvm.memcpy. // CHECK-NOT: call void @llvm.memcpy.
let mut x = std::mem::MaybeUninit::uninit(); let mut x = std::mem::MaybeUninit::uninit();
unsafe { unsafe {
init_unwind(x.as_mut_ptr()); init_unwind(x.as_mut_ptr());