From d71ed8d19bcfdbd6eb3bf2620071f2ab95470a57 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 11 Aug 2025 17:18:23 +0200 Subject: 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. --- tests/codegen-llvm/function-arguments.rs | 20 ++++++++++---------- tests/codegen-llvm/range-attribute.rs | 2 +- tests/codegen-llvm/read-only-capture-opt.rs | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 tests/codegen-llvm/read-only-capture-opt.rs (limited to 'tests/codegen-llvm') diff --git a/tests/codegen-llvm/function-arguments.rs b/tests/codegen-llvm/function-arguments.rs index a3fafbe6f82..db508682862 100644 --- a/tests/codegen-llvm/function-arguments.rs +++ b/tests/codegen-llvm/function-arguments.rs @@ -80,7 +80,7 @@ pub fn option_nonzero_int(x: Option>) -> Option> { 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 { // . 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) -> Box { // With a custom allocator, it should *not* have `noalias`. (See // 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) { 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>) -> Option &[u16] { x diff --git a/tests/codegen-llvm/range-attribute.rs b/tests/codegen-llvm/range-attribute.rs index b81ff9ab3e2..865d36d4747 100644 --- a/tests/codegen-llvm/range-attribute.rs +++ b/tests/codegen-llvm/range-attribute.rs @@ -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() diff --git a/tests/codegen-llvm/read-only-capture-opt.rs b/tests/codegen-llvm/read-only-capture-opt.rs new file mode 100644 index 00000000000..78d56f8efc2 --- /dev/null +++ b/tests/codegen-llvm/read-only-capture-opt.rs @@ -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 +} -- cgit 1.4.1-3-g733a5