[llvm] [InstCombine] Div ceil optimizations (PR #190175)

Takashi Idobe via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 11 13:27:04 PDT 2026


================
@@ -10361,6 +10361,10 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned,
         SI->getFalseValue(), ForSigned, UseInstrInfo, AC, CtxI, DT, Depth + 1);
     CR = CRTrue.unionWith(CRFalse);
     CR = CR.intersectWith(getRangeForSelectPattern(*SI, IIQ));
+  } else if (auto *TI = dyn_cast<TruncInst>(V)) {
+    ConstantRange SrcCR = computeConstantRange(
+        TI->getOperand(0), ForSigned, UseInstrInfo, AC, CtxI, DT, Depth + 1);
+    CR = SrcCR.truncate(BitWidth, TI->getNoWrapKind());
----------------
Takashiidobe wrote:

Good catch, I forgot to add this as a test case.

The case this is required for is the rust code case:

```rust
#[unsafe(no_mangle)]
pub fn div_ceil_with_range(x: u32) -> u32 {
    x.count_zeros().div_ceil(7)
}
```

Which turns into this:

```llvm
define noundef range(i32 0, 6) i32 @div_ceil_with_range(i32 noundef %x) unnamed_addr {
start:
  %self = xor i32 %x, -1
  %0 = tail call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 %self)
  %d.lhs.trunc = trunc nuw nsw i32 %0 to i8
  %d3 = udiv i8 %d.lhs.trunc, 7
  %d.zext = zext nneg i8 %d3 to i32
  %r4 = urem i8 %d.lhs.trunc, 7
  %_6.not = icmp ne i8 %r4, 0
  %1 = zext i1 %_6.not to i32
  %_0.sroa.0.0 = add nuw nsw i32 %1, %d.zext
  ret i32 %_0.sroa.0.0
}
```

I added a test case for this which gets optimized. Without the trunc change it doesn't optimize.

```llvm
; Trunc form: X comes from trunc nuw of an i32 with range info.
define i32 @divceil_trunc_nuw_range(i32 range(i32 0, 33) %x_wide) {
  %x = trunc nuw i32 %x_wide to i8
  %q = udiv i8 %x, 7
  %r = urem i8 %x, 7
  %cond = icmp ne i8 %r, 0
  %q_ext = zext i8 %q to i32
  %round = zext i1 %cond to i32
  %result = add i32 %q_ext, %round
  ret i32 %result
}
```

https://github.com/llvm/llvm-project/pull/190175


More information about the llvm-commits mailing list