-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
split_at fails to optimize bounds check #74938
Comments
In fact, this has nothing to do with slices: https://rust.godbolt.org/z/7oG6qE change from 3 to 4 (or other powers of 2) to see everything optimized away. Edit: reduced it some more: https://rust.godbolt.org/z/bEoj8x |
There are no optimization in GCC and Clang either: https://godbolt.org/z/M88ndP #include <stddef.h>
const size_t N = 3;
int foo(size_t len) {
size_t newlen = (len / N) * N;
return newlen <= len;
} Should |
So this is more as LLVM part: @rustbot modify labels: +A-LLVM +A-mir-opt |
What's interesting is that the power of 2 optimization is done by rustc somewhere - the same MIR is translated to radically different LLVM-IR when N is 2^n. Trying to find where this happens without knowledge of the compiler is rather difficult, though :) |
Clang could do that, change |
https://github.com/llvm/llvm-project/blob/63d3aeb529a7b0fb95c2092ca38ad21c1f5cfd74/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp#L311 needs to set a nuw flag. Then it will probably fold. |
Cool. So if I understand correctly, the following is happening: The bounds check boils down to basically And for some reason, LLVM can't seem to deduce that this comparison will always be false. Adding this as an assumption will make the check be optimized: |
Ignore me, I missed that the assume caused the IR to have However, it does optimize if a LLVM doesn't know about this optimization but it is apparently valid (thanks John Regehr) so I'll submit a patch to LLVM. |
I've submitted a patch to LLVM adding this optimization: https://reviews.llvm.org/D85092 |
Does anyone want to file a bug against GCC? I registered an account two days ago but they hasn't accepted my account yet. |
This revision adds the following peephole optimization and it's negation: %a = urem i64 %x, %y %b = icmp ule i64 %a, %x ====> %b = true With John Regehr's help this optimization was checked with Alive2 which suggests it should be valid. This pattern occurs in the bound checks of Rust code, the program const N: usize = 3; const T = u8; pub fn split_mutiple(slice: &[T]) -> (&[T], &[T]) { let len = slice.len() / N; slice.split_at(len * N) } the method call slice.split_at will check that len * N is within the bounds of slice, this bounds check is after some transformations turned into the urem seen above and then LLVM fails to optimize it any further. Adding this optimization would cause this bounds check to be fully optimized away. ref: rust-lang/rust#74938 Differential Revision: https://reviews.llvm.org/D85092
This revision adds the following peephole optimization and it's negation: %a = urem i64 %x, %y %b = icmp ule i64 %a, %x ====> %b = true With John Regehr's help this optimization was checked with Alive2 which suggests it should be valid. This pattern occurs in the bound checks of Rust code, the program const N: usize = 3; const T = u8; pub fn split_mutiple(slice: &[T]) -> (&[T], &[T]) { let len = slice.len() / N; slice.split_at(len * N) } the method call slice.split_at will check that len * N is within the bounds of slice, this bounds check is after some transformations turned into the urem seen above and then LLVM fails to optimize it any further. Adding this optimization would cause this bounds check to be fully optimized away. ref: rust-lang/rust#74938 Differential Revision: https://reviews.llvm.org/D85092
^ as you can see above this optimization has landed in LLVM master, so whenever LLVM is bumped we should get this for free. |
Does it land with LLVM 11? |
it's not in RC-1 which was released a week ago, I don't know if they will include it in a future release candidate or not. |
This will be solved in LLVM 12 as my commit missed the cutoff (it was before the issue was even filed) |
I filed the gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97529 |
I confirmed that with #81451 the bounds check is removed. |
This revision adds the following peephole optimization and it's negation: %a = urem i64 %x, %y %b = icmp ule i64 %a, %x ====> %b = true With John Regehr's help this optimization was checked with Alive2 which suggests it should be valid. This pattern occurs in the bound checks of Rust code, the program const N: usize = 3; const T = u8; pub fn split_mutiple(slice: &[T]) -> (&[T], &[T]) { let len = slice.len() / N; slice.split_at(len * N) } the method call slice.split_at will check that len * N is within the bounds of slice, this bounds check is after some transformations turned into the urem seen above and then LLVM fails to optimize it any further. Adding this optimization would cause this bounds check to be fully optimized away. ref: rust-lang/rust#74938 Differential Revision: https://reviews.llvm.org/D85092
Can we close this issue? |
@lcnr could you test again and close this issue? |
this has been fixed, would be nice to add a codegen test for this, asserting that |
This revision adds the following peephole optimization and it's negation: %a = urem i64 %x, %y %b = icmp ule i64 %a, %x ====> %b = true With John Regehr's help this optimization was checked with Alive2 which suggests it should be valid. This pattern occurs in the bound checks of Rust code, the program const N: usize = 3; const T = u8; pub fn split_mutiple(slice: &[T]) -> (&[T], &[T]) { let len = slice.len() / N; slice.split_at(len * N) } the method call slice.split_at will check that len * N is within the bounds of slice, this bounds check is after some transformations turned into the urem seen above and then LLVM fails to optimize it any further. Adding this optimization would cause this bounds check to be fully optimized away. ref: rust-lang/rust#74938 Differential Revision: https://reviews.llvm.org/D85092
Add codegen tests for E-needs-test close rust-lang#36010 close rust-lang#68667 close rust-lang#74938 close rust-lang#83585 close rust-lang#93036 close rust-lang#109328 close rust-lang#110797 close rust-lang#111508 close rust-lang#112509 close rust-lang#113757 close rust-lang#120440 close rust-lang#118392 close rust-lang#71096 r? nikic
Add codegen tests for E-needs-test close rust-lang#36010 close rust-lang#68667 close rust-lang#74938 close rust-lang#83585 close rust-lang#93036 close rust-lang#109328 close rust-lang#110797 close rust-lang#111508 close rust-lang#112509 close rust-lang#113757 close rust-lang#120440 close rust-lang#118392 close rust-lang#71096 r? nikic
Add codegen tests for E-needs-test close rust-lang#36010 close rust-lang#68667 close rust-lang#74938 close rust-lang#83585 close rust-lang#93036 close rust-lang#109328 close rust-lang#110797 close rust-lang#111508 close rust-lang#112509 close rust-lang#113757 close rust-lang#120440 close rust-lang#118392 close rust-lang#71096 r? nikic
Add codegen tests for E-needs-test close rust-lang#36010 close rust-lang#68667 close rust-lang#74938 close rust-lang#83585 close rust-lang#93036 close rust-lang#109328 close rust-lang#110797 close rust-lang#111508 close rust-lang#112509 close rust-lang#113757 close rust-lang#120440 close rust-lang#118392 close rust-lang#71096 r? nikic
https://rust.godbolt.org/z/E1PnPj
results in the following assembly
When using
const N: usize = 4
the check is correctly optimized away:This happens both on all stable versions I have tested and on the most recent nightly.
The text was updated successfully, but these errors were encountered: