[llvm] de9830f - [InstCombine] Treat sdiv as udiv in foldICmpDivConstant when both operands are non-negative (#188731)

via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 23 02:26:34 PDT 2026


Author: lijinpei-amd
Date: 2026-04-23T11:26:30+02:00
New Revision: de9830fe40bf0bcca05dccd9fb9bcd983eaea3bf

URL: https://github.com/llvm/llvm-project/commit/de9830fe40bf0bcca05dccd9fb9bcd983eaea3bf
DIFF: https://github.com/llvm/llvm-project/commit/de9830fe40bf0bcca05dccd9fb9bcd983eaea3bf.diff

LOG: [InstCombine] Treat sdiv as udiv in foldICmpDivConstant when both operands are non-negative (#188731)

When foldICmpDivConstant encounters a signedness mismatch between the
sdiv and the icmp (e.g. icmp ugt (sdiv X, 64), 2), it previously bailed
out. This patch allows it to proceed by checking whether the sdiv's
dividend is known non-negative (via assumes/known bits) and the divisor
is a positive constant. In that case, sdiv is equivalent to udiv, so we
set DivIsSigned=false and let the existing udiv folding handle the rest.

---------

Co-authored-by: Claude Opus 4.6 <noreply at anthropic.com>

Added: 
    llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 6f1ab31fe03b2..a53f825f43a80 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -2814,8 +2814,15 @@ Instruction *InstCombinerImpl::foldICmpDivConstant(ICmpInst &Cmp,
   // (x /u C2) <u C.  Simply casting the operands and result won't
   // work. :(  The if statement below tests that condition and bails
   // if it finds it.
-  if (!Cmp.isEquality() && DivIsSigned != Cmp.isSigned())
-    return nullptr;
+  // However, when the divisor is a positive constant and the dividend is
+  // known non-negative, sdiv is equivalent to udiv, so we can lower
+  // DivIsSigned and proceed through the unsigned path.
+  if (!Cmp.isEquality() && DivIsSigned != Cmp.isSigned()) {
+    if (!DivIsSigned || !C2->isStrictlyPositive() ||
+        !isKnownNonNegative(X, SQ.getWithInstruction(&Cmp)))
+      return nullptr;
+    DivIsSigned = false;
+  }
 
   // The ProdOV computation fails on divide by 0 and divide by -1. Cases with
   // INT_MIN will also fail if the divisor is 1. Although folds of all these

diff  --git a/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll b/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll
new file mode 100644
index 0000000000000..27c0ff485c5e1
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll
@@ -0,0 +1,209 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; Test that foldICmpDivConstant can treat sdiv as udiv when the dividend
+; is known non-negative (e.g. via a dominating condition), allowing the
+; fold to proceed despite a signed div / unsigned icmp mismatch.
+; Non-power-of-two divisors are used to prevent sdiv->lshr conversion.
+; The sdiv is placed in the entry block where x's sign is unknown, and
+; the icmp is in a block dominated by a condition that proves x >= 0.
+
+declare void @use(i32)
+
+; Positive test: x > 255 dominates the icmp, proving x non-negative.
+; sdiv(x, 65) = udiv(x, 65) when x >= 0, and udiv(x, 65) > 2 when x >= 195.
+; Since x > 255 >= 195, the result folds to true.
+define i1 @icmp_sdiv_domcond(i32 %x) {
+; CHECK-LABEL: @icmp_sdiv_domcond(
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[X:%.*]], 65
+; CHECK-NEXT:    call void @use(i32 [[DIV]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp sgt i32 [[X]], 255
+; CHECK-NEXT:    br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
+; CHECK:       if.true:
+; CHECK-NEXT:    ret i1 true
+; CHECK:       if.false:
+; CHECK-NEXT:    ret i1 false
+;
+  %div = sdiv i32 %x, 65
+  call void @use(i32 %div)
+  %cond = icmp sgt i32 %x, 255
+  br i1 %cond, label %if.true, label %if.false
+
+if.true:
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+
+if.false:
+  ret i1 false
+}
+
+; Positive test: add+sdiv pattern (mirrors real-world kernel case).
+; x+63 > 255 dominates the icmp, proving x+63 non-negative.
+define i1 @icmp_add_sdiv_domcond(i32 %x) {
+; CHECK-LABEL: @icmp_add_sdiv_domcond(
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[X:%.*]], 63
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[ADD]], 65
+; CHECK-NEXT:    call void @use(i32 [[DIV]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp sgt i32 [[ADD]], 255
+; CHECK-NEXT:    br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
+; CHECK:       if.true:
+; CHECK-NEXT:    ret i1 true
+; CHECK:       if.false:
+; CHECK-NEXT:    ret i1 false
+;
+  %add = add i32 %x, 63
+  %div = sdiv i32 %add, 65
+  call void @use(i32 %div)
+  %cond = icmp sgt i32 %add, 255
+  br i1 %cond, label %if.true, label %if.false
+
+if.true:
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+
+if.false:
+  ret i1 false
+}
+
+; Positive test: udiv case (no signedness mismatch, baseline behavior).
+define i1 @icmp_udiv_domcond(i32 %x) {
+; CHECK-LABEL: @icmp_udiv_domcond(
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[X:%.*]], 65
+; CHECK-NEXT:    call void @use(i32 [[DIV]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp ugt i32 [[X]], 255
+; CHECK-NEXT:    br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
+; CHECK:       if.true:
+; CHECK-NEXT:    ret i1 true
+; CHECK:       if.false:
+; CHECK-NEXT:    ret i1 false
+;
+  %div = udiv i32 %x, 65
+  call void @use(i32 %div)
+  %cond = icmp ugt i32 %x, 255
+  br i1 %cond, label %if.true, label %if.false
+
+if.true:
+  %cmp = icmp ugt i32 %div, 2
+  ret i1 %cmp
+
+if.false:
+  ret i1 false
+}
+
+; Negative test: dominating condition is too weak.
+; x > 127 does NOT prove sdiv(x, 65) > 2 (e.g. x=128, sdiv(128,65)=1).
+define i1 @icmp_sdiv_domcond_too_weak(i32 %x) {
+; CHECK-LABEL: @icmp_sdiv_domcond_too_weak(
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[X:%.*]], 65
+; CHECK-NEXT:    call void @use(i32 [[DIV]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp sgt i32 [[X]], 127
+; CHECK-NEXT:    br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
+; CHECK:       if.true:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[X]], 194
+; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK:       if.false:
+; CHECK-NEXT:    ret i1 false
+;
+  %div = sdiv i32 %x, 65
+  call void @use(i32 %div)
+  %cond = icmp sgt i32 %x, 127
+  br i1 %cond, label %if.true, label %if.false
+
+if.true:
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+
+if.false:
+  ret i1 false
+}
+
+; Negative test: no dominating condition, x's sign is unknown.
+define i1 @icmp_sdiv_no_domcond(i32 %x) {
+; CHECK-LABEL: @icmp_sdiv_no_domcond(
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[X:%.*]], 65
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ugt i32 [[DIV]], 2
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %div = sdiv i32 %x, 65
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+}
+
+; Negative test: comparison is on the boundary.
+; x > 194 means x >= 195, sdiv(195, 65) = 3, but 3 > 3 is false.
+define i1 @icmp_sdiv_domcond_boundary(i32 %x) {
+; CHECK-LABEL: @icmp_sdiv_domcond_boundary(
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[X:%.*]], 65
+; CHECK-NEXT:    call void @use(i32 [[DIV]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp sgt i32 [[X]], 194
+; CHECK-NEXT:    br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
+; CHECK:       if.true:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[X]], 259
+; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK:       if.false:
+; CHECK-NEXT:    ret i1 false
+;
+  %div = sdiv i32 %x, 65
+  call void @use(i32 %div)
+  %cond = icmp sgt i32 %x, 194
+  br i1 %cond, label %if.true, label %if.false
+
+if.true:
+  %cmp = icmp samesign ugt i32 %div, 3
+  ret i1 %cmp
+
+if.false:
+  ret i1 false
+}
+
+; Negative test: dividend is known negative, sdiv != udiv.
+define i1 @icmp_sdiv_negative_dividend(i32 %x) {
+; CHECK-LABEL: @icmp_sdiv_negative_dividend(
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[X:%.*]], 65
+; CHECK-NEXT:    call void @use(i32 [[DIV]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[X]], -100
+; CHECK-NEXT:    br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
+; CHECK:       if.true:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ugt i32 [[DIV]], 2
+; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK:       if.false:
+; CHECK-NEXT:    ret i1 false
+;
+  %div = sdiv i32 %x, 65
+  call void @use(i32 %div)
+  %cond = icmp slt i32 %x, -100
+  br i1 %cond, label %if.true, label %if.false
+
+if.true:
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+
+if.false:
+  ret i1 false
+}
+
+; Negative test: divisor is negative, sdiv(x, -65) != udiv(x, -65).
+define i1 @icmp_sdiv_negative_divisor(i32 %x) {
+; CHECK-LABEL: @icmp_sdiv_negative_divisor(
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[X:%.*]], -65
+; CHECK-NEXT:    call void @use(i32 [[DIV]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp sgt i32 [[X]], 255
+; CHECK-NEXT:    br i1 [[COND]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
+; CHECK:       if.true:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ugt i32 [[DIV]], 2
+; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK:       if.false:
+; CHECK-NEXT:    ret i1 false
+;
+  %div = sdiv i32 %x, -65
+  call void @use(i32 %div)
+  %cond = icmp sgt i32 %x, 255
+  br i1 %cond, label %if.true, label %if.false
+
+if.true:
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+
+if.false:
+  ret i1 false
+}


        


More information about the llvm-commits mailing list