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

Reply via email to