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

via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 20 01:29:51 PDT 2026


Author: Florian Hahn
Date: 2026-07-20T08:29:46Z
New Revision: 72e0b55f9b1fd69030d7e9853a708bfd2fba36ec

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

LOG: [ConstraintSys] Solve sub-system with variables needed for query (#210432)

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

Note that we will now stop to simplify conditions in code we proved
dead/unreachable earlier; previously, unrelated constraints in the
system that form contradictions would allow proving any unrelated fact.
After pruning, unrelated facts will no longer contribute.

Added: 
    

Modified: 
    llvm/include/llvm/Analysis/ConstraintSystem.h
    llvm/lib/Analysis/ConstraintSystem.cpp
    llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
    llvm/test/Transforms/ConstraintElimination/gep-arithmetic-signed-predicates.ll

Removed: 
    


################################################################################
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..609d33ac1b3f7 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,66 @@ 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 : InSystem.set_bits())
+    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 +299,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 c0fb704306d36..3e52093248969 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(
@@ -1867,7 +1869,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
 ;


        


More information about the llvm-commits mailing list