diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 680ad98593e7e..4ea0d06e26a0a 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -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); } } diff --git a/tests/codegen-llvm/function-arguments.rs b/tests/codegen-llvm/function-arguments.rs index a0744e44c61ec..c8aaf00442e9c 100644 --- a/tests/codegen-llvm/function-arguments.rs +++ b/tests/codegen-llvm/function-arguments.rs @@ -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]) {} @@ -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]) {} @@ -259,7 +267,9 @@ 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 865d36d474745..b057b2386e993 100644 --- a/tests/codegen-llvm/range-attribute.rs +++ b/tests/codegen-llvm/range-attribute.rs @@ -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() } diff --git a/tests/codegen-llvm/slice-as_chunks.rs b/tests/codegen-llvm/slice-as_chunks.rs index 337eb8981f6dd..0f6ae21fa214e 100644 --- a/tests/codegen-llvm/slice-as_chunks.rs +++ b/tests/codegen-llvm/slice-as_chunks.rs @@ -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 diff --git a/tests/codegen-llvm/slice-iter-nonnull.rs b/tests/codegen-llvm/slice-iter-nonnull.rs index 87907e7ad0a30..280594456c931 100644 --- a/tests/codegen-llvm/slice-iter-nonnull.rs +++ b/tests/codegen-llvm/slice-iter-nonnull.rs @@ -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 @@ -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 diff --git a/tests/codegen-llvm/slice-len-math.rs b/tests/codegen-llvm/slice-len-math.rs new file mode 100644 index 0000000000000..4b7a6adb22c62 --- /dev/null +++ b/tests/codegen-llvm/slice-len-math.rs @@ -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 + // 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()) +} diff --git a/tests/codegen-llvm/slice-ref-equality.rs b/tests/codegen-llvm/slice-ref-equality.rs index 2940378da3cdd..a5994c8794856 100644 --- a/tests/codegen-llvm/slice-ref-equality.rs +++ b/tests/codegen-llvm/slice-ref-equality.rs @@ -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 @@ -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 @@ -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], y: &[NonZero]) -> bool { // CHECK: icmp eq [[USIZE]] %x.1, %y.1 @@ -78,8 +78,8 @@ fn eq_slice_of_nonzero(x: &[NonZero], y: &[NonZero]) -> 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>], y: &[Option>]) -> bool { // CHECK: icmp eq [[USIZE]] %x.1, %y.1