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:
parent
8365fcb2b8
commit
d71ed8d19b
10 changed files with 48 additions and 14 deletions
|
|
@ -42,12 +42,13 @@ trait ArgAttributesExt {
|
|||
const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] =
|
||||
[(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::NoCapture, llvm::AttributeKind::NoCapture),
|
||||
(ArgAttribute::NonNull, llvm::AttributeKind::NonNull),
|
||||
(ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly),
|
||||
(ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef),
|
||||
(ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly),
|
||||
];
|
||||
|
||||
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 {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,6 +251,7 @@ pub(crate) enum AttributeKind {
|
|||
Writable = 42,
|
||||
DeadOnUnwind = 43,
|
||||
DeadOnReturn = 44,
|
||||
CapturesReadOnly = 45,
|
||||
}
|
||||
|
||||
/// LLVMIntPredicate
|
||||
|
|
|
|||
|
|
@ -278,6 +278,7 @@ enum class LLVMRustAttributeKind {
|
|||
Writable = 42,
|
||||
DeadOnUnwind = 43,
|
||||
DeadOnReturn = 44,
|
||||
CapturesReadOnly = 45,
|
||||
};
|
||||
|
||||
static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
|
||||
|
|
@ -376,6 +377,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
|
|||
#else
|
||||
report_fatal_error("DeadOnReturn attribute requires LLVM 21 or later");
|
||||
#endif
|
||||
case LLVMRustAttributeKind::CapturesReadOnly:
|
||||
report_fatal_error("Should be handled separately");
|
||||
}
|
||||
report_fatal_error("bad LLVMRustAttributeKind");
|
||||
}
|
||||
|
|
@ -430,6 +433,11 @@ LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttributeKind RustAttr) {
|
|||
if (RustAttr == LLVMRustAttributeKind::NoCapture) {
|
||||
return wrap(Attribute::getWithCaptureInfo(*unwrap(C), CaptureInfo::none()));
|
||||
}
|
||||
if (RustAttr == LLVMRustAttributeKind::CapturesReadOnly) {
|
||||
return wrap(Attribute::getWithCaptureInfo(
|
||||
*unwrap(C), CaptureInfo(CaptureComponents::Address |
|
||||
CaptureComponents::ReadProvenance)));
|
||||
}
|
||||
#endif
|
||||
return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ mod attr_impl {
|
|||
const ReadOnly = 1 << 4;
|
||||
const InReg = 1 << 5;
|
||||
const NoUndef = 1 << 6;
|
||||
const CapturesReadOnly = 1 << 7;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
// 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`
|
||||
#[no_mangle]
|
||||
pub fn readonly_borrow(_: &i32) {}
|
||||
|
|
@ -91,12 +91,12 @@ pub fn readonly_borrow_ret() -> &'static i32 {
|
|||
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
|
||||
#[no_mangle]
|
||||
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
|
||||
#[no_mangle]
|
||||
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>.
|
||||
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.
|
||||
#[no_mangle]
|
||||
pub fn notunpin_borrow(_: &NotUnpin) {}
|
||||
|
|
@ -138,12 +138,12 @@ pub fn notunpin_borrow(_: &NotUnpin) {}
|
|||
#[no_mangle]
|
||||
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`
|
||||
#[no_mangle]
|
||||
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]
|
||||
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
|
||||
// <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.
|
||||
// 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]
|
||||
pub fn _box_custom(x: Box<i32, &std::alloc::Global>) {
|
||||
drop(x)
|
||||
|
|
@ -208,7 +208,7 @@ pub fn struct_return() -> S {
|
|||
#[no_mangle]
|
||||
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`
|
||||
#[no_mangle]
|
||||
pub fn slice(_: &[u8]) {}
|
||||
|
|
@ -227,7 +227,7 @@ pub fn unsafe_slice(_: &[UnsafeInner]) {}
|
|||
#[no_mangle]
|
||||
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`
|
||||
#[no_mangle]
|
||||
pub fn str(_: &[u8]) {}
|
||||
|
|
@ -259,7 +259,7 @@ pub fn trait_option(x: Option<Box<dyn Drop + Unpin>>) -> Option<Box<dyn Drop + U
|
|||
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]
|
||||
pub fn return_slice(x: &[u16]) -> &[u16] {
|
||||
x
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ pub fn enum2_value(x: Enum2) -> Enum2 {
|
|||
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]
|
||||
pub fn takes_slice(x: &[i32]) -> usize {
|
||||
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(
|
||||
ArgAttributes {
|
||||
regular: NoAlias | NonNull | ReadOnly | NoUndef,
|
||||
regular: NoAlias | NonNull | ReadOnly | NoUndef | CapturesReadOnly,
|
||||
arg_ext: None,
|
||||
pointee_size: Size(2 bytes),
|
||||
pointee_align: Some(
|
||||
|
|
|
|||
|
|
@ -939,7 +939,7 @@ error: fn_abi_of(assoc_test) = FnAbi {
|
|||
},
|
||||
mode: Direct(
|
||||
ArgAttributes {
|
||||
regular: NoAlias | NonNull | ReadOnly | NoUndef,
|
||||
regular: NoAlias | NonNull | ReadOnly | NoUndef | CapturesReadOnly,
|
||||
arg_ext: None,
|
||||
pointee_size: Size(2 bytes),
|
||||
pointee_align: Some(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue