[llvm] [ConstraintSys] Solve sub-system with variables needed for query (NFC) (PR #210432)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 19 05:45:54 PDT 2026


https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/210432

>From 1d3b7a5a4b8cb2e22e9d4a99aa9d8a2ca3492d13 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 17 Jul 2026 13:18:24 +0100
Subject: [PATCH 1/2] [ConstraintSys] Solve sub-system with variables needed
 for query (NFC)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Update ConstraintSystem to only solve the sub-system containing all
variables relevant to a given query.

The sub-system contains the transitive closure of all variables in rows
involving the variables in the constraint to prove.

The iterative collection loop only needs very few iterations to
complete. The pruned system can significantly speed up Fourier–Motzkin
elimination and reduce the cost of copying the system.

This helps to notably decrease compile-time in cases when there are
larger numbers of variables & rows (especially during (Thin)LTO).

Highlights include
 * stage1-ReleaseThinLTO: -0.16%
 * stage1-ReleaseLTO-g: -0.19%
 * stage1-aarch64-O3: -0.04%

https://llvm-compile-time-tracker.com/compare.php?from=1f2772f8e26beb909c2a559f1fb08697e3e06909&to=77fbe7926609da96a193254e44efd1d6f8a097de&stat=instructions%3Au
---
 llvm/include/llvm/Analysis/ConstraintSystem.h |  7 ++
 llvm/lib/Analysis/ConstraintSystem.cpp        | 90 ++++++++++++++++++-
 .../Scalar/ConstraintElimination.cpp          | 24 ++---
 .../gep-arithmetic-signed-predicates.ll       |  3 +-
 4 files changed, 108 insertions(+), 16 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ConstraintSystem.h b/llvm/include/llvm/Analysis/ConstraintSystem.h
index 1d9ac49a54745..a2bb1df191a62 100644
--- a/llvm/include/llvm/Analysis/ConstraintSystem.h
+++ b/llvm/include/llvm/Analysis/ConstraintSystem.h
@@ -144,7 +144,14 @@ class ConstraintSystem {
     return R;
   }
 
+  /// Build and return a sub-system of constraints connected (transitively) to
+  /// query \p R, with variables compacted to a dense index range. Also
+  /// translate \p R's entries to the sub-system.
+  LLVM_ABI std::pair<ConstraintSystem, SmallVector<int64_t, 8>>
+  getSubSystem(ArrayRef<int64_t> R) const;
+
   LLVM_ABI bool isConditionImplied(SmallVector<int64_t, 8> R) const;
+  LLVM_ABI bool isConditionImpliedInSubSystem(SmallVector<int64_t, 8> R) const;
 
   SmallVector<int64_t> getLastConstraint() const {
     assert(!Constraints.empty() && "Constraint system is empty");
diff --git a/llvm/lib/Analysis/ConstraintSystem.cpp b/llvm/lib/Analysis/ConstraintSystem.cpp
index bc08e76cf4753..57b440f771b2a 100644
--- a/llvm/lib/Analysis/ConstraintSystem.cpp
+++ b/llvm/lib/Analysis/ConstraintSystem.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Analysis/ConstraintSystem.h"
+#include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/IR/Value.h"
@@ -198,10 +199,15 @@ void ConstraintSystem::dump() const {
         break;
       if (E.Id == 0)
         continue;
+      // The Value2Index map (and hence Names) may be absent, e.g. for the
+      // temporary system solved in isConditionImplied. Fall back to a generic
+      // variable name in that case.
+      std::string Name = E.Id <= Names.size() ? Names[E.Id - 1]
+                                              : ("%v" + std::to_string(E.Id));
       std::string Coefficient;
       if (E.Coefficient != 1)
         Coefficient = std::to_string(E.Coefficient) + " * ";
-      Parts.push_back(Coefficient + Names[E.Id - 1]);
+      Parts.push_back(Coefficient + Name);
     }
     // assert(!Parts.empty() && "need to have at least some parts");
     int64_t ConstPart = 0;
@@ -221,6 +227,67 @@ bool ConstraintSystem::mayHaveSolution() {
   return HasSolution;
 }
 
+std::pair<ConstraintSystem, SmallVector<int64_t, 8>>
+ConstraintSystem::getSubSystem(ArrayRef<int64_t> R) const {
+  // Only constraints that share a variable (transitively) with a query R can
+  // affect whether system + !R has a solution.
+  //
+  // Mark variables in the query and collect to the transitive closure over
+  // variables that co-occur in a constraint row.
+  ConstraintSystem SubSystem;
+  SmallBitVector InSystem(NumVariables + 1, false);
+  for (unsigned Id = 1, E = R.size(); Id < E; ++Id)
+    if (R[Id] != 0)
+      InSystem[Id] = true;
+  bool Changed = true;
+  while (Changed) {
+    Changed = false;
+    for (const auto &Row : Constraints) {
+      // No common variables, skip.
+      if (none_of(Row,
+                  [&](const Entry &E) { return E.Id != 0 && InSystem[E.Id]; }))
+        continue;
+      for (const Entry &E : Row)
+        if (E.Id != 0 && !InSystem[E.Id]) {
+          InSystem[E.Id] = true;
+          Changed = true;
+        }
+    }
+  }
+
+  // Assign compact indices to the variables of the sub-system.
+  SmallVector<unsigned, 16> OldToNew;
+  OldToNew.assign(NumVariables + 1, 0);
+  unsigned NextIdx = 1;
+  for (unsigned Id = 1; Id <= NumVariables; ++Id)
+    if (InSystem[Id])
+      OldToNew[Id] = NextIdx++;
+
+  // Build new compact set of rows.
+  SubSystem.NumVariables = NextIdx;
+  for (const auto &Row : Constraints) {
+    if (none_of(Row,
+                [&](const Entry &E) { return E.Id != 0 && InSystem[E.Id]; }))
+      continue;
+    SmallVector<Entry, 8> NewRow;
+    for (const Entry &E : Row) {
+      if (!E.Id)
+        NewRow.emplace_back(E.Coefficient, E.Id);
+      else if (unsigned New = OldToNew[E.Id])
+        NewRow.emplace_back(E.Coefficient, New);
+    }
+    SubSystem.Constraints.push_back(std::move(NewRow));
+  }
+
+  // Remap the query row into the component's compact index space.
+  SmallVector<int64_t, 8> NewR(SubSystem.NumVariables, 0);
+  NewR[0] = R[0];
+  for (unsigned Id = 1, E = R.size(); Id < E; ++Id)
+    if (R[Id] != 0)
+      NewR[OldToNew[Id]] = R[Id];
+  return {std::move(SubSystem), std::move(NewR)};
+}
+
 bool ConstraintSystem::isConditionImplied(SmallVector<int64_t, 8> R) const {
   // If all variable coefficients are 0, we have 'C >= 0'. If the constant is >=
   // 0, R is always true, regardless of the system.
@@ -233,7 +300,22 @@ bool ConstraintSystem::isConditionImplied(SmallVector<int64_t, 8> R) const {
   if (R.empty())
     return false;
 
-  auto NewSystem = *this;
-  NewSystem.addVariableRow(R);
-  return !NewSystem.mayHaveSolution();
+  auto Copy = *this;
+  Copy.addVariableRow(R);
+  return !Copy.mayHaveSolution();
+}
+
+bool ConstraintSystem::isConditionImpliedInSubSystem(
+    SmallVector<int64_t, 8> R) const {
+  if (R.empty())
+    return false;
+
+  // Queries with no variables are trivially decided without building any
+  // component.
+  if (all_of(ArrayRef(R).drop_front(1), equal_to(0)))
+    return R[0] >= 0;
+
+  // A single query: build the component and solve it in place.
+  const auto &[SubCS, NewR] = getSubSystem(R);
+  return SubCS.isConditionImplied(NewR);
 }
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index cdd15f04730ba..4c9c50589c23c 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -822,12 +822,13 @@ bool ConstraintTy::isValid(const ConstraintInfo &Info) const {
 
 std::optional<bool>
 ConstraintTy::isImpliedBy(const ConstraintSystem &CS) const {
-  bool IsConditionImplied = CS.isConditionImplied(Coefficients);
+  const auto &[SubCS, NewCoefficients] = CS.getSubSystem(Coefficients);
+  bool IsConditionImplied = SubCS.isConditionImplied(NewCoefficients);
 
   if (IsEq || IsNe) {
-    auto NegatedOrEqual = ConstraintSystem::negateOrEqual(Coefficients);
+    auto NegatedOrEqual = ConstraintSystem::negateOrEqual(NewCoefficients);
     bool IsNegatedOrEqualImplied =
-        !NegatedOrEqual.empty() && CS.isConditionImplied(NegatedOrEqual);
+        !NegatedOrEqual.empty() && SubCS.isConditionImplied(NegatedOrEqual);
 
     // In order to check that `%a == %b` is true (equality), both conditions `%a
     // >= %b` and `%a <= %b` must hold true. When checking for equality (`IsEq`
@@ -835,12 +836,13 @@ ConstraintTy::isImpliedBy(const ConstraintSystem &CS) const {
     if (IsConditionImplied && IsNegatedOrEqualImplied)
       return IsEq;
 
-    auto Negated = ConstraintSystem::negate(Coefficients);
-    bool IsNegatedImplied = !Negated.empty() && CS.isConditionImplied(Negated);
+    auto Negated = ConstraintSystem::negate(NewCoefficients);
+    bool IsNegatedImplied =
+        !Negated.empty() && SubCS.isConditionImplied(Negated);
 
-    auto StrictLessThan = ConstraintSystem::toStrictLessThan(Coefficients);
+    auto StrictLessThan = ConstraintSystem::toStrictLessThan(NewCoefficients);
     bool IsStrictLessThanImplied =
-        !StrictLessThan.empty() && CS.isConditionImplied(StrictLessThan);
+        !StrictLessThan.empty() && SubCS.isConditionImplied(StrictLessThan);
 
     // In order to check that `%a != %b` is true (non-equality), either
     // condition `%a > %b` or `%a < %b` must hold true. When checking for
@@ -855,8 +857,8 @@ ConstraintTy::isImpliedBy(const ConstraintSystem &CS) const {
   if (IsConditionImplied)
     return true;
 
-  auto Negated = ConstraintSystem::negate(Coefficients);
-  auto IsNegatedImplied = !Negated.empty() && CS.isConditionImplied(Negated);
+  auto Negated = ConstraintSystem::negate(NewCoefficients);
+  auto IsNegatedImplied = !Negated.empty() && SubCS.isConditionImplied(Negated);
   if (IsNegatedImplied)
     return false;
 
@@ -868,7 +870,7 @@ bool ConstraintInfo::doesHold(CmpInst::Predicate Pred, Value *A,
                               Value *B) const {
   auto R = getConstraintForSolving(Pred, A, B);
   return R.isValid(*this) &&
-         getCS(R.IsSigned).isConditionImplied(R.Coefficients);
+         getCS(R.IsSigned).isConditionImpliedInSubSystem(R.Coefficients);
 }
 
 void ConstraintInfo::transferToOtherSystem(
@@ -1866,7 +1868,7 @@ tryToSimplifyOverflowMath(IntrinsicInst *II, ConstraintInfo &Info,
       return false;
 
     auto &CSToUse = Info.getCS(R.IsSigned);
-    return CSToUse.isConditionImplied(R.Coefficients);
+    return CSToUse.isConditionImpliedInSubSystem(R.Coefficients);
   };
 
   bool Changed = false;
diff --git a/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-signed-predicates.ll b/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-signed-predicates.ll
index c9f4984bcba60..8c28b0b9079e9 100644
--- a/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-signed-predicates.ll
+++ b/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-signed-predicates.ll
@@ -616,7 +616,8 @@ define i4 @ptr_N_signed_positive_assume(ptr %src, ptr %lower, ptr %upper, i16 %N
 ; CHECK-NEXT:    [[SRC_STEP:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i16 [[STEP]]
 ; CHECK-NEXT:    [[CMP_STEP_START:%.*]] = icmp slt ptr [[SRC_STEP]], [[LOWER]]
 ; CHECK-NEXT:    [[CMP_STEP_END:%.*]] = icmp sge ptr [[SRC_STEP]], [[UPPER]]
-; CHECK-NEXT:    br i1 true, label [[TRAP_BB]], label [[EXIT]]
+; CHECK-NEXT:    [[OR_CHECK:%.*]] = or i1 [[CMP_STEP_START]], [[CMP_STEP_END]]
+; CHECK-NEXT:    br i1 [[OR_CHECK]], label [[TRAP_BB]], label [[EXIT]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret i4 3
 ;

>From c88770850dda9451f2278726b1343c902a858ea4 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sat, 18 Jul 2026 20:01:24 +0100
Subject: [PATCH 2/2] !fixup use set_bits

---
 llvm/lib/Analysis/ConstraintSystem.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Analysis/ConstraintSystem.cpp b/llvm/lib/Analysis/ConstraintSystem.cpp
index 57b440f771b2a..609d33ac1b3f7 100644
--- a/llvm/lib/Analysis/ConstraintSystem.cpp
+++ b/llvm/lib/Analysis/ConstraintSystem.cpp
@@ -259,9 +259,8 @@ ConstraintSystem::getSubSystem(ArrayRef<int64_t> R) const {
   SmallVector<unsigned, 16> OldToNew;
   OldToNew.assign(NumVariables + 1, 0);
   unsigned NextIdx = 1;
-  for (unsigned Id = 1; Id <= NumVariables; ++Id)
-    if (InSystem[Id])
-      OldToNew[Id] = NextIdx++;
+  for (unsigned Id : InSystem.set_bits())
+    OldToNew[Id] = NextIdx++;
 
   // Build new compact set of rows.
   SubSystem.NumVariables = NextIdx;



More information about the llvm-commits mailing list