Skip to content

Commit a4009ef

Browse files
committed
Auto merge of #123575 - scottmcm:assume-raw-parts, r=<try>
Add some targeted `assume(bytes <= isize::MAX)` around slices Putting it on every single slice length was pretty expensive, but let's see if doing it just in `slice::from_raw_parts(_mut)` and `slice::Iter(Mut)::len` works better.
2 parents aa1c459 + 2be8c36 commit a4009ef

File tree

7 files changed

+77
-18
lines changed

7 files changed

+77
-18
lines changed

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::operand::{OperandRef, OperandValue};
22
use super::place::PlaceRef;
33
use super::FunctionCx;
4+
use crate::common::IntPredicate;
45
use crate::errors;
56
use crate::errors::InvalidMonomorphization;
67
use crate::meth;
@@ -499,6 +500,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
499500
// The `_unsigned` version knows the relative ordering of the pointers,
500501
// so can use `sub nuw` and `udiv exact` instead of dealing in signed.
501502
let d = bx.unchecked_usub(a, b);
503+
if let OptLevel::Default | OptLevel::Aggressive = bx.sess().opts.optimize {
504+
// Despite dealing in `usize`, it's still not allowed for the
505+
// pointers to be more than `isize::MAX` apart.
506+
let inrange = bx.icmp(IntPredicate::IntSGE, d, bx.const_usize(0));
507+
bx.assume(inrange);
508+
}
502509
bx.exactudiv(d, pointee_size)
503510
}
504511
}

library/core/src/mem/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,17 @@ pub trait SizedTypeProperties: Sized {
12351235
#[doc(hidden)]
12361236
#[unstable(feature = "sized_type_properties", issue = "none")]
12371237
const IS_ZST: bool = size_of::<Self>() == 0;
1238+
1239+
/// The largest safe length for a `[Self]`.
1240+
///
1241+
/// Anything larger than this would make `size_of_val` overflow `isize::MAX`,
1242+
/// which is never allowed for a single object.
1243+
#[doc(hidden)]
1244+
#[unstable(feature = "sized_type_properties", issue = "none")]
1245+
const MAX_SLICE_LEN: usize = match size_of::<Self>() {
1246+
0 => usize::MAX,
1247+
n => (isize::MAX as usize) / n,
1248+
};
12381249
}
12391250
#[doc(hidden)]
12401251
#[unstable(feature = "sized_type_properties", issue = "none")]

library/core/src/slice/raw.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! Free functions to create `&[T]` and `&mut [T]`.
22
33
use crate::array;
4-
use crate::mem::{align_of, size_of};
4+
use crate::intrinsics;
5+
use crate::mem::{align_of, SizedTypeProperties};
56
use crate::ops::Range;
67
use crate::ptr;
78
use crate::ub_checks;
@@ -98,13 +99,14 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
9899
"slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`",
99100
(
100101
data: *mut () = data as *mut (),
101-
size: usize = size_of::<T>(),
102102
align: usize = align_of::<T>(),
103103
len: usize = len,
104+
max_len: usize = T::MAX_SLICE_LEN,
104105
) =>
105106
ub_checks::is_aligned_and_not_null(data, align)
106-
&& ub_checks::is_valid_allocation_size(size, len)
107+
&& len <= max_len
107108
);
109+
intrinsics::assume(len <= T::MAX_SLICE_LEN);
108110
&*ptr::slice_from_raw_parts(data, len)
109111
}
110112
}
@@ -152,13 +154,14 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m
152154
"slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`",
153155
(
154156
data: *mut () = data as *mut (),
155-
size: usize = size_of::<T>(),
156157
align: usize = align_of::<T>(),
157158
len: usize = len,
159+
max_len: usize = T::MAX_SLICE_LEN,
158160
) =>
159161
ub_checks::is_aligned_and_not_null(data, align)
160-
&& ub_checks::is_valid_allocation_size(size, len)
162+
&& len <= max_len
161163
);
164+
intrinsics::assume(len <= T::MAX_SLICE_LEN);
162165
&mut *ptr::slice_from_raw_parts_mut(data, len)
163166
}
164167
}

library/core/src/ub_checks.rs

-6
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,6 @@ pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> boo
115115
!ptr.is_null() && ptr.is_aligned_to(align)
116116
}
117117

118-
#[inline]
119-
pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool {
120-
let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size };
121-
len <= max_len
122-
}
123-
124118
/// Checks whether the regions of memory starting at `src` and `dst` of size
125119
/// `count * size` do *not* overlap.
126120
///

tests/codegen/intrinsics/offset_from.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
//@ compile-flags: -C opt-level=1
1+
//@ revisions: OPT1 OPT2
2+
//@ [OPT1] compile-flags: -Copt-level=1
3+
//@ [OPT2] compile-flags: -Copt-level=2
24
//@ only-64bit (because we're using [ui]size)
35

46
#![crate_type = "lib"]
@@ -17,8 +19,9 @@ pub unsafe fn offset_from_odd_size(a: *const RGB, b: *const RGB) -> isize {
1719
// CHECK: start
1820
// CHECK-NEXT: ptrtoint
1921
// CHECK-NEXT: ptrtoint
20-
// CHECK-NEXT: sub i64
21-
// CHECK-NEXT: sdiv exact i64 %{{[0-9]+}}, 3
22+
// CHECK-NEXT: %[[D:.+]] = sub i64
23+
// CHECK-NOT: assume
24+
// CHECK-NEXT: sdiv exact i64 %[[D]], 3
2225
// CHECK-NEXT: ret i64
2326
ptr_offset_from(a, b)
2427
}
@@ -29,8 +32,11 @@ pub unsafe fn offset_from_unsigned_odd_size(a: *const RGB, b: *const RGB) -> usi
2932
// CHECK: start
3033
// CHECK-NEXT: ptrtoint
3134
// CHECK-NEXT: ptrtoint
32-
// CHECK-NEXT: sub nuw i64
33-
// CHECK-NEXT: udiv exact i64 %{{[0-9]+}}, 3
35+
// CHECK-NEXT: %[[D:.+]] = sub nuw i64
36+
// OPT1-NOT: assume
37+
// OPT2-NEXT: %[[POS:.+]] = icmp sgt i64 %[[D]], -1
38+
// OPT2-NEXT: tail call void @llvm.assume(i1 %[[POS]])
39+
// CHECK-NEXT: udiv exact i64 %[[D]], 3
3440
// CHECK-NEXT: ret i64
3541
ptr_offset_from_unsigned(a, b)
3642
}

tests/codegen/slice-iter-len-eq-zero.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,21 @@
44
type Demo = [u8; 3];
55

66
// CHECK-LABEL: @slice_iter_len_eq_zero
7+
// CHECK-SAME: ptr noundef nonnull %0
8+
// CHECK-SAME: ptr noundef %1
79
#[no_mangle]
810
pub fn slice_iter_len_eq_zero(y: std::slice::Iter<'_, Demo>) -> bool {
9-
// CHECK-NOT: sub
10-
// CHECK: %[[RET:.+]] = icmp eq ptr {{%1|%0}}, {{%1|%0}}
11+
// There's no `nonnull` in the signature, so we end up with an assume.
12+
// CHECK: %[[NNULL:.+]] = icmp ne ptr %1, null
13+
// CHECK: tail call void @llvm.assume(i1 %[[NNULL]])
14+
15+
// CHECK-DAG: %[[E:.+]] = ptrtoint ptr %1 to i
16+
// CHECK-DAG: %[[B:.+]] = ptrtoint ptr %0 to i
17+
// CHECK: %[[D:.+]] = sub nuw {{.+}} %[[E]], %[[B]]
18+
// CHECK: %[[NNEG:.+]] = icmp sgt {{.+}} %[[D]], -1
19+
// CHECK: tail call void @llvm.assume(i1 %[[NNEG]])
20+
21+
// CHECK: %[[RET:.+]] = icmp eq ptr {{%1, %0|%0, %1}}
1122
// CHECK: ret i1 %[[RET]]
1223
y.len() == 0
1324
}

tests/codegen/vec-in-place.rs

+27
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ pub struct Baz {
3838
pub fn vec_iterator_cast_primitive(vec: Vec<i8>) -> Vec<u8> {
3939
// CHECK-NOT: loop
4040
// CHECK-NOT: call
41+
42+
// CHECK: %[[LEN_IN_ISIZE:.+]] = icmp sgt i{{.+}}, -1
43+
// CHECK-NEXT: call void @llvm.assume(i1 %[[LEN_IN_ISIZE]])
44+
45+
// CHECK-NOT: loop
46+
// CHECK-NOT: call
47+
4148
vec.into_iter().map(|e| e as u8).collect()
4249
}
4350

@@ -46,6 +53,13 @@ pub fn vec_iterator_cast_primitive(vec: Vec<i8>) -> Vec<u8> {
4653
pub fn vec_iterator_cast_wrapper(vec: Vec<u8>) -> Vec<Wrapper<u8>> {
4754
// CHECK-NOT: loop
4855
// CHECK-NOT: call
56+
57+
// CHECK: %[[LEN_IN_ISIZE:.+]] = icmp sgt i{{.+}}, -1
58+
// CHECK-NEXT: call void @llvm.assume(i1 %[[LEN_IN_ISIZE]])
59+
60+
// CHECK-NOT: loop
61+
// CHECK-NOT: call
62+
4963
vec.into_iter().map(|e| Wrapper(e)).collect()
5064
}
5165

@@ -54,6 +68,13 @@ pub fn vec_iterator_cast_wrapper(vec: Vec<u8>) -> Vec<Wrapper<u8>> {
5468
pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> {
5569
// CHECK-NOT: loop
5670
// CHECK-NOT: call
71+
72+
// CHECK: %[[LEN_IN_ISIZE:.+]] = icmp sgt i{{.+}}, -1
73+
// CHECK-NEXT: call void @llvm.assume(i1 %[[LEN_IN_ISIZE]])
74+
75+
// CHECK-NOT: loop
76+
// CHECK-NOT: call
77+
5778
vec.into_iter().map(|e| e.0).collect()
5879
}
5980

@@ -90,3 +111,9 @@ pub fn vec_iterator_cast_deaggregate_fold(vec: Vec<Baz>) -> Vec<[u64; 4]> {
90111
// correct.
91112
vec.into_iter().map(|e| unsafe { std::mem::transmute(e) }).collect()
92113
}
114+
115+
// Make sure that the `NOT`s above don't trigger on the `nocallback` attribute further down.
116+
117+
// CHECK-LABEL: @last_definition
118+
#[no_mangle]
119+
pub fn last_definition() {}

0 commit comments

Comments
 (0)