[llvm] [ConstraintSystem] Build constraint rows in sparse form (NFC). (PR #215735)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 14 13:08:00 PDT 2026


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

>From 3eb9b7d604908dc5486f1bc405f379585d204852 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 14 Aug 2026 13:17:45 +0100
Subject: [PATCH 1/4] [ConstraintElimination] Fix trivially true compares with
 no variables.

getConstraintForSolving handles 'X uge 0' and '0 ule X' directly, returning a
constraint that is trivially true. The returned row was sized
Value2Index.size(), which is 0 if the unsigned system does not have any
variables yet, e.g. in a function without arguments. An empty row means the
condition could not be decomposed, so the fast path was discarded and the
compare not simplified.

Size the row to include the entry for the constant part, so it is never empty.
---
 .../Scalar/ConstraintElimination.cpp          |  8 ++++--
 .../Transforms/ConstraintElimination/shl.ll   |  6 ++--
 .../Transforms/ConstraintElimination/uge.ll   | 28 +++++++++++++++++++
 3 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index f77426f2b6322..c3c6e81065bac 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -822,9 +822,11 @@ ConstraintTy ConstraintInfo::getConstraintForSolving(CmpInst::Predicate Pred,
   if ((Pred == CmpInst::ICMP_ULE && Op0 == NullC) ||
       (Pred == CmpInst::ICMP_UGE && Op1 == NullC)) {
     auto &Value2Index = getValue2Index(false);
-    // Return constraint that's trivially true.
-    return ConstraintTy(SmallVector<int64_t, 8>(Value2Index.size(), 0), false,
-                        false, false);
+    // Return constraint that's trivially true. The row needs the entry for the
+    // constant part, even if the system does not have any variables yet;
+    // otherwise it would be empty and treated as a failed decomposition.
+    return ConstraintTy(SmallVector<int64_t, 8>(Value2Index.size() + 1, 0),
+                        false, false, false);
   }
 
   // If both operands are known to be non-negative, change signed predicates to
diff --git a/llvm/test/Transforms/ConstraintElimination/shl.ll b/llvm/test/Transforms/ConstraintElimination/shl.ll
index fe053120ac871..3affdd2a5291f 100644
--- a/llvm/test/Transforms/ConstraintElimination/shl.ll
+++ b/llvm/test/Transforms/ConstraintElimination/shl.ll
@@ -1240,8 +1240,7 @@ define i1 @shl_overflow_2() {
 ; CHECK-LABEL: @shl_overflow_2(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[SHL_UB:%.*]] = shl nuw nsw i256 0, 64
-; CHECK-NEXT:    [[SHL_CMP:%.*]] = icmp uge i256 [[SHL_UB]], 0
-; CHECK-NEXT:    ret i1 [[SHL_CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
 entry:
   %shl.ub = shl nuw nsw i256 0, 64
@@ -1253,8 +1252,7 @@ define i1 @shl_overflow_3() {
 ; CHECK-LABEL: @shl_overflow_3(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[SHL_UB:%.*]] = shl nuw nsw i256 0, 65
-; CHECK-NEXT:    [[SHL_CMP:%.*]] = icmp uge i256 [[SHL_UB]], 0
-; CHECK-NEXT:    ret i1 [[SHL_CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
 entry:
   %shl.ub = shl nuw nsw i256 0, 65
diff --git a/llvm/test/Transforms/ConstraintElimination/uge.ll b/llvm/test/Transforms/ConstraintElimination/uge.ll
index 2ac078eefe14b..c436906383805 100644
--- a/llvm/test/Transforms/ConstraintElimination/uge.ll
+++ b/llvm/test/Transforms/ConstraintElimination/uge.ll
@@ -241,3 +241,31 @@ bb2:
 exit:
   ret i8 20
 }
+
+declare i8 @get()
+
+; Trivially true compares against 0 must be simplified even if the unsigned
+; system does not have any variables yet, e.g. in a function without arguments.
+define i1 @uge_zero_no_function_args() {
+; CHECK-LABEL: @uge_zero_no_function_args(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[X:%.*]] = call i8 @get()
+; CHECK-NEXT:    ret i1 true
+;
+entry:
+  %x = call i8 @get()
+  %c = icmp uge i8 %x, 0
+  ret i1 %c
+}
+
+define i1 @ule_zero_no_function_args() {
+; CHECK-LABEL: @ule_zero_no_function_args(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[X:%.*]] = call i8 @get()
+; CHECK-NEXT:    ret i1 true
+;
+entry:
+  %x = call i8 @get()
+  %c = icmp ule i8 0, %x
+  ret i1 %c
+}

>From da6c956241422830cf2bfa90e9f62d379724d452 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 11 Aug 2026 19:36:34 +0100
Subject: [PATCH 2/4] [ConstraintSystem] Build constraint rows in sparse form
 (NFC).

ConstraintSystem stores its rows sparsely, as (coefficient, variable
index) entries ordered by index, but ConstraintElimination built every
row as a dense vector sized after the number of variables of the whole
system, which addVariableRow then scanned twice to convert back to the
sparse form. This causes a lot of unnecessary memory traffic, as the
size of the dense rows grows with the number of variables in the system.

Expose sparse RowTy from ConstraintSystem and update ConstraintElimination to
use that in ConstraintElimination. Getting coefficients during
constraint construction now needs a linear scan to find the right entry,
but the number of variables per constraint is very low in most cases, so
this is very quick in practice, and trying to sort the variables first
is more overhead.

Key invariants are:
 * the entries in a row are ordered by ID
 * the constant part is the first entry, and never gets dropped.

This improves compile time and more notably makes adding more
constraints more scalable. This will help to reduce the compile-time
impact of follow-up patches, that add more facts

 * stage1-O3: -0.04%
 * stage1-ReleaseThinLTO: -0.07%
 * stage1-ReleaseLTO-g: -0.06%
 * stage1-aarch64-O3: -0.04%
 * stage2-O3: -0.05%
 * clang: -0.08%

https://llvm-compile-time-tracker.com/compare.php?from=e580aa348b52b98f0643f9448ede377abc5eaae5&to=69ab8e078b6d125f87296dfa71e6a6b41be7dddc&stat=instructions:u
---
 llvm/include/llvm/Analysis/ConstraintSystem.h | 115 +++++++++--------
 llvm/lib/Analysis/ConstraintSystem.cpp        |  82 ++++++------
 .../Scalar/ConstraintElimination.cpp          | 120 +++++++++---------
 .../Analysis/ConstraintSystemTest.cpp         | 112 +++++++++-------
 4 files changed, 220 insertions(+), 209 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ConstraintSystem.h b/llvm/include/llvm/Analysis/ConstraintSystem.h
index a2bb1df191a62..ea08d356c827f 100644
--- a/llvm/include/llvm/Analysis/ConstraintSystem.h
+++ b/llvm/include/llvm/Analysis/ConstraintSystem.h
@@ -21,6 +21,7 @@ namespace llvm {
 
 class Value;
 class ConstraintSystem {
+public:
   struct Entry {
     int64_t Coefficient;
     uint16_t Id;
@@ -29,26 +30,38 @@ class ConstraintSystem {
         : Coefficient(Coefficient), Id(Id) {}
   };
 
-  static int64_t getConstPart(const Entry &E) {
-    if (E.Id == 0)
-      return E.Coefficient;
-    return 0;
+  /// A single constraint, storing only entries with non-zero coefficients,
+  /// ordered by increasing variable index. The constant part is kept even if
+  /// zero.
+  using RowTy = SmallVector<Entry, 8>;
+
+private:
+  /// Returns the coefficient of the variable with index \p Id in \p R, which
+  /// must be the last entry of \p R if present, and 0 otherwise.
+  static int64_t getLastCoefficient(ArrayRef<Entry> R, uint16_t Id) {
+    if (R.empty() || R.back().Id != Id)
+      return 0;
+    return R.back().Coefficient;
   }
 
-  static int64_t getLastCoefficient(ArrayRef<Entry> Row, uint16_t Id) {
-    if (Row.empty())
+  /// Returns true if \p R does not have an entry for any variable, i.e. it is
+  /// of the form 'c >= 0'.
+  static bool isConstantOnly(ArrayRef<Entry> R) {
+    return all_of(R, [](const Entry &E) { return E.Id == 0; });
+  }
+
+  /// Returns the constant part of \p R, which is 0 if \p R does not have an
+  /// entry for it.
+  static int64_t getConstant(ArrayRef<Entry> R) {
+    if (R.empty() || R.front().Id != 0)
       return 0;
-    if (Row.back().Id == Id)
-      return Row.back().Coefficient;
-    return 0;
+    return R.front().Coefficient;
   }
 
   size_t NumVariables = 0;
 
   /// Current linear constraints in the system.
-  /// An entry of the form c0, c1, ... cn represents the following constraint:
-  ///   c0 >= v0 * c1 + .... + v{n-1} * cn
-  SmallVector<SmallVector<Entry, 8>, 4> Constraints;
+  SmallVector<RowTy, 4> Constraints;
 
   /// A map of variables (IR values) to their corresponding index in the
   /// constraint system.
@@ -74,22 +87,20 @@ class ConstraintSystem {
   ConstraintSystem(const DenseMap<Value *, unsigned> &Value2Index)
       : NumVariables(Value2Index.size()), Value2Index(Value2Index) {}
 
-  bool addVariableRow(ArrayRef<int64_t> R) {
-    assert(Constraints.empty() || R.size() == NumVariables);
+  /// Add \p R to the system, where \p NumCols is the number of columns of \p R.
+  bool addRow(ArrayRef<Entry> R, size_t NumCols) {
     // If all variable coefficients are 0, the constraint does not provide any
     // usable information.
-    if (all_of(ArrayRef(R).drop_front(1), [](int64_t C) { return C == 0; }))
+    if (isConstantOnly(R))
       return false;
 
-    SmallVector<Entry, 4> NewRow;
-    for (const auto &[Idx, C] : enumerate(R)) {
-      if (C == 0)
-        continue;
-      NewRow.emplace_back(C, Idx);
-    }
-    if (Constraints.empty())
-      NumVariables = R.size();
-    Constraints.push_back(std::move(NewRow));
+    NumVariables = std::max(NumCols, NumVariables);
+    // Only keep non-zero coefficients; in particular drop the entry for the
+    // constant part if it is 0.
+    RowTy &NewRow = Constraints.emplace_back();
+    for (const Entry &E : R)
+      if (E.Coefficient != 0)
+        NewRow.push_back(E);
     return true;
   }
 
@@ -98,67 +109,55 @@ class ConstraintSystem {
     return Value2Index;
   }
 
-  bool addVariableRowFill(ArrayRef<int64_t> R) {
-    // If all variable coefficients are 0, the constraint does not provide any
-    // usable information.
-    if (all_of(ArrayRef(R).drop_front(1), [](int64_t C) { return C == 0; }))
-      return false;
-
-    NumVariables = std::max(R.size(), NumVariables);
-    return addVariableRow(R);
-  }
-
   /// Returns true if there may be a solution for the constraints in the system.
   LLVM_ABI bool mayHaveSolution();
 
-  static SmallVector<int64_t, 8> negate(SmallVector<int64_t, 8> R) {
+  static RowTy negate(RowTy R) {
+    assert(!R.empty() && R.front().Id == 0 && "row must have a constant entry");
     // The negated constraint R is obtained by multiplying by -1 and adding 1 to
     // the constant.
-    if (AddOverflow(R[0], int64_t(1), R[0]))
+    if (AddOverflow(R[0].Coefficient, int64_t(1), R[0].Coefficient))
       return {};
 
-    return negateOrEqual(R);
+    return negateOrEqual(std::move(R));
   }
 
-  /// Multiplies each coefficient in the given vector by -1. Does not modify the
-  /// original vector.
+  /// Multiplies each coefficient in the given row by -1. Does not modify the
+  /// original row.
   ///
-  /// \param R The vector of coefficients to be negated.
-  static SmallVector<int64_t, 8> negateOrEqual(SmallVector<int64_t, 8> R) {
+  /// \param R The row of coefficients to be negated.
+  static RowTy negateOrEqual(RowTy R) {
     // The negated constraint R is obtained by multiplying by -1.
-    for (auto &C : R)
-      if (MulOverflow(C, int64_t(-1), C))
+    for (Entry &E : R)
+      if (MulOverflow(E.Coefficient, int64_t(-1), E.Coefficient))
         return {};
     return R;
   }
 
-  /// Converts the given vector to form a strict less than inequality. Does not
-  /// modify the original vector.
+  /// Converts the given row to form a strict less than inequality. Does not
+  /// modify the original row.
   ///
-  /// \param R The vector of coefficients to be converted.
-  static SmallVector<int64_t, 8> toStrictLessThan(SmallVector<int64_t, 8> R) {
+  /// \param R The row of coefficients to be converted.
+  static RowTy toStrictLessThan(RowTy R) {
+    assert(!R.empty() && R.front().Id == 0 && "row must have a constant entry");
     // The strict less than is obtained by subtracting 1 from the constant.
-    if (SubOverflow(R[0], int64_t(1), R[0])) {
+    if (SubOverflow(R[0].Coefficient, int64_t(1), R[0].Coefficient))
       return {};
-    }
     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 std::pair<ConstraintSystem, RowTy>
+  getSubSystem(ArrayRef<Entry> R) const;
 
-  LLVM_ABI bool isConditionImplied(SmallVector<int64_t, 8> R) const;
-  LLVM_ABI bool isConditionImpliedInSubSystem(SmallVector<int64_t, 8> R) const;
+  LLVM_ABI bool isConditionImplied(RowTy R) const;
+  LLVM_ABI bool isConditionImpliedInSubSystem(ArrayRef<Entry> R) const;
 
-  SmallVector<int64_t> getLastConstraint() const {
+  const RowTy &getLastConstraint() const {
     assert(!Constraints.empty() && "Constraint system is empty");
-    SmallVector<int64_t> Result(NumVariables, 0);
-    for (auto &Entry : Constraints.back())
-      Result[Entry.Id] = Entry.Coefficient;
-    return Result;
+    return Constraints.back();
   }
 
   void popLastConstraint() { Constraints.pop_back(); }
diff --git a/llvm/lib/Analysis/ConstraintSystem.cpp b/llvm/lib/Analysis/ConstraintSystem.cpp
index 609d33ac1b3f7..acdc7893cec10 100644
--- a/llvm/lib/Analysis/ConstraintSystem.cpp
+++ b/llvm/lib/Analysis/ConstraintSystem.cpp
@@ -34,9 +34,9 @@ bool ConstraintSystem::eliminateUsingFM() {
 
   // First, either remove the variable in place if it is 0 or add the row to
   // RemainingRows and remove it from the system.
-  SmallVector<SmallVector<Entry, 8>, 4> RemainingRows;
+  SmallVector<RowTy, 4> RemainingRows;
   for (unsigned R1 = 0; R1 < Constraints.size();) {
-    SmallVector<Entry, 8> &Row1 = Constraints[R1];
+    RowTy &Row1 = Constraints[R1];
     if (getLastCoefficient(Row1, LastIdx) == 0) {
       if (Row1.size() > 0 && Row1.back().Id == LastIdx)
         Row1.pop_back();
@@ -75,7 +75,7 @@ bool ConstraintSystem::eliminateUsingFM() {
         std::swap(LowerLast, UpperLast);
       }
 
-      SmallVector<Entry, 8> NR;
+      RowTy NR;
       unsigned IdxUpper = 0;
       unsigned IdxLower = 0;
       auto &LowerRow = RemainingRows[LowerR];
@@ -160,16 +160,10 @@ bool ConstraintSystem::mayHaveSolutionImpl() {
       return true;
   }
 
-  if (Constraints.empty() || NumVariables > 1)
-    return true;
-
-  return all_of(Constraints, [](auto &R) {
-    if (R.empty())
-      return true;
-    if (R[0].Id == 0)
-      return R[0].Coefficient >= 0;
-    return true;
-  });
+  // All variables have been eliminated, so all remaining rows are of the form
+  // 'c >= 0'.
+  return all_of(Constraints,
+                [](ArrayRef<Entry> R) { return getConstant(R) >= 0; });
 }
 
 SmallVector<std::string> ConstraintSystem::getVarNamesList() const {
@@ -210,11 +204,7 @@ void ConstraintSystem::dump() const {
       Parts.push_back(Coefficient + Name);
     }
     // assert(!Parts.empty() && "need to have at least some parts");
-    int64_t ConstPart = 0;
-    if (Row[0].Id == 0)
-      ConstPart = Row[0].Coefficient;
-    LLVM_DEBUG(dbgs() << join(Parts, std::string(" + "))
-                      << " <= " << std::to_string(ConstPart) << "\n");
+    dbgs() << join(Parts, " + ") << " <= " << getConstant(Row) << "\n";
   }
 #endif
 }
@@ -227,8 +217,8 @@ bool ConstraintSystem::mayHaveSolution() {
   return HasSolution;
 }
 
-std::pair<ConstraintSystem, SmallVector<int64_t, 8>>
-ConstraintSystem::getSubSystem(ArrayRef<int64_t> R) const {
+std::pair<ConstraintSystem, ConstraintSystem::RowTy>
+ConstraintSystem::getSubSystem(ArrayRef<Entry> R) const {
   // Only constraints that share a variable (transitively) with a query R can
   // affect whether system + !R has a solution.
   //
@@ -236,16 +226,20 @@ ConstraintSystem::getSubSystem(ArrayRef<int64_t> R) const {
   // 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;
+  for (const Entry &E : R)
+    if (E.Id != 0)
+      InSystem[E.Id] = true;
+  auto SharesVariable = [&InSystem](ArrayRef<Entry> Row) {
+    return any_of(Row, [&InSystem](const Entry &E) {
+      return E.Id != 0 && InSystem[E.Id];
+    });
+  };
   bool Changed = true;
   while (Changed) {
     Changed = false;
-    for (const auto &Row : Constraints) {
+    for (const RowTy &Row : Constraints) {
       // No common variables, skip.
-      if (none_of(Row,
-                  [&](const Entry &E) { return E.Id != 0 && InSystem[E.Id]; }))
+      if (!SharesVariable(Row))
         continue;
       for (const Entry &E : Row)
         if (E.Id != 0 && !InSystem[E.Id]) {
@@ -256,19 +250,17 @@ ConstraintSystem::getSubSystem(ArrayRef<int64_t> R) const {
   }
 
   // Assign compact indices to the variables of the sub-system.
-  SmallVector<unsigned, 16> OldToNew;
-  OldToNew.assign(NumVariables + 1, 0);
+  SmallVector<unsigned, 16> OldToNew(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]; }))
+  for (const RowTy &Row : Constraints) {
+    if (!SharesVariable(Row))
       continue;
-    SmallVector<Entry, 8> NewRow;
+    RowTy NewRow;
     for (const Entry &E : Row) {
       if (!E.Id)
         NewRow.emplace_back(E.Coefficient, E.Id);
@@ -279,40 +271,38 @@ ConstraintSystem::getSubSystem(ArrayRef<int64_t> R) const {
   }
 
   // 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];
+  RowTy NewR(1, Entry(getConstant(R), 0));
+  for (const Entry &E : R)
+    if (E.Id != 0)
+      NewR.emplace_back(E.Coefficient, OldToNew[E.Id]);
   return {std::move(SubSystem), std::move(NewR)};
 }
 
-bool ConstraintSystem::isConditionImplied(SmallVector<int64_t, 8> R) const {
+bool ConstraintSystem::isConditionImplied(RowTy 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.
-  if (all_of(ArrayRef(R).drop_front(1), equal_to(0)))
-    return R[0] >= 0;
+  if (isConstantOnly(R))
+    return getConstant(R) >= 0;
 
   // If there is no solution with the negation of R added to the system, the
   // condition must hold based on the existing constraints.
-  R = ConstraintSystem::negate(R);
+  R = ConstraintSystem::negate(std::move(R));
   if (R.empty())
     return false;
 
   auto Copy = *this;
-  Copy.addVariableRow(R);
+  Copy.addRow(R, NumVariables);
   return !Copy.mayHaveSolution();
 }
 
-bool ConstraintSystem::isConditionImpliedInSubSystem(
-    SmallVector<int64_t, 8> R) const {
+bool ConstraintSystem::isConditionImpliedInSubSystem(ArrayRef<Entry> 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;
+  if (isConstantOnly(R))
+    return getConstant(R) >= 0;
 
   // A single query: build the component and solve it in place.
   const auto &[SubCS, NewR] = getSubSystem(R);
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index c3c6e81065bac..f489196fb68e9 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -76,6 +76,9 @@ static Instruction *getContextInstForUse(Use &U) {
 }
 
 namespace {
+using Entry = ConstraintSystem::Entry;
+using RowTy = ConstraintSystem::RowTy;
+
 /// Struct to express a condition of the form %Op0 Pred %Op1.
 struct ConditionTy {
   CmpPredicate Pred;
@@ -212,20 +215,22 @@ struct StackEntry {
 };
 
 struct ConstraintTy {
-  SmallVector<int64_t, 8> Coefficients;
+  RowTy Coefficients;
+
+  /// Number of columns the constraint is defined over, i.e. the number of
+  /// variables of the system it was built for plus one for the constant.
+  unsigned NumCols = 0;
 
   bool IsSigned = false;
 
   ConstraintTy() = default;
 
-  ConstraintTy(SmallVector<int64_t, 8> Coefficients, bool IsSigned, bool IsEq,
+  ConstraintTy(RowTy Coefficients, unsigned NumCols, bool IsSigned, bool IsEq,
                bool IsNe)
-      : Coefficients(std::move(Coefficients)), IsSigned(IsSigned), IsEq(IsEq),
-        IsNe(IsNe) {}
-
-  unsigned size() const { return Coefficients.size(); }
+      : Coefficients(std::move(Coefficients)), NumCols(NumCols),
+        IsSigned(IsSigned), IsEq(IsEq), IsNe(IsNe) {}
 
-  bool empty() const { return Coefficients.empty(); }
+  bool empty() const { return NumCols == 0; }
 
   bool isEq() const { return IsEq; }
 
@@ -261,12 +266,9 @@ class ConstraintInfo {
       : UnsignedCS(FunctionArgs), SignedCS(FunctionArgs), DL(DL) {
     auto &Value2Index = getValue2Index(false);
     // Add Arg > -1 constraints to unsigned system for all function arguments.
-    for (Value *Arg : FunctionArgs) {
-      ConstraintTy VarPos(SmallVector<int64_t, 8>(Value2Index.size() + 1, 0),
-                          false, false, false);
-      VarPos.Coefficients[Value2Index[Arg]] = -1;
-      UnsignedCS.addVariableRow(VarPos.Coefficients);
-    }
+    for (Value *Arg : FunctionArgs)
+      UnsignedCS.addRow({Entry(0, 0), Entry(-1, Value2Index.at(Arg))},
+                        Value2Index.size() + 1);
   }
 
   DenseMap<Value *, unsigned> &getValue2Index(bool Signed) {
@@ -759,34 +761,34 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
 
   // First try to look up \p V in Value2Index and NewVariables. Otherwise add a
   // new entry to NewVariables.
-  SmallDenseMap<Value *, unsigned> NewIndexMap;
-  auto GetOrAddIndex = [&Value2Index, &NewVariables,
-                        &NewIndexMap](Value *V) -> unsigned {
+  auto GetOrAddIndex = [&Value2Index, &NewVariables](Value *V) -> unsigned {
     auto V2I = Value2Index.find(V);
     if (V2I != Value2Index.end())
       return V2I->second;
-    auto [It, Inserted] = NewIndexMap.try_emplace(
-        V, Value2Index.size() + NewVariables.size() + 1);
-    if (Inserted)
+    unsigned Idx = find(NewVariables, V) - NewVariables.begin();
+    if (Idx == NewVariables.size())
       NewVariables.push_back(V);
-    return It->second;
+    return Value2Index.size() + Idx + 1;
   };
 
-  // Make sure all variables have entries in Value2Index or NewVariables.
-  for (const auto &KV : concat<DecompEntry>(VariablesA, VariablesB))
-    GetOrAddIndex(KV.Variable);
-
   // Build result constraint, by first adding all coefficients from A and then
   // subtracting all coefficients from B.
-  ConstraintTy Res(
-      SmallVector<int64_t, 8>(Value2Index.size() + NewVariables.size() + 1, 0),
-      IsSigned, IsEq, IsNe);
-  auto &R = Res.Coefficients;
+  RowTy R(1, Entry(0, 0));
+  // Returns a reference to the coefficient for the variable with index Idx.
+  auto GetCoefficient = [&R](unsigned Idx) -> int64_t & {
+    // The entry for Idx, or the place to insert it at, is the first entry with
+    // an index >= Idx.
+    Entry *I =
+        find_if(drop_begin(R), [Idx](const Entry &E) { return E.Id >= Idx; });
+    if (I == R.end() || I->Id != Idx)
+      I = R.insert(I, Entry(0, Idx));
+    return I->Coefficient;
+  };
   for (const auto &KV : VariablesA)
-    R[GetOrAddIndex(KV.Variable)] += KV.Coefficient;
+    GetCoefficient(GetOrAddIndex(KV.Variable)) += KV.Coefficient;
 
   for (const auto &KV : VariablesB) {
-    auto &Coeff = R[GetOrAddIndex(KV.Variable)];
+    auto &Coeff = GetCoefficient(GetOrAddIndex(KV.Variable));
     if (SubOverflow(Coeff, KV.Coefficient, Coeff))
       return {};
   }
@@ -797,20 +799,19 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
   if (Pred == CmpInst::ICMP_SLT || Pred == CmpInst::ICMP_ULT)
     if (AddOverflow(OffsetSum, int64_t(-1), OffsetSum))
       return {};
-  R[0] = OffsetSum;
+  R[0].Coefficient = OffsetSum;
 
-  // Remove any (Coefficient, Variable) entry where the Coefficient is 0 for new
-  // variables.
-  while (!NewVariables.empty()) {
-    int64_t Last = R.back();
-    if (Last != 0)
-      break;
-    R.pop_back();
-    Value *RemovedV = NewVariables.pop_back_val();
-    NewIndexMap.erase(RemovedV);
-  }
+  // Drop coefficients that cancelled out.
+  erase_if(R, [](const Entry &E) { return E.Id != 0 && E.Coefficient == 0; });
+
+  // Remove any new variable without a coefficient in the row.
+  while (!NewVariables.empty() &&
+         R.back().Id < Value2Index.size() + NewVariables.size())
+    NewVariables.pop_back();
 
-  return Res;
+  return ConstraintTy(std::move(R),
+                      Value2Index.size() + NewVariables.size() + 1, IsSigned,
+                      IsEq, IsNe);
 }
 
 ConstraintTy ConstraintInfo::getConstraintForSolving(CmpInst::Predicate Pred,
@@ -821,12 +822,10 @@ ConstraintTy ConstraintInfo::getConstraintForSolving(CmpInst::Predicate Pred,
   // for all variables in the unsigned system.
   if ((Pred == CmpInst::ICMP_ULE && Op0 == NullC) ||
       (Pred == CmpInst::ICMP_UGE && Op1 == NullC)) {
-    auto &Value2Index = getValue2Index(false);
-    // Return constraint that's trivially true. The row needs the entry for the
-    // constant part, even if the system does not have any variables yet;
-    // otherwise it would be empty and treated as a failed decomposition.
-    return ConstraintTy(SmallVector<int64_t, 8>(Value2Index.size() + 1, 0),
-                        false, false, false);
+    // Return constraint that's trivially true.
+    return ConstraintTy(RowTy(1, Entry(0, 0)),
+                        getValue2Index(false).size() + 1, /*IsSigned=*/false,
+                        /*IsEq=*/false, /*IsNe=*/false);
   }
 
   // If both operands are known to be non-negative, change signed predicates to
@@ -959,10 +958,10 @@ void ConstraintInfo::transferToOtherSystem(
 
 #ifndef NDEBUG
 
-static void dumpConstraint(ArrayRef<int64_t> C,
+static void dumpConstraint(ArrayRef<Entry> C,
                            const DenseMap<Value *, unsigned> &Value2Index) {
   ConstraintSystem CS(Value2Index);
-  CS.addVariableRowFill(C);
+  CS.addRow(C, Value2Index.size() + 1);
   CS.dump();
 }
 #endif
@@ -1878,10 +1877,7 @@ void ConstraintInfo::addFactImpl(CmpInst::Predicate Pred, Value *A, Value *B,
   LLVM_DEBUG(dbgs() << "Adding '"; dumpUnpackedICmp(dbgs(), Pred, A, B);
              dbgs() << "'\n");
   auto &CSToUse = getCS(R.IsSigned);
-  if (R.Coefficients.empty())
-    return;
-
-  bool Added = CSToUse.addVariableRowFill(R.Coefficients);
+  bool Added = CSToUse.addRow(R.Coefficients, R.NumCols);
   if (!Added)
     return;
 
@@ -1905,10 +1901,9 @@ void ConstraintInfo::addFactImpl(CmpInst::Predicate Pred, Value *A, Value *B,
 
   if (!R.IsSigned) {
     for (Value *V : NewVariables) {
-      ConstraintTy VarPos(SmallVector<int64_t, 8>(Value2Index.size() + 1, 0),
-                          false, false, false);
-      VarPos.Coefficients[Value2Index[V]] = -1;
-      CSToUse.addVariableRow(VarPos.Coefficients);
+      // Add V > -1 constraints for all new variables.
+      CSToUse.addRow({Entry(0, 0), Entry(-1, Value2Index.at(V))},
+                     Value2Index.size() + 1);
       DFSInStack.emplace_back(NumIn, NumOut, R.IsSigned,
                               SmallVector<Value *, 2>());
     }
@@ -1916,10 +1911,10 @@ void ConstraintInfo::addFactImpl(CmpInst::Predicate Pred, Value *A, Value *B,
 
   if (R.isEq()) {
     // Also add the inverted constraint for equality constraints.
-    for (auto &Coeff : R.Coefficients)
-      if (MulOverflow(Coeff, int64_t(-1), Coeff))
+    for (Entry &E : R.Coefficients)
+      if (MulOverflow(E.Coefficient, int64_t(-1), E.Coefficient))
         return;
-    CSToUse.addVariableRowFill(R.Coefficients);
+    CSToUse.addRow(R.Coefficients, R.NumCols);
 
     DFSInStack.emplace_back(NumIn, NumOut, R.IsSigned,
                             SmallVector<Value *, 2>());
@@ -1967,7 +1962,8 @@ tryToSimplifyOverflowMath(IntrinsicInst *II, ConstraintInfo &Info,
   auto DoesConditionHold = [](CmpInst::Predicate Pred, Value *A, Value *B,
                               ConstraintInfo &Info) {
     auto R = Info.getConstraintForSolving(Pred, A, B);
-    if (R.size() < 2)
+    // Nothing can be proven if the system has no variables.
+    if (R.NumCols < 2)
       return false;
 
     auto &CSToUse = Info.getCS(R.IsSigned);
diff --git a/llvm/unittests/Analysis/ConstraintSystemTest.cpp b/llvm/unittests/Analysis/ConstraintSystemTest.cpp
index febeb982c87af..710f465803d82 100644
--- a/llvm/unittests/Analysis/ConstraintSystemTest.cpp
+++ b/llvm/unittests/Analysis/ConstraintSystemTest.cpp
@@ -7,21 +7,47 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Analysis/ConstraintSystem.h"
+#include "llvm/ADT/STLExtras.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
 
 namespace {
 
+using RowTy = ConstraintSystem::RowTy;
+
+/// Convert the dense coefficient vector \p R, indexed by variable with the
+/// constant part at index 0, to a row.
+static RowTy toRow(ArrayRef<int64_t> R) {
+  RowTy Row;
+  Row.emplace_back(R[0], 0);
+  for (auto [Idx, C] : enumerate(R.drop_front()))
+    if (C != 0)
+      Row.emplace_back(C, Idx + 1);
+  return Row;
+}
+
+/// Add the dense coefficient vector \p R to \p CS.
+static void addVariableRow(ConstraintSystem &CS, ArrayRef<int64_t> R) {
+  CS.addRow(toRow(R), R.size());
+}
+
+/// Returns true if the condition described by the dense coefficient vector \p R
+/// is implied by \p CS.
+static bool isConditionImplied(const ConstraintSystem &CS,
+                               ArrayRef<int64_t> R) {
+  return CS.isConditionImplied(toRow(R));
+}
+
 TEST(ConstraintSolverTest, TestSolutionChecks) {
   {
     ConstraintSystem CS;
     // x + y <= 10, x >= 5, y >= 6, x <= 10, y <= 10
-    CS.addVariableRow({10, 1, 1});
-    CS.addVariableRow({-5, -1, 0});
-    CS.addVariableRow({-6, 0, -1});
-    CS.addVariableRow({10, 1, 0});
-    CS.addVariableRow({10, 0, 1});
+    addVariableRow(CS, {10, 1, 1});
+    addVariableRow(CS, {-5, -1, 0});
+    addVariableRow(CS, {-6, 0, -1});
+    addVariableRow(CS, {10, 1, 0});
+    addVariableRow(CS, {10, 0, 1});
 
     EXPECT_FALSE(CS.mayHaveSolution());
   }
@@ -29,11 +55,11 @@ TEST(ConstraintSolverTest, TestSolutionChecks) {
   {
     ConstraintSystem CS;
     // x + y <= 10, x >= 2, y >= 3, x <= 10, y <= 10
-    CS.addVariableRow({10, 1, 1});
-    CS.addVariableRow({-2, -1, 0});
-    CS.addVariableRow({-3, 0, -1});
-    CS.addVariableRow({10, 1, 0});
-    CS.addVariableRow({10, 0, 1});
+    addVariableRow(CS, {10, 1, 1});
+    addVariableRow(CS, {-2, -1, 0});
+    addVariableRow(CS, {-3, 0, -1});
+    addVariableRow(CS, {10, 1, 0});
+    addVariableRow(CS, {10, 0, 1});
 
     EXPECT_TRUE(CS.mayHaveSolution());
   }
@@ -41,9 +67,9 @@ TEST(ConstraintSolverTest, TestSolutionChecks) {
   {
     ConstraintSystem CS;
     // x + y <= 10, x >= 10, y >= 10; does not have a solution.
-    CS.addVariableRow({10, 1, 1});
-    CS.addVariableRow({-10, -1, 0});
-    CS.addVariableRow({-10, 0, -1});
+    addVariableRow(CS, {10, 1, 1});
+    addVariableRow(CS, {-10, -1, 0});
+    addVariableRow(CS, {-10, 0, -1});
 
     EXPECT_FALSE(CS.mayHaveSolution());
   }
@@ -51,9 +77,9 @@ TEST(ConstraintSolverTest, TestSolutionChecks) {
   {
     ConstraintSystem CS;
     // x + y >= 20, 10 >= x, 10 >= y; does HAVE a solution.
-    CS.addVariableRow({-20, -1, -1});
-    CS.addVariableRow({-10, -1, 0});
-    CS.addVariableRow({-10, 0, -1});
+    addVariableRow(CS, {-20, -1, -1});
+    addVariableRow(CS, {-10, -1, 0});
+    addVariableRow(CS, {-10, 0, -1});
 
     EXPECT_TRUE(CS.mayHaveSolution());
   }
@@ -62,9 +88,9 @@ TEST(ConstraintSolverTest, TestSolutionChecks) {
     ConstraintSystem CS;
 
     // 2x + y + 3z <= 10,  2x + y >= 10, y >= 1
-    CS.addVariableRow({10, 2, 1, 3});
-    CS.addVariableRow({-10, -2, -1, 0});
-    CS.addVariableRow({-1, 0, 0, -1});
+    addVariableRow(CS, {10, 2, 1, 3});
+    addVariableRow(CS, {-10, -2, -1, 0});
+    addVariableRow(CS, {-1, 0, 0, -1});
 
     EXPECT_FALSE(CS.mayHaveSolution());
   }
@@ -73,8 +99,8 @@ TEST(ConstraintSolverTest, TestSolutionChecks) {
     ConstraintSystem CS;
 
     // 2x + y + 3z <= 10,  2x + y >= 10
-    CS.addVariableRow({10, 2, 1, 3});
-    CS.addVariableRow({-10, -2, -1, 0});
+    addVariableRow(CS, {10, 2, 1, 3});
+    addVariableRow(CS, {-10, -2, -1, 0});
 
     EXPECT_TRUE(CS.mayHaveSolution());
   }
@@ -85,47 +111,47 @@ TEST(ConstraintSolverTest, IsConditionImplied) {
     // For the test below, we assume we know
     // x <= 5 && y <= 3
     ConstraintSystem CS;
-    CS.addVariableRow({5, 1, 0});
-    CS.addVariableRow({3, 0, 1});
+    addVariableRow(CS, {5, 1, 0});
+    addVariableRow(CS, {3, 0, 1});
 
     // x + y <= 6 does not hold.
-    EXPECT_FALSE(CS.isConditionImplied({6, 1, 1}));
+    EXPECT_FALSE(isConditionImplied(CS, {6, 1, 1}));
     // x + y <= 7 does not hold.
-    EXPECT_FALSE(CS.isConditionImplied({7, 1, 1}));
+    EXPECT_FALSE(isConditionImplied(CS, {7, 1, 1}));
     // x + y <= 8 does hold.
-    EXPECT_TRUE(CS.isConditionImplied({8, 1, 1}));
+    EXPECT_TRUE(isConditionImplied(CS, {8, 1, 1}));
 
     // 2 * x + y <= 12 does hold.
-    EXPECT_FALSE(CS.isConditionImplied({12, 2, 1}));
+    EXPECT_FALSE(isConditionImplied(CS, {12, 2, 1}));
     // 2 * x + y <= 13 does hold.
-    EXPECT_TRUE(CS.isConditionImplied({13, 2, 1}));
+    EXPECT_TRUE(isConditionImplied(CS, {13, 2, 1}));
 
     //  x + y <= 12 does hold.
-    EXPECT_FALSE(CS.isConditionImplied({12, 2, 1}));
+    EXPECT_FALSE(isConditionImplied(CS, {12, 2, 1}));
     // 2 * x + y <= 13 does hold.
-    EXPECT_TRUE(CS.isConditionImplied({13, 2, 1}));
+    EXPECT_TRUE(isConditionImplied(CS, {13, 2, 1}));
 
     // x <= y == x - y <= 0 does not hold.
-    EXPECT_FALSE(CS.isConditionImplied({0, 1, -1}));
+    EXPECT_FALSE(isConditionImplied(CS, {0, 1, -1}));
     // y <= x == -x + y <= 0 does not hold.
-    EXPECT_FALSE(CS.isConditionImplied({0, -1, 1}));
+    EXPECT_FALSE(isConditionImplied(CS, {0, -1, 1}));
   }
 
   {
     // For the test below, we assume we know
     // x + 1 <= y + 1 == x - y <= 0
     ConstraintSystem CS;
-    CS.addVariableRow({0, 1, -1});
+    addVariableRow(CS, {0, 1, -1});
 
     // x <= y == x - y <= 0 does hold.
-    EXPECT_TRUE(CS.isConditionImplied({0, 1, -1}));
+    EXPECT_TRUE(isConditionImplied(CS, {0, 1, -1}));
     // y <= x == -x + y <= 0 does not hold.
-    EXPECT_FALSE(CS.isConditionImplied({0, -1, 1}));
+    EXPECT_FALSE(isConditionImplied(CS, {0, -1, 1}));
 
     // x <= y + 10 == x - y <= 10 does hold.
-    EXPECT_TRUE(CS.isConditionImplied({10, 1, -1}));
+    EXPECT_TRUE(isConditionImplied(CS, {10, 1, -1}));
     // x + 10 <= y == x - y <= -10 does NOT hold.
-    EXPECT_FALSE(CS.isConditionImplied({-10, 1, -1}));
+    EXPECT_FALSE(isConditionImplied(CS, {-10, 1, -1}));
   }
 
   {
@@ -133,13 +159,13 @@ TEST(ConstraintSolverTest, IsConditionImplied) {
     // x <= y == x - y <= 0
     // y <= z == y - x <= 0
     ConstraintSystem CS;
-    CS.addVariableRow({0, 1, -1, 0});
-    CS.addVariableRow({0, 0, 1, -1});
+    addVariableRow(CS, {0, 1, -1, 0});
+    addVariableRow(CS, {0, 0, 1, -1});
 
     // z <= y == -y + z <= 0 does not hold.
-    EXPECT_FALSE(CS.isConditionImplied({0, 0, -1, 1}));
+    EXPECT_FALSE(isConditionImplied(CS, {0, 0, -1, 1}));
     // x <= z == x - z <= 0 does hold.
-    EXPECT_TRUE(CS.isConditionImplied({0, 1, 0, -1}));
+    EXPECT_TRUE(isConditionImplied(CS, {0, 1, 0, -1}));
   }
 }
 
@@ -147,7 +173,7 @@ TEST(ConstraintSolverTest, IsConditionImpliedOverflow) {
   ConstraintSystem CS;
   // Make sure isConditionImplied returns false when there is an overflow.
   int64_t Limit = std::numeric_limits<int64_t>::max();
-  CS.addVariableRow({Limit - 1, Limit - 2, Limit - 3});
-  EXPECT_FALSE(CS.isConditionImplied({Limit - 1, Limit - 2, Limit - 3}));
+  addVariableRow(CS, {Limit - 1, Limit - 2, Limit - 3});
+  EXPECT_FALSE(isConditionImplied(CS, {Limit - 1, Limit - 2, Limit - 3}));
 }
 } // namespace

>From f8a5e1dc2034f01733147689b7fd83bd7a42db5a Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 13 Aug 2026 09:48:45 +0100
Subject: [PATCH 3/4] !fixup address comments

---
 llvm/include/llvm/Analysis/ConstraintSystem.h        | 7 ++++---
 llvm/lib/Transforms/Scalar/ConstraintElimination.cpp | 5 ++---
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ConstraintSystem.h b/llvm/include/llvm/Analysis/ConstraintSystem.h
index ea08d356c827f..7bca38a905a31 100644
--- a/llvm/include/llvm/Analysis/ConstraintSystem.h
+++ b/llvm/include/llvm/Analysis/ConstraintSystem.h
@@ -31,8 +31,7 @@ class ConstraintSystem {
   };
 
   /// A single constraint, storing only entries with non-zero coefficients,
-  /// ordered by increasing variable index. The constant part is kept even if
-  /// zero.
+  /// ordered by increasing variable index.
   using RowTy = SmallVector<Entry, 8>;
 
 private:
@@ -47,7 +46,7 @@ class ConstraintSystem {
   /// Returns true if \p R does not have an entry for any variable, i.e. it is
   /// of the form 'c >= 0'.
   static bool isConstantOnly(ArrayRef<Entry> R) {
-    return all_of(R, [](const Entry &E) { return E.Id == 0; });
+    return R.empty() || (R.size() == 1 && R.front().Id == 0);
   }
 
   /// Returns the constant part of \p R, which is 0 if \p R does not have an
@@ -61,6 +60,8 @@ class ConstraintSystem {
   size_t NumVariables = 0;
 
   /// Current linear constraints in the system.
+  /// Each entry represents a constraint like
+  ///   c0 >= v0 * c1 + .... + v{n-1} * cn
   SmallVector<RowTy, 4> Constraints;
 
   /// A map of variables (IR values) to their corresponding index in the
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index f489196fb68e9..5053cc30027bc 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -805,9 +805,8 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
   erase_if(R, [](const Entry &E) { return E.Id != 0 && E.Coefficient == 0; });
 
   // Remove any new variable without a coefficient in the row.
-  while (!NewVariables.empty() &&
-         R.back().Id < Value2Index.size() + NewVariables.size())
-    NewVariables.pop_back();
+  if (R.back().Id >= Value2Index.size())
+    NewVariables.resize(R.back().Id - Value2Index.size());
 
   return ConstraintTy(std::move(R),
                       Value2Index.size() + NewVariables.size() + 1, IsSigned,

>From 5e499235afaf1bd4a896373510ad98c3d4a05dbe Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 14 Aug 2026 20:49:31 +0100
Subject: [PATCH 4/4] !fixup clarify NumVars, asserts

---
 llvm/include/llvm/Analysis/ConstraintSystem.h | 36 ++++++++-------
 llvm/lib/Analysis/ConstraintSystem.cpp        | 23 +++++-----
 .../Scalar/ConstraintElimination.cpp          | 45 ++++++++++---------
 .../Analysis/ConstraintSystemTest.cpp         |  2 +-
 4 files changed, 54 insertions(+), 52 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ConstraintSystem.h b/llvm/include/llvm/Analysis/ConstraintSystem.h
index 7bca38a905a31..11f40de9968c5 100644
--- a/llvm/include/llvm/Analysis/ConstraintSystem.h
+++ b/llvm/include/llvm/Analysis/ConstraintSystem.h
@@ -30,19 +30,21 @@ class ConstraintSystem {
         : Coefficient(Coefficient), Id(Id) {}
   };
 
-  /// A single constraint, storing only entries with non-zero coefficients,
-  /// ordered by increasing variable index.
+  /// A single constraint of the form 'c >= v1 * c1 + ... + vn * cn'.
   using RowTy = SmallVector<Entry, 8>;
 
 private:
-  /// Returns the coefficient of the variable with index \p Id in \p R, which
-  /// must be the last entry of \p R if present, and 0 otherwise.
   static int64_t getLastCoefficient(ArrayRef<Entry> R, uint16_t Id) {
     if (R.empty() || R.back().Id != Id)
       return 0;
     return R.back().Coefficient;
   }
 
+  /// Returns true if \p R has an entry for the constant part.
+  static bool hasConstantEntry(ArrayRef<Entry> R) {
+    return !R.empty() && R.front().Id == 0;
+  }
+
   /// Returns true if \p R does not have an entry for any variable, i.e. it is
   /// of the form 'c >= 0'.
   static bool isConstantOnly(ArrayRef<Entry> R) {
@@ -52,11 +54,11 @@ class ConstraintSystem {
   /// Returns the constant part of \p R, which is 0 if \p R does not have an
   /// entry for it.
   static int64_t getConstant(ArrayRef<Entry> R) {
-    if (R.empty() || R.front().Id != 0)
-      return 0;
-    return R.front().Coefficient;
+    return hasConstantEntry(R) ? R.front().Coefficient : 0;
   }
 
+  /// Number of variables in the system, not counting the constant part. The
+  /// variables use the indices 1 to NumVariables.
   size_t NumVariables = 0;
 
   /// Current linear constraints in the system.
@@ -88,14 +90,14 @@ class ConstraintSystem {
   ConstraintSystem(const DenseMap<Value *, unsigned> &Value2Index)
       : NumVariables(Value2Index.size()), Value2Index(Value2Index) {}
 
-  /// Add \p R to the system, where \p NumCols is the number of columns of \p R.
-  bool addRow(ArrayRef<Entry> R, size_t NumCols) {
+  bool addRow(ArrayRef<Entry> R, size_t NumVars) {
     // If all variable coefficients are 0, the constraint does not provide any
     // usable information.
     if (isConstantOnly(R))
       return false;
 
-    NumVariables = std::max(NumCols, NumVariables);
+    assert(NumVars >= R.back().Id && "NumVars must cover all variables in R");
+    NumVariables = std::max(NumVars, NumVariables);
     // Only keep non-zero coefficients; in particular drop the entry for the
     // constant part if it is 0.
     RowTy &NewRow = Constraints.emplace_back();
@@ -114,7 +116,7 @@ class ConstraintSystem {
   LLVM_ABI bool mayHaveSolution();
 
   static RowTy negate(RowTy R) {
-    assert(!R.empty() && R.front().Id == 0 && "row must have a constant entry");
+    assert(hasConstantEntry(R) && "row must have a constant entry");
     // The negated constraint R is obtained by multiplying by -1 and adding 1 to
     // the constant.
     if (AddOverflow(R[0].Coefficient, int64_t(1), R[0].Coefficient))
@@ -123,8 +125,8 @@ class ConstraintSystem {
     return negateOrEqual(std::move(R));
   }
 
-  /// Multiplies each coefficient in the given row by -1. Does not modify the
-  /// original row.
+  /// Multiplies each coefficient in the given row by -1. Returns an empty row
+  /// on overflow. Does not modify the original row.
   ///
   /// \param R The row of coefficients to be negated.
   static RowTy negateOrEqual(RowTy R) {
@@ -135,12 +137,12 @@ class ConstraintSystem {
     return R;
   }
 
-  /// Converts the given row to form a strict less than inequality. Does not
-  /// modify the original row.
+  /// Converts the given row to form a strict less than inequality. Returns an
+  /// empty row on overflow. Does not modify the original row.
   ///
   /// \param R The row of coefficients to be converted.
   static RowTy toStrictLessThan(RowTy R) {
-    assert(!R.empty() && R.front().Id == 0 && "row must have a constant entry");
+    assert(hasConstantEntry(R) && "row must have a constant entry");
     // The strict less than is obtained by subtracting 1 from the constant.
     if (SubOverflow(R[0].Coefficient, int64_t(1), R[0].Coefficient))
       return {};
@@ -163,7 +165,7 @@ class ConstraintSystem {
 
   void popLastConstraint() { Constraints.pop_back(); }
   void popLastNVariables(unsigned N) {
-    assert(NumVariables > N);
+    assert(NumVariables >= N);
     NumVariables -= N;
   }
 
diff --git a/llvm/lib/Analysis/ConstraintSystem.cpp b/llvm/lib/Analysis/ConstraintSystem.cpp
index acdc7893cec10..56cb646093f83 100644
--- a/llvm/lib/Analysis/ConstraintSystem.cpp
+++ b/llvm/lib/Analysis/ConstraintSystem.cpp
@@ -30,7 +30,7 @@ bool ConstraintSystem::eliminateUsingFM() {
   assert(!Constraints.empty() &&
          "should only be called for non-empty constraint systems");
 
-  unsigned LastIdx = NumVariables - 1;
+  unsigned LastIdx = NumVariables;
 
   // First, either remove the variable in place if it is 0 or add the row to
   // RemainingRows and remove it from the system.
@@ -155,13 +155,13 @@ bool ConstraintSystem::eliminateUsingFM() {
 }
 
 bool ConstraintSystem::mayHaveSolutionImpl() {
-  while (!Constraints.empty() && NumVariables > 1) {
+  while (!Constraints.empty() && NumVariables > 0) {
     if (!eliminateUsingFM())
       return true;
   }
 
-  // All variables have been eliminated, so all remaining rows are of the form
-  // 'c >= 0'.
+  assert((Constraints.empty() || NumVariables == 0) &&
+         "non-empty system must have all variables eliminated");
   return all_of(Constraints,
                 [](ArrayRef<Entry> R) { return getConstant(R) >= 0; });
 }
@@ -189,7 +189,7 @@ void ConstraintSystem::dump() const {
   for (const auto &Row : Constraints) {
     SmallVector<std::string, 16> Parts;
     for (const Entry &E : Row) {
-      if (E.Id >= NumVariables)
+      if (E.Id > NumVariables)
         break;
       if (E.Id == 0)
         continue;
@@ -203,8 +203,8 @@ void ConstraintSystem::dump() const {
         Coefficient = std::to_string(E.Coefficient) + " * ";
       Parts.push_back(Coefficient + Name);
     }
-    // assert(!Parts.empty() && "need to have at least some parts");
-    dbgs() << join(Parts, " + ") << " <= " << getConstant(Row) << "\n";
+    LLVM_DEBUG(dbgs() << join(Parts, " + ") << " <= " << getConstant(Row)
+                      << "\n");
   }
 #endif
 }
@@ -256,16 +256,15 @@ ConstraintSystem::getSubSystem(ArrayRef<Entry> R) const {
     OldToNew[Id] = NextIdx++;
 
   // Build new compact set of rows.
-  SubSystem.NumVariables = NextIdx;
+  SubSystem.NumVariables = NextIdx - 1;
   for (const RowTy &Row : Constraints) {
     if (!SharesVariable(Row))
       continue;
     RowTy 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);
+      unsigned New = OldToNew[E.Id];
+      assert((E.Id == 0) == (New == 0) && "constant entry must be preserved");
+      NewRow.emplace_back(E.Coefficient, New);
     }
     SubSystem.Constraints.push_back(std::move(NewRow));
   }
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 5053cc30027bc..1b88009dbc8ed 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -217,20 +217,23 @@ struct StackEntry {
 struct ConstraintTy {
   RowTy Coefficients;
 
-  /// Number of columns the constraint is defined over, i.e. the number of
-  /// variables of the system it was built for plus one for the constant.
-  unsigned NumCols = 0;
+  /// Number of variables the constraint is defined over.
+  unsigned NumVars = 0;
 
   bool IsSigned = false;
 
   ConstraintTy() = default;
 
-  ConstraintTy(RowTy Coefficients, unsigned NumCols, bool IsSigned, bool IsEq,
+  ConstraintTy(RowTy Coefficients, unsigned NumVars, bool IsSigned, bool IsEq,
                bool IsNe)
-      : Coefficients(std::move(Coefficients)), NumCols(NumCols),
+      : Coefficients(std::move(Coefficients)), NumVars(NumVars),
         IsSigned(IsSigned), IsEq(IsEq), IsNe(IsNe) {}
 
-  bool empty() const { return NumCols == 0; }
+  bool empty() const { return Coefficients.empty(); }
+
+  /// Returns true if the constraint does not reference any variable, i.e. it is
+  /// of the form 'c >= 0'.
+  bool isConstantOnly() const { return Coefficients.size() < 2; }
 
   bool isEq() const { return IsEq; }
 
@@ -268,7 +271,7 @@ class ConstraintInfo {
     // Add Arg > -1 constraints to unsigned system for all function arguments.
     for (Value *Arg : FunctionArgs)
       UnsignedCS.addRow({Entry(0, 0), Entry(-1, Value2Index.at(Arg))},
-                        Value2Index.size() + 1);
+                        Value2Index.size());
   }
 
   DenseMap<Value *, unsigned> &getValue2Index(bool Signed) {
@@ -774,7 +777,6 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
   // Build result constraint, by first adding all coefficients from A and then
   // subtracting all coefficients from B.
   RowTy R(1, Entry(0, 0));
-  // Returns a reference to the coefficient for the variable with index Idx.
   auto GetCoefficient = [&R](unsigned Idx) -> int64_t & {
     // The entry for Idx, or the place to insert it at, is the first entry with
     // an index >= Idx.
@@ -805,12 +807,11 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
   erase_if(R, [](const Entry &E) { return E.Id != 0 && E.Coefficient == 0; });
 
   // Remove any new variable without a coefficient in the row.
-  if (R.back().Id >= Value2Index.size())
-    NewVariables.resize(R.back().Id - Value2Index.size());
+  unsigned NumV2I = Value2Index.size();
+  NewVariables.truncate(R.back().Id > NumV2I ? R.back().Id - NumV2I : 0);
 
-  return ConstraintTy(std::move(R),
-                      Value2Index.size() + NewVariables.size() + 1, IsSigned,
-                      IsEq, IsNe);
+  return ConstraintTy(std::move(R), Value2Index.size() + NewVariables.size(),
+                      IsSigned, IsEq, IsNe);
 }
 
 ConstraintTy ConstraintInfo::getConstraintForSolving(CmpInst::Predicate Pred,
@@ -822,9 +823,8 @@ ConstraintTy ConstraintInfo::getConstraintForSolving(CmpInst::Predicate Pred,
   if ((Pred == CmpInst::ICMP_ULE && Op0 == NullC) ||
       (Pred == CmpInst::ICMP_UGE && Op1 == NullC)) {
     // Return constraint that's trivially true.
-    return ConstraintTy(RowTy(1, Entry(0, 0)),
-                        getValue2Index(false).size() + 1, /*IsSigned=*/false,
-                        /*IsEq=*/false, /*IsNe=*/false);
+    return ConstraintTy(RowTy(1, Entry(0, 0)), /*NumVars=*/0,
+                        /*IsSigned=*/false, /*IsEq=*/false, /*IsNe=*/false);
   }
 
   // If both operands are known to be non-negative, change signed predicates to
@@ -960,7 +960,7 @@ void ConstraintInfo::transferToOtherSystem(
 static void dumpConstraint(ArrayRef<Entry> C,
                            const DenseMap<Value *, unsigned> &Value2Index) {
   ConstraintSystem CS(Value2Index);
-  CS.addRow(C, Value2Index.size() + 1);
+  CS.addRow(C, Value2Index.size());
   CS.dump();
 }
 #endif
@@ -1876,7 +1876,7 @@ void ConstraintInfo::addFactImpl(CmpInst::Predicate Pred, Value *A, Value *B,
   LLVM_DEBUG(dbgs() << "Adding '"; dumpUnpackedICmp(dbgs(), Pred, A, B);
              dbgs() << "'\n");
   auto &CSToUse = getCS(R.IsSigned);
-  bool Added = CSToUse.addRow(R.Coefficients, R.NumCols);
+  bool Added = CSToUse.addRow(R.Coefficients, R.NumVars);
   if (!Added)
     return;
 
@@ -1902,7 +1902,7 @@ void ConstraintInfo::addFactImpl(CmpInst::Predicate Pred, Value *A, Value *B,
     for (Value *V : NewVariables) {
       // Add V > -1 constraints for all new variables.
       CSToUse.addRow({Entry(0, 0), Entry(-1, Value2Index.at(V))},
-                     Value2Index.size() + 1);
+                     Value2Index.size());
       DFSInStack.emplace_back(NumIn, NumOut, R.IsSigned,
                               SmallVector<Value *, 2>());
     }
@@ -1913,7 +1913,7 @@ void ConstraintInfo::addFactImpl(CmpInst::Predicate Pred, Value *A, Value *B,
     for (Entry &E : R.Coefficients)
       if (MulOverflow(E.Coefficient, int64_t(-1), E.Coefficient))
         return;
-    CSToUse.addRow(R.Coefficients, R.NumCols);
+    CSToUse.addRow(R.Coefficients, R.NumVars);
 
     DFSInStack.emplace_back(NumIn, NumOut, R.IsSigned,
                             SmallVector<Value *, 2>());
@@ -1961,8 +1961,9 @@ tryToSimplifyOverflowMath(IntrinsicInst *II, ConstraintInfo &Info,
   auto DoesConditionHold = [](CmpInst::Predicate Pred, Value *A, Value *B,
                               ConstraintInfo &Info) {
     auto R = Info.getConstraintForSolving(Pred, A, B);
-    // Nothing can be proven if the system has no variables.
-    if (R.NumCols < 2)
+    // Nothing can be proven if the constraint has no variables. This also
+    // covers rows that could not be decomposed, which are empty.
+    if (R.isConstantOnly())
       return false;
 
     auto &CSToUse = Info.getCS(R.IsSigned);
diff --git a/llvm/unittests/Analysis/ConstraintSystemTest.cpp b/llvm/unittests/Analysis/ConstraintSystemTest.cpp
index 710f465803d82..a40131d4d2809 100644
--- a/llvm/unittests/Analysis/ConstraintSystemTest.cpp
+++ b/llvm/unittests/Analysis/ConstraintSystemTest.cpp
@@ -29,7 +29,7 @@ static RowTy toRow(ArrayRef<int64_t> R) {
 
 /// Add the dense coefficient vector \p R to \p CS.
 static void addVariableRow(ConstraintSystem &CS, ArrayRef<int64_t> R) {
-  CS.addRow(toRow(R), R.size());
+  CS.addRow(toRow(R), R.size() - 1);
 }
 
 /// Returns true if the condition described by the dense coefficient vector \p R



More information about the llvm-commits mailing list