-
Notifications
You must be signed in to change notification settings - Fork 13.7k
Description
I wrote a simple loop indexing into a slice, to test rustc's ability to optimize away bounds checks if it knows an index is in bounds. Even with this very simple test case, I can't seem to get rust to omit the bounds checks no matter what assert!
I add. (I know that I could trivially write this code using iterators instead, but I'm trying to figure out rust's ability to optimize here.)
Test case (edited since original posting to augment the assert!
further):
#[no_mangle]
fn f(slice: &[u64], start: usize, end: usize) -> u64 {
let mut total = 0;
assert!(start < end && start < slice.len() && end <= slice.len());
for i in start..end {
total += slice[i];
}
total
}
I put that into the compiler explorer, with -O
, and the resulting assembly looks like this:
f:
push rax
cmp rdx, rcx
jae .LBB5_8
cmp rsi, rdx
jbe .LBB5_8
cmp rsi, rcx
jb .LBB5_8
xor eax, eax
.LBB5_4:
cmp rdx, rsi
jae .LBB5_7
add rax, qword ptr [rdi + 8*rdx]
add rdx, 1
cmp rcx, rdx
jne .LBB5_4
pop rcx
ret
.LBB5_7:
lea rax, [rip + .L__unnamed_5]
mov rdi, rdx
mov rdx, rax
call qword ptr [rip + core::panicking::panic_bounds_check@GOTPCREL]
ud2
.LBB5_8:
call std::panicking::begin_panic
ud2
Based on the x86 calling convention, rdi
contains the slice base address, rsi
contains the slice length, rdx
contains start
, and rcx
contains end
.
So, the first three comparisons verify the assertion and jump to .LBB5_8
if it fails, to panic.
Then inside the loop, there's still another comparison of rdx
to rsi
, and a jump to .LBB5_7
to panic if out of bounds.
As far as I can tell, that's exactly the same comparison. Shouldn't rustc be able to optimize away that bounds check?
Things I've tested:
- I tried replacing the
assert!
with anif
andunreachable!
, or anif
andunsafe { std::hint::unreachable_unchecked() }
, but in both cases the loop still checked if the index was in bounds on each iteration. - I tried using
-Zmutable-noalias=yes
, which didn't help. - I tried various forms of the assertion condition.
Ideally, rustc should be able to optimize away the bounds check in the loop, based on the assertion. Even better would be if rustc could hoist the bounds check out of the loop even without the assertion, but that seems like a harder problem.