1e8d8bef9SDimitry Andric //===- ConstraintSytem.cpp - A system of linear constraints. ----*- C++ -*-===//
2e8d8bef9SDimitry Andric //
3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e8d8bef9SDimitry Andric //
7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8e8d8bef9SDimitry Andric
9e8d8bef9SDimitry Andric #include "llvm/Analysis/ConstraintSystem.h"
10e8d8bef9SDimitry Andric #include "llvm/ADT/SmallVector.h"
11e8d8bef9SDimitry Andric #include "llvm/Support/MathExtras.h"
12e8d8bef9SDimitry Andric #include "llvm/ADT/StringExtras.h"
13fe013be4SDimitry Andric #include "llvm/IR/Value.h"
14e8d8bef9SDimitry Andric #include "llvm/Support/Debug.h"
15e8d8bef9SDimitry Andric
16e8d8bef9SDimitry Andric #include <string>
17e8d8bef9SDimitry Andric
18e8d8bef9SDimitry Andric using namespace llvm;
19e8d8bef9SDimitry Andric
20e8d8bef9SDimitry Andric #define DEBUG_TYPE "constraint-system"
21e8d8bef9SDimitry Andric
eliminateUsingFM()22e8d8bef9SDimitry Andric bool ConstraintSystem::eliminateUsingFM() {
23e8d8bef9SDimitry Andric // Implementation of Fourier–Motzkin elimination, with some tricks from the
24e8d8bef9SDimitry Andric // paper Pugh, William. "The Omega test: a fast and practical integer
25e8d8bef9SDimitry Andric // programming algorithm for dependence
26e8d8bef9SDimitry Andric // analysis."
27e8d8bef9SDimitry Andric // Supercomputing'91: Proceedings of the 1991 ACM/
28e8d8bef9SDimitry Andric // IEEE conference on Supercomputing. IEEE, 1991.
29e8d8bef9SDimitry Andric assert(!Constraints.empty() &&
30e8d8bef9SDimitry Andric "should only be called for non-empty constraint systems");
31e8d8bef9SDimitry Andric
32fe013be4SDimitry Andric unsigned LastIdx = NumVariables - 1;
33fe013be4SDimitry Andric
34fe013be4SDimitry Andric // First, either remove the variable in place if it is 0 or add the row to
35fe013be4SDimitry Andric // RemainingRows and remove it from the system.
36fe013be4SDimitry Andric SmallVector<SmallVector<Entry, 8>, 4> RemainingRows;
37fe013be4SDimitry Andric for (unsigned R1 = 0; R1 < Constraints.size();) {
38fe013be4SDimitry Andric SmallVector<Entry, 8> &Row1 = Constraints[R1];
39fe013be4SDimitry Andric if (getLastCoefficient(Row1, LastIdx) == 0) {
40fe013be4SDimitry Andric if (Row1.size() > 0 && Row1.back().Id == LastIdx)
41fe013be4SDimitry Andric Row1.pop_back();
42fe013be4SDimitry Andric R1++;
43fe013be4SDimitry Andric } else {
44fe013be4SDimitry Andric std::swap(Constraints[R1], Constraints.back());
45fe013be4SDimitry Andric RemainingRows.push_back(std::move(Constraints.back()));
46fe013be4SDimitry Andric Constraints.pop_back();
47e8d8bef9SDimitry Andric }
48e8d8bef9SDimitry Andric }
49e8d8bef9SDimitry Andric
50fe013be4SDimitry Andric // Process rows where the variable is != 0.
51fe013be4SDimitry Andric unsigned NumRemainingConstraints = RemainingRows.size();
52fe013be4SDimitry Andric for (unsigned R1 = 0; R1 < NumRemainingConstraints; R1++) {
53e8d8bef9SDimitry Andric // FIXME do not use copy
54fe013be4SDimitry Andric for (unsigned R2 = R1 + 1; R2 < NumRemainingConstraints; R2++) {
55e8d8bef9SDimitry Andric if (R1 == R2)
56e8d8bef9SDimitry Andric continue;
57e8d8bef9SDimitry Andric
58fe013be4SDimitry Andric int64_t UpperLast = getLastCoefficient(RemainingRows[R2], LastIdx);
59fe013be4SDimitry Andric int64_t LowerLast = getLastCoefficient(RemainingRows[R1], LastIdx);
60fe013be4SDimitry Andric assert(
61fe013be4SDimitry Andric UpperLast != 0 && LowerLast != 0 &&
62fe013be4SDimitry Andric "RemainingRows should only contain rows where the variable is != 0");
63e8d8bef9SDimitry Andric
64fe013be4SDimitry Andric if ((LowerLast < 0 && UpperLast < 0) || (LowerLast > 0 && UpperLast > 0))
65e8d8bef9SDimitry Andric continue;
66e8d8bef9SDimitry Andric
67e8d8bef9SDimitry Andric unsigned LowerR = R1;
68e8d8bef9SDimitry Andric unsigned UpperR = R2;
69fe013be4SDimitry Andric if (UpperLast < 0) {
70e8d8bef9SDimitry Andric std::swap(LowerR, UpperR);
71fe013be4SDimitry Andric std::swap(LowerLast, UpperLast);
72fe013be4SDimitry Andric }
73e8d8bef9SDimitry Andric
74fe013be4SDimitry Andric SmallVector<Entry, 8> NR;
75fe013be4SDimitry Andric unsigned IdxUpper = 0;
76fe013be4SDimitry Andric unsigned IdxLower = 0;
77fe013be4SDimitry Andric auto &LowerRow = RemainingRows[LowerR];
78fe013be4SDimitry Andric auto &UpperRow = RemainingRows[UpperR];
79fe013be4SDimitry Andric while (true) {
80fe013be4SDimitry Andric if (IdxUpper >= UpperRow.size() || IdxLower >= LowerRow.size())
81fe013be4SDimitry Andric break;
82e8d8bef9SDimitry Andric int64_t M1, M2, N;
83fe013be4SDimitry Andric int64_t UpperV = 0;
84fe013be4SDimitry Andric int64_t LowerV = 0;
85fe013be4SDimitry Andric uint16_t CurrentId = std::numeric_limits<uint16_t>::max();
86fe013be4SDimitry Andric if (IdxUpper < UpperRow.size()) {
87fe013be4SDimitry Andric CurrentId = std::min(UpperRow[IdxUpper].Id, CurrentId);
88fe013be4SDimitry Andric }
89fe013be4SDimitry Andric if (IdxLower < LowerRow.size()) {
90fe013be4SDimitry Andric CurrentId = std::min(LowerRow[IdxLower].Id, CurrentId);
91fe013be4SDimitry Andric }
92fe013be4SDimitry Andric
93fe013be4SDimitry Andric if (IdxUpper < UpperRow.size() && UpperRow[IdxUpper].Id == CurrentId) {
94fe013be4SDimitry Andric UpperV = UpperRow[IdxUpper].Coefficient;
95fe013be4SDimitry Andric IdxUpper++;
96fe013be4SDimitry Andric }
97fe013be4SDimitry Andric
98*cdc20ff6SDimitry Andric if (MulOverflow(UpperV, -1 * LowerLast, M1))
99e8d8bef9SDimitry Andric return false;
100fe013be4SDimitry Andric if (IdxLower < LowerRow.size() && LowerRow[IdxLower].Id == CurrentId) {
101fe013be4SDimitry Andric LowerV = LowerRow[IdxLower].Coefficient;
102fe013be4SDimitry Andric IdxLower++;
103fe013be4SDimitry Andric }
104fe013be4SDimitry Andric
105*cdc20ff6SDimitry Andric if (MulOverflow(LowerV, UpperLast, M2))
106e8d8bef9SDimitry Andric return false;
107e8d8bef9SDimitry Andric if (AddOverflow(M1, M2, N))
108e8d8bef9SDimitry Andric return false;
109fe013be4SDimitry Andric if (N == 0)
110fe013be4SDimitry Andric continue;
111fe013be4SDimitry Andric NR.emplace_back(N, CurrentId);
112e8d8bef9SDimitry Andric }
113fe013be4SDimitry Andric if (NR.empty())
114fe013be4SDimitry Andric continue;
115fe013be4SDimitry Andric Constraints.push_back(std::move(NR));
116e8d8bef9SDimitry Andric // Give up if the new system gets too big.
117fe013be4SDimitry Andric if (Constraints.size() > 500)
118e8d8bef9SDimitry Andric return false;
119e8d8bef9SDimitry Andric }
120e8d8bef9SDimitry Andric }
121fe013be4SDimitry Andric NumVariables -= 1;
122e8d8bef9SDimitry Andric
123e8d8bef9SDimitry Andric return true;
124e8d8bef9SDimitry Andric }
125e8d8bef9SDimitry Andric
mayHaveSolutionImpl()126e8d8bef9SDimitry Andric bool ConstraintSystem::mayHaveSolutionImpl() {
127fe013be4SDimitry Andric while (!Constraints.empty() && NumVariables > 1) {
128e8d8bef9SDimitry Andric if (!eliminateUsingFM())
129e8d8bef9SDimitry Andric return true;
130e8d8bef9SDimitry Andric }
131e8d8bef9SDimitry Andric
132fe013be4SDimitry Andric if (Constraints.empty() || NumVariables > 1)
133e8d8bef9SDimitry Andric return true;
134e8d8bef9SDimitry Andric
135fe013be4SDimitry Andric return all_of(Constraints, [](auto &R) {
136fe013be4SDimitry Andric if (R.empty())
137fe013be4SDimitry Andric return true;
138fe013be4SDimitry Andric if (R[0].Id == 0)
139fe013be4SDimitry Andric return R[0].Coefficient >= 0;
140fe013be4SDimitry Andric return true;
141fe013be4SDimitry Andric });
142e8d8bef9SDimitry Andric }
143e8d8bef9SDimitry Andric
getVarNamesList() const144fe013be4SDimitry Andric SmallVector<std::string> ConstraintSystem::getVarNamesList() const {
145fe013be4SDimitry Andric SmallVector<std::string> Names(Value2Index.size(), "");
146fe013be4SDimitry Andric #ifndef NDEBUG
147fe013be4SDimitry Andric for (auto &[V, Index] : Value2Index) {
148fe013be4SDimitry Andric std::string OperandName;
149fe013be4SDimitry Andric if (V->getName().empty())
150fe013be4SDimitry Andric OperandName = V->getNameOrAsOperand();
151fe013be4SDimitry Andric else
152fe013be4SDimitry Andric OperandName = std::string("%") + V->getName().str();
153fe013be4SDimitry Andric Names[Index - 1] = OperandName;
154e8d8bef9SDimitry Andric }
155fe013be4SDimitry Andric #endif
156fe013be4SDimitry Andric return Names;
157e8d8bef9SDimitry Andric }
158e8d8bef9SDimitry Andric
dump() const159e8d8bef9SDimitry Andric void ConstraintSystem::dump() const {
160fe013be4SDimitry Andric #ifndef NDEBUG
161fe013be4SDimitry Andric if (Constraints.empty())
162fe013be4SDimitry Andric return;
163fe013be4SDimitry Andric SmallVector<std::string> Names = getVarNamesList();
164fe013be4SDimitry Andric for (const auto &Row : Constraints) {
165fe013be4SDimitry Andric SmallVector<std::string, 16> Parts;
166fe013be4SDimitry Andric for (unsigned I = 0, S = Row.size(); I < S; ++I) {
167fe013be4SDimitry Andric if (Row[I].Id >= NumVariables)
168fe013be4SDimitry Andric break;
169fe013be4SDimitry Andric if (Row[I].Id == 0)
170fe013be4SDimitry Andric continue;
171fe013be4SDimitry Andric std::string Coefficient;
172fe013be4SDimitry Andric if (Row[I].Coefficient != 1)
173fe013be4SDimitry Andric Coefficient = std::to_string(Row[I].Coefficient) + " * ";
174fe013be4SDimitry Andric Parts.push_back(Coefficient + Names[Row[I].Id - 1]);
175fe013be4SDimitry Andric }
176fe013be4SDimitry Andric // assert(!Parts.empty() && "need to have at least some parts");
177fe013be4SDimitry Andric int64_t ConstPart = 0;
178fe013be4SDimitry Andric if (Row[0].Id == 0)
179fe013be4SDimitry Andric ConstPart = Row[0].Coefficient;
180fe013be4SDimitry Andric LLVM_DEBUG(dbgs() << join(Parts, std::string(" + "))
181fe013be4SDimitry Andric << " <= " << std::to_string(ConstPart) << "\n");
182fe013be4SDimitry Andric }
183fe013be4SDimitry Andric #endif
184e8d8bef9SDimitry Andric }
185e8d8bef9SDimitry Andric
mayHaveSolution()186e8d8bef9SDimitry Andric bool ConstraintSystem::mayHaveSolution() {
187fe013be4SDimitry Andric LLVM_DEBUG(dbgs() << "---\n");
188e8d8bef9SDimitry Andric LLVM_DEBUG(dump());
189e8d8bef9SDimitry Andric bool HasSolution = mayHaveSolutionImpl();
190e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << (HasSolution ? "sat" : "unsat") << "\n");
191e8d8bef9SDimitry Andric return HasSolution;
192e8d8bef9SDimitry Andric }
193e8d8bef9SDimitry Andric
isConditionImplied(SmallVector<int64_t,8> R) const19404eeddc0SDimitry Andric bool ConstraintSystem::isConditionImplied(SmallVector<int64_t, 8> R) const {
195e8d8bef9SDimitry Andric // If all variable coefficients are 0, we have 'C >= 0'. If the constant is >=
196e8d8bef9SDimitry Andric // 0, R is always true, regardless of the system.
197bdd1243dSDimitry Andric if (all_of(ArrayRef(R).drop_front(1), [](int64_t C) { return C == 0; }))
198e8d8bef9SDimitry Andric return R[0] >= 0;
199e8d8bef9SDimitry Andric
200e8d8bef9SDimitry Andric // If there is no solution with the negation of R added to the system, the
201e8d8bef9SDimitry Andric // condition must hold based on the existing constraints.
202e8d8bef9SDimitry Andric R = ConstraintSystem::negate(R);
203fe013be4SDimitry Andric if (R.empty())
204fe013be4SDimitry Andric return false;
205e8d8bef9SDimitry Andric
206e8d8bef9SDimitry Andric auto NewSystem = *this;
207e8d8bef9SDimitry Andric NewSystem.addVariableRow(R);
208e8d8bef9SDimitry Andric return !NewSystem.mayHaveSolution();
209e8d8bef9SDimitry Andric }
210