[llvm] adddcef - [ValueTracking][InstCombine] Preserve samesign when flipping icmp strictness (#209097)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 11 22:04:34 PDT 2026


Author: Kiva
Date: 2026-08-12T13:04:29+08:00
New Revision: adddcef03b6d1e4b20ee7ebad6d32926ab632d9d

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

LOG: [ValueTracking][InstCombine] Preserve samesign when flipping icmp strictness (#209097)

Preserve the `samesign` flag when canonicalizing non-strict integer
comparisons to strict form, so subsequent folds can use it. For example:

```
icmp samesign uge i8 %x, 42
  ->
icmp samesign ugt i8 %x, 41
```

Adjusting the constant is only valid if it preserves the comparison's
poison domain. For a `samesign` predicate, this requires the adjustment
to preserve the constant's sign bit.

This patch checks the normal signed or unsigned overflow boundary, and
all sign-crossing cases like:
```
increment: signed-max -> signed-min
increment: -1         -> 0
decrement: signed-min -> signed-max
decrement: 0          -> -1
```

Added: 
    llvm/test/Transforms/InstCombine/icmp-samesign-canonicalize.ll

Modified: 
    llvm/include/llvm/Analysis/ValueTracking.h
    llvm/lib/Analysis/ValueTracking.cpp
    llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Removed: 
    


################################################################################
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 44a1240f5635a..28d47d8f7df27 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -8964,8 +8964,17 @@ 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;
+
+    // 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;
@@ -9013,7 +9022,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 cc79f67e9f32f..741d8c89f6dbd 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7362,7 +7362,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;
@@ -7377,7 +7377,10 @@ static ICmpInst *canonicalizeCmpWithConstant(ICmpInst &I) {
   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-canonicalize.ll b/llvm/test/Transforms/InstCombine/icmp-samesign-canonicalize.ll
new file mode 100644
index 0000000000000..e0d8073dcbe6d
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-samesign-canonicalize.ll
@@ -0,0 +1,102 @@
+; 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 @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 @ule_smax(i8 %x) {
+; CHECK-LABEL: define i1 @ule_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ule i8 [[X]], 127
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %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 samesign ugt i8 [[X]], 126
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign uge i8 %x, 127
+  ret i1 %cmp
+}
+
+define i1 @uge_smin(i8 %x) {
+; CHECK-LABEL: define i1 @uge_smin(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign uge i8 [[X]], -128
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign uge i8 %x, -128
+  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
+}
+
+define <2 x i1> @ule_mixed_sign_crossing_vec(<2 x i8> %x) {
+; CHECK-LABEL: define <2 x i1> @ule_mixed_sign_crossing_vec(
+; CHECK-SAME: <2 x i8> [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ule <2 x i8> [[X]], <i8 42, i8 127>
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %cmp = icmp samesign ule <2 x i8> %x, <i8 42, i8 127>
+  ret <2 x i1> %cmp
+}


        


More information about the llvm-commits mailing list