Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion compiler/rustc_codegen_llvm/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]> {
Expand Down Expand Up @@ -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));
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ pub(crate) enum AttributeKind {
Writable = 42,
DeadOnUnwind = 43,
DeadOnReturn = 44,
CapturesReadOnly = 45,
}

/// LLVMIntPredicate
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ enum class LLVMRustAttributeKind {
Writable = 42,
DeadOnUnwind = 43,
DeadOnReturn = 44,
CapturesReadOnly = 45,
};

static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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)));
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/callconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ty_utils/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
20 changes: 10 additions & 10 deletions tests/codegen-llvm/function-arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand All @@ -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) {}
Expand Down Expand Up @@ -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) {}
Expand All @@ -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>) {}

Expand Down Expand Up @@ -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)
Expand All @@ -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]) {}
Expand All @@ -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]) {}
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/codegen-llvm/range-attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
18 changes: 18 additions & 0 deletions tests/codegen-llvm/read-only-capture-opt.rs
Original file line number Diff line number Diff line change
@@ -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
}
2 changes: 1 addition & 1 deletion tests/ui/abi/debug.generic.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/abi/debug.riscv64.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ fn main() {
// The `Box` has been deallocated by now, so this is a dangling reference!
let r: &u8 = &*r;
println!("{:p}", r);
println!("{}", i);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These adjustments are necessary so these tests still get miscompiled.


// The following might segfault. Or it might not.
// Depends on the platform semantics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ fn main() {
// The `Box` has been deallocated by now, so this is a dangling reference!
let r: &u8 = &*r;
println!("{:p}", r);
println!("{}", i);

// The following might segfault. Or it might not.
// Depends on the platform semantics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ fn main() {
// The `Box` has been deallocated by now, so this is a dangling reference!
let r: &u8 = &*r;
println!("{:p}", r);
println!("{}", i);

// The following might segfault. Or it might not.
// Depends on the platform semantics
Expand Down
Loading