[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