Auto merge of #145259 - nikic:read-only-capture, r=wesleywiser
Tell LLVM about read-only captures `&Freeze` parameters are not only `readonly` within the function, but any captures of the pointer can also only be used for reads. This can now be encoded using the `captures(address, read_provenance)` attribute.
This commit is contained in:
commit
125ff8a788
13 changed files with 51 additions and 14 deletions
|
|
@ -42,12 +42,13 @@ trait ArgAttributesExt {
|
||||||
const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] =
|
const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] =
|
||||||
[(ArgAttribute::InReg, llvm::AttributeKind::InReg)];
|
[(ArgAttribute::InReg, llvm::AttributeKind::InReg)];
|
||||||
|
|
||||||
const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 5] = [
|
const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 6] = [
|
||||||
(ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias),
|
(ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias),
|
||||||
(ArgAttribute::NoCapture, llvm::AttributeKind::NoCapture),
|
(ArgAttribute::NoCapture, llvm::AttributeKind::NoCapture),
|
||||||
(ArgAttribute::NonNull, llvm::AttributeKind::NonNull),
|
(ArgAttribute::NonNull, llvm::AttributeKind::NonNull),
|
||||||
(ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly),
|
(ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly),
|
||||||
(ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef),
|
(ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef),
|
||||||
|
(ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly),
|
||||||
];
|
];
|
||||||
|
|
||||||
fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 8]> {
|
fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 8]> {
|
||||||
|
|
@ -83,6 +84,10 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
|
||||||
}
|
}
|
||||||
for (attr, llattr) in OPTIMIZATION_ATTRIBUTES {
|
for (attr, llattr) in OPTIMIZATION_ATTRIBUTES {
|
||||||
if regular.contains(attr) {
|
if regular.contains(attr) {
|
||||||
|
// captures(address, read_provenance) is only available since LLVM 21.
|
||||||
|
if attr == ArgAttribute::CapturesReadOnly && llvm_util::get_version() < (21, 0, 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
attrs.push(llattr.create_attr(cx.llcx));
|
attrs.push(llattr.create_attr(cx.llcx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -251,6 +251,7 @@ pub(crate) enum AttributeKind {
|
||||||
Writable = 42,
|
Writable = 42,
|
||||||
DeadOnUnwind = 43,
|
DeadOnUnwind = 43,
|
||||||
DeadOnReturn = 44,
|
DeadOnReturn = 44,
|
||||||
|
CapturesReadOnly = 45,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// LLVMIntPredicate
|
/// LLVMIntPredicate
|
||||||
|
|
|
||||||
|
|
@ -278,6 +278,7 @@ enum class LLVMRustAttributeKind {
|
||||||
Writable = 42,
|
Writable = 42,
|
||||||
DeadOnUnwind = 43,
|
DeadOnUnwind = 43,
|
||||||
DeadOnReturn = 44,
|
DeadOnReturn = 44,
|
||||||
|
CapturesReadOnly = 45,
|
||||||
};
|
};
|
||||||
|
|
||||||
static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
|
static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
|
||||||
|
|
@ -376,6 +377,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
|
||||||
#else
|
#else
|
||||||
report_fatal_error("DeadOnReturn attribute requires LLVM 21 or later");
|
report_fatal_error("DeadOnReturn attribute requires LLVM 21 or later");
|
||||||
#endif
|
#endif
|
||||||
|
case LLVMRustAttributeKind::CapturesReadOnly:
|
||||||
|
report_fatal_error("Should be handled separately");
|
||||||
}
|
}
|
||||||
report_fatal_error("bad LLVMRustAttributeKind");
|
report_fatal_error("bad LLVMRustAttributeKind");
|
||||||
}
|
}
|
||||||
|
|
@ -430,6 +433,11 @@ LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttributeKind RustAttr) {
|
||||||
if (RustAttr == LLVMRustAttributeKind::NoCapture) {
|
if (RustAttr == LLVMRustAttributeKind::NoCapture) {
|
||||||
return wrap(Attribute::getWithCaptureInfo(*unwrap(C), CaptureInfo::none()));
|
return wrap(Attribute::getWithCaptureInfo(*unwrap(C), CaptureInfo::none()));
|
||||||
}
|
}
|
||||||
|
if (RustAttr == LLVMRustAttributeKind::CapturesReadOnly) {
|
||||||
|
return wrap(Attribute::getWithCaptureInfo(
|
||||||
|
*unwrap(C), CaptureInfo(CaptureComponents::Address |
|
||||||
|
CaptureComponents::ReadProvenance)));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr)));
|
return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ mod attr_impl {
|
||||||
const ReadOnly = 1 << 4;
|
const ReadOnly = 1 << 4;
|
||||||
const InReg = 1 << 5;
|
const InReg = 1 << 5;
|
||||||
const NoUndef = 1 << 6;
|
const NoUndef = 1 << 6;
|
||||||
|
const CapturesReadOnly = 1 << 7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rustc_data_structures::external_bitflags_debug! { ArgAttribute }
|
rustc_data_structures::external_bitflags_debug! { ArgAttribute }
|
||||||
|
|
|
||||||
|
|
@ -356,6 +356,7 @@ fn arg_attrs_for_rust_scalar<'tcx>(
|
||||||
|
|
||||||
if matches!(kind, PointerKind::SharedRef { frozen: true }) && !is_return {
|
if matches!(kind, PointerKind::SharedRef { frozen: true }) && !is_return {
|
||||||
attrs.set(ArgAttribute::ReadOnly);
|
attrs.set(ArgAttribute::ReadOnly);
|
||||||
|
attrs.set(ArgAttribute::CapturesReadOnly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ pub fn option_nonzero_int(x: Option<NonZero<u64>>) -> Option<NonZero<u64>> {
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK: @readonly_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1)
|
// CHECK: @readonly_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1)
|
||||||
// FIXME #25759 This should also have `nocapture`
|
// FIXME #25759 This should also have `nocapture`
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn readonly_borrow(_: &i32) {}
|
pub fn readonly_borrow(_: &i32) {}
|
||||||
|
|
@ -91,12 +91,12 @@ pub fn readonly_borrow_ret() -> &'static i32 {
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK: @static_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1)
|
// CHECK: @static_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1)
|
||||||
// static borrow may be captured
|
// static borrow may be captured
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn static_borrow(_: &'static i32) {}
|
pub fn static_borrow(_: &'static i32) {}
|
||||||
|
|
||||||
// CHECK: @named_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1)
|
// CHECK: @named_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1)
|
||||||
// borrow with named lifetime may be captured
|
// borrow with named lifetime may be captured
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn named_borrow<'r>(_: &'r i32) {}
|
pub fn named_borrow<'r>(_: &'r i32) {}
|
||||||
|
|
@ -129,7 +129,7 @@ pub fn mutable_borrow_ret() -> &'static mut i32 {
|
||||||
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>.
|
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>.
|
||||||
pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {}
|
pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {}
|
||||||
|
|
||||||
// CHECK: @notunpin_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1)
|
// CHECK: @notunpin_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1)
|
||||||
// But `&NotUnpin` behaves perfectly normal.
|
// But `&NotUnpin` behaves perfectly normal.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn notunpin_borrow(_: &NotUnpin) {}
|
pub fn notunpin_borrow(_: &NotUnpin) {}
|
||||||
|
|
@ -138,12 +138,12 @@ pub fn notunpin_borrow(_: &NotUnpin) {}
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn indirect_struct(_: S) {}
|
pub fn indirect_struct(_: S) {}
|
||||||
|
|
||||||
// CHECK: @borrowed_struct(ptr noalias noundef readonly align 4 dereferenceable(32) %_1)
|
// CHECK: @borrowed_struct(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(32) %_1)
|
||||||
// FIXME #25759 This should also have `nocapture`
|
// FIXME #25759 This should also have `nocapture`
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn borrowed_struct(_: &S) {}
|
pub fn borrowed_struct(_: &S) {}
|
||||||
|
|
||||||
// CHECK: @option_borrow(ptr noalias noundef readonly align 4 dereferenceable_or_null(4) %_x)
|
// CHECK: @option_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable_or_null(4) %_x)
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn option_borrow(_x: Option<&i32>) {}
|
pub fn option_borrow(_x: Option<&i32>) {}
|
||||||
|
|
||||||
|
|
@ -185,7 +185,7 @@ pub fn _box(x: Box<i32>) -> Box<i32> {
|
||||||
// With a custom allocator, it should *not* have `noalias`. (See
|
// With a custom allocator, it should *not* have `noalias`. (See
|
||||||
// <https://github.com/rust-lang/miri/issues/3341> for why.) The second argument is the allocator,
|
// <https://github.com/rust-lang/miri/issues/3341> for why.) The second argument is the allocator,
|
||||||
// which is a reference here that still carries `noalias` as usual.
|
// which is a reference here that still carries `noalias` as usual.
|
||||||
// CHECK: @_box_custom(ptr noundef nonnull align 4 %x.0, ptr noalias noundef nonnull readonly align 1 %x.1)
|
// CHECK: @_box_custom(ptr noundef nonnull align 4 %x.0, ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %x.1)
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn _box_custom(x: Box<i32, &std::alloc::Global>) {
|
pub fn _box_custom(x: Box<i32, &std::alloc::Global>) {
|
||||||
drop(x)
|
drop(x)
|
||||||
|
|
@ -208,7 +208,7 @@ pub fn struct_return() -> S {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn helper(_: usize) {}
|
pub fn helper(_: usize) {}
|
||||||
|
|
||||||
// CHECK: @slice(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1)
|
// CHECK: @slice(ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0, [[USIZE]] noundef %_1.1)
|
||||||
// FIXME #25759 This should also have `nocapture`
|
// FIXME #25759 This should also have `nocapture`
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn slice(_: &[u8]) {}
|
pub fn slice(_: &[u8]) {}
|
||||||
|
|
@ -227,7 +227,7 @@ pub fn unsafe_slice(_: &[UnsafeInner]) {}
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn raw_slice(_: *const [u8]) {}
|
pub fn raw_slice(_: *const [u8]) {}
|
||||||
|
|
||||||
// CHECK: @str(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1)
|
// CHECK: @str(ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0, [[USIZE]] noundef %_1.1)
|
||||||
// FIXME #25759 This should also have `nocapture`
|
// FIXME #25759 This should also have `nocapture`
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn str(_: &[u8]) {}
|
pub fn str(_: &[u8]) {}
|
||||||
|
|
@ -259,7 +259,7 @@ pub fn trait_option(x: Option<Box<dyn Drop + Unpin>>) -> Option<Box<dyn Drop + U
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK: { ptr, [[USIZE]] } @return_slice(ptr noalias noundef nonnull readonly align 2 %x.0, [[USIZE]] noundef %x.1)
|
// CHECK: { ptr, [[USIZE]] } @return_slice(ptr noalias noundef nonnull readonly align 2{{( captures\(address, read_provenance\))?}} %x.0, [[USIZE]] noundef %x.1)
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn return_slice(x: &[u16]) -> &[u16] {
|
pub fn return_slice(x: &[u16]) -> &[u16] {
|
||||||
x
|
x
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ pub fn enum2_value(x: Enum2) -> Enum2 {
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK: noundef [[USIZE]] @takes_slice(ptr noalias noundef nonnull readonly align 4 %x.0, [[USIZE]] noundef %x.1)
|
// CHECK: noundef [[USIZE]] @takes_slice(ptr {{.*}} %x.0, [[USIZE]] noundef %x.1)
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn takes_slice(x: &[i32]) -> usize {
|
pub fn takes_slice(x: &[i32]) -> usize {
|
||||||
x.len()
|
x.len()
|
||||||
|
|
|
||||||
18
tests/codegen-llvm/read-only-capture-opt.rs
Normal file
18
tests/codegen-llvm/read-only-capture-opt.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
//@ compile-flags: -C opt-level=3 -Z mir-opt-level=0
|
||||||
|
//@ min-llvm-version: 21
|
||||||
|
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
unsafe extern "C" {
|
||||||
|
safe fn do_something(p: &i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub fn test() -> i32 {
|
||||||
|
// CHECK-LABEL: @test(
|
||||||
|
// CHECK: ret i32 0
|
||||||
|
let i = 0;
|
||||||
|
do_something(&i);
|
||||||
|
do_something(&i);
|
||||||
|
i
|
||||||
|
}
|
||||||
|
|
@ -939,7 +939,7 @@ error: fn_abi_of(assoc_test) = FnAbi {
|
||||||
},
|
},
|
||||||
mode: Direct(
|
mode: Direct(
|
||||||
ArgAttributes {
|
ArgAttributes {
|
||||||
regular: NoAlias | NonNull | ReadOnly | NoUndef,
|
regular: NoAlias | NonNull | ReadOnly | NoUndef | CapturesReadOnly,
|
||||||
arg_ext: None,
|
arg_ext: None,
|
||||||
pointee_size: Size(2 bytes),
|
pointee_size: Size(2 bytes),
|
||||||
pointee_align: Some(
|
pointee_align: Some(
|
||||||
|
|
|
||||||
|
|
@ -939,7 +939,7 @@ error: fn_abi_of(assoc_test) = FnAbi {
|
||||||
},
|
},
|
||||||
mode: Direct(
|
mode: Direct(
|
||||||
ArgAttributes {
|
ArgAttributes {
|
||||||
regular: NoAlias | NonNull | ReadOnly | NoUndef,
|
regular: NoAlias | NonNull | ReadOnly | NoUndef | CapturesReadOnly,
|
||||||
arg_ext: None,
|
arg_ext: None,
|
||||||
pointee_size: Size(2 bytes),
|
pointee_size: Size(2 bytes),
|
||||||
pointee_align: Some(
|
pointee_align: Some(
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ fn main() {
|
||||||
// The `Box` has been deallocated by now, so this is a dangling reference!
|
// The `Box` has been deallocated by now, so this is a dangling reference!
|
||||||
let r: &u8 = &*r;
|
let r: &u8 = &*r;
|
||||||
println!("{:p}", r);
|
println!("{:p}", r);
|
||||||
|
println!("{}", i);
|
||||||
|
|
||||||
// The following might segfault. Or it might not.
|
// The following might segfault. Or it might not.
|
||||||
// Depends on the platform semantics
|
// Depends on the platform semantics
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ fn main() {
|
||||||
// The `Box` has been deallocated by now, so this is a dangling reference!
|
// The `Box` has been deallocated by now, so this is a dangling reference!
|
||||||
let r: &u8 = &*r;
|
let r: &u8 = &*r;
|
||||||
println!("{:p}", r);
|
println!("{:p}", r);
|
||||||
|
println!("{}", i);
|
||||||
|
|
||||||
// The following might segfault. Or it might not.
|
// The following might segfault. Or it might not.
|
||||||
// Depends on the platform semantics
|
// Depends on the platform semantics
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ fn main() {
|
||||||
// The `Box` has been deallocated by now, so this is a dangling reference!
|
// The `Box` has been deallocated by now, so this is a dangling reference!
|
||||||
let r: &u8 = &*r;
|
let r: &u8 = &*r;
|
||||||
println!("{:p}", r);
|
println!("{:p}", r);
|
||||||
|
println!("{}", i);
|
||||||
|
|
||||||
// The following might segfault. Or it might not.
|
// The following might segfault. Or it might not.
|
||||||
// Depends on the platform semantics
|
// Depends on the platform semantics
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue