Skip to content

Commit 91a7e3f

Browse files
committed
Add assumes to slice length calls
Since `.len()` on slices is safe, let's see how this impacts things vs what we could do with 121965 and LLVM 19
1 parent fd27e87 commit 91a7e3f

8 files changed

+76
-18
lines changed

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
753753
}
754754
// use common size calculation for non zero-sized types
755755
let cg_value = self.codegen_place(bx, place.as_ref());
756-
cg_value.len(bx.cx())
756+
let length = cg_value.len(bx.cx());
757+
758+
if bx.sess().opts.optimize != OptLevel::No {
759+
let unit_bytes = cg_value.layout.field(bx, 0).size.bytes();
760+
if unit_bytes > 0 {
761+
let byte_size = if unit_bytes == 1 {
762+
length
763+
} else {
764+
bx.unchecked_umul(length, bx.const_usize(unit_bytes))
765+
};
766+
let limit = bx.icmp(IntPredicate::IntSGE, byte_size, bx.const_usize(0));
767+
bx.assume(limit);
768+
}
769+
}
770+
771+
length
757772
}
758773

759774
/// Codegen an `Rvalue::AddressOf` or `Rvalue::Ref`

tests/codegen/issues/issue-98294-get-mut-copy-from-slice-opt.rs

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
pub fn test(a: &mut [u8], offset: usize, bytes: &[u8]) {
99
// CHECK-LABEL: @test(
1010
// CHECK-NOT: call
11+
// CHECK: call void @llvm.assume
12+
// CHECK-NOT: call
13+
// CHECK: call void @llvm.assume
14+
// CHECK-NOT: call
1115
// CHECK: call void @llvm.memcpy
1216
// CHECK-NOT: call
1317
// CHECK: }

tests/codegen/slice-as_chunks.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,23 @@
77
// CHECK-LABEL: @chunks4
88
#[no_mangle]
99
pub fn chunks4(x: &[u8]) -> &[[u8; 4]] {
10-
// CHECK-NEXT: start:
11-
// CHECK-NEXT: lshr i64 %x.1, 2
1210
// CHECK-NOT: shl
1311
// CHECK-NOT: mul
1412
// CHECK-NOT: udiv
1513
// CHECK-NOT: urem
16-
// CHECK: ret
14+
// CHECK: %[[NEWLEN:.+]] = lshr i64 %x.1, 2
15+
// CHECK: %[[A:.+]] = insertvalue { ptr, i64 } poison, ptr %x.0, 0
16+
// CHECK: %[[B:.+]] = insertvalue { ptr, i64 } %[[A]], i64 %[[NEWLEN]], 1
17+
// CHECK: ret { ptr, i64 } %[[B]]
1718
x.as_chunks().0
1819
}
1920

2021
// CHECK-LABEL: @chunks4_with_remainder
2122
#[no_mangle]
2223
pub fn chunks4_with_remainder(x: &[u8]) -> (&[[u8; 4]], &[u8]) {
23-
// CHECK-DAG: and i64 %x.1, -4
24+
// CHECK-DAG: and i64 %x.1, [[#0x7FFFFFFFFFFFFFFC]]
2425
// CHECK-DAG: and i64 %x.1, 3
25-
// CHECK-DAG: lshr
26+
// CHECK-DAG: lshr i64 %x.1, 2
2627
// CHECK-NOT: mul
2728
// CHECK-NOT: udiv
2829
// CHECK-NOT: urem

tests/codegen/slice-iter-fold.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
#![crate_type = "lib"]
33

44
// CHECK-LABEL: @slice_fold_to_last
5+
// CHECK-SAME: %slice.0, [[USIZE:i[0-9]+]] noundef %slice.1)
56
#[no_mangle]
67
pub fn slice_fold_to_last(slice: &[i32]) -> Option<&i32> {
7-
// CHECK-NOT: loop
8-
// CHECK-NOT: br
9-
// CHECK-NOT: call
10-
// CHECK: ret
8+
// CHECK: %[[END:.+]] = getelementptr inbounds i32, ptr %slice.0, [[USIZE]] %slice.1
9+
// CHECK: %[[EMPTY:.+]] = icmp eq [[USIZE]] %slice.1, 0
10+
// CHECK: %[[LAST:.+]] = getelementptr i32, ptr %[[END]], i64 -1
11+
// CHECK: %[[R:.+]] = select i1 %[[EMPTY]], ptr null, ptr %[[LAST]]
12+
// CHECK: ret ptr %[[R]]
1113
slice.iter().fold(None, |_, i| Some(i))
1214
}

tests/codegen/slice-iter-nonnull.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,13 @@ pub fn slice_iter_next_back<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'
5252
// CHECK-LABEL: @slice_iter_new
5353
// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef %slice.1)
5454
#[no_mangle]
55-
pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> {
55+
pub fn slice_iter_new(slice: &[u8]) -> std::slice::Iter<'_, u8> {
5656
// CHECK-NOT: slice
57-
// CHECK: %[[END:.+]] = getelementptr inbounds i32{{.+}} %slice.0{{.+}} %slice.1
57+
// CHECK: %[[NONNEG:.+]] = icmp sgt i64 %slice.1, -1
58+
// CHECK-NOT: slice
59+
// CHECK: call void @llvm.assume(i1 %[[NONNEG]])
60+
// CHECK-NOT: slice
61+
// CHECK: %[[END:.+]] = getelementptr inbounds i8{{.+}} %slice.0{{.+}} %slice.1
5862
// CHECK-NOT: slice
5963
// CHECK: insertvalue {{.+}} ptr %slice.0, 0
6064
// CHECK-NOT: slice
@@ -67,9 +71,13 @@ pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> {
6771
// CHECK-LABEL: @slice_iter_mut_new
6872
// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef %slice.1)
6973
#[no_mangle]
70-
pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> {
74+
pub fn slice_iter_mut_new(slice: &mut [u8]) -> std::slice::IterMut<'_, u8> {
75+
// CHECK-NOT: slice
76+
// CHECK: %[[NONNEG:.+]] = icmp sgt i64 %slice.1, -1
77+
// CHECK-NOT: slice
78+
// CHECK: call void @llvm.assume(i1 %[[NONNEG]])
7179
// CHECK-NOT: slice
72-
// CHECK: %[[END:.+]] = getelementptr inbounds i32{{.+}} %slice.0{{.+}} %slice.1
80+
// CHECK: %[[END:.+]] = getelementptr inbounds i8{{.+}} %slice.0{{.+}} %slice.1
7381
// CHECK-NOT: slice
7482
// CHECK: insertvalue {{.+}} ptr %slice.0, 0
7583
// CHECK-NOT: slice

tests/codegen/slice-length-limits.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//@ compile-flags: -O
2+
//@ min-llvm-version: 18.0
3+
4+
#![crate_type = "lib"]
5+
6+
// Confirm that the `assume` calls from the length allows LLVM to know that some
7+
// math on the indices can be done without overflow risk.
8+
9+
// CHECK-LABEL: @byte_slice_length_demo
10+
#[no_mangle]
11+
pub unsafe fn byte_slice_length_demo(x: &[u8]) {
12+
// CHECK: %[[Y:.+]] = phi [[USIZE:i[0-9]+]]
13+
// CHECK-SAME: [ 0, %start ]
14+
// CHECK: %[[PLUS_ONE:.+]] = add nuw nsw [[USIZE]] %[[Y]], 1
15+
// CHECK: call void @do_something([[USIZE]] noundef %[[PLUS_ONE]])
16+
// CHECK: %[[TIMES_TWO:.+]] = shl nuw [[USIZE]] %[[Y]], 1
17+
// CHECK: call void @do_something([[USIZE]] noundef %[[TIMES_TWO]])
18+
for y in 0..x.len() {
19+
do_something(y + 1);
20+
do_something(y * 2);
21+
}
22+
}
23+
24+
extern {
25+
fn do_something(x: usize);
26+
}

tests/codegen/slice-ref-equality.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ pub fn is_zero_array(data: &[u8; 4]) -> bool {
4747
// CHECK-SAME: [[USIZE]] noundef %3
4848
#[no_mangle]
4949
fn eq_slice_of_nested_u8(x: &[[u8; 3]], y: &[[u8; 3]]) -> bool {
50+
// CHECK: %[[BYTES:.+]] = mul nuw [[USIZE]] %1, 3
5051
// CHECK: icmp eq [[USIZE]] %1, %3
51-
// CHECK: %[[BYTES:.+]] = mul nsw [[USIZE]] %1, 3
5252
// CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
5353
// CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
5454
x == y
@@ -59,8 +59,8 @@ fn eq_slice_of_nested_u8(x: &[[u8; 3]], y: &[[u8; 3]]) -> bool {
5959
// CHECK-SAME: [[USIZE]] noundef %3
6060
#[no_mangle]
6161
fn eq_slice_of_i32(x: &[i32], y: &[i32]) -> bool {
62+
// CHECK: %[[BYTES:.+]] = shl nuw [[USIZE]] %1, 2
6263
// CHECK: icmp eq [[USIZE]] %1, %3
63-
// CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2
6464
// CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
6565
// CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
6666
x == y
@@ -71,8 +71,8 @@ fn eq_slice_of_i32(x: &[i32], y: &[i32]) -> bool {
7171
// CHECK-SAME: [[USIZE]] noundef %3
7272
#[no_mangle]
7373
fn eq_slice_of_nonzero(x: &[NonZero<i32>], y: &[NonZero<i32>]) -> bool {
74+
// CHECK: %[[BYTES:.+]] = shl nuw [[USIZE]] %1, 2
7475
// CHECK: icmp eq [[USIZE]] %1, %3
75-
// CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2
7676
// CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
7777
// CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
7878
x == y
@@ -83,8 +83,8 @@ fn eq_slice_of_nonzero(x: &[NonZero<i32>], y: &[NonZero<i32>]) -> bool {
8383
// CHECK-SAME: [[USIZE]] noundef %3
8484
#[no_mangle]
8585
fn eq_slice_of_option_of_nonzero(x: &[Option<NonZero<i16>>], y: &[Option<NonZero<i16>>]) -> bool {
86+
// CHECK: %[[BYTES:.+]] = shl nuw [[USIZE]] %1, 1
8687
// CHECK: icmp eq [[USIZE]] %1, %3
87-
// CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 1
8888
// CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
8989
// CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
9090
x == y

tests/codegen/vecdeque-drain.rs

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ pub fn truncate(v: &mut VecDeque<i32>, n: usize) {
4343
// CHECK-NOT: call
4444
// CHECK: br
4545
// CHECK-NOT: call
46+
// CHECK: call void @llvm.assume
47+
// CHECK-NOT: call
4648
// CHECK: br
4749
// CHECK-NOT: call
4850
// CHECK: br

0 commit comments

Comments
 (0)