[llvm] [InstCombine] Use samesign constraints in unsigned known-bits folds (PR #209097)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 13 22:00:32 PDT 2026


https://github.com/imkiva updated https://github.com/llvm/llvm-project/pull/209097

>From d0cad7a208f61dac116c00e00d2ba75411303507 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Mon, 13 Jul 2026 14:10:09 +0800
Subject: [PATCH 1/3] [InstCombine] Use samesign constraints in unsigned
 known-bits folds

---
 llvm/include/llvm/Analysis/ValueTracking.h    |   2 +
 llvm/lib/Analysis/ValueTracking.cpp           |  17 +-
 .../InstCombine/InstCombineCompares.cpp       |  24 ++-
 .../InstCombine/icmp-samesign-known-bits.ll   | 188 ++++++++++++++++++
 .../Transforms/LoopVectorize/runtime-check.ll |   2 +-
 5 files changed, 227 insertions(+), 6 deletions(-)
 create mode 100644 llvm/test/Transforms/InstCombine/icmp-samesign-known-bits.ll

diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index c87e39bca8215..f622fd2ad8491 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -864,6 +864,8 @@ LLVM_ABI bool mustExecuteUBIfPoisonOnPathTo(Instruction *Root,
 /// form with the strictness flipped predicate. Return the new predicate and
 /// corresponding constant RHS if possible. Otherwise return std::nullopt.
 /// E.g., (icmp sgt X, 0) -> (icmp sle X, 1).
+/// For a samesign predicate, fail if adjusting the constant would change its
+/// sign bit, because that would change the comparison's poison domain.
 LLVM_ABI std::optional<std::pair<CmpPredicate, Constant *>>
 getFlippedStrictnessPredicateAndConstant(CmpPredicate Pred, Constant *C);
 
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index efc14f9a639da..4307c92c30172 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -8925,8 +8925,18 @@ llvm::getFlippedStrictnessPredicateAndConstant(CmpPredicate Pred, Constant *C) {
 
   // Check if the constant operand can be safely incremented/decremented
   // without overflowing/underflowing.
-  auto ConstantIsOk = [WillIncrement, IsSigned](ConstantInt *C) {
-    return WillIncrement ? !C->isMaxValue(IsSigned) : !C->isMinValue(IsSigned);
+  auto ConstantIsOk = [Pred, WillIncrement, IsSigned](ConstantInt *C) {
+    if (WillIncrement ? C->isMaxValue(IsSigned) : C->isMinValue(IsSigned))
+      return false;
+
+    if (!Pred.hasSameSign())
+      return true;
+
+    // Preserve samesign only if adjusting the constant does not change its
+    // sign bit, and therefore does not change the poison domain.
+    const APInt &Value = C->getValue();
+    APInt Adjusted = WillIncrement ? Value + 1 : Value - 1;
+    return Value.isNegative() == Adjusted.isNegative();
   };
 
   Constant *SafeReplacementConstant = nullptr;
@@ -8974,7 +8984,8 @@ llvm::getFlippedStrictnessPredicateAndConstant(CmpPredicate Pred, Constant *C) {
     C = Constant::replaceUndefsWith(C, SafeReplacementConstant);
   }
 
-  CmpInst::Predicate NewPred = CmpInst::getFlippedStrictnessPredicate(Pred);
+  CmpPredicate NewPred(CmpInst::getFlippedStrictnessPredicate(Pred),
+                       Pred.hasSameSign());
 
   // Increment or decrement the constant.
   Constant *OneOrNegOne = ConstantInt::get(Type, WillIncrement ? 1 : -1, true);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 42c2983034e22..0a73791b0d8ed 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -6991,6 +6991,22 @@ Instruction *InstCombinerImpl::foldICmpUsingKnownBits(ICmpInst &I) {
       return &I;
   }
 
+  // If an unsigned samesign comparison is not poison, both operands have the
+  // same sign bit. Propagate a known sign bit between the temporary KnownBits
+  // values so the existing range folds can use that constraint.
+  if (I.hasSameSign() && I.isUnsigned()) {
+    auto PropagateSignBit = [](const KnownBits &From, KnownBits &To) {
+      if (To.isNegative() || To.isNonNegative())
+        return;
+      if (From.isNegative())
+        To.makeNegative();
+      else if (From.isNonNegative())
+        To.makeNonNegative();
+    };
+    PropagateSignBit(Op0Known, Op1Known);
+    PropagateSignBit(Op1Known, Op0Known);
+  }
+
   if (!isa<Constant>(Op0) && Op0Known.isConstant())
     return new ICmpInst(
         Pred, ConstantExpr::getIntegerValue(Ty, Op0Known.getConstant()), Op1);
@@ -7276,11 +7292,15 @@ static ICmpInst *canonicalizeCmpWithConstant(ICmpInst &I) {
   if (!Op1C)
     return nullptr;
 
-  auto FlippedStrictness = getFlippedStrictnessPredicateAndConstant(Pred, Op1C);
+  auto FlippedStrictness =
+      getFlippedStrictnessPredicateAndConstant(I.getCmpPredicate(), Op1C);
   if (!FlippedStrictness)
     return nullptr;
 
-  return new ICmpInst(FlippedStrictness->first, Op0, FlippedStrictness->second);
+  auto *NewCmp =
+      new ICmpInst(FlippedStrictness->first, Op0, FlippedStrictness->second);
+  NewCmp->setSameSign(FlippedStrictness->first.hasSameSign());
+  return NewCmp;
 }
 
 /// If we have a comparison with a non-canonical predicate, if we can update
diff --git a/llvm/test/Transforms/InstCombine/icmp-samesign-known-bits.ll b/llvm/test/Transforms/InstCombine/icmp-samesign-known-bits.ll
new file mode 100644
index 0000000000000..b3f075bc297fb
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-samesign-known-bits.ll
@@ -0,0 +1,188 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @ugt_smax_minus_one(i8 %x) {
+; CHECK-LABEL: define i1 @ugt_smax_minus_one(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], 127
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign ugt i8 %x, 126
+  ret i1 %cmp
+}
+
+define i1 @ult_smax(i8 %x) {
+; CHECK-LABEL: define i1 @ult_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[X]], 127
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign ult i8 %x, 127
+  ret i1 %cmp
+}
+
+define i1 @ugt_smax(i8 %x) {
+; CHECK-LABEL: define i1 @ugt_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %cmp = icmp samesign ugt i8 %x, 127
+  ret i1 %cmp
+}
+
+define i1 @ule_smax(i8 %x) {
+; CHECK-LABEL: define i1 @ule_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %cmp = icmp samesign ule i8 %x, 127
+  ret i1 %cmp
+}
+
+define i1 @uge_smax(i8 %x) {
+; CHECK-LABEL: define i1 @uge_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], 127
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign uge i8 %x, 127
+  ret i1 %cmp
+}
+
+define i1 @uge_non_endpoint(i8 %x) {
+; CHECK-LABEL: define i1 @uge_non_endpoint(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ugt i8 [[X]], 41
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign uge i8 %x, 42
+  ret i1 %cmp
+}
+
+define <2 x i1> @ule_non_endpoint_vec(<2 x i8> %x) {
+; CHECK-LABEL: define <2 x i1> @ule_non_endpoint_vec(
+; CHECK-SAME: <2 x i8> [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ult <2 x i8> [[X]], splat (i8 43)
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %cmp = icmp samesign ule <2 x i8> %x, splat (i8 42)
+  ret <2 x i1> %cmp
+}
+
+define i1 @ugt_zero(i8 %x) {
+; CHECK-LABEL: define i1 @ugt_zero(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[X]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign ugt i8 %x, 0
+  ret i1 %cmp
+}
+
+define i1 @ult_zero(i8 %x) {
+; CHECK-LABEL: define i1 @ult_zero(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %cmp = icmp samesign ult i8 %x, 0
+  ret i1 %cmp
+}
+
+define i1 @uge_smin(i8 %x) {
+; CHECK-LABEL: define i1 @uge_smin(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %cmp = icmp samesign uge i8 %x, -128
+  ret i1 %cmp
+}
+
+define i1 @ugt_negative_endpoint(i8 %x) {
+; CHECK-LABEL: define i1 @ugt_negative_endpoint(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign ugt i8 %x, -2
+  ret i1 %cmp
+}
+
+define i1 @ult_negative_endpoint(i8 %x) {
+; CHECK-LABEL: define i1 @ult_negative_endpoint(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], -128
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign ult i8 %x, -127
+  ret i1 %cmp
+}
+
+define <2 x i1> @ugt_smax_minus_one_vec(<2 x i8> %x) {
+; CHECK-LABEL: define <2 x i1> @ugt_smax_minus_one_vec(
+; CHECK-SAME: <2 x i8> [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[X]], splat (i8 127)
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %cmp = icmp samesign ugt <2 x i8> %x, splat (i8 126)
+  ret <2 x i1> %cmp
+}
+
+define i1 @ugt_smax_minus_one_no_samesign(i8 %x) {
+; CHECK-LABEL: define i1 @ugt_smax_minus_one_no_samesign(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[X]], 126
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp ugt i8 %x, 126
+  ret i1 %cmp
+}
+
+define i1 @ule_smax_no_samesign(i8 %x) {
+; CHECK-LABEL: define i1 @ule_smax_no_samesign(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[X]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp ule i8 %x, 127
+  ret i1 %cmp
+}
+
+define i1 @sge_zero_sign_crossing(i8 %x) {
+; CHECK-LABEL: define i1 @sge_zero_sign_crossing(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign sge i8 [[X]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign sge i8 %x, 0
+  ret i1 %cmp
+}
+
+define i1 @sle_minus_one_sign_crossing(i8 %x) {
+; CHECK-LABEL: define i1 @sle_minus_one_sign_crossing(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign sle i8 [[X]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign sle i8 %x, -1
+  ret i1 %cmp
+}
+
+define i1 @sge_non_endpoint(i8 %x) {
+; CHECK-LABEL: define i1 @sge_non_endpoint(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign sgt i8 [[X]], 41
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign sge i8 %x, 42
+  ret i1 %cmp
+}
+
+define <2 x i1> @uge_mixed_sign_crossing_vec(<2 x i8> %x) {
+; CHECK-LABEL: define <2 x i1> @uge_mixed_sign_crossing_vec(
+; CHECK-SAME: <2 x i8> [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign uge <2 x i8> [[X]], <i8 42, i8 -128>
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %cmp = icmp samesign uge <2 x i8> %x, <i8 42, i8 -128>
+  ret <2 x i1> %cmp
+}
diff --git a/llvm/test/Transforms/LoopVectorize/runtime-check.ll b/llvm/test/Transforms/LoopVectorize/runtime-check.ll
index d7a747bfe6d66..82ff133f2c04f 100644
--- a/llvm/test/Transforms/LoopVectorize/runtime-check.ll
+++ b/llvm/test/Transforms/LoopVectorize/runtime-check.ll
@@ -478,7 +478,7 @@ define void @test_scev_check_mul_add_expansion(ptr %out, ptr %in, i32 %len, i32
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[PRE_1:%.*]] = icmp samesign ugt i32 [[D:%.*]], 5
 ; CHECK-NEXT:    tail call void @llvm.assume(i1 [[PRE_1]])
-; CHECK-NEXT:    [[PRE_2:%.*]] = icmp ult i32 [[D]], 7
+; CHECK-NEXT:    [[PRE_2:%.*]] = icmp samesign ult i32 [[D]], 7
 ; CHECK-NEXT:    tail call void @llvm.assume(i1 [[PRE_2]])
 ; CHECK-NEXT:    [[PRE_3:%.*]] = icmp slt i32 [[D]], [[LEN:%.*]]
 ; CHECK-NEXT:    tail call void @llvm.assume(i1 [[PRE_3]])

>From 7d8bc01672b42bfbd153dbce63d52d87a9685970 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Tue, 14 Jul 2026 12:34:26 +0800
Subject: [PATCH 2/3] Address review comments

---
 llvm/lib/Analysis/ValueTracking.cpp                     | 9 ++++-----
 llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 4 ++--
 2 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 4307c92c30172..b387650cba4d9 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -8932,11 +8932,10 @@ llvm::getFlippedStrictnessPredicateAndConstant(CmpPredicate Pred, Constant *C) {
     if (!Pred.hasSameSign())
       return true;
 
-    // Preserve samesign only if adjusting the constant does not change its
-    // sign bit, and therefore does not change the poison domain.
-    const APInt &Value = C->getValue();
-    APInt Adjusted = WillIncrement ? Value + 1 : Value - 1;
-    return Value.isNegative() == Adjusted.isNegative();
+    // Crossing the corresponding boundary in the other ordering changes the
+    // sign bit, and therefore changes the poison domain.
+    return WillIncrement ? !C->isMaxValue(!IsSigned)
+                         : !C->isMinValue(!IsSigned);
   };
 
   Constant *SafeReplacementConstant = nullptr;
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 0a73791b0d8ed..6dcdd6e264f51 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7281,7 +7281,7 @@ Instruction *InstCombinerImpl::foldICmpUsingBoolRange(ICmpInst &I) {
 /// it into the appropriate icmp lt or icmp gt instruction. This transform
 /// allows them to be folded in visitICmpInst.
 static ICmpInst *canonicalizeCmpWithConstant(ICmpInst &I) {
-  ICmpInst::Predicate Pred = I.getPredicate();
+  CmpPredicate Pred = I.getCmpPredicate();
   if (ICmpInst::isEquality(Pred) || !ICmpInst::isIntPredicate(Pred) ||
       InstCombiner::isCanonicalPredicate(Pred))
     return nullptr;
@@ -7293,7 +7293,7 @@ static ICmpInst *canonicalizeCmpWithConstant(ICmpInst &I) {
     return nullptr;
 
   auto FlippedStrictness =
-      getFlippedStrictnessPredicateAndConstant(I.getCmpPredicate(), Op1C);
+      getFlippedStrictnessPredicateAndConstant(Pred, Op1C);
   if (!FlippedStrictness)
     return nullptr;
 

>From 776f394ca719c205004950ac4417afd92ead091a Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Tue, 14 Jul 2026 12:58:49 +0800
Subject: [PATCH 3/3] clang-format

---
 llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 6dcdd6e264f51..f65671a4ef206 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7292,8 +7292,7 @@ static ICmpInst *canonicalizeCmpWithConstant(ICmpInst &I) {
   if (!Op1C)
     return nullptr;
 
-  auto FlippedStrictness =
-      getFlippedStrictnessPredicateAndConstant(Pred, Op1C);
+  auto FlippedStrictness = getFlippedStrictnessPredicateAndConstant(Pred, Op1C);
   if (!FlippedStrictness)
     return nullptr;
 



More information about the llvm-commits mailing list