[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 05:21:29 PDT 2026
https://github.com/lijinpei-amd created https://github.com/llvm/llvm-project/pull/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.
>From 184272702e2ff1ed88678d73aa08b6a1435d625c Mon Sep 17 00:00:00 2001
From: Li Jinpei <jinpli at amd.com>
Date: Thu, 26 Mar 2026 18:47:49 +0800
Subject: [PATCH] [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, 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>
---
.../InstCombine/InstCombineCompares.cpp | 12 +-
.../InstCombine/icmp-binop-assume.ll | 104 ++++++++++++++++++
2 files changed, 114 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/Transforms/InstCombine/icmp-binop-assume.ll
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-binop-assume.ll b/llvm/test/Transforms/InstCombine/icmp-binop-assume.ll
new file mode 100644
index 0000000000000..8aecf28ef1220
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-binop-assume.ll
@@ -0,0 +1,104 @@
+; 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.
+
+declare void @llvm.assume(i1 noundef)
+
+; 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) {
+; CHECK-LABEL: @icmp_sdiv_assume_sgt(
+; CHECK: ret i1 true
+ %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
+}
+
+; 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) {
+; CHECK-LABEL: @icmp_add_sdiv_assume(
+; CHECK: ret i1 true
+ %add = add i32 %x, 63
+ %div = sdiv i32 %add, 65
+ %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) {
+; CHECK-LABEL: @icmp_udiv_assume(
+; CHECK: ret i1 true
+ %assume_cond = icmp ugt i32 %x, 255
+ call void @llvm.assume(i1 %assume_cond)
+ %div = udiv i32 %x, 65
+ %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
+ %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
+ %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
+ %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
+ %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
+ %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
+}
More information about the llvm-commits
mailing list