[llvm] [ConstraintElim] Tighten bounds using inequalities with constants. (PR #213049)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 30 11:52:42 PDT 2026
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/213049
>From 3ebb5f753f005a50e94c87b28f6f99638718317c Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 27 Jul 2026 13:53:30 +0100
Subject: [PATCH 1/2] [ConstraintElim] Tighten bounds using inequalities with
constants.
Update ConstraintElimination to tighten existing bounds using
inequalities. Given an inequality %a != %b and %a >= %b, we can tighten
the fact to %a > %b.
For now this is limited to constant %b.
This helps to remove some checks in practice:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/819
Compile-time impact in the noise,
https://llvm-compile-time-tracker.com/compare.php?from=b84c5f4abcfb464e362bcd2b8eb58ef96ecbae9d&to=d7a600e360dd2341c81d7f2760c72040cb485131&stat=instructions%3Au
---
.../Scalar/ConstraintElimination.cpp | 46 +++++++++++++++++++
.../ConstraintElimination/ne-tightening.ll | 24 ++++------
2 files changed, 54 insertions(+), 16 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index bc79bd7068f14..c6db9e01a3db8 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -324,6 +324,11 @@ class ConstraintInfo {
void addFactImpl(CmpInst::Predicate Pred, Value *A, Value *B, unsigned NumIn,
unsigned NumOut, SmallVectorImpl<StackEntry> &DFSInStack,
bool ForceSignedSystem);
+
+ /// Try to use the inequality \p A != \p B to tighten a non-strict bound the
+ /// system already implies to the corresponding strict bound.
+ void tightenBoundUsingNe(Value *A, Value *B, unsigned NumIn, unsigned NumOut,
+ SmallVectorImpl<StackEntry> &DFSInStack);
};
/// Represents a (Coefficient * Variable) entry after IR decomposition.
@@ -1781,6 +1786,47 @@ void ConstraintInfo::addFact(CmpInst::Predicate Pred, Value *A, Value *B,
// If the Pred is eq/ne, also add the fact to signed system.
if (CmpInst::isEquality(Pred))
addFactImpl(Pred, A, B, NumIn, NumOut, DFSInStack, true);
+ if (Pred == CmpInst::ICMP_NE)
+ tightenBoundUsingNe(A, B, NumIn, NumOut, DFSInStack);
+}
+
+void ConstraintInfo::tightenBoundUsingNe(
+ Value *A, Value *B, unsigned NumIn, unsigned NumOut,
+ SmallVectorImpl<StackEntry> &DFSInStack) {
+ if (!isa<ConstantInt>(B) || !A->getType()->isIntegerTy())
+ return;
+
+ for (bool IsSigned : {false, true}) {
+ // In the unsigned system `A u>= 0` holds for every A, so getConstraint
+ // already turned `A != 0` into `A u> 0`.
+ if (!IsSigned && match(B, m_Zero()))
+ continue;
+
+ // Skip if there are any unknown variables.
+ const auto &Value2Index = getValue2Index(IsSigned);
+ if (any_of(decompose(A, *this, IsSigned, DL).Vars,
+ [&Value2Index](const DecompEntry &E) {
+ return !Value2Index.contains(E.Variable);
+ }))
+ continue;
+
+ // If the system implies `A >= B` then together with `A != B` we get the
+ // strict `A > B`; symmetrically `A <= B` becomes `A < B`.
+ for (CmpInst::Predicate NonStrict :
+ {IsSigned ? CmpInst::ICMP_SGE : CmpInst::ICMP_UGE,
+ IsSigned ? CmpInst::ICMP_SLE : CmpInst::ICMP_ULE}) {
+ if (!doesHold(NonStrict, A, B))
+ continue;
+ CmpInst::Predicate Strict = CmpInst::getStrictPredicate(NonStrict);
+ LLVM_DEBUG(dbgs() << "Tightening '";
+ dumpUnpackedICmp(dbgs(), NonStrict, A, B); dbgs() << "' to '";
+ dumpUnpackedICmp(dbgs(), Strict, A, B);
+ dbgs() << "' using inequality\n");
+ addFactImpl(Strict, A, B, NumIn, NumOut, DFSInStack,
+ /*ForceSignedSystem=*/false);
+ break;
+ }
+ }
}
void ConstraintInfo::addFactImpl(CmpInst::Predicate Pred, Value *A, Value *B,
diff --git a/llvm/test/Transforms/ConstraintElimination/ne-tightening.ll b/llvm/test/Transforms/ConstraintElimination/ne-tightening.ll
index a7967e9b09051..baf44151a4b61 100644
--- a/llvm/test/Transforms/ConstraintElimination/ne-tightening.ll
+++ b/llvm/test/Transforms/ConstraintElimination/ne-tightening.ll
@@ -10,8 +10,7 @@ define i1 @sge_zero_and_ne_zero_implies_sgt_zero(i64 %n) {
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[B:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[B]])
-; CHECK-NEXT: [[C:%.*]] = icmp sgt i64 [[N]], 0
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%a = icmp sge i64 %n, 0
call void @llvm.assume(i1 %a)
@@ -30,8 +29,7 @@ define i1 @sge_zero_and_ne_zero_from_branch(i64 %n) {
; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[N]], 0
; CHECK-NEXT: br i1 [[Z]], label %[[ELSE:.*]], label %[[CONT:.*]]
; CHECK: [[CONT]]:
-; CHECK-NEXT: [[C:%.*]] = icmp sgt i64 [[N]], 0
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
; CHECK: [[ELSE]]:
; CHECK-NEXT: ret i1 false
;
@@ -56,8 +54,7 @@ define i1 @sle_zero_and_ne_zero_implies_slt_zero(i64 %n) {
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[B:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[B]])
-; CHECK-NEXT: [[C:%.*]] = icmp slt i64 [[N]], 0
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%a = icmp sle i64 %n, 0
call void @llvm.assume(i1 %a)
@@ -74,8 +71,7 @@ define i1 @sge_const_and_ne_const_implies_sge_const_plus_one(i64 %n) {
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[B:%.*]] = icmp ne i64 [[N]], 10
; CHECK-NEXT: call void @llvm.assume(i1 [[B]])
-; CHECK-NEXT: [[C:%.*]] = icmp sge i64 [[N]], 11
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%a = icmp sge i64 %n, 10
call void @llvm.assume(i1 %a)
@@ -92,8 +88,7 @@ define i1 @sle_const_and_ne_const_implies_sle_const_minus_one(i64 %n) {
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[B:%.*]] = icmp ne i64 [[N]], 10
; CHECK-NEXT: call void @llvm.assume(i1 [[B]])
-; CHECK-NEXT: [[C:%.*]] = icmp sle i64 [[N]], 9
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%a = icmp sle i64 %n, 10
call void @llvm.assume(i1 %a)
@@ -110,8 +105,7 @@ define i1 @uge_const_and_ne_const_implies_ugt_const(i64 %n) {
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[B:%.*]] = icmp ne i64 [[N]], 10
; CHECK-NEXT: call void @llvm.assume(i1 [[B]])
-; CHECK-NEXT: [[C:%.*]] = icmp ugt i64 [[N]], 10
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%a = icmp uge i64 %n, 10
call void @llvm.assume(i1 %a)
@@ -129,8 +123,7 @@ define i1 @ule_const_and_ne_const_implies_ult_const(i64 %n) {
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[B:%.*]] = icmp ne i64 [[N]], 10
; CHECK-NEXT: call void @llvm.assume(i1 [[B]])
-; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[N]], 10
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%a = icmp ule i64 %n, 10
call void @llvm.assume(i1 %a)
@@ -148,8 +141,7 @@ define i1 @sge_and_ne_on_add(i64 %n) {
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[B:%.*]] = icmp ne i64 [[ADD]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[B]])
-; CHECK-NEXT: [[C:%.*]] = icmp sge i64 [[N]], 0
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%add = add nsw i64 %n, 1
%a = icmp sge i64 %add, 0
>From 1af625cd193597a33cb534e6cff42dad16058eae Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 30 Jul 2026 19:50:13 +0100
Subject: [PATCH 2/2] !fixup move predicates to variable
---
llvm/lib/Transforms/Scalar/ConstraintElimination.cpp | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index c6db9e01a3db8..efe1920486df6 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1812,9 +1812,11 @@ void ConstraintInfo::tightenBoundUsingNe(
// If the system implies `A >= B` then together with `A != B` we get the
// strict `A > B`; symmetrically `A <= B` becomes `A < B`.
- for (CmpInst::Predicate NonStrict :
- {IsSigned ? CmpInst::ICMP_SGE : CmpInst::ICMP_UGE,
- IsSigned ? CmpInst::ICMP_SLE : CmpInst::ICMP_ULE}) {
+ CmpInst::Predicate GEPred =
+ IsSigned ? CmpInst::ICMP_SGE : CmpInst::ICMP_UGE;
+ CmpInst::Predicate LEPred =
+ IsSigned ? CmpInst::ICMP_SLE : CmpInst::ICMP_ULE;
+ for (CmpInst::Predicate NonStrict : {GEPred, LEPred}) {
if (!doesHold(NonStrict, A, B))
continue;
CmpInst::Predicate Strict = CmpInst::getStrictPredicate(NonStrict);
More information about the llvm-commits
mailing list