[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