| Issue |
175282
|
| Summary |
Simplify `x != 0 ? WIDTH - ctlz(x) : 0` to just `WIDTH - ctlz(x)`
|
| Labels |
new issue
|
| Assignees |
|
| Reporter |
scottmcm
|
This was originally raised for rust 3½ years ago in https://github.com/rust-lang/rust/issues/100422#issuecomment-1231555770, and appears that it's still not optimized today. Notably, this pattern is meaningful because <https://doc.rust-lang.org/std/primitive.u32.html#method.checked_ilog2> exists in rust, and people are likely to think about it like this, since `ilog2` is a better API in the sense that (unlike `leading_zeros`) it's not bitwidth-dependent. (See below for rust repros; the primary ones for this issue are the LLVM ones.)
Today, opt doesn't further optimize this code: <https://llvm.godbolt.org/z/chTMEzfnT>
```llvm
define noundef range(i32 0, 33) i32 @src(i32 noundef %some_u32) unnamed_addr {
start:
%.not = icmp eq i32 %some_u32, 0
%0 = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %some_u32, i1 true)
%_0.i.i = sub nuw nsw i32 32, %0
%phi.call = select i1 %.not, i32 0, i32 %_0.i.i
ret i32 %phi.call
}
```
But it should <https://alive2.llvm.org/ce/z/mB9H8_> optimize to
```llvm
define noundef range(i32 0, 33) i32 @tgt(i32 noundef %some_u32) unnamed_addr {
start:
%0 = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %some_u32, i1 false)
%_0.i.i = sub nuw nsw i32 32, %0
ret i32 %_0.i.i
}
```
That way it can just be `lzcnt`+`mov`+`sub`, rather than that *plus* `test`+`cmov`.
---
As of `rustc 1.94.0-nightly (31cd367b9 2026-01-08)` with `LLVM version: 21.1.8`, both of these have the issue:
```rust
#[unsafe(no_mangle)]
pub fn demo1(some_u32: u32) -> u32 {
some_u32.checked_ilog2().map_or(0, |n| n + 1)
}
#[unsafe(no_mangle)]
pub fn demo2(some_u32: u32) -> u32 {
(some_u32.checked_ilog2().unwrap_or(u32::MAX) as u32).wrapping_add(1)
}
```
<https://rust.godbolt.org/z/PnT1nKcqd>
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs