[llvm] optimize isDivZero analysis in InstructionSimplify (PR #190710)
Takashi Idobe via llvm-commits
llvm-commits at lists.llvm.org
Sat Apr 11 16:38:07 PDT 2026
https://github.com/Takashiidobe updated https://github.com/llvm/llvm-project/pull/190710
>From 31a91ef820f79015182efb4fdc865e55706b9e6a Mon Sep 17 00:00:00 2001
From: Takashiidobe <idobetakashi at gmail.com>
Date: Mon, 6 Apr 2026 19:24:53 -0400
Subject: [PATCH 1/2] optimize isDivZero analysis in InstructionSimplify
---
llvm/lib/Analysis/InstructionSimplify.cpp | 36 +++++++++----
llvm/test/Transforms/InstSimplify/div.ll | 61 ++++++++++++++++++++---
llvm/test/Transforms/InstSimplify/rem.ll | 6 +--
3 files changed, 81 insertions(+), 22 deletions(-)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 12c3edc0ced30..17a6bd533d042 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -989,13 +989,24 @@ static bool isDivZero(Value *X, Value *Y, const SimplifyQuery &Q,
if (match(X, m_SRem(m_Value(), m_Specific(Y))))
return true;
- // |X| / |Y| --> 0
- //
- // We require that 1 operand is a simple constant. That could be extended to
- // 2 variables if we computed the sign bit for each.
+ // Is the signed dividend's magnitude always less than the divisor's?
+ // Use range analysis for both operands. ConstantRange::abs() maps
+ // INT_MIN to itself as an unsigned value (a very large number), so the
+ // INT_MIN divisor case is handled correctly without a special case.
+ // This handles non-constant operands with range metadata or known bits.
+ ConstantRange XRange =
+ computeConstantRangeIncludingKnownBits(X, /*ForSigned=*/true, Q);
+ ConstantRange YRange =
+ computeConstantRangeIncludingKnownBits(Y, /*ForSigned=*/true, Q);
+ if (XRange.abs().icmp(CmpInst::ICMP_ULT, YRange.abs()))
+ return true;
+
+ // Fall back to the original constant-matching approach for structural
+ // cases (e.g., sext/zext vs. constant) that computeConstantRange does
+ // not yet handle.
//
- // Make sure that a constant is not the minimum signed value because taking
- // the abs() of that is undefined.
+ // Make sure that a constant is not the minimum signed value because
+ // taking the abs() of that is undefined.
Type *Ty = X->getType();
const APInt *C;
if (match(X, m_APInt(C)) && !C->isMinSignedValue()) {
@@ -1029,11 +1040,14 @@ static bool isDivZero(Value *X, Value *Y, const SimplifyQuery &Q,
// IsSigned == false.
- // Is the unsigned dividend known to be less than a constant divisor?
- // TODO: Convert this (and above) to range analysis
- // ("computeConstantRangeIncludingKnownBits")?
- const APInt *C;
- if (match(Y, m_APInt(C)) && computeKnownBits(X, Q).getMaxValue().ult(*C))
+ // Is the unsigned dividend always less than the divisor?
+ // Use range analysis for both operands so non-constant divisors with
+ // known bits or range metadata are also handled.
+ ConstantRange XRange =
+ computeConstantRangeIncludingKnownBits(X, /*ForSigned=*/false, Q);
+ ConstantRange YRange =
+ computeConstantRangeIncludingKnownBits(Y, /*ForSigned=*/false, Q);
+ if (XRange.icmp(CmpInst::ICMP_ULT, YRange))
return true;
// Try again for any divisor:
diff --git a/llvm/test/Transforms/InstSimplify/div.ll b/llvm/test/Transforms/InstSimplify/div.ll
index 6566fa0292a25..0ca4e1ae3b362 100644
--- a/llvm/test/Transforms/InstSimplify/div.ll
+++ b/llvm/test/Transforms/InstSimplify/div.ll
@@ -182,14 +182,9 @@ define i8 @not_udiv_dividend_known_smaller_than_constant_divisor2(i1 %b) {
ret i8 %r
}
-; This would require computing known bits on both x and y. Is it worth doing?
-
define i32 @udiv_dividend_known_smaller_than_divisor(i32 %x, i32 %y) {
; CHECK-LABEL: @udiv_dividend_known_smaller_than_divisor(
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 250
-; CHECK-NEXT: [[OR:%.*]] = or i32 [[Y:%.*]], 251
-; CHECK-NEXT: [[DIV:%.*]] = udiv i32 [[AND]], [[OR]]
-; CHECK-NEXT: ret i32 [[DIV]]
+; CHECK-NEXT: ret i32 0
;
%and = and i32 %x, 250
%or = or i32 %y, 251
@@ -668,3 +663,57 @@ define i8 @sdiv_mul_nuw(i8 %x) {
%b = sdiv i8 %a, 24
ret i8 %b
}
+
+; urem folds to dividend when dividend < divisor (both variable with known bits).
+define i32 @urem_dividend_known_smaller_than_divisor(i32 %x, i32 %y) {
+; CHECK-LABEL: @urem_dividend_known_smaller_than_divisor(
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 250
+; CHECK-NEXT: ret i32 [[AND]]
+;
+ %and = and i32 %x, 250
+ %or = or i32 %y, 251
+ %rem = urem i32 %and, %or
+ ret i32 %rem
+}
+
+; udiv folds to 0 when range metadata on both non-constant operands proves dividend < divisor.
+define i32 @udiv_both_nonconst_range_metadata() {
+; CHECK-LABEL: @udiv_both_nonconst_range_metadata(
+; CHECK-NEXT: [[DIVIDEND:%.*]] = call i32 @external(), !range [[RNG0]]
+; CHECK-NEXT: [[DIVISOR:%.*]] = call i32 @external(), !range [[RNG1:![0-9]+]]
+; CHECK-NEXT: ret i32 0
+;
+ %dividend = call i32 @external(), !range !0 ; [0, 3)
+ %divisor = call i32 @external(), !range !1 ; [3, 100)
+ %div = udiv i32 %dividend, %divisor
+ ret i32 %div
+}
+
+; sdiv folds to 0 when range metadata on both non-constant operands proves |dividend| < |divisor|.
+define i32 @sdiv_both_nonconst_range_metadata() {
+; CHECK-LABEL: @sdiv_both_nonconst_range_metadata(
+; CHECK-NEXT: [[DIVIDEND:%.*]] = call i32 @external(), !range [[RNG0]]
+; CHECK-NEXT: [[DIVISOR:%.*]] = call i32 @external(), !range [[RNG1]]
+; CHECK-NEXT: ret i32 0
+;
+ %dividend = call i32 @external(), !range !0 ; [0, 3)
+ %divisor = call i32 @external(), !range !1 ; [3, 100)
+ %div = sdiv i32 %dividend, %divisor
+ ret i32 %div
+}
+
+; Negative test: dividend can equal the minimum of the divisor range, so no fold.
+define i32 @not_udiv_both_nonconst_range_metadata() {
+; CHECK-LABEL: @not_udiv_both_nonconst_range_metadata(
+; CHECK-NEXT: [[DIVIDEND:%.*]] = call i32 @external(), !range [[RNG1]]
+; CHECK-NEXT: [[DIVISOR:%.*]] = call i32 @external(), !range [[RNG1]]
+; CHECK-NEXT: [[DIV:%.*]] = udiv i32 [[DIVIDEND]], [[DIVISOR]]
+; CHECK-NEXT: ret i32 [[DIV]]
+;
+ %dividend = call i32 @external(), !range !1 ; [3, 100) - can be >= divisor
+ %divisor = call i32 @external(), !range !1 ; [3, 100)
+ %div = udiv i32 %dividend, %divisor
+ ret i32 %div
+}
+
+!1 = !{i32 3, i32 100}
diff --git a/llvm/test/Transforms/InstSimplify/rem.ll b/llvm/test/Transforms/InstSimplify/rem.ll
index 4fb1d9167ab7b..3ac1e67302c2c 100644
--- a/llvm/test/Transforms/InstSimplify/rem.ll
+++ b/llvm/test/Transforms/InstSimplify/rem.ll
@@ -234,14 +234,10 @@ define i32 @not_urem_constant_dividend_known_smaller_than_divisor(i32 %x) {
ret i32 %r
}
-; This would require computing known bits on both x and y. Is it worth doing?
-
define i32 @urem_dividend_known_smaller_than_divisor(i32 %x, i32 %y) {
; CHECK-LABEL: @urem_dividend_known_smaller_than_divisor(
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 250
-; CHECK-NEXT: [[OR:%.*]] = or i32 [[Y:%.*]], 251
-; CHECK-NEXT: [[R:%.*]] = urem i32 [[AND]], [[OR]]
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-NEXT: ret i32 [[AND]]
;
%and = and i32 %x, 250
%or = or i32 %y, 251
>From fa03e24e9351068e4b0c6795fd95d4e37f57c852 Mon Sep 17 00:00:00 2001
From: Takashiidobe <idobetakashi at gmail.com>
Date: Sat, 11 Apr 2026 19:37:53 -0400
Subject: [PATCH 2/2] fix failing tests, don't propagate poison
---
llvm/lib/Analysis/InstructionSimplify.cpp | 6 ++++++
llvm/test/Transforms/SCCP/binaryops-constexprs.ll | 3 +--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 17a6bd533d042..47e58c511034e 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -984,6 +984,12 @@ static bool isDivZero(Value *X, Value *Y, const SimplifyQuery &Q,
if (!MaxRecurse--)
return false;
+ // Vector constants with poison cannot be simplified away.
+ Constant *CX, *CY;
+ if ((match(X, m_Constant(CX)) && CX->containsUndefOrPoisonElement()) ||
+ (match(Y, m_Constant(CY)) && CY->containsUndefOrPoisonElement()))
+ return false;
+
if (IsSigned) {
// (X srem Y) sdiv Y --> 0
if (match(X, m_SRem(m_Value(), m_Specific(Y))))
diff --git a/llvm/test/Transforms/SCCP/binaryops-constexprs.ll b/llvm/test/Transforms/SCCP/binaryops-constexprs.ll
index bf4a366f5c1f2..0db9103b626d1 100644
--- a/llvm/test/Transforms/SCCP/binaryops-constexprs.ll
+++ b/llvm/test/Transforms/SCCP/binaryops-constexprs.ll
@@ -111,8 +111,7 @@ define void @udiv_constexpr(i32 %a) {
; CHECK-NEXT: call void @use.i1(i1 [[FALSE_1]])
; CHECK-NEXT: [[COND_1:%.*]] = icmp eq i32 [[UDIV_2]], 10
; CHECK-NEXT: call void @use.i1(i1 [[COND_1]])
-; CHECK-NEXT: [[UDIV_3:%.*]] = udiv i32 ptrtoint (ptr inttoptr (i32 20 to ptr) to i32), ptrtoint (ptr inttoptr (i32 100 to ptr) to i32)
-; CHECK-NEXT: call void @use.i32(i32 [[UDIV_3]])
+; CHECK-NEXT: call void @use.i32(i32 0)
; CHECK-NEXT: ret void
;
entry:
More information about the llvm-commits
mailing list