[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
Thu Mar 26 22:03:56 PDT 2026


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

>From 7af870c25117d72cc12aa66500c5119d1c34fc53 Mon Sep 17 00:00:00 2001
From: Li Jinpei <jinpli at amd.com>
Date: Fri, 27 Mar 2026 12:26:44 +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           | 166 ++++++++++++++++++
 1 file changed, 166 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..99d5b4cbe7191
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll
@@ -0,0 +1,166 @@
+; 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 llvm.assume), allowing the fold to
+; proceed despite a signed div / unsigned icmp mismatch.
+; Non-power-of-two divisors are used to prevent sdiv->lshr conversion.
+; Filler instructions are placed between the sdiv and the assume to
+; exceed the 15-instruction scan limit in isValidAssumeForContext,
+; preventing visitSDiv from using the assume to convert sdiv to udiv.
+
+declare void @llvm.assume(i1 noundef)
+declare void @use(i32)
+
+; Positive test: assume(x > 255) proves x non-negative, so sdiv(x, 65) = udiv(x, 65).
+; udiv(x, 65) > 2 folds via foldICmpDivConstant, then assume proves result true.
+define i1 @icmp_sdiv_assume_sgt(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_sdiv_assume_sgt(
+; CHECK-NOT:     ret i1 true
+; CHECK:         ret i1
+  %div = sdiv i32 %x, 65
+  %a1 = add i32 %y, 1
+  %a2 = add i32 %a1, 2
+  %a3 = add i32 %a2, 3
+  %a4 = add i32 %a3, 4
+  %a5 = add i32 %a4, 5
+  %a6 = add i32 %a5, 6
+  %a7 = add i32 %a6, 7
+  %a8 = add i32 %a7, 8
+  %a9 = add i32 %a8, 9
+  %a10 = add i32 %a9, 10
+  %a11 = add i32 %a10, 11
+  %a12 = add i32 %a11, 12
+  %a13 = add i32 %a12, 13
+  %a14 = add i32 %a13, 14
+  %a15 = add i32 %a14, 15
+  %a16 = add i32 %a15, 16
+  call void @use(i32 %a16)
+  %assume_cond = icmp sgt i32 %x, 255
+  call void @llvm.assume(i1 %assume_cond)
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+}
+
+; Positive test: add+sdiv pattern (mirrors real-world kernel case).
+; assume(x+63 > 255) proves x+63 non-negative, sdiv(x+63, 65) >= 3 > 2.
+define i1 @icmp_add_sdiv_assume(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_add_sdiv_assume(
+; CHECK-NOT:     ret i1 true
+; CHECK:         ret i1
+  %add = add i32 %x, 63
+  %div = sdiv i32 %add, 65
+  %a1 = add i32 %y, 1
+  %a2 = add i32 %a1, 2
+  %a3 = add i32 %a2, 3
+  %a4 = add i32 %a3, 4
+  %a5 = add i32 %a4, 5
+  %a6 = add i32 %a5, 6
+  %a7 = add i32 %a6, 7
+  %a8 = add i32 %a7, 8
+  %a9 = add i32 %a8, 9
+  %a10 = add i32 %a9, 10
+  %a11 = add i32 %a10, 11
+  %a12 = add i32 %a11, 12
+  %a13 = add i32 %a12, 13
+  %a14 = add i32 %a13, 14
+  %a15 = add i32 %a14, 15
+  %a16 = add i32 %a15, 16
+  call void @use(i32 %a16)
+  %assume_cond = icmp sgt i32 %add, 255
+  call void @llvm.assume(i1 %assume_cond)
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+}
+
+; Positive test: udiv case (no signedness mismatch, but tests the assume path).
+define i1 @icmp_udiv_assume(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_udiv_assume(
+; CHECK:         ret i1 true
+  %div = udiv i32 %x, 65
+  %a1 = add i32 %y, 1
+  %a2 = add i32 %a1, 2
+  %a3 = add i32 %a2, 3
+  %a4 = add i32 %a3, 4
+  %a5 = add i32 %a4, 5
+  %a6 = add i32 %a5, 6
+  %a7 = add i32 %a6, 7
+  %a8 = add i32 %a7, 8
+  %a9 = add i32 %a8, 9
+  %a10 = add i32 %a9, 10
+  %a11 = add i32 %a10, 11
+  %a12 = add i32 %a11, 12
+  %a13 = add i32 %a12, 13
+  %a14 = add i32 %a13, 14
+  %a15 = add i32 %a14, 15
+  %a16 = add i32 %a15, 16
+  call void @use(i32 %a16)
+  %assume_cond = icmp ugt i32 %x, 255
+  call void @llvm.assume(i1 %assume_cond)
+  %cmp = icmp ugt i32 %div, 2
+  ret i1 %cmp
+}
+
+; Negative test: assume(x > 127) does NOT prove sdiv(x, 65) > 2.
+; x = 128 gives sdiv(128, 65) = 1 which is not > 2.
+define i1 @icmp_sdiv_assume_too_weak(i32 %x) {
+; CHECK-LABEL: @icmp_sdiv_assume_too_weak(
+; CHECK-NOT:     ret i1 true
+; CHECK:         ret i1
+  %assume_cond = icmp sgt i32 %x, 127
+  call void @llvm.assume(i1 %assume_cond)
+  %div = sdiv i32 %x, 65
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+}
+
+; Negative test: no assume present, X is not known non-negative,
+; so sdiv cannot be treated as udiv and the signedness mismatch bails.
+define i1 @icmp_sdiv_no_assume(i32 %x) {
+; CHECK-LABEL: @icmp_sdiv_no_assume(
+; CHECK-NOT:     ret i1 true
+; CHECK:         ret i1
+  %div = sdiv i32 %x, 65
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+}
+
+; Negative test: assume provides range but comparison is on the boundary.
+; assume(x > 194) means x >= 195, sdiv(195, 65) = 3, so sdiv(x,65) >= 3.
+; But 3 > 3 is false, so this should NOT fold to true.
+define i1 @icmp_sdiv_assume_boundary(i32 %x) {
+; CHECK-LABEL: @icmp_sdiv_assume_boundary(
+; CHECK-NOT:     ret i1 true
+; CHECK:         ret i1
+  %assume_cond = icmp sgt i32 %x, 194
+  call void @llvm.assume(i1 %assume_cond)
+  %div = sdiv i32 %x, 65
+  %cmp = icmp samesign ugt i32 %div, 3
+  ret i1 %cmp
+}
+
+; Negative test: dividend is known negative, so sdiv != udiv.
+; assume(x < -100) means x is negative, sdiv and udiv give different results.
+; The fold should not treat sdiv as udiv.
+define i1 @icmp_sdiv_negative_dividend(i32 %x) {
+; CHECK-LABEL: @icmp_sdiv_negative_dividend(
+; CHECK-NOT:     ret i1 true
+; CHECK:         ret i1
+  %assume_cond = icmp slt i32 %x, -100
+  call void @llvm.assume(i1 %assume_cond)
+  %div = sdiv i32 %x, 65
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+}
+
+; Negative test: divisor is negative. Even with non-negative dividend,
+; sdiv(x, -65) != udiv(x, -65), so we cannot treat sdiv as udiv.
+define i1 @icmp_sdiv_negative_divisor(i32 %x) {
+; CHECK-LABEL: @icmp_sdiv_negative_divisor(
+; CHECK-NOT:     ret i1 true
+; CHECK:         ret i1
+  %assume_cond = icmp sgt i32 %x, 255
+  call void @llvm.assume(i1 %assume_cond)
+  %div = sdiv i32 %x, -65
+  %cmp = icmp samesign ugt i32 %div, 2
+  ret i1 %cmp
+}

>From b46fa3ad37d4d31a6a4a43063aab1aed1a3d84d6 Mon Sep 17 00:00:00 2001
From: Li Jinpei <jinpli at amd.com>
Date: Fri, 27 Mar 2026 12:31:40 +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 |  6 ++----
 2 files changed, 12 insertions(+), 6 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 99d5b4cbe7191..4d027d132a3f9 100644
--- a/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-sdiv-assume.ll
@@ -15,8 +15,7 @@ declare void @use(i32)
 ; udiv(x, 65) > 2 folds via foldICmpDivConstant, then assume proves result true.
 define i1 @icmp_sdiv_assume_sgt(i32 %x, i32 %y) {
 ; CHECK-LABEL: @icmp_sdiv_assume_sgt(
-; CHECK-NOT:     ret i1 true
-; CHECK:         ret i1
+; CHECK:         ret i1 true
   %div = sdiv i32 %x, 65
   %a1 = add i32 %y, 1
   %a2 = add i32 %a1, 2
@@ -45,8 +44,7 @@ define i1 @icmp_sdiv_assume_sgt(i32 %x, i32 %y) {
 ; assume(x+63 > 255) proves x+63 non-negative, sdiv(x+63, 65) >= 3 > 2.
 define i1 @icmp_add_sdiv_assume(i32 %x, i32 %y) {
 ; CHECK-LABEL: @icmp_add_sdiv_assume(
-; CHECK-NOT:     ret i1 true
-; CHECK:         ret i1
+; CHECK:         ret i1 true
   %add = add i32 %x, 63
   %div = sdiv i32 %add, 65
   %a1 = add i32 %y, 1



More information about the llvm-commits mailing list