mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
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:
@@ -42,12 +42,13 @@ fn apply_attrs_to_callsite(
|
||||
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 apply_attrs_to_callsite(
|
||||
}
|
||||
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 @@ impl ArgAttribute: u8 {
|
||||
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()
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user