Skip to content
Open
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
22 changes: 22 additions & 0 deletions compiler/rustc_codegen_llvm/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,28 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
let ii = apply(b);
if let BackendRepr::ScalarPair(scalar_a, scalar_b) = arg.layout.backend_repr {
apply_range_attr(llvm::AttributePlace::Argument(i), scalar_a);
let primitive_b = scalar_b.primitive();
let scalar_b = if let rustc_abi::Primitive::Int(int, false) = primitive_b
&& let ty::Ref(_, pointee_ty, _) = *arg.layout.ty.kind()
&& let ty::Slice(element_ty) = *pointee_ty.kind()
&& let elem_size = cx.layout_of(element_ty).size
&& elem_size != rustc_abi::Size::ZERO
{
// Ideally the layout calculations would have set the range,
// but that's complicated due to cycles, so in the mean time
// we calculate and apply it here.
debug_assert!(scalar_b.is_always_valid(cx));
let isize_max = int.signed_max() as u64;
rustc_abi::Scalar::Initialized {
value: primitive_b,
valid_range: rustc_abi::WrappingRange {
start: 0,
end: u128::from(isize_max / elem_size.bytes()),
},
}
} else {
scalar_b
};
apply_range_attr(llvm::AttributePlace::Argument(ii), scalar_b);
}
}
Expand Down
20 changes: 15 additions & 5 deletions tests/codegen-llvm/function-arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,17 +208,23 @@ pub fn struct_return() -> S {
#[no_mangle]
pub fn helper(_: usize) {}

// CHECK: @slice(ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0, [[USIZE]] noundef %_1.1)
// CHECK: @slice(
// CHECK-SAME: ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0,
// CHECK-SAME: [[USIZE]] noundef range({{i32 0, -2147483648|i64 0, -9223372036854775808}}) %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn slice(_: &[u8]) {}

// CHECK: @mutable_slice(ptr noalias noundef nonnull align 1 %_1.0, [[USIZE]] noundef %_1.1)
// CHECK: @mutable_slice(
// CHECK-SAME: ptr noalias noundef nonnull align 1 %_1.0,
// CHECK-SAME: [[USIZE]] noundef range({{i32 0, -2147483648|i64 0, -9223372036854775808}}) %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn mutable_slice(_: &mut [u8]) {}

// CHECK: @unsafe_slice(ptr noundef nonnull align 2 %_1.0, [[USIZE]] noundef %_1.1)
// CHECK: @unsafe_slice(
// CHECK-SAME: ptr noundef nonnull align 2 %_1.0,
// CHECK-SAME: [[USIZE]] noundef range({{i32 0, 1073741824|i64 0, 4611686018427387904}}) %_1.1)
// unsafe interior means this isn't actually readonly and there may be aliases ...
#[no_mangle]
pub fn unsafe_slice(_: &[UnsafeInner]) {}
Expand All @@ -227,7 +233,9 @@ pub fn unsafe_slice(_: &[UnsafeInner]) {}
#[no_mangle]
pub fn raw_slice(_: *const [u8]) {}

// CHECK: @str(ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0, [[USIZE]] noundef %_1.1)
// CHECK: @str(
// CHECK-SAME: ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0,
// CHECK-SAME: [[USIZE]] noundef range({{i32 0, -2147483648|i64 0, -9223372036854775808}}) %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn str(_: &[u8]) {}
Expand Down Expand Up @@ -259,7 +267,9 @@ 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{{( captures\(address, read_provenance\))?}} %x.0, [[USIZE]] noundef %x.1)
// CHECK: { ptr, [[USIZE]] } @return_slice(
// CHECK-SAME: ptr noalias noundef nonnull readonly align 2{{( captures\(address, read_provenance\))?}} %x.0,
// CHECK-SAME: [[USIZE]] noundef range({{i32 0, 1073741824|i64 0, 4611686018427387904}}) %x.1)
#[no_mangle]
pub fn return_slice(x: &[u16]) -> &[u16] {
x
Expand Down
22 changes: 20 additions & 2 deletions tests/codegen-llvm/range-attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,26 @@ pub fn enum2_value(x: Enum2) -> Enum2 {
x
}

// CHECK: noundef [[USIZE]] @takes_slice(ptr {{.*}} %x.0, [[USIZE]] noundef %x.1)
// CHECK: noundef [[USIZE]] @takes_slice_4(ptr {{.*}} %x.0, [[USIZE]] noundef
// bit32-SAME: range(i32 0, [[#0x20000000]])
// bit64-SAME: range(i64 0, [[#0x2000000000000000]])
// CHECK-SAME: %x.1)
#[no_mangle]
pub fn takes_slice(x: &[i32]) -> usize {
pub fn takes_slice_4(x: &[i32]) -> usize {
x.len()
}

// CHECK: noundef [[USIZE]] @takes_slice_3(ptr {{.*}} %x.0, [[USIZE]] noundef
// bit32-SAME: range(i32 0, [[#0x2AAAAAAB]])
// bit64-SAME: range(i64 0, [[#0x2AAAAAAAAAAAAAAB]])
// CHECK-SAME: %x.1)
#[no_mangle]
pub fn takes_slice_3(x: &[[u8; 3]]) -> usize {
x.len()
}

// CHECK: noundef [[USIZE]] @takes_zst_slice(ptr {{.*}} %x.0, [[USIZE]] noundef %x.1)
#[no_mangle]
pub fn takes_zst_slice(x: &[()]) -> usize {
x.len()
}
2 changes: 1 addition & 1 deletion tests/codegen-llvm/slice-as_chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub fn chunks4(x: &[u8]) -> &[[u8; 4]] {
// CHECK-LABEL: @chunks4_with_remainder
#[no_mangle]
pub fn chunks4_with_remainder(x: &[u8]) -> (&[[u8; 4]], &[u8]) {
// CHECK-DAG: and i64 %x.1, -4
// CHECK-DAG: and i64 %x.1, [[#0x7FFFFFFFFFFFFFFC]]
// CHECK-DAG: and i64 %x.1, 3
// CHECK-DAG: lshr
// CHECK-NOT: mul
Expand Down
4 changes: 2 additions & 2 deletions tests/codegen-llvm/slice-iter-nonnull.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub fn slice_iter_next_back<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'
// attribute is there, and confirms adding the assume back doesn't do anything.

// CHECK-LABEL: @slice_iter_new
// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef %slice.1)
// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef range({{.+}}) %slice.1)
#[no_mangle]
pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> {
// CHECK-NOT: slice
Expand All @@ -66,7 +66,7 @@ pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> {
}

// CHECK-LABEL: @slice_iter_mut_new
// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef %slice.1)
// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef range({{.+}}) %slice.1)
#[no_mangle]
pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> {
// CHECK-NOT: slice
Expand Down
32 changes: 32 additions & 0 deletions tests/codegen-llvm/slice-len-math.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//@ compile-flags: -C opt-level=3
#![crate_type = "lib"]

#[no_mangle]
// CHECK-LABEL: @len_plus_ten_a
pub fn len_plus_ten_a(s: &[u8]) -> usize {
// CHECK: start:
// CHECK-NOT: add
// CHECK: %[[R:.+]] = add nuw i{{.+}} %s.1, 10
// CHECK-NEXT: ret {{.+}} %[[R]]
s.len().wrapping_add(10)
}

#[no_mangle]
// CHECK-LABEL: @len_plus_ten_b
pub fn len_plus_ten_b(s: &[u32]) -> usize {
// CHECK: start:
// CHECK-NOT: add
// CHECK: %[[R:.+]] = add nuw nsw i{{.+}} %s.1, 10
Copy link
Member Author

Choose a reason for hiding this comment

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

This file is the simplest demonstration of LLVM taking advantage of it. See https://rust.godbolt.org/z/7jxqsaaTx showing that it didn't know nuw/nsw for these things previously.

// CHECK-NEXT: ret {{.+}} %[[R]]
s.len().wrapping_add(10)
}

#[no_mangle]
// CHECK-LABEL: @len_plus_len
pub fn len_plus_len(x: &[u8], y: &[u8]) -> usize {
// CHECK: start:
// CHECK-NOT: add
// CHECK: %[[R:.+]] = add nuw i{{.+}} {{%x.1, %y.1|%y.1, %x.1}}
// CHECK-NEXT: ret {{.+}} %[[R]]
usize::wrapping_add(x.len(), y.len())
}
16 changes: 8 additions & 8 deletions tests/codegen-llvm/slice-ref-equality.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ pub fn is_zero_array(data: &[u8; 4]) -> bool {
// equality for non-byte types also just emit a `bcmp`, not a loop.

// CHECK-LABEL: @eq_slice_of_nested_u8(
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
// CHECK-SAME: [[USIZE]] noundef %y.1
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef range({{.+}}) %x.1
// CHECK-SAME: [[USIZE]] noundef range({{.+}}) %y.1
#[no_mangle]
fn eq_slice_of_nested_u8(x: &[[u8; 3]], y: &[[u8; 3]]) -> bool {
// CHECK: icmp eq [[USIZE]] %x.1, %y.1
Expand All @@ -54,8 +54,8 @@ fn eq_slice_of_nested_u8(x: &[[u8; 3]], y: &[[u8; 3]]) -> bool {
}

// CHECK-LABEL: @eq_slice_of_i32(
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
// CHECK-SAME: [[USIZE]] noundef %y.1
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef range({{.+}}) %x.1
// CHECK-SAME: [[USIZE]] noundef range({{.+}}) %y.1
#[no_mangle]
fn eq_slice_of_i32(x: &[i32], y: &[i32]) -> bool {
// CHECK: icmp eq [[USIZE]] %x.1, %y.1
Expand All @@ -66,8 +66,8 @@ fn eq_slice_of_i32(x: &[i32], y: &[i32]) -> bool {
}

// CHECK-LABEL: @eq_slice_of_nonzero(
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
// CHECK-SAME: [[USIZE]] noundef %y.1
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef range({{.+}}) %x.1
// CHECK-SAME: [[USIZE]] noundef range({{.+}}) %y.1
#[no_mangle]
fn eq_slice_of_nonzero(x: &[NonZero<i32>], y: &[NonZero<i32>]) -> bool {
// CHECK: icmp eq [[USIZE]] %x.1, %y.1
Expand All @@ -78,8 +78,8 @@ fn eq_slice_of_nonzero(x: &[NonZero<i32>], y: &[NonZero<i32>]) -> bool {
}

// CHECK-LABEL: @eq_slice_of_option_of_nonzero(
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
// CHECK-SAME: [[USIZE]] noundef %y.1
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef range({{.+}}) %x.1
// CHECK-SAME: [[USIZE]] noundef range({{.+}}) %y.1
#[no_mangle]
fn eq_slice_of_option_of_nonzero(x: &[Option<NonZero<i16>>], y: &[Option<NonZero<i16>>]) -> bool {
// CHECK: icmp eq [[USIZE]] %x.1, %y.1
Expand Down
Loading