[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