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

via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 27 09:53:31 PDT 2026


https://github.com/lijinpei-amd updated https://github.com/llvm/llvm-project/pull/188731

>From 2e378ea846029d180f096e2c145159a3536745d7 Mon Sep 17 00:00:00 2001
From: Li Jinpei <jinpli at amd.com>
Date: Fri, 27 Mar 2026 22:53:36 +0800
Subject: [PATCH 1/2] [InstCombine] Add tests for icmp sdiv folding with
 assume-derived non-negativity

Pre-commit tests for an upcoming change that allows foldICmpDivConstant
to treat sdiv as udiv when both operands are known non-negative.

Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 .../InstCombine/icmp-sdiv-assume.ll           | 211 ++++++++++++++++++
 1 file changed, 211 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll

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..8bdbe1130c4f6
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll
@@ -0,0 +1,211 @@
+; 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:    [[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
+}
+
+; 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:    [[CMP:%.*]] = icmp samesign ugt i32 [[DIV]], 2
+; CHECK-NEXT:    ret i1 [[CMP]]
+; 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 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, 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 samesign ugt i32 [[DIV]], 3
+; 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
+}

>From c0cd1e42ae0b31bb03e1030584c4e64857f8fbf6 Mon Sep 17 00:00:00 2001
From: Li Jinpei <jinpli at amd.com>
Date: Fri, 27 Mar 2026 22:59:12 +0800
Subject: [PATCH 2/2] [InstCombine] Treat sdiv as udiv in foldICmpDivConstant
 when both operands are non-negative

When foldICmpDivConstant encounters a signedness mismatch between the
sdiv and the icmp (e.g. icmp ugt (sdiv X, 65), 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>
---
 .../Transforms/InstCombine/InstCombineCompares.cpp   | 12 ++++++++++--
 llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll | 10 ++++------
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index dc860700db91b..64ab6bece865e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -2813,8 +2813,16 @@ 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, if the sdiv operands are both known non-negative, the sdiv
+  // is equivalent to udiv, so we can treat it as unsigned and proceed
+  // with an unsigned comparison.
+  if (!Cmp.isEquality() && DivIsSigned != Cmp.isSigned()) {
+    if (DivIsSigned && C2->isStrictlyPositive() &&
+        isKnownNonNegative(X, SQ.getWithInstruction(&Cmp)))
+      DivIsSigned = false;
+    else
+      return nullptr;
+  }
 
   // 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
index 8bdbe1130c4f6..27c0ff485c5e1 100644
--- a/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll
@@ -20,8 +20,7 @@ define i1 @icmp_sdiv_domcond(i32 %x) {
 ; 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-NEXT:    ret i1 true
 ; CHECK:       if.false:
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -48,8 +47,7 @@ define i1 @icmp_add_sdiv_domcond(i32 %x) {
 ; CHECK-NEXT:    [[COND:%.*]] = icmp sgt i32 [[ADD]], 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-NEXT:    ret i1 true
 ; CHECK:       if.false:
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -101,7 +99,7 @@ define i1 @icmp_sdiv_domcond_too_weak(i32 %x) {
 ; 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 samesign ugt i32 [[DIV]], 2
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[X]], 194
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ; CHECK:       if.false:
 ; CHECK-NEXT:    ret i1 false
@@ -140,7 +138,7 @@ define i1 @icmp_sdiv_domcond_boundary(i32 %x) {
 ; 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 samesign ugt i32 [[DIV]], 3
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[X]], 259
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ; CHECK:       if.false:
 ; CHECK-NEXT:    ret i1 false



More information about the llvm-commits mailing list