[llvm] [ConstraintSystem] Build constraint rows in sparse form (NFC). (PR #215735)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 11 23:22:25 PDT 2026
https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/215735
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.
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
>From 3a31eabd98ca55eaceaa8dec40aa71dc6da1b599 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] [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 | 115 +++++++++---------
.../Analysis/ConstraintSystemTest.cpp | 112 ++++++++++-------
4 files changed, 218 insertions(+), 206 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 f77426f2b6322..14eb4743d3d1f 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,10 +822,9 @@ 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.
- return ConstraintTy(SmallVector<int64_t, 8>(Value2Index.size(), 0), false,
- false, false);
+ return ConstraintTy(RowTy(1, Entry(0, 0)), getValue2Index(false).size(),
+ /*IsSigned=*/false, /*IsEq=*/false, /*IsNe=*/false);
}
// If both operands are known to be non-negative, change signed predicates to
@@ -957,10 +957,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
@@ -1876,10 +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);
- if (R.Coefficients.empty())
- return;
-
- bool Added = CSToUse.addVariableRowFill(R.Coefficients);
+ bool Added = CSToUse.addRow(R.Coefficients, R.NumCols);
if (!Added)
return;
@@ -1903,10 +1900,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>());
}
@@ -1914,10 +1910,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>());
@@ -1965,7 +1961,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
More information about the llvm-commits
mailing list