[llvm] a4cad33 - [KnownBits] Use X < Y identity and umin(MaxX, MaxY - 1) bound in urem (#210262)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 17 03:49:36 PDT 2026
Author: Joel Walker
Date: 2026-07-17T12:49:32+02:00
New Revision: a4cad33430cf5d2d03eb91ce5e41ed1b9ef7cc4d
URL: https://github.com/llvm/llvm-project/commit/a4cad33430cf5d2d03eb91ce5e41ed1b9ef7cc4d
DIFF: https://github.com/llvm/llvm-project/commit/a4cad33430cf5d2d03eb91ce5e41ed1b9ef7cc4d.diff
LOG: [KnownBits] Use X < Y identity and umin(MaxX, MaxY - 1) bound in urem (#210262)
Tightens `KnownBits::urem` with the two facts `ConstantRange::urem`
already uses:
- `L % R == L` when `MaxL < MinR`: return the LHS bits unchanged. This
can propagate known one bits, which the current leading-zeros-only
reasoning never produces.
- `L % R <= umin(MaxL, MaxR - 1)`, since a zero divisor is UB: keep that
bound's leading zeros. This is one bit stronger than the per-operand
leading-zero count when `MaxR` is a power of two, and together with
`remGetLowBits` it subsumes the power-of-two-constant special case,
which is removed.
Soundness is covered by the existing exhaustive unit test (widths 1 and
4; additionally brute-forced at width 5). Across all 6480 valid width-4
KnownBits pairs, non-optimal results drop from 3332 to 2090. The
residual is number-theoretic (e.g. the exact value of `10 % 3`), out of
reach of leading-bit/range reasoning, so `CheckOptimality` stays off for
urem, as for udiv (#209360).
Assisted by Claude (Anthropic).
Added:
Modified:
llvm/lib/Support/KnownBits.cpp
llvm/test/CodeGen/AArch64/GlobalISel/knownbits-urem.mir
llvm/test/Transforms/InstCombine/known-bits.ll
Removed:
################################################################################
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 9678225dff5f5..c3acd2936e2ee 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -1332,19 +1332,21 @@ KnownBits KnownBits::remGetLowBits(const KnownBits &LHS, const KnownBits &RHS) {
}
KnownBits KnownBits::urem(const KnownBits &LHS, const KnownBits &RHS) {
+ // L % R for L < R is L. (A zero divisor is UB, and if MinR is zero this
+ // condition cannot hold anyway.)
+ if (LHS.getMaxValue().ult(RHS.getMinValue()))
+ return LHS;
+
KnownBits Known = remGetLowBits(LHS, RHS);
- if (RHS.isConstant() && RHS.getConstant().isPowerOf2()) {
- // NB: Low bits set in `remGetLowBits`.
- APInt HighBits = ~(RHS.getConstant() - 1);
- Known.Zero |= std::move(HighBits);
- return Known;
- }
- // Since the result is less than or equal to either operand, any leading
- // zero bits in either operand must also exist in the result.
- uint32_t Leaders =
- std::max(LHS.countMinLeadingZeros(), RHS.countMinLeadingZeros());
- Known.Zero.setHighBits(Leaders);
+ // L % R is at most L and, since a zero divisor is UB, strictly less than
+ // R, so the result has at least as many leading zeros as
+ // umin(MaxL, MaxR - 1), as in ConstantRange::urem. For a power-of-two
+ // constant R this computes the exact L & (R - 1): remGetLowBits preserved
+ // the low bits and this clears the bits from log2(R) up. If R is known to
+ // be zero, MaxR - 1 wraps around, which is harmless: every input is UB.
+ APInt MaxRes = APIntOps::umin(LHS.getMaxValue(), RHS.getMaxValue() - 1);
+ Known.Zero.setHighBits(MaxRes.countLeadingZeros());
return Known;
}
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-urem.mir b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-urem.mir
index 6aa1415d30f45..7dd491fca289f 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-urem.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-urem.mir
@@ -99,3 +99,57 @@ body: |
$w0 = COPY %2(i32)
RET_ReallyLR implicit $w0
...
+
+# The divisor is 0 (excluded: UB) or 8, so the result is at most 7: the
+# leading-zero count comes from MaxRHS - 1, not MaxRHS.
+# CHECK-LABEL: name: @urem_masked_divisor
+# CHECK-NEXT: %0:_ KnownBits:???????????????????????????????? SignBits:1
+# CHECK-NEXT: %1:_ KnownBits:???????????????????????????????? SignBits:1
+# CHECK-NEXT: %2:_ KnownBits:00000000000000000000000000001000 SignBits:28
+# CHECK-NEXT: %3:_ KnownBits:0000000000000000000000000000?000 SignBits:28
+# CHECK-NEXT: %4:_ KnownBits:00000000000000000000000000000??? SignBits:29
+
+name: urem_masked_divisor
+body: |
+ bb.1:
+ liveins: $w0, $w1
+
+ %0:_(i32) = COPY $w0
+ %1:_(i32) = COPY $w1
+ %2:_(i32) = G_CONSTANT i32 8
+ %3:_(i32) = G_AND %1, %2
+ %4:_(i32) = G_UREM %0, %3
+ $w0 = COPY %4(i32)
+ RET_ReallyLR implicit $w0
+...
+
+# The LHS is 4 or 5 and the divisor is at least 8, so the result is the LHS,
+# including its known one bit.
+# CHECK-LABEL: name: @urem_lhs_known_smaller
+# CHECK-NEXT: %0:_ KnownBits:???????????????????????????????? SignBits:1
+# CHECK-NEXT: %1:_ KnownBits:???????????????????????????????? SignBits:1
+# CHECK-NEXT: %2:_ KnownBits:00000000000000000000000000000001 SignBits:31
+# CHECK-NEXT: %3:_ KnownBits:0000000000000000000000000000000? SignBits:31
+# CHECK-NEXT: %4:_ KnownBits:00000000000000000000000000000100 SignBits:29
+# CHECK-NEXT: %5:_ KnownBits:0000000000000000000000000000010? SignBits:29
+# CHECK-NEXT: %6:_ KnownBits:00000000000000000000000000001000 SignBits:28
+# CHECK-NEXT: %7:_ KnownBits:????????????????????????????1??? SignBits:1
+# CHECK-NEXT: %8:_ KnownBits:0000000000000000000000000000010? SignBits:29
+
+name: urem_lhs_known_smaller
+body: |
+ bb.1:
+ liveins: $w0, $w1
+
+ %0:_(i32) = COPY $w0
+ %1:_(i32) = COPY $w1
+ %2:_(i32) = G_CONSTANT i32 1
+ %3:_(i32) = G_AND %0, %2
+ %4:_(i32) = G_CONSTANT i32 4
+ %5:_(i32) = G_OR %3, %4
+ %6:_(i32) = G_CONSTANT i32 8
+ %7:_(i32) = G_OR %1, %6
+ %8:_(i32) = G_UREM %5, %7
+ $w0 = COPY %8(i32)
+ RET_ReallyLR implicit $w0
+...
diff --git a/llvm/test/Transforms/InstCombine/known-bits.ll b/llvm/test/Transforms/InstCombine/known-bits.ll
index 1981445f3bea6..d6b6232e92a49 100644
--- a/llvm/test/Transforms/InstCombine/known-bits.ll
+++ b/llvm/test/Transforms/InstCombine/known-bits.ll
@@ -2467,3 +2467,27 @@ define i1 @udiv_high_known_ones_ugt_63(i8 %x, i8 %y) {
%r = icmp ugt i8 %q, 63
ret i1 %r
}
+
+define i1 @urem_smaller_lhs_bit_identity(i8 %x, i8 %y) {
+; CHECK-LABEL: @urem_smaller_lhs_bit_identity(
+; CHECK-NEXT: ret i1 true
+;
+ %l = and i8 %x, 5
+ %d = or i8 %y, 8
+ %r = urem i8 %l, %d
+ %t = and i8 %r, 2
+ %c = icmp eq i8 %t, 0
+ ret i1 %c
+}
+
+define i1 @urem_smaller_lhs_known_ones_ugt_3(i8 %x, i8 %y) {
+; CHECK-LABEL: @urem_smaller_lhs_known_ones_ugt_3(
+; CHECK-NEXT: ret i1 true
+;
+ %l0 = and i8 %x, 1
+ %l = or i8 %l0, 4
+ %d = or i8 %y, 8
+ %r = urem i8 %l, %d
+ %c = icmp ugt i8 %r, 3
+ ret i1 %c
+}
More information about the llvm-commits
mailing list