1bb901355SGroverkss //===- IntegerRelation.cpp - MLIR IntegerRelation Class ---------------===// 2bb901355SGroverkss // 3bb901355SGroverkss // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bb901355SGroverkss // See https://llvm.org/LICENSE.txt for license information. 5bb901355SGroverkss // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bb901355SGroverkss // 7bb901355SGroverkss //===----------------------------------------------------------------------===// 8bb901355SGroverkss // 9bb901355SGroverkss // A class to represent an relation over integer tuples. A relation is 10bb901355SGroverkss // represented as a constraint system over a space of tuples of integer valued 11d95140a5SGroverkss // variables supporting symbolic variables and existential quantification. 12bb901355SGroverkss // 13bb901355SGroverkss //===----------------------------------------------------------------------===// 14bb901355SGroverkss 15bb901355SGroverkss #include "mlir/Analysis/Presburger/IntegerRelation.h" 162ee87cd6SMehdi Amini #include "mlir/Analysis/Presburger/Fraction.h" 17bb901355SGroverkss #include "mlir/Analysis/Presburger/LinearTransform.h" 1879ad5fb2SArjun P #include "mlir/Analysis/Presburger/PWMAFunction.h" 19ff1d9a4bSGroverkss #include "mlir/Analysis/Presburger/PresburgerRelation.h" 202ee87cd6SMehdi Amini #include "mlir/Analysis/Presburger/PresburgerSpace.h" 21bb901355SGroverkss #include "mlir/Analysis/Presburger/Simplex.h" 22bb901355SGroverkss #include "mlir/Analysis/Presburger/Utils.h" 23bb901355SGroverkss #include "llvm/ADT/DenseMap.h" 24bb901355SGroverkss #include "llvm/ADT/DenseSet.h" 252ee87cd6SMehdi Amini #include "llvm/ADT/STLExtras.h" 26562790f3SAbhinav271828 #include "llvm/ADT/Sequence.h" 272ee87cd6SMehdi Amini #include "llvm/ADT/SmallBitVector.h" 28bb901355SGroverkss #include "llvm/Support/Debug.h" 29f819302aSRamkumar Ramachandra #include "llvm/Support/LogicalResult.h" 302ee87cd6SMehdi Amini #include "llvm/Support/raw_ostream.h" 312ee87cd6SMehdi Amini #include <algorithm> 322ee87cd6SMehdi Amini #include <cassert> 332ee87cd6SMehdi Amini #include <functional> 342ee87cd6SMehdi Amini #include <memory> 35*27402735SAmy Wang #include <numeric> 36a1fe1f5fSKazu Hirata #include <optional> 37*27402735SAmy Wang #include <sstream> 38*27402735SAmy Wang #include <string> 392ee87cd6SMehdi Amini #include <utility> 402ee87cd6SMehdi Amini #include <vector> 41bb901355SGroverkss 42bb901355SGroverkss #define DEBUG_TYPE "presburger" 43bb901355SGroverkss 44bb901355SGroverkss using namespace mlir; 45bb901355SGroverkss using namespace presburger; 46bb901355SGroverkss 47bb901355SGroverkss using llvm::SmallDenseMap; 48bb901355SGroverkss using llvm::SmallDenseSet; 49bb901355SGroverkss 50bb901355SGroverkss std::unique_ptr<IntegerRelation> IntegerRelation::clone() const { 51bb901355SGroverkss return std::make_unique<IntegerRelation>(*this); 52bb901355SGroverkss } 53bb901355SGroverkss 54bb901355SGroverkss std::unique_ptr<IntegerPolyhedron> IntegerPolyhedron::clone() const { 55bb901355SGroverkss return std::make_unique<IntegerPolyhedron>(*this); 56bb901355SGroverkss } 57bb901355SGroverkss 588a7ead69SArjun P void IntegerRelation::setSpace(const PresburgerSpace &oSpace) { 59d95140a5SGroverkss assert(space.getNumVars() == oSpace.getNumVars() && "invalid space!"); 608a7ead69SArjun P space = oSpace; 618a7ead69SArjun P } 628a7ead69SArjun P 638a7ead69SArjun P void IntegerRelation::setSpaceExceptLocals(const PresburgerSpace &oSpace) { 64d95140a5SGroverkss assert(oSpace.getNumLocalVars() == 0 && "no locals should be present!"); 65d95140a5SGroverkss assert(oSpace.getNumVars() <= getNumVars() && "invalid space!"); 66d95140a5SGroverkss unsigned newNumLocals = getNumVars() - oSpace.getNumVars(); 678a7ead69SArjun P space = oSpace; 68d95140a5SGroverkss space.insertVar(VarKind::Local, 0, newNumLocals); 698a7ead69SArjun P } 708a7ead69SArjun P 71d70bfeb4SBharathi Ramana Joshi void IntegerRelation::setId(VarKind kind, unsigned i, Identifier id) { 72d70bfeb4SBharathi Ramana Joshi assert(space.isUsingIds() && 73d70bfeb4SBharathi Ramana Joshi "space must be using identifiers to set an identifier"); 74d70bfeb4SBharathi Ramana Joshi assert(kind != VarKind::Local && "local variables cannot have identifiers"); 75d70bfeb4SBharathi Ramana Joshi assert(i < space.getNumVarKind(kind) && "invalid variable index"); 7624da7fa0SBharathi Ramana Joshi space.setId(kind, i, id); 7724da7fa0SBharathi Ramana Joshi } 7824da7fa0SBharathi Ramana Joshi 7924da7fa0SBharathi Ramana Joshi ArrayRef<Identifier> IntegerRelation::getIds(VarKind kind) { 8024da7fa0SBharathi Ramana Joshi if (!space.isUsingIds()) 8124da7fa0SBharathi Ramana Joshi space.resetIds(); 8224da7fa0SBharathi Ramana Joshi return space.getIds(kind); 83d70bfeb4SBharathi Ramana Joshi } 84d70bfeb4SBharathi Ramana Joshi 85bb901355SGroverkss void IntegerRelation::append(const IntegerRelation &other) { 8620aedb14SGroverkss assert(space.isEqual(other.getSpace()) && "Spaces must be equal."); 87bb901355SGroverkss 88bb901355SGroverkss inequalities.reserveRows(inequalities.getNumRows() + 89bb901355SGroverkss other.getNumInequalities()); 90bb901355SGroverkss equalities.reserveRows(equalities.getNumRows() + other.getNumEqualities()); 91bb901355SGroverkss 92bb901355SGroverkss for (unsigned r = 0, e = other.getNumInequalities(); r < e; r++) { 93bb901355SGroverkss addInequality(other.getInequality(r)); 94bb901355SGroverkss } 95bb901355SGroverkss for (unsigned r = 0, e = other.getNumEqualities(); r < e; r++) { 96bb901355SGroverkss addEquality(other.getEquality(r)); 97bb901355SGroverkss } 98bb901355SGroverkss } 99bb901355SGroverkss 100b68e78ceSArjun P IntegerRelation IntegerRelation::intersect(IntegerRelation other) const { 101b68e78ceSArjun P IntegerRelation result = *this; 102d95140a5SGroverkss result.mergeLocalVars(other); 103b68e78ceSArjun P result.append(other); 104b68e78ceSArjun P return result; 105b68e78ceSArjun P } 106b68e78ceSArjun P 107bb901355SGroverkss bool IntegerRelation::isEqual(const IntegerRelation &other) const { 1088eebb47fSGroverkss assert(space.isCompatible(other.getSpace()) && "Spaces must be compatible."); 109ff1d9a4bSGroverkss return PresburgerRelation(*this).isEqual(PresburgerRelation(other)); 110bb901355SGroverkss } 111bb901355SGroverkss 11239b93955Sgilsaia bool IntegerRelation::isObviouslyEqual(const IntegerRelation &other) const { 113f40af3b3Sgilsaia if (!space.isEqual(other.getSpace())) 114f40af3b3Sgilsaia return false; 115f40af3b3Sgilsaia if (getNumEqualities() != other.getNumEqualities()) 116f40af3b3Sgilsaia return false; 117f40af3b3Sgilsaia if (getNumInequalities() != other.getNumInequalities()) 118f40af3b3Sgilsaia return false; 119f40af3b3Sgilsaia 120f40af3b3Sgilsaia unsigned cols = getNumCols(); 121f40af3b3Sgilsaia for (unsigned i = 0, eqs = getNumEqualities(); i < eqs; ++i) { 122f40af3b3Sgilsaia for (unsigned j = 0; j < cols; ++j) { 123f40af3b3Sgilsaia if (atEq(i, j) != other.atEq(i, j)) 124f40af3b3Sgilsaia return false; 125f40af3b3Sgilsaia } 126f40af3b3Sgilsaia } 127f40af3b3Sgilsaia for (unsigned i = 0, ineqs = getNumInequalities(); i < ineqs; ++i) { 128f40af3b3Sgilsaia for (unsigned j = 0; j < cols; ++j) { 129f40af3b3Sgilsaia if (atIneq(i, j) != other.atIneq(i, j)) 130f40af3b3Sgilsaia return false; 131f40af3b3Sgilsaia } 132f40af3b3Sgilsaia } 133f40af3b3Sgilsaia return true; 134f40af3b3Sgilsaia } 135f40af3b3Sgilsaia 136bb901355SGroverkss bool IntegerRelation::isSubsetOf(const IntegerRelation &other) const { 1378eebb47fSGroverkss assert(space.isCompatible(other.getSpace()) && "Spaces must be compatible."); 138ff1d9a4bSGroverkss return PresburgerRelation(*this).isSubsetOf(PresburgerRelation(other)); 139bb901355SGroverkss } 140bb901355SGroverkss 141bb901355SGroverkss MaybeOptimum<SmallVector<Fraction, 8>> 142bb901355SGroverkss IntegerRelation::findRationalLexMin() const { 143d95140a5SGroverkss assert(getNumSymbolVars() == 0 && "Symbols are not supported!"); 144bb901355SGroverkss MaybeOptimum<SmallVector<Fraction, 8>> maybeLexMin = 145bb901355SGroverkss LexSimplex(*this).findRationalLexMin(); 146bb901355SGroverkss 147bb901355SGroverkss if (!maybeLexMin.isBounded()) 148bb901355SGroverkss return maybeLexMin; 149bb901355SGroverkss 150bb901355SGroverkss // The Simplex returns the lexmin over all the variables including locals. But 151bb901355SGroverkss // locals are not actually part of the space and should not be returned in the 152d95140a5SGroverkss // result. Since the locals are placed last in the list of variables, they 153bb901355SGroverkss // will be minimized last in the lexmin. So simply truncating out the locals 154bb901355SGroverkss // from the end of the answer gives the desired lexmin over the dimensions. 155d95140a5SGroverkss assert(maybeLexMin->size() == getNumVars() && 156bb901355SGroverkss "Incorrect number of vars in lexMin!"); 157d95140a5SGroverkss maybeLexMin->resize(getNumDimAndSymbolVars()); 158bb901355SGroverkss return maybeLexMin; 159bb901355SGroverkss } 160bb901355SGroverkss 1611a0e67d7SRamkumar Ramachandra MaybeOptimum<SmallVector<DynamicAPInt, 8>> 1621a0e67d7SRamkumar Ramachandra IntegerRelation::findIntegerLexMin() const { 163d95140a5SGroverkss assert(getNumSymbolVars() == 0 && "Symbols are not supported!"); 1641a0e67d7SRamkumar Ramachandra MaybeOptimum<SmallVector<DynamicAPInt, 8>> maybeLexMin = 165bb901355SGroverkss LexSimplex(*this).findIntegerLexMin(); 166bb901355SGroverkss 167bb901355SGroverkss if (!maybeLexMin.isBounded()) 168bb901355SGroverkss return maybeLexMin.getKind(); 169bb901355SGroverkss 170bb901355SGroverkss // The Simplex returns the lexmin over all the variables including locals. But 171bb901355SGroverkss // locals are not actually part of the space and should not be returned in the 172d95140a5SGroverkss // result. Since the locals are placed last in the list of variables, they 173bb901355SGroverkss // will be minimized last in the lexmin. So simply truncating out the locals 174bb901355SGroverkss // from the end of the answer gives the desired lexmin over the dimensions. 175d95140a5SGroverkss assert(maybeLexMin->size() == getNumVars() && 176bb901355SGroverkss "Incorrect number of vars in lexMin!"); 177d95140a5SGroverkss maybeLexMin->resize(getNumDimAndSymbolVars()); 178bb901355SGroverkss return maybeLexMin; 179bb901355SGroverkss } 180bb901355SGroverkss 1811a0e67d7SRamkumar Ramachandra static bool rangeIsZero(ArrayRef<DynamicAPInt> range) { 1821a0e67d7SRamkumar Ramachandra return llvm::all_of(range, [](const DynamicAPInt &x) { return x == 0; }); 1838a67c6eeSArjun P } 1848a67c6eeSArjun P 185d95140a5SGroverkss static void removeConstraintsInvolvingVarRange(IntegerRelation &poly, 186d95140a5SGroverkss unsigned begin, unsigned count) { 1878a67c6eeSArjun P // We loop until i > 0 and index into i - 1 to avoid sign issues. 1888a67c6eeSArjun P // 1898a67c6eeSArjun P // We iterate backwards so that whether we remove constraint i - 1 or not, the 1908a67c6eeSArjun P // next constraint to be tested is always i - 2. 1918a67c6eeSArjun P for (unsigned i = poly.getNumEqualities(); i > 0; i--) 1928a67c6eeSArjun P if (!rangeIsZero(poly.getEquality(i - 1).slice(begin, count))) 1938a67c6eeSArjun P poly.removeEquality(i - 1); 1948a67c6eeSArjun P for (unsigned i = poly.getNumInequalities(); i > 0; i--) 1958a67c6eeSArjun P if (!rangeIsZero(poly.getInequality(i - 1).slice(begin, count))) 1968a67c6eeSArjun P poly.removeInequality(i - 1); 1978a67c6eeSArjun P } 19893b9f50bSArjun P 19993b9f50bSArjun P IntegerRelation::CountsSnapshot IntegerRelation::getCounts() const { 20020aedb14SGroverkss return {getSpace(), getNumInequalities(), getNumEqualities()}; 20120aedb14SGroverkss } 20220aedb14SGroverkss 203d95140a5SGroverkss void IntegerRelation::truncateVarKind(VarKind kind, unsigned num) { 204d95140a5SGroverkss unsigned curNum = getNumVarKind(kind); 205d95140a5SGroverkss assert(num <= curNum && "Can't truncate to more vars!"); 206d95140a5SGroverkss removeVarRange(kind, num, curNum); 20793b9f50bSArjun P } 20893b9f50bSArjun P 209d95140a5SGroverkss void IntegerRelation::truncateVarKind(VarKind kind, 21093b9f50bSArjun P const CountsSnapshot &counts) { 211d95140a5SGroverkss truncateVarKind(kind, counts.getSpace().getNumVarKind(kind)); 21293b9f50bSArjun P } 21393b9f50bSArjun P 21493b9f50bSArjun P void IntegerRelation::truncate(const CountsSnapshot &counts) { 215d95140a5SGroverkss truncateVarKind(VarKind::Domain, counts); 216d95140a5SGroverkss truncateVarKind(VarKind::Range, counts); 217d95140a5SGroverkss truncateVarKind(VarKind::Symbol, counts); 218d95140a5SGroverkss truncateVarKind(VarKind::Local, counts); 21993b9f50bSArjun P removeInequalityRange(counts.getNumIneqs(), getNumInequalities()); 2209615d717SArjun P removeEqualityRange(counts.getNumEqs(), getNumEqualities()); 22193b9f50bSArjun P } 22293b9f50bSArjun P 223dda8b1ceSArjun P PresburgerRelation IntegerRelation::computeReprWithOnlyDivLocals() const { 2248a7ead69SArjun P // If there are no locals, we're done. 225d95140a5SGroverkss if (getNumLocalVars() == 0) 226dda8b1ceSArjun P return PresburgerRelation(*this); 2278a7ead69SArjun P 2288a7ead69SArjun P // Move all the non-div locals to the end, as the current API to 22956863adfSiambrj // SymbolicLexOpt requires these to form a contiguous range. 2308a7ead69SArjun P // 2318a7ead69SArjun P // Take a copy so we can perform mutations. 232dda8b1ceSArjun P IntegerRelation copy = *this; 233479c4f64SGroverkss std::vector<MaybeLocalRepr> reprs(getNumLocalVars()); 234479c4f64SGroverkss copy.getLocalReprs(&reprs); 2358a7ead69SArjun P 2368a7ead69SArjun P // Iterate through all the locals. The last `numNonDivLocals` are the locals 2378a7ead69SArjun P // that have been scanned already and do not have division representations. 2388a7ead69SArjun P unsigned numNonDivLocals = 0; 239d95140a5SGroverkss unsigned offset = copy.getVarKindOffset(VarKind::Local); 240d95140a5SGroverkss for (unsigned i = 0, e = copy.getNumLocalVars(); i < e - numNonDivLocals;) { 2418a7ead69SArjun P if (!reprs[i]) { 2428a7ead69SArjun P // Whenever we come across a local that does not have a division 2438a7ead69SArjun P // representation, we swap it to the `numNonDivLocals`-th last position 2448a7ead69SArjun P // and increment `numNonDivLocal`s. `reprs` also needs to be swapped. 245d95140a5SGroverkss copy.swapVar(offset + i, offset + e - numNonDivLocals - 1); 2468a7ead69SArjun P std::swap(reprs[i], reprs[e - numNonDivLocals - 1]); 2478a7ead69SArjun P ++numNonDivLocals; 2488a7ead69SArjun P continue; 2498a7ead69SArjun P } 2508a7ead69SArjun P ++i; 2518a7ead69SArjun P } 2528a7ead69SArjun P 2538a7ead69SArjun P // If there are no non-div locals, we're done. 2548a7ead69SArjun P if (numNonDivLocals == 0) 255dda8b1ceSArjun P return PresburgerRelation(*this); 2568a7ead69SArjun P 2578a7ead69SArjun P // We computeSymbolicIntegerLexMin by considering the non-div locals as 2588a7ead69SArjun P // "non-symbols" and considering everything else as "symbols". This will 2598a7ead69SArjun P // compute a function mapping assignments to "symbols" to the 2608a7ead69SArjun P // lexicographically minimal valid assignment of "non-symbols", when a 2618a7ead69SArjun P // satisfying assignment exists. It separately returns the set of assignments 2628a7ead69SArjun P // to the "symbols" such that a satisfying assignment to the "non-symbols" 2638a7ead69SArjun P // exists but the lexmin is unbounded. We basically want to find the set of 2648a7ead69SArjun P // values of the "symbols" such that an assignment to the "non-symbols" 2658a7ead69SArjun P // exists, which is the union of the domain of the returned lexmin function 2668a7ead69SArjun P // and the returned set of assignments to the "symbols" that makes the lexmin 2678a7ead69SArjun P // unbounded. 26856863adfSiambrj SymbolicLexOpt lexminResult = 2698a7ead69SArjun P SymbolicLexSimplex(copy, /*symbolOffset*/ 0, 2708a7ead69SArjun P IntegerPolyhedron(PresburgerSpace::getSetSpace( 271d95140a5SGroverkss /*numDims=*/copy.getNumVars() - numNonDivLocals))) 2728a7ead69SArjun P .computeSymbolicIntegerLexMin(); 273206a6037SBenjamin Kramer PresburgerRelation result = 27456863adfSiambrj lexminResult.lexopt.getDomain().unionSet(lexminResult.unboundedDomain); 2758a7ead69SArjun P 2768a7ead69SArjun P // The result set might lie in the wrong space -- all its ids are dims. 2778a7ead69SArjun P // Set it to the desired space and return. 2788a7ead69SArjun P PresburgerSpace space = getSpace(); 279d95140a5SGroverkss space.removeVarRange(VarKind::Local, 0, getNumLocalVars()); 2808a7ead69SArjun P result.setSpace(space); 2818a7ead69SArjun P return result; 2828a7ead69SArjun P } 2838a7ead69SArjun P 28456863adfSiambrj SymbolicLexOpt IntegerRelation::findSymbolicIntegerLexMin() const { 285c4abef28SArjun P // Symbol and Domain vars will be used as symbols for symbolic lexmin. 286c4abef28SArjun P // In other words, for every value of the symbols and domain, return the 287c4abef28SArjun P // lexmin value of the (range, locals). 288c4abef28SArjun P llvm::SmallBitVector isSymbol(getNumVars(), false); 289c4abef28SArjun P isSymbol.set(getVarKindOffset(VarKind::Symbol), 290c4abef28SArjun P getVarKindEnd(VarKind::Symbol)); 291c4abef28SArjun P isSymbol.set(getVarKindOffset(VarKind::Domain), 292c4abef28SArjun P getVarKindEnd(VarKind::Domain)); 29379ad5fb2SArjun P // Compute the symbolic lexmin of the dims and locals, with the symbols being 29479ad5fb2SArjun P // the actual symbols of this set. 295bb2226acSGroverkss // The resultant space of lexmin is the space of the relation itself. 29656863adfSiambrj SymbolicLexOpt result = 297c4abef28SArjun P SymbolicLexSimplex(*this, 298c4abef28SArjun P IntegerPolyhedron(PresburgerSpace::getSetSpace( 299c4abef28SArjun P /*numDims=*/getNumDomainVars(), 300c4abef28SArjun P /*numSymbols=*/getNumSymbolVars())), 301c4abef28SArjun P isSymbol) 30279ad5fb2SArjun P .computeSymbolicIntegerLexMin(); 30379ad5fb2SArjun P 30479ad5fb2SArjun P // We want to return only the lexmin over the dims, so strip the locals from 30579ad5fb2SArjun P // the computed lexmin. 30656863adfSiambrj result.lexopt.removeOutputs(result.lexopt.getNumOutputs() - getNumLocalVars(), 30756863adfSiambrj result.lexopt.getNumOutputs()); 30879ad5fb2SArjun P return result; 30979ad5fb2SArjun P } 31079ad5fb2SArjun P 31156863adfSiambrj /// findSymbolicIntegerLexMax is implemented using findSymbolicIntegerLexMin as 31256863adfSiambrj /// follows: 31356863adfSiambrj /// 1. A new relation is created which is `this` relation with the sign of 31456863adfSiambrj /// each dimension variable in range flipped; 31556863adfSiambrj /// 2. findSymbolicIntegerLexMin is called on the range negated relation to 31656863adfSiambrj /// compute the negated lexmax of `this` relation; 31756863adfSiambrj /// 3. The sign of the negated lexmax is flipped and returned. 31856863adfSiambrj SymbolicLexOpt IntegerRelation::findSymbolicIntegerLexMax() const { 31956863adfSiambrj IntegerRelation flippedRel = *this; 32056863adfSiambrj // Flip range sign by flipping the sign of range variables in all constraints. 32156863adfSiambrj for (unsigned j = getNumDomainVars(), 32256863adfSiambrj b = getNumDomainVars() + getNumRangeVars(); 32356863adfSiambrj j < b; j++) { 32456863adfSiambrj for (unsigned i = 0, a = getNumEqualities(); i < a; i++) 32556863adfSiambrj flippedRel.atEq(i, j) = -1 * atEq(i, j); 32656863adfSiambrj for (unsigned i = 0, a = getNumInequalities(); i < a; i++) 32756863adfSiambrj flippedRel.atIneq(i, j) = -1 * atIneq(i, j); 32856863adfSiambrj } 32956863adfSiambrj // Compute negated lexmax by computing lexmin. 33056863adfSiambrj SymbolicLexOpt flippedSymbolicIntegerLexMax = 33156863adfSiambrj flippedRel.findSymbolicIntegerLexMin(), 33256863adfSiambrj symbolicIntegerLexMax( 33356863adfSiambrj flippedSymbolicIntegerLexMax.lexopt.getSpace()); 33456863adfSiambrj // Get lexmax by flipping range sign in the PWMA constraints. 33556863adfSiambrj for (auto &flippedPiece : 33656863adfSiambrj flippedSymbolicIntegerLexMax.lexopt.getAllPieces()) { 337c1b99746SAbhinav271828 IntMatrix mat = flippedPiece.output.getOutputMatrix(); 33856863adfSiambrj for (unsigned i = 0, e = mat.getNumRows(); i < e; i++) 33956863adfSiambrj mat.negateRow(i); 34056863adfSiambrj MultiAffineFunction maf(flippedPiece.output.getSpace(), mat); 34156863adfSiambrj PWMAFunction::Piece piece = {flippedPiece.domain, maf}; 34256863adfSiambrj symbolicIntegerLexMax.lexopt.addPiece(piece); 34356863adfSiambrj } 34456863adfSiambrj symbolicIntegerLexMax.unboundedDomain = 34556863adfSiambrj flippedSymbolicIntegerLexMax.unboundedDomain; 34656863adfSiambrj return symbolicIntegerLexMax; 34756863adfSiambrj } 34856863adfSiambrj 349a18f843fSGroverkss PresburgerRelation 350a18f843fSGroverkss IntegerRelation::subtract(const PresburgerRelation &set) const { 351a18f843fSGroverkss return PresburgerRelation(*this).subtract(set); 352a18f843fSGroverkss } 353a18f843fSGroverkss 354d95140a5SGroverkss unsigned IntegerRelation::insertVar(VarKind kind, unsigned pos, unsigned num) { 355d95140a5SGroverkss assert(pos <= getNumVarKind(kind)); 356bb901355SGroverkss 357d95140a5SGroverkss unsigned insertPos = space.insertVar(kind, pos, num); 358bb901355SGroverkss inequalities.insertColumns(insertPos, num); 359bb901355SGroverkss equalities.insertColumns(insertPos, num); 360bb901355SGroverkss return insertPos; 361bb901355SGroverkss } 362bb901355SGroverkss 363d95140a5SGroverkss unsigned IntegerRelation::appendVar(VarKind kind, unsigned num) { 364d95140a5SGroverkss unsigned pos = getNumVarKind(kind); 365d95140a5SGroverkss return insertVar(kind, pos, num); 366bb901355SGroverkss } 367bb901355SGroverkss 3681a0e67d7SRamkumar Ramachandra void IntegerRelation::addEquality(ArrayRef<DynamicAPInt> eq) { 369bb901355SGroverkss assert(eq.size() == getNumCols()); 370bb901355SGroverkss unsigned row = equalities.appendExtraRow(); 371bb901355SGroverkss for (unsigned i = 0, e = eq.size(); i < e; ++i) 372bb901355SGroverkss equalities(row, i) = eq[i]; 373bb901355SGroverkss } 374bb901355SGroverkss 3751a0e67d7SRamkumar Ramachandra void IntegerRelation::addInequality(ArrayRef<DynamicAPInt> inEq) { 376bb901355SGroverkss assert(inEq.size() == getNumCols()); 377bb901355SGroverkss unsigned row = inequalities.appendExtraRow(); 378bb901355SGroverkss for (unsigned i = 0, e = inEq.size(); i < e; ++i) 379bb901355SGroverkss inequalities(row, i) = inEq[i]; 380bb901355SGroverkss } 381bb901355SGroverkss 382d95140a5SGroverkss void IntegerRelation::removeVar(VarKind kind, unsigned pos) { 383d95140a5SGroverkss removeVarRange(kind, pos, pos + 1); 384bb901355SGroverkss } 385bb901355SGroverkss 386d95140a5SGroverkss void IntegerRelation::removeVar(unsigned pos) { removeVarRange(pos, pos + 1); } 387bb901355SGroverkss 388d95140a5SGroverkss void IntegerRelation::removeVarRange(VarKind kind, unsigned varStart, 389d95140a5SGroverkss unsigned varLimit) { 390d95140a5SGroverkss assert(varLimit <= getNumVarKind(kind)); 391c896e654SGroverkss 392d95140a5SGroverkss if (varStart >= varLimit) 393c896e654SGroverkss return; 394c896e654SGroverkss 395d95140a5SGroverkss // Remove eliminated variables from the constraints. 396d95140a5SGroverkss unsigned offset = getVarKindOffset(kind); 397d95140a5SGroverkss equalities.removeColumns(offset + varStart, varLimit - varStart); 398d95140a5SGroverkss inequalities.removeColumns(offset + varStart, varLimit - varStart); 399c896e654SGroverkss 400d95140a5SGroverkss // Remove eliminated variables from the space. 401d95140a5SGroverkss space.removeVarRange(kind, varStart, varLimit); 402bb901355SGroverkss } 403bb901355SGroverkss 404d95140a5SGroverkss void IntegerRelation::removeVarRange(unsigned varStart, unsigned varLimit) { 405d95140a5SGroverkss assert(varLimit <= getNumVars()); 406bb901355SGroverkss 407d95140a5SGroverkss if (varStart >= varLimit) 408c896e654SGroverkss return; 409c896e654SGroverkss 410d95140a5SGroverkss // Helper function to remove vars of the specified kind in the given range 411c896e654SGroverkss // [start, limit), The range is absolute (i.e. it is not relative to the kind 412d95140a5SGroverkss // of variable). Also updates `limit` to reflect the deleted variables. 413d95140a5SGroverkss auto removeVarKindInRange = [this](VarKind kind, unsigned &start, 414c896e654SGroverkss unsigned &limit) { 415c896e654SGroverkss if (start >= limit) 416c896e654SGroverkss return; 417c896e654SGroverkss 418d95140a5SGroverkss unsigned offset = getVarKindOffset(kind); 419d95140a5SGroverkss unsigned num = getNumVarKind(kind); 420c896e654SGroverkss 421c896e654SGroverkss // Get `start`, `limit` relative to the specified kind. 422c896e654SGroverkss unsigned relativeStart = 423c896e654SGroverkss start <= offset ? 0 : std::min(num, start - offset); 424c896e654SGroverkss unsigned relativeLimit = 425c896e654SGroverkss limit <= offset ? 0 : std::min(num, limit - offset); 426c896e654SGroverkss 427d95140a5SGroverkss // Remove vars of the specified kind in the relative range. 428d95140a5SGroverkss removeVarRange(kind, relativeStart, relativeLimit); 429c896e654SGroverkss 430d95140a5SGroverkss // Update `limit` to reflect deleted variables. 431d95140a5SGroverkss // `start` does not need to be updated because any variables that are 432c896e654SGroverkss // deleted are after position `start`. 433c896e654SGroverkss limit -= relativeLimit - relativeStart; 434c896e654SGroverkss }; 435c896e654SGroverkss 436d95140a5SGroverkss removeVarKindInRange(VarKind::Domain, varStart, varLimit); 437d95140a5SGroverkss removeVarKindInRange(VarKind::Range, varStart, varLimit); 438d95140a5SGroverkss removeVarKindInRange(VarKind::Symbol, varStart, varLimit); 439d95140a5SGroverkss removeVarKindInRange(VarKind::Local, varStart, varLimit); 440bb901355SGroverkss } 441bb901355SGroverkss 442bb901355SGroverkss void IntegerRelation::removeEquality(unsigned pos) { 443bb901355SGroverkss equalities.removeRow(pos); 444bb901355SGroverkss } 445bb901355SGroverkss 446bb901355SGroverkss void IntegerRelation::removeInequality(unsigned pos) { 447bb901355SGroverkss inequalities.removeRow(pos); 448bb901355SGroverkss } 449bb901355SGroverkss 450bb901355SGroverkss void IntegerRelation::removeEqualityRange(unsigned start, unsigned end) { 451bb901355SGroverkss if (start >= end) 452bb901355SGroverkss return; 453bb901355SGroverkss equalities.removeRows(start, end - start); 454bb901355SGroverkss } 455bb901355SGroverkss 456bb901355SGroverkss void IntegerRelation::removeInequalityRange(unsigned start, unsigned end) { 457bb901355SGroverkss if (start >= end) 458bb901355SGroverkss return; 459bb901355SGroverkss inequalities.removeRows(start, end - start); 460bb901355SGroverkss } 461bb901355SGroverkss 462d95140a5SGroverkss void IntegerRelation::swapVar(unsigned posA, unsigned posB) { 463d95140a5SGroverkss assert(posA < getNumVars() && "invalid position A"); 464d95140a5SGroverkss assert(posB < getNumVars() && "invalid position B"); 465bb901355SGroverkss 466bb901355SGroverkss if (posA == posB) 467bb901355SGroverkss return; 468bb901355SGroverkss 4698d7c9798SBharathi Ramana Joshi VarKind kindA = space.getVarKindAt(posA); 4708d7c9798SBharathi Ramana Joshi VarKind kindB = space.getVarKindAt(posB); 4718d7c9798SBharathi Ramana Joshi unsigned relativePosA = posA - getVarKindOffset(kindA); 4728d7c9798SBharathi Ramana Joshi unsigned relativePosB = posB - getVarKindOffset(kindB); 4738d7c9798SBharathi Ramana Joshi space.swapVar(kindA, kindB, relativePosA, relativePosB); 4748d7c9798SBharathi Ramana Joshi 475bb901355SGroverkss inequalities.swapColumns(posA, posB); 476bb901355SGroverkss equalities.swapColumns(posA, posB); 477bb901355SGroverkss } 478bb901355SGroverkss 479bb901355SGroverkss void IntegerRelation::clearConstraints() { 480bb901355SGroverkss equalities.resizeVertically(0); 481bb901355SGroverkss inequalities.resizeVertically(0); 482bb901355SGroverkss } 483bb901355SGroverkss 484d95140a5SGroverkss /// Gather all lower and upper bounds of the variable at `pos`, and 485bb901355SGroverkss /// optionally any equalities on it. In addition, the bounds are to be 486d95140a5SGroverkss /// independent of variables in position range [`offset`, `offset` + `num`). 487bb901355SGroverkss void IntegerRelation::getLowerAndUpperBoundIndices( 488bb901355SGroverkss unsigned pos, SmallVectorImpl<unsigned> *lbIndices, 489bb901355SGroverkss SmallVectorImpl<unsigned> *ubIndices, SmallVectorImpl<unsigned> *eqIndices, 490bb901355SGroverkss unsigned offset, unsigned num) const { 491d95140a5SGroverkss assert(pos < getNumVars() && "invalid position"); 492bb901355SGroverkss assert(offset + num < getNumCols() && "invalid range"); 493bb901355SGroverkss 494d95140a5SGroverkss // Checks for a constraint that has a non-zero coeff for the variables in 495bb901355SGroverkss // the position range [offset, offset + num) while ignoring `pos`. 496bb901355SGroverkss auto containsConstraintDependentOnRange = [&](unsigned r, bool isEq) { 497bb901355SGroverkss unsigned c, f; 498bb901355SGroverkss auto cst = isEq ? getEquality(r) : getInequality(r); 499bb901355SGroverkss for (c = offset, f = offset + num; c < f; ++c) { 500bb901355SGroverkss if (c == pos) 501bb901355SGroverkss continue; 502bb901355SGroverkss if (cst[c] != 0) 503bb901355SGroverkss break; 504bb901355SGroverkss } 505bb901355SGroverkss return c < f; 506bb901355SGroverkss }; 507bb901355SGroverkss 508bb901355SGroverkss // Gather all lower bounds and upper bounds of the variable. Since the 509bb901355SGroverkss // canonical form c_1*x_1 + c_2*x_2 + ... + c_0 >= 0, a constraint is a lower 510bb901355SGroverkss // bound for x_i if c_i >= 1, and an upper bound if c_i <= -1. 511bb901355SGroverkss for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 512bb901355SGroverkss // The bounds are to be independent of [offset, offset + num) columns. 513bb901355SGroverkss if (containsConstraintDependentOnRange(r, /*isEq=*/false)) 514bb901355SGroverkss continue; 515bb901355SGroverkss if (atIneq(r, pos) >= 1) { 516bb901355SGroverkss // Lower bound. 517266a5a9cSRamkumar Ramachandra lbIndices->emplace_back(r); 518bb901355SGroverkss } else if (atIneq(r, pos) <= -1) { 519bb901355SGroverkss // Upper bound. 520266a5a9cSRamkumar Ramachandra ubIndices->emplace_back(r); 521bb901355SGroverkss } 522bb901355SGroverkss } 523bb901355SGroverkss 524bb901355SGroverkss // An equality is both a lower and upper bound. Record any equalities 525d95140a5SGroverkss // involving the pos^th variable. 526bb901355SGroverkss if (!eqIndices) 527bb901355SGroverkss return; 528bb901355SGroverkss 529bb901355SGroverkss for (unsigned r = 0, e = getNumEqualities(); r < e; r++) { 530bb901355SGroverkss if (atEq(r, pos) == 0) 531bb901355SGroverkss continue; 532bb901355SGroverkss if (containsConstraintDependentOnRange(r, /*isEq=*/true)) 533bb901355SGroverkss continue; 534266a5a9cSRamkumar Ramachandra eqIndices->emplace_back(r); 535bb901355SGroverkss } 536bb901355SGroverkss } 537bb901355SGroverkss 538bb901355SGroverkss bool IntegerRelation::hasConsistentState() const { 539bb901355SGroverkss if (!inequalities.hasConsistentState()) 540bb901355SGroverkss return false; 541bb901355SGroverkss if (!equalities.hasConsistentState()) 542bb901355SGroverkss return false; 543bb901355SGroverkss return true; 544bb901355SGroverkss } 545bb901355SGroverkss 5461a0e67d7SRamkumar Ramachandra void IntegerRelation::setAndEliminate(unsigned pos, 5471a0e67d7SRamkumar Ramachandra ArrayRef<DynamicAPInt> values) { 548bb901355SGroverkss if (values.empty()) 549bb901355SGroverkss return; 550d95140a5SGroverkss assert(pos + values.size() <= getNumVars() && 551bb901355SGroverkss "invalid position or too many values"); 552bb901355SGroverkss // Setting x_j = p in sum_i a_i x_i + c is equivalent to adding p*a_j to the 553d95140a5SGroverkss // constant term and removing the var x_j. We do this for all the vars 554bb901355SGroverkss // pos, pos + 1, ... pos + values.size() - 1. 555bb901355SGroverkss unsigned constantColPos = getNumCols() - 1; 556bb901355SGroverkss for (unsigned i = 0, numVals = values.size(); i < numVals; ++i) 557bb901355SGroverkss inequalities.addToColumn(i + pos, constantColPos, values[i]); 558bb901355SGroverkss for (unsigned i = 0, numVals = values.size(); i < numVals; ++i) 559bb901355SGroverkss equalities.addToColumn(i + pos, constantColPos, values[i]); 560d95140a5SGroverkss removeVarRange(pos, pos + values.size()); 561bb901355SGroverkss } 562bb901355SGroverkss 563bb901355SGroverkss void IntegerRelation::clearAndCopyFrom(const IntegerRelation &other) { 564bb901355SGroverkss *this = other; 565bb901355SGroverkss } 566bb901355SGroverkss 567bb901355SGroverkss // Searches for a constraint with a non-zero coefficient at `colIdx` in 568bb901355SGroverkss // equality (isEq=true) or inequality (isEq=false) constraints. 569bb901355SGroverkss // Returns true and sets row found in search in `rowIdx`, false otherwise. 570bb901355SGroverkss bool IntegerRelation::findConstraintWithNonZeroAt(unsigned colIdx, bool isEq, 571bb901355SGroverkss unsigned *rowIdx) const { 572bb901355SGroverkss assert(colIdx < getNumCols() && "position out of bounds"); 5731a0e67d7SRamkumar Ramachandra auto at = [&](unsigned rowIdx) -> DynamicAPInt { 574bb901355SGroverkss return isEq ? atEq(rowIdx, colIdx) : atIneq(rowIdx, colIdx); 575bb901355SGroverkss }; 576bb901355SGroverkss unsigned e = isEq ? getNumEqualities() : getNumInequalities(); 577bb901355SGroverkss for (*rowIdx = 0; *rowIdx < e; ++(*rowIdx)) { 578bb901355SGroverkss if (at(*rowIdx) != 0) { 579bb901355SGroverkss return true; 580bb901355SGroverkss } 581bb901355SGroverkss } 582bb901355SGroverkss return false; 583bb901355SGroverkss } 584bb901355SGroverkss 585bb901355SGroverkss void IntegerRelation::normalizeConstraintsByGCD() { 586bb901355SGroverkss for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) 587bb901355SGroverkss equalities.normalizeRow(i); 588bb901355SGroverkss for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) 589bb901355SGroverkss inequalities.normalizeRow(i); 590bb901355SGroverkss } 591bb901355SGroverkss 592bb901355SGroverkss bool IntegerRelation::hasInvalidConstraint() const { 593bb901355SGroverkss assert(hasConsistentState()); 594bb901355SGroverkss auto check = [&](bool isEq) -> bool { 595bb901355SGroverkss unsigned numCols = getNumCols(); 596bb901355SGroverkss unsigned numRows = isEq ? getNumEqualities() : getNumInequalities(); 597bb901355SGroverkss for (unsigned i = 0, e = numRows; i < e; ++i) { 598bb901355SGroverkss unsigned j; 599bb901355SGroverkss for (j = 0; j < numCols - 1; ++j) { 6001a0e67d7SRamkumar Ramachandra DynamicAPInt v = isEq ? atEq(i, j) : atIneq(i, j); 601bb901355SGroverkss // Skip rows with non-zero variable coefficients. 602bb901355SGroverkss if (v != 0) 603bb901355SGroverkss break; 604bb901355SGroverkss } 605bb901355SGroverkss if (j < numCols - 1) { 606bb901355SGroverkss continue; 607bb901355SGroverkss } 608bb901355SGroverkss // Check validity of constant term at 'numCols - 1' w.r.t 'isEq'. 609bb901355SGroverkss // Example invalid constraints include: '1 == 0' or '-1 >= 0' 6101a0e67d7SRamkumar Ramachandra DynamicAPInt v = isEq ? atEq(i, numCols - 1) : atIneq(i, numCols - 1); 611bb901355SGroverkss if ((isEq && v != 0) || (!isEq && v < 0)) { 612bb901355SGroverkss return true; 613bb901355SGroverkss } 614bb901355SGroverkss } 615bb901355SGroverkss return false; 616bb901355SGroverkss }; 617bb901355SGroverkss if (check(/*isEq=*/true)) 618bb901355SGroverkss return true; 619bb901355SGroverkss return check(/*isEq=*/false); 620bb901355SGroverkss } 621bb901355SGroverkss 622d95140a5SGroverkss /// Eliminate variable from constraint at `rowIdx` based on coefficient at 623bb901355SGroverkss /// pivotRow, pivotCol. Columns in range [elimColStart, pivotCol) will not be 624bb901355SGroverkss /// updated as they have already been eliminated. 625bb901355SGroverkss static void eliminateFromConstraint(IntegerRelation *constraints, 626bb901355SGroverkss unsigned rowIdx, unsigned pivotRow, 627bb901355SGroverkss unsigned pivotCol, unsigned elimColStart, 628bb901355SGroverkss bool isEq) { 629bb901355SGroverkss // Skip if equality 'rowIdx' if same as 'pivotRow'. 630bb901355SGroverkss if (isEq && rowIdx == pivotRow) 631bb901355SGroverkss return; 6321a0e67d7SRamkumar Ramachandra auto at = [&](unsigned i, unsigned j) -> DynamicAPInt { 633bb901355SGroverkss return isEq ? constraints->atEq(i, j) : constraints->atIneq(i, j); 634bb901355SGroverkss }; 6351a0e67d7SRamkumar Ramachandra DynamicAPInt leadCoeff = at(rowIdx, pivotCol); 636bb901355SGroverkss // Skip if leading coefficient at 'rowIdx' is already zero. 637bb901355SGroverkss if (leadCoeff == 0) 638bb901355SGroverkss return; 6391a0e67d7SRamkumar Ramachandra DynamicAPInt pivotCoeff = constraints->atEq(pivotRow, pivotCol); 6406d6f6c4dSArjun P int sign = (leadCoeff * pivotCoeff > 0) ? -1 : 1; 6411a0e67d7SRamkumar Ramachandra DynamicAPInt lcm = llvm::lcm(pivotCoeff, leadCoeff); 6421a0e67d7SRamkumar Ramachandra DynamicAPInt pivotMultiplier = sign * (lcm / abs(pivotCoeff)); 6431a0e67d7SRamkumar Ramachandra DynamicAPInt rowMultiplier = lcm / abs(leadCoeff); 644bb901355SGroverkss 645bb901355SGroverkss unsigned numCols = constraints->getNumCols(); 646bb901355SGroverkss for (unsigned j = 0; j < numCols; ++j) { 647bb901355SGroverkss // Skip updating column 'j' if it was just eliminated. 648bb901355SGroverkss if (j >= elimColStart && j < pivotCol) 649bb901355SGroverkss continue; 6501a0e67d7SRamkumar Ramachandra DynamicAPInt v = pivotMultiplier * constraints->atEq(pivotRow, j) + 651bb901355SGroverkss rowMultiplier * at(rowIdx, j); 652bb901355SGroverkss isEq ? constraints->atEq(rowIdx, j) = v 653bb901355SGroverkss : constraints->atIneq(rowIdx, j) = v; 654bb901355SGroverkss } 655bb901355SGroverkss } 656bb901355SGroverkss 657d95140a5SGroverkss /// Returns the position of the variable that has the minimum <number of lower 658bb901355SGroverkss /// bounds> times <number of upper bounds> from the specified range of 659d95140a5SGroverkss /// variables [start, end). It is often best to eliminate in the increasing 660bb901355SGroverkss /// order of these counts when doing Fourier-Motzkin elimination since FM adds 661bb901355SGroverkss /// that many new constraints. 662d95140a5SGroverkss static unsigned getBestVarToEliminate(const IntegerRelation &cst, 663d95140a5SGroverkss unsigned start, unsigned end) { 664d95140a5SGroverkss assert(start < cst.getNumVars() && end < cst.getNumVars() + 1); 665bb901355SGroverkss 666bb901355SGroverkss auto getProductOfNumLowerUpperBounds = [&](unsigned pos) { 667bb901355SGroverkss unsigned numLb = 0; 668bb901355SGroverkss unsigned numUb = 0; 669bb901355SGroverkss for (unsigned r = 0, e = cst.getNumInequalities(); r < e; r++) { 670bb901355SGroverkss if (cst.atIneq(r, pos) > 0) { 671bb901355SGroverkss ++numLb; 672bb901355SGroverkss } else if (cst.atIneq(r, pos) < 0) { 673bb901355SGroverkss ++numUb; 674bb901355SGroverkss } 675bb901355SGroverkss } 676bb901355SGroverkss return numLb * numUb; 677bb901355SGroverkss }; 678bb901355SGroverkss 679bb901355SGroverkss unsigned minLoc = start; 680bb901355SGroverkss unsigned min = getProductOfNumLowerUpperBounds(start); 681bb901355SGroverkss for (unsigned c = start + 1; c < end; c++) { 682bb901355SGroverkss unsigned numLbUbProduct = getProductOfNumLowerUpperBounds(c); 683bb901355SGroverkss if (numLbUbProduct < min) { 684bb901355SGroverkss min = numLbUbProduct; 685bb901355SGroverkss minLoc = c; 686bb901355SGroverkss } 687bb901355SGroverkss } 688bb901355SGroverkss return minLoc; 689bb901355SGroverkss } 690bb901355SGroverkss 691d95140a5SGroverkss // Checks for emptiness of the set by eliminating variables successively and 692bb901355SGroverkss // using the GCD test (on all equality constraints) and checking for trivially 693bb901355SGroverkss // invalid constraints. Returns 'true' if the constraint system is found to be 694bb901355SGroverkss // empty; false otherwise. 695bb901355SGroverkss bool IntegerRelation::isEmpty() const { 696bb901355SGroverkss if (isEmptyByGCDTest() || hasInvalidConstraint()) 697bb901355SGroverkss return true; 698bb901355SGroverkss 699bb901355SGroverkss IntegerRelation tmpCst(*this); 700bb901355SGroverkss 701bb901355SGroverkss // First, eliminate as many local variables as possible using equalities. 702bb901355SGroverkss tmpCst.removeRedundantLocalVars(); 703bb901355SGroverkss if (tmpCst.isEmptyByGCDTest() || tmpCst.hasInvalidConstraint()) 704bb901355SGroverkss return true; 705bb901355SGroverkss 706d95140a5SGroverkss // Eliminate as many variables as possible using Gaussian elimination. 707bb901355SGroverkss unsigned currentPos = 0; 708d95140a5SGroverkss while (currentPos < tmpCst.getNumVars()) { 709d95140a5SGroverkss tmpCst.gaussianEliminateVars(currentPos, tmpCst.getNumVars()); 710bb901355SGroverkss ++currentPos; 711bb901355SGroverkss // We check emptiness through trivial checks after eliminating each ID to 712bb901355SGroverkss // detect emptiness early. Since the checks isEmptyByGCDTest() and 713bb901355SGroverkss // hasInvalidConstraint() are linear time and single sweep on the constraint 714bb901355SGroverkss // buffer, this appears reasonable - but can optimize in the future. 715bb901355SGroverkss if (tmpCst.hasInvalidConstraint() || tmpCst.isEmptyByGCDTest()) 716bb901355SGroverkss return true; 717bb901355SGroverkss } 718bb901355SGroverkss 719bb901355SGroverkss // Eliminate the remaining using FM. 720d95140a5SGroverkss for (unsigned i = 0, e = tmpCst.getNumVars(); i < e; i++) { 721bb901355SGroverkss tmpCst.fourierMotzkinEliminate( 722d95140a5SGroverkss getBestVarToEliminate(tmpCst, 0, tmpCst.getNumVars())); 723bb901355SGroverkss // Check for a constraint explosion. This rarely happens in practice, but 724bb901355SGroverkss // this check exists as a safeguard against improperly constructed 725bb901355SGroverkss // constraint systems or artificially created arbitrarily complex systems 726bb901355SGroverkss // that aren't the intended use case for IntegerRelation. This is 727bb901355SGroverkss // needed since FM has a worst case exponential complexity in theory. 728d95140a5SGroverkss if (tmpCst.getNumConstraints() >= kExplosionFactor * getNumVars()) { 729bb901355SGroverkss LLVM_DEBUG(llvm::dbgs() << "FM constraint explosion detected\n"); 730bb901355SGroverkss return false; 731bb901355SGroverkss } 732bb901355SGroverkss 733bb901355SGroverkss // FM wouldn't have modified the equalities in any way. So no need to again 734bb901355SGroverkss // run GCD test. Check for trivial invalid constraints. 735bb901355SGroverkss if (tmpCst.hasInvalidConstraint()) 736bb901355SGroverkss return true; 737bb901355SGroverkss } 738bb901355SGroverkss return false; 739bb901355SGroverkss } 740bb901355SGroverkss 74139b93955Sgilsaia bool IntegerRelation::isObviouslyEmpty() const { 7421837579bSMehdi Amini return isEmptyByGCDTest() || hasInvalidConstraint(); 74339b93955Sgilsaia } 74439b93955Sgilsaia 745bb901355SGroverkss // Runs the GCD test on all equality constraints. Returns 'true' if this test 746bb901355SGroverkss // fails on any equality. Returns 'false' otherwise. 747bb901355SGroverkss // This test can be used to disprove the existence of a solution. If it returns 748bb901355SGroverkss // true, no integer solution to the equality constraints can exist. 749bb901355SGroverkss // 750bb901355SGroverkss // GCD test definition: 751bb901355SGroverkss // 752bb901355SGroverkss // The equality constraint: 753bb901355SGroverkss // 754bb901355SGroverkss // c_1*x_1 + c_2*x_2 + ... + c_n*x_n = c_0 755bb901355SGroverkss // 756bb901355SGroverkss // has an integer solution iff: 757bb901355SGroverkss // 758bb901355SGroverkss // GCD of c_1, c_2, ..., c_n divides c_0. 759bb901355SGroverkss bool IntegerRelation::isEmptyByGCDTest() const { 760bb901355SGroverkss assert(hasConsistentState()); 761bb901355SGroverkss unsigned numCols = getNumCols(); 762bb901355SGroverkss for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) { 7631a0e67d7SRamkumar Ramachandra DynamicAPInt gcd = abs(atEq(i, 0)); 764bb901355SGroverkss for (unsigned j = 1; j < numCols - 1; ++j) { 7651a0e67d7SRamkumar Ramachandra gcd = llvm::gcd(gcd, abs(atEq(i, j))); 766bb901355SGroverkss } 7671a0e67d7SRamkumar Ramachandra DynamicAPInt v = abs(atEq(i, numCols - 1)); 768bb901355SGroverkss if (gcd > 0 && (v % gcd != 0)) { 769bb901355SGroverkss return true; 770bb901355SGroverkss } 771bb901355SGroverkss } 772bb901355SGroverkss return false; 773bb901355SGroverkss } 774bb901355SGroverkss 775bb901355SGroverkss // Returns a matrix where each row is a vector along which the polytope is 776bb901355SGroverkss // bounded. The span of the returned vectors is guaranteed to contain all 777bb901355SGroverkss // such vectors. The returned vectors are NOT guaranteed to be linearly 778bb901355SGroverkss // independent. This function should not be called on empty sets. 779bb901355SGroverkss // 780bb901355SGroverkss // It is sufficient to check the perpendiculars of the constraints, as the set 781bb901355SGroverkss // of perpendiculars which are bounded must span all bounded directions. 782c1b99746SAbhinav271828 IntMatrix IntegerRelation::getBoundedDirections() const { 783bb901355SGroverkss // Note that it is necessary to add the equalities too (which the constructor 784bb901355SGroverkss // does) even though we don't need to check if they are bounded; whether an 785bb901355SGroverkss // inequality is bounded or not depends on what other constraints, including 786bb901355SGroverkss // equalities, are present. 787bb901355SGroverkss Simplex simplex(*this); 788bb901355SGroverkss 789bb901355SGroverkss assert(!simplex.isEmpty() && "It is not meaningful to ask whether a " 790bb901355SGroverkss "direction is bounded in an empty set."); 791bb901355SGroverkss 792bb901355SGroverkss SmallVector<unsigned, 8> boundedIneqs; 793bb901355SGroverkss // The constructor adds the inequalities to the simplex first, so this 794bb901355SGroverkss // processes all the inequalities. 795bb901355SGroverkss for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) { 796bb901355SGroverkss if (simplex.isBoundedAlongConstraint(i)) 797266a5a9cSRamkumar Ramachandra boundedIneqs.emplace_back(i); 798bb901355SGroverkss } 799bb901355SGroverkss 800bb901355SGroverkss // The direction vector is given by the coefficients and does not include the 801bb901355SGroverkss // constant term, so the matrix has one fewer column. 802bb901355SGroverkss unsigned dirsNumCols = getNumCols() - 1; 803c1b99746SAbhinav271828 IntMatrix dirs(boundedIneqs.size() + getNumEqualities(), dirsNumCols); 804bb901355SGroverkss 805bb901355SGroverkss // Copy the bounded inequalities. 806bb901355SGroverkss unsigned row = 0; 807bb901355SGroverkss for (unsigned i : boundedIneqs) { 808bb901355SGroverkss for (unsigned col = 0; col < dirsNumCols; ++col) 809bb901355SGroverkss dirs(row, col) = atIneq(i, col); 810bb901355SGroverkss ++row; 811bb901355SGroverkss } 812bb901355SGroverkss 813bb901355SGroverkss // Copy the equalities. All the equalities' perpendiculars are bounded. 814bb901355SGroverkss for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) { 815bb901355SGroverkss for (unsigned col = 0; col < dirsNumCols; ++col) 816bb901355SGroverkss dirs(row, col) = atEq(i, col); 817bb901355SGroverkss ++row; 818bb901355SGroverkss } 819bb901355SGroverkss 820bb901355SGroverkss return dirs; 821bb901355SGroverkss } 822bb901355SGroverkss 823d66cbc56SKazu Hirata bool IntegerRelation::isIntegerEmpty() const { return !findIntegerSample(); } 824bb901355SGroverkss 825bb901355SGroverkss /// Let this set be S. If S is bounded then we directly call into the GBR 826bb901355SGroverkss /// sampling algorithm. Otherwise, there are some unbounded directions, i.e., 827bb901355SGroverkss /// vectors v such that S extends to infinity along v or -v. In this case we 828bb901355SGroverkss /// use an algorithm described in the integer set library (isl) manual and used 829bb901355SGroverkss /// by the isl_set_sample function in that library. The algorithm is: 830bb901355SGroverkss /// 831bb901355SGroverkss /// 1) Apply a unimodular transform T to S to obtain S*T, such that all 832bb901355SGroverkss /// dimensions in which S*T is bounded lie in the linear span of a prefix of the 833bb901355SGroverkss /// dimensions. 834bb901355SGroverkss /// 835bb901355SGroverkss /// 2) Construct a set B by removing all constraints that involve 836bb901355SGroverkss /// the unbounded dimensions and then deleting the unbounded dimensions. Note 837bb901355SGroverkss /// that B is a Bounded set. 838bb901355SGroverkss /// 839bb901355SGroverkss /// 3) Try to obtain a sample from B using the GBR sampling 840bb901355SGroverkss /// algorithm. If no sample is found, return that S is empty. 841bb901355SGroverkss /// 842bb901355SGroverkss /// 4) Otherwise, substitute the obtained sample into S*T to obtain a set 843bb901355SGroverkss /// C. C is a full-dimensional Cone and always contains a sample. 844bb901355SGroverkss /// 845bb901355SGroverkss /// 5) Obtain an integer sample from C. 846bb901355SGroverkss /// 847bb901355SGroverkss /// 6) Return T*v, where v is the concatenation of the samples from B and C. 848bb901355SGroverkss /// 849bb901355SGroverkss /// The following is a sketch of a proof that 850bb901355SGroverkss /// a) If the algorithm returns empty, then S is empty. 851bb901355SGroverkss /// b) If the algorithm returns a sample, it is a valid sample in S. 852bb901355SGroverkss /// 853bb901355SGroverkss /// The algorithm returns empty only if B is empty, in which case S*T is 854bb901355SGroverkss /// certainly empty since B was obtained by removing constraints and then 855bb901355SGroverkss /// deleting unconstrained dimensions from S*T. Since T is unimodular, a vector 856bb901355SGroverkss /// v is in S*T iff T*v is in S. So in this case, since 857bb901355SGroverkss /// S*T is empty, S is empty too. 858bb901355SGroverkss /// 859bb901355SGroverkss /// Otherwise, the algorithm substitutes the sample from B into S*T. All the 860bb901355SGroverkss /// constraints of S*T that did not involve unbounded dimensions are satisfied 861bb901355SGroverkss /// by this substitution. All dimensions in the linear span of the dimensions 862bb901355SGroverkss /// outside the prefix are unbounded in S*T (step 1). Substituting values for 863bb901355SGroverkss /// the bounded dimensions cannot make these dimensions bounded, and these are 864bb901355SGroverkss /// the only remaining dimensions in C, so C is unbounded along every vector (in 865bb901355SGroverkss /// the positive or negative direction, or both). C is hence a full-dimensional 866bb901355SGroverkss /// cone and therefore always contains an integer point. 867bb901355SGroverkss /// 868bb901355SGroverkss /// Concatenating the samples from B and C gives a sample v in S*T, so the 869bb901355SGroverkss /// returned sample T*v is a sample in S. 8701a0e67d7SRamkumar Ramachandra std::optional<SmallVector<DynamicAPInt, 8>> 8710a81ace0SKazu Hirata IntegerRelation::findIntegerSample() const { 872bb901355SGroverkss // First, try the GCD test heuristic. 873bb901355SGroverkss if (isEmptyByGCDTest()) 874bb901355SGroverkss return {}; 875bb901355SGroverkss 876bb901355SGroverkss Simplex simplex(*this); 877bb901355SGroverkss if (simplex.isEmpty()) 878bb901355SGroverkss return {}; 879bb901355SGroverkss 880bb901355SGroverkss // For a bounded set, we directly call into the GBR sampling algorithm. 881bb901355SGroverkss if (!simplex.isUnbounded()) 882bb901355SGroverkss return simplex.findIntegerSample(); 883bb901355SGroverkss 884bb901355SGroverkss // The set is unbounded. We cannot directly use the GBR algorithm. 885bb901355SGroverkss // 886bb901355SGroverkss // m is a matrix containing, in each row, a vector in which S is 887bb901355SGroverkss // bounded, such that the linear span of all these dimensions contains all 888bb901355SGroverkss // bounded dimensions in S. 889c1b99746SAbhinav271828 IntMatrix m = getBoundedDirections(); 890bb901355SGroverkss // In column echelon form, each row of m occupies only the first rank(m) 891bb901355SGroverkss // columns and has zeros on the other columns. The transform T that brings S 892bb901355SGroverkss // to column echelon form is unimodular as well, so this is a suitable 893bb901355SGroverkss // transform to use in step 1 of the algorithm. 894bb901355SGroverkss std::pair<unsigned, LinearTransform> result = 8959e2ace1cSMehdi Amini LinearTransform::makeTransformToColumnEchelon(m); 896bb901355SGroverkss const LinearTransform &transform = result.second; 897bb901355SGroverkss // 1) Apply T to S to obtain S*T. 898bb901355SGroverkss IntegerRelation transformedSet = transform.applyTo(*this); 899bb901355SGroverkss 900bb901355SGroverkss // 2) Remove the unbounded dimensions and constraints involving them to 901bb901355SGroverkss // obtain a bounded set. 902bb901355SGroverkss IntegerRelation boundedSet(transformedSet); 903bb901355SGroverkss unsigned numBoundedDims = result.first; 904d95140a5SGroverkss unsigned numUnboundedDims = getNumVars() - numBoundedDims; 905d95140a5SGroverkss removeConstraintsInvolvingVarRange(boundedSet, numBoundedDims, 9068a67c6eeSArjun P numUnboundedDims); 907d95140a5SGroverkss boundedSet.removeVarRange(numBoundedDims, boundedSet.getNumVars()); 908bb901355SGroverkss 909bb901355SGroverkss // 3) Try to obtain a sample from the bounded set. 9101a0e67d7SRamkumar Ramachandra std::optional<SmallVector<DynamicAPInt, 8>> boundedSample = 911bb901355SGroverkss Simplex(boundedSet).findIntegerSample(); 912bb901355SGroverkss if (!boundedSample) 913bb901355SGroverkss return {}; 914bb901355SGroverkss assert(boundedSet.containsPoint(*boundedSample) && 915bb901355SGroverkss "Simplex returned an invalid sample!"); 916bb901355SGroverkss 917bb901355SGroverkss // 4) Substitute the values of the bounded dimensions into S*T to obtain a 918bb901355SGroverkss // full-dimensional cone, which necessarily contains an integer sample. 919bb901355SGroverkss transformedSet.setAndEliminate(0, *boundedSample); 920bb901355SGroverkss IntegerRelation &cone = transformedSet; 921bb901355SGroverkss 922bb901355SGroverkss // 5) Obtain an integer sample from the cone. 923bb901355SGroverkss // 924bb901355SGroverkss // We shrink the cone such that for any rational point in the shrunken cone, 925bb901355SGroverkss // rounding up each of the point's coordinates produces a point that still 926bb901355SGroverkss // lies in the original cone. 927bb901355SGroverkss // 928bb901355SGroverkss // Rounding up a point x adds a number e_i in [0, 1) to each coordinate x_i. 929bb901355SGroverkss // For each inequality sum_i a_i x_i + c >= 0 in the original cone, the 930bb901355SGroverkss // shrunken cone will have the inequality tightened by some amount s, such 931bb901355SGroverkss // that if x satisfies the shrunken cone's tightened inequality, then x + e 932bb901355SGroverkss // satisfies the original inequality, i.e., 933bb901355SGroverkss // 934bb901355SGroverkss // sum_i a_i x_i + c + s >= 0 implies sum_i a_i (x_i + e_i) + c >= 0 935bb901355SGroverkss // 936bb901355SGroverkss // for any e_i values in [0, 1). In fact, we will handle the slightly more 937bb901355SGroverkss // general case where e_i can be in [0, 1]. For example, consider the 938bb901355SGroverkss // inequality 2x_1 - 3x_2 - 7x_3 - 6 >= 0, and let x = (3, 0, 0). How low 939bb901355SGroverkss // could the LHS go if we added a number in [0, 1] to each coordinate? The LHS 940bb901355SGroverkss // is minimized when we add 1 to the x_i with negative coefficient a_i and 941bb901355SGroverkss // keep the other x_i the same. In the example, we would get x = (3, 1, 1), 942bb901355SGroverkss // changing the value of the LHS by -3 + -7 = -10. 943bb901355SGroverkss // 944bb901355SGroverkss // In general, the value of the LHS can change by at most the sum of the 945bb901355SGroverkss // negative a_i, so we accomodate this by shifting the inequality by this 946bb901355SGroverkss // amount for the shrunken cone. 947bb901355SGroverkss for (unsigned i = 0, e = cone.getNumInequalities(); i < e; ++i) { 948d95140a5SGroverkss for (unsigned j = 0; j < cone.getNumVars(); ++j) { 9491a0e67d7SRamkumar Ramachandra DynamicAPInt coeff = cone.atIneq(i, j); 950bb901355SGroverkss if (coeff < 0) 951d95140a5SGroverkss cone.atIneq(i, cone.getNumVars()) += coeff; 952bb901355SGroverkss } 953bb901355SGroverkss } 954bb901355SGroverkss 955bb901355SGroverkss // Obtain an integer sample in the cone by rounding up a rational point from 956bb901355SGroverkss // the shrunken cone. Shrinking the cone amounts to shifting its apex 957bb901355SGroverkss // "inwards" without changing its "shape"; the shrunken cone is still a 958bb901355SGroverkss // full-dimensional cone and is hence non-empty. 959bb901355SGroverkss Simplex shrunkenConeSimplex(cone); 960bb901355SGroverkss assert(!shrunkenConeSimplex.isEmpty() && "Shrunken cone cannot be empty!"); 961bb901355SGroverkss 962bb901355SGroverkss // The sample will always exist since the shrunken cone is non-empty. 963bb901355SGroverkss SmallVector<Fraction, 8> shrunkenConeSample = 964bb901355SGroverkss *shrunkenConeSimplex.getRationalSample(); 965bb901355SGroverkss 9661a0e67d7SRamkumar Ramachandra SmallVector<DynamicAPInt, 8> coneSample( 9671a0e67d7SRamkumar Ramachandra llvm::map_range(shrunkenConeSample, ceil)); 968bb901355SGroverkss 969bb901355SGroverkss // 6) Return transform * concat(boundedSample, coneSample). 9701a0e67d7SRamkumar Ramachandra SmallVector<DynamicAPInt, 8> &sample = *boundedSample; 971bb901355SGroverkss sample.append(coneSample.begin(), coneSample.end()); 972bb901355SGroverkss return transform.postMultiplyWithColumn(sample); 973bb901355SGroverkss } 974bb901355SGroverkss 975bb901355SGroverkss /// Helper to evaluate an affine expression at a point. 976bb901355SGroverkss /// The expression is a list of coefficients for the dimensions followed by the 977bb901355SGroverkss /// constant term. 9781a0e67d7SRamkumar Ramachandra static DynamicAPInt valueAt(ArrayRef<DynamicAPInt> expr, 9791a0e67d7SRamkumar Ramachandra ArrayRef<DynamicAPInt> point) { 980bb901355SGroverkss assert(expr.size() == 1 + point.size() && 981bb901355SGroverkss "Dimensionalities of point and expression don't match!"); 9821a0e67d7SRamkumar Ramachandra DynamicAPInt value = expr.back(); 983bb901355SGroverkss for (unsigned i = 0; i < point.size(); ++i) 984bb901355SGroverkss value += expr[i] * point[i]; 985bb901355SGroverkss return value; 986bb901355SGroverkss } 987bb901355SGroverkss 988bb901355SGroverkss /// A point satisfies an equality iff the value of the equality at the 989bb901355SGroverkss /// expression is zero, and it satisfies an inequality iff the value of the 990bb901355SGroverkss /// inequality at that point is non-negative. 9911a0e67d7SRamkumar Ramachandra bool IntegerRelation::containsPoint(ArrayRef<DynamicAPInt> point) const { 992bb901355SGroverkss for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) { 993bb901355SGroverkss if (valueAt(getEquality(i), point) != 0) 994bb901355SGroverkss return false; 995bb901355SGroverkss } 996bb901355SGroverkss for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) { 997bb901355SGroverkss if (valueAt(getInequality(i), point) < 0) 998bb901355SGroverkss return false; 999bb901355SGroverkss } 1000bb901355SGroverkss return true; 1001bb901355SGroverkss } 1002bb901355SGroverkss 10034418669fSArjun P /// Just substitute the values given and check if an integer sample exists for 1004d95140a5SGroverkss /// the local vars. 10054418669fSArjun P /// 10064418669fSArjun P /// TODO: this could be made more efficient by handling divisions separately. 10074418669fSArjun P /// Instead of finding an integer sample over all the locals, we can first 10084418669fSArjun P /// compute the values of the locals that have division representations and 10094418669fSArjun P /// only use the integer emptiness check for the locals that don't have this. 10104418669fSArjun P /// Handling this correctly requires ordering the divs, though. 10111a0e67d7SRamkumar Ramachandra std::optional<SmallVector<DynamicAPInt, 8>> 10121a0e67d7SRamkumar Ramachandra IntegerRelation::containsPointNoLocal(ArrayRef<DynamicAPInt> point) const { 1013d95140a5SGroverkss assert(point.size() == getNumVars() - getNumLocalVars() && 1014d95140a5SGroverkss "Point should contain all vars except locals!"); 1015d95140a5SGroverkss assert(getVarKindOffset(VarKind::Local) == getNumVars() - getNumLocalVars() && 10164418669fSArjun P "This function depends on locals being stored last!"); 10174418669fSArjun P IntegerRelation copy = *this; 10184418669fSArjun P copy.setAndEliminate(0, point); 10194418669fSArjun P return copy.findIntegerSample(); 10204418669fSArjun P } 10214418669fSArjun P 1022479c4f64SGroverkss DivisionRepr 1023479c4f64SGroverkss IntegerRelation::getLocalReprs(std::vector<MaybeLocalRepr> *repr) const { 1024d95140a5SGroverkss SmallVector<bool, 8> foundRepr(getNumVars(), false); 1025d95140a5SGroverkss for (unsigned i = 0, e = getNumDimAndSymbolVars(); i < e; ++i) 1026bb901355SGroverkss foundRepr[i] = true; 1027bb901355SGroverkss 1028479c4f64SGroverkss unsigned localOffset = getVarKindOffset(VarKind::Local); 1029479c4f64SGroverkss DivisionRepr divs(getNumVars(), getNumLocalVars()); 1030bb901355SGroverkss bool changed; 1031bb901355SGroverkss do { 1032bb901355SGroverkss // Each time changed is true, at end of this iteration, one or more local 1033bb901355SGroverkss // vars have been detected as floor divs. 1034bb901355SGroverkss changed = false; 1035d95140a5SGroverkss for (unsigned i = 0, e = getNumLocalVars(); i < e; ++i) { 1036479c4f64SGroverkss if (!foundRepr[i + localOffset]) { 1037479c4f64SGroverkss MaybeLocalRepr res = 1038479c4f64SGroverkss computeSingleVarRepr(*this, foundRepr, localOffset + i, 1039479c4f64SGroverkss divs.getDividend(i), divs.getDenom(i)); 1040479c4f64SGroverkss if (!res) { 1041479c4f64SGroverkss // No representation was found, so clear the representation and 1042479c4f64SGroverkss // continue. 1043479c4f64SGroverkss divs.clearRepr(i); 1044bb901355SGroverkss continue; 1045479c4f64SGroverkss } 1046479c4f64SGroverkss foundRepr[localOffset + i] = true; 1047479c4f64SGroverkss if (repr) 1048479c4f64SGroverkss (*repr)[i] = res; 1049bb901355SGroverkss changed = true; 1050bb901355SGroverkss } 1051bb901355SGroverkss } 1052bb901355SGroverkss } while (changed); 1053bb901355SGroverkss 1054479c4f64SGroverkss return divs; 1055bb901355SGroverkss } 1056bb901355SGroverkss 1057bb901355SGroverkss /// Tightens inequalities given that we are dealing with integer spaces. This is 1058bb901355SGroverkss /// analogous to the GCD test but applied to inequalities. The constant term can 1059bb901355SGroverkss /// be reduced to the preceding multiple of the GCD of the coefficients, i.e., 1060bb901355SGroverkss /// 64*i - 100 >= 0 => 64*i - 128 >= 0 (since 'i' is an integer). This is a 1061bb901355SGroverkss /// fast method - linear in the number of coefficients. 1062bb901355SGroverkss // Example on how this affects practical cases: consider the scenario: 1063bb901355SGroverkss // 64*i >= 100, j = 64*i; without a tightening, elimination of i would yield 1064bb901355SGroverkss // j >= 100 instead of the tighter (exact) j >= 128. 1065bb901355SGroverkss void IntegerRelation::gcdTightenInequalities() { 1066bb901355SGroverkss unsigned numCols = getNumCols(); 1067bb901355SGroverkss for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) { 1068bb901355SGroverkss // Normalize the constraint and tighten the constant term by the GCD. 10691a0e67d7SRamkumar Ramachandra DynamicAPInt gcd = inequalities.normalizeRow(i, getNumCols() - 1); 1070bb901355SGroverkss if (gcd > 1) 10716d6f6c4dSArjun P atIneq(i, numCols - 1) = floorDiv(atIneq(i, numCols - 1), gcd); 1072bb901355SGroverkss } 1073bb901355SGroverkss } 1074bb901355SGroverkss 1075d95140a5SGroverkss // Eliminates all variable variables in column range [posStart, posLimit). 1076bb901355SGroverkss // Returns the number of variables eliminated. 1077d95140a5SGroverkss unsigned IntegerRelation::gaussianEliminateVars(unsigned posStart, 1078bb901355SGroverkss unsigned posLimit) { 1079d95140a5SGroverkss // Return if variable positions to eliminate are out of range. 1080d95140a5SGroverkss assert(posLimit <= getNumVars()); 1081bb901355SGroverkss assert(hasConsistentState()); 1082bb901355SGroverkss 1083bb901355SGroverkss if (posStart >= posLimit) 1084bb901355SGroverkss return 0; 1085bb901355SGroverkss 1086bb901355SGroverkss gcdTightenInequalities(); 1087bb901355SGroverkss 1088bb901355SGroverkss unsigned pivotCol = 0; 1089bb901355SGroverkss for (pivotCol = posStart; pivotCol < posLimit; ++pivotCol) { 1090bb901355SGroverkss // Find a row which has a non-zero coefficient in column 'j'. 1091bb901355SGroverkss unsigned pivotRow; 1092bb901355SGroverkss if (!findConstraintWithNonZeroAt(pivotCol, /*isEq=*/true, &pivotRow)) { 1093bb901355SGroverkss // No pivot row in equalities with non-zero at 'pivotCol'. 1094bb901355SGroverkss if (!findConstraintWithNonZeroAt(pivotCol, /*isEq=*/false, &pivotRow)) { 1095bb901355SGroverkss // If inequalities are also non-zero in 'pivotCol', it can be 1096bb901355SGroverkss // eliminated. 1097bb901355SGroverkss continue; 1098bb901355SGroverkss } 1099bb901355SGroverkss break; 1100bb901355SGroverkss } 1101bb901355SGroverkss 1102d95140a5SGroverkss // Eliminate variable at 'pivotCol' from each equality row. 1103bb901355SGroverkss for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) { 1104bb901355SGroverkss eliminateFromConstraint(this, i, pivotRow, pivotCol, posStart, 1105bb901355SGroverkss /*isEq=*/true); 1106bb901355SGroverkss equalities.normalizeRow(i); 1107bb901355SGroverkss } 1108bb901355SGroverkss 1109d95140a5SGroverkss // Eliminate variable at 'pivotCol' from each inequality row. 1110bb901355SGroverkss for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) { 1111bb901355SGroverkss eliminateFromConstraint(this, i, pivotRow, pivotCol, posStart, 1112bb901355SGroverkss /*isEq=*/false); 1113bb901355SGroverkss inequalities.normalizeRow(i); 1114bb901355SGroverkss } 1115bb901355SGroverkss removeEquality(pivotRow); 1116bb901355SGroverkss gcdTightenInequalities(); 1117bb901355SGroverkss } 1118bb901355SGroverkss // Update position limit based on number eliminated. 1119bb901355SGroverkss posLimit = pivotCol; 1120bb901355SGroverkss // Remove eliminated columns from all constraints. 1121d95140a5SGroverkss removeVarRange(posStart, posLimit); 1122bb901355SGroverkss return posLimit - posStart; 1123bb901355SGroverkss } 1124bb901355SGroverkss 112539b93955Sgilsaia bool IntegerRelation::gaussianEliminate() { 112639b93955Sgilsaia gcdTightenInequalities(); 112739b93955Sgilsaia unsigned firstVar = 0, vars = getNumVars(); 112839b93955Sgilsaia unsigned nowDone, eqs, pivotRow; 112939b93955Sgilsaia for (nowDone = 0, eqs = getNumEqualities(); nowDone < eqs; ++nowDone) { 113039b93955Sgilsaia // Finds the first non-empty column. 113139b93955Sgilsaia for (; firstVar < vars; ++firstVar) { 113239b93955Sgilsaia if (!findConstraintWithNonZeroAt(firstVar, true, &pivotRow)) 113339b93955Sgilsaia continue; 113439b93955Sgilsaia break; 113539b93955Sgilsaia } 113639b93955Sgilsaia // The matrix has been normalized to row echelon form. 113739b93955Sgilsaia if (firstVar >= vars) 113839b93955Sgilsaia break; 113939b93955Sgilsaia 114039b93955Sgilsaia // The first pivot row found is below where it should currently be placed. 114139b93955Sgilsaia if (pivotRow > nowDone) { 114239b93955Sgilsaia equalities.swapRows(pivotRow, nowDone); 114339b93955Sgilsaia pivotRow = nowDone; 114439b93955Sgilsaia } 114539b93955Sgilsaia 114639b93955Sgilsaia // Normalize all lower equations and all inequalities. 114739b93955Sgilsaia for (unsigned i = nowDone + 1; i < eqs; ++i) { 114839b93955Sgilsaia eliminateFromConstraint(this, i, pivotRow, firstVar, 0, true); 114939b93955Sgilsaia equalities.normalizeRow(i); 115039b93955Sgilsaia } 115139b93955Sgilsaia for (unsigned i = 0, ineqs = getNumInequalities(); i < ineqs; ++i) { 115239b93955Sgilsaia eliminateFromConstraint(this, i, pivotRow, firstVar, 0, false); 115339b93955Sgilsaia inequalities.normalizeRow(i); 115439b93955Sgilsaia } 115539b93955Sgilsaia gcdTightenInequalities(); 115639b93955Sgilsaia } 115739b93955Sgilsaia 115839b93955Sgilsaia // No redundant rows. 115939b93955Sgilsaia if (nowDone == eqs) 116039b93955Sgilsaia return false; 116139b93955Sgilsaia 116239b93955Sgilsaia // Check to see if the redundant rows constant is zero, a non-zero value means 116339b93955Sgilsaia // the set is empty. 116439b93955Sgilsaia for (unsigned i = nowDone; i < eqs; ++i) { 116539b93955Sgilsaia if (atEq(i, vars) == 0) 116639b93955Sgilsaia continue; 116739b93955Sgilsaia 116839b93955Sgilsaia *this = getEmpty(getSpace()); 116939b93955Sgilsaia return true; 117039b93955Sgilsaia } 117139b93955Sgilsaia // Eliminate rows that are confined to be all zeros. 117239b93955Sgilsaia removeEqualityRange(nowDone, eqs); 117339b93955Sgilsaia return true; 117439b93955Sgilsaia } 117539b93955Sgilsaia 1176bb901355SGroverkss // A more complex check to eliminate redundant inequalities. Uses FourierMotzkin 1177bb901355SGroverkss // to check if a constraint is redundant. 1178bb901355SGroverkss void IntegerRelation::removeRedundantInequalities() { 1179bb901355SGroverkss SmallVector<bool, 32> redun(getNumInequalities(), false); 1180bb901355SGroverkss // To check if an inequality is redundant, we replace the inequality by its 1181bb901355SGroverkss // complement (for eg., i - 1 >= 0 by i <= 0), and check if the resulting 1182bb901355SGroverkss // system is empty. If it is, the inequality is redundant. 1183bb901355SGroverkss IntegerRelation tmpCst(*this); 1184bb901355SGroverkss for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 1185bb901355SGroverkss // Change the inequality to its complement. 1186bb901355SGroverkss tmpCst.inequalities.negateRow(r); 1187ef95a6e8SArjun P --tmpCst.atIneq(r, tmpCst.getNumCols() - 1); 1188bb901355SGroverkss if (tmpCst.isEmpty()) { 1189bb901355SGroverkss redun[r] = true; 1190bb901355SGroverkss // Zero fill the redundant inequality. 1191bb901355SGroverkss inequalities.fillRow(r, /*value=*/0); 1192bb901355SGroverkss tmpCst.inequalities.fillRow(r, /*value=*/0); 1193bb901355SGroverkss } else { 1194bb901355SGroverkss // Reverse the change (to avoid recreating tmpCst each time). 1195ef95a6e8SArjun P ++tmpCst.atIneq(r, tmpCst.getNumCols() - 1); 1196bb901355SGroverkss tmpCst.inequalities.negateRow(r); 1197bb901355SGroverkss } 1198bb901355SGroverkss } 1199bb901355SGroverkss 1200bb901355SGroverkss unsigned pos = 0; 1201bb901355SGroverkss for (unsigned r = 0, e = getNumInequalities(); r < e; ++r) { 1202bb901355SGroverkss if (!redun[r]) 1203bb901355SGroverkss inequalities.copyRow(r, pos++); 1204bb901355SGroverkss } 1205bb901355SGroverkss inequalities.resizeVertically(pos); 1206bb901355SGroverkss } 1207bb901355SGroverkss 1208bb901355SGroverkss // A more complex check to eliminate redundant inequalities and equalities. Uses 1209bb901355SGroverkss // Simplex to check if a constraint is redundant. 1210bb901355SGroverkss void IntegerRelation::removeRedundantConstraints() { 1211bb901355SGroverkss // First, we run gcdTightenInequalities. This allows us to catch some 1212bb901355SGroverkss // constraints which are not redundant when considering rational solutions 1213bb901355SGroverkss // but are redundant in terms of integer solutions. 1214bb901355SGroverkss gcdTightenInequalities(); 1215bb901355SGroverkss Simplex simplex(*this); 1216bb901355SGroverkss simplex.detectRedundant(); 1217bb901355SGroverkss 1218bb901355SGroverkss unsigned pos = 0; 1219bb901355SGroverkss unsigned numIneqs = getNumInequalities(); 1220bb901355SGroverkss // Scan to get rid of all inequalities marked redundant, in-place. In Simplex, 1221bb901355SGroverkss // the first constraints added are the inequalities. 1222bb901355SGroverkss for (unsigned r = 0; r < numIneqs; r++) { 1223bb901355SGroverkss if (!simplex.isMarkedRedundant(r)) 1224bb901355SGroverkss inequalities.copyRow(r, pos++); 1225bb901355SGroverkss } 1226bb901355SGroverkss inequalities.resizeVertically(pos); 1227bb901355SGroverkss 1228bb901355SGroverkss // Scan to get rid of all equalities marked redundant, in-place. In Simplex, 1229bb901355SGroverkss // after the inequalities, a pair of constraints for each equality is added. 1230bb901355SGroverkss // An equality is redundant if both the inequalities in its pair are 1231bb901355SGroverkss // redundant. 1232bb901355SGroverkss pos = 0; 1233bb901355SGroverkss for (unsigned r = 0, e = getNumEqualities(); r < e; r++) { 1234bb901355SGroverkss if (!(simplex.isMarkedRedundant(numIneqs + 2 * r) && 1235bb901355SGroverkss simplex.isMarkedRedundant(numIneqs + 2 * r + 1))) 1236bb901355SGroverkss equalities.copyRow(r, pos++); 1237bb901355SGroverkss } 1238bb901355SGroverkss equalities.resizeVertically(pos); 1239bb901355SGroverkss } 1240bb901355SGroverkss 12411a0e67d7SRamkumar Ramachandra std::optional<DynamicAPInt> IntegerRelation::computeVolume() const { 1242d95140a5SGroverkss assert(getNumSymbolVars() == 0 && "Symbols are not yet supported!"); 1243bb901355SGroverkss 1244bb901355SGroverkss Simplex simplex(*this); 1245bb901355SGroverkss // If the polytope is rationally empty, there are certainly no integer 1246bb901355SGroverkss // points. 1247bb901355SGroverkss if (simplex.isEmpty()) 12481a0e67d7SRamkumar Ramachandra return DynamicAPInt(0); 1249bb901355SGroverkss 1250d95140a5SGroverkss // Just find the maximum and minimum integer value of each non-local var 1251d95140a5SGroverkss // separately, thus finding the number of integer values each such var can 1252bb901355SGroverkss // take. Multiplying these together gives a valid overapproximation of the 1253bb901355SGroverkss // number of integer points in the relation. The result this gives is 1254d95140a5SGroverkss // equivalent to projecting (rationally) the relation onto its non-local vars 1255bb901355SGroverkss // and returning the number of integer points in a minimal axis-parallel 1256bb901355SGroverkss // hyperrectangular overapproximation of that. 1257bb901355SGroverkss // 1258bb901355SGroverkss // We also handle the special case where one dimension is unbounded and 1259bb901355SGroverkss // another dimension can take no integer values. In this case, the volume is 1260bb901355SGroverkss // zero. 1261bb901355SGroverkss // 1262bb901355SGroverkss // If there is no such empty dimension, if any dimension is unbounded we 1263bb901355SGroverkss // just return the result as unbounded. 12641a0e67d7SRamkumar Ramachandra DynamicAPInt count(1); 12651a0e67d7SRamkumar Ramachandra SmallVector<DynamicAPInt, 8> dim(getNumVars() + 1); 1266d95140a5SGroverkss bool hasUnboundedVar = false; 1267d95140a5SGroverkss for (unsigned i = 0, e = getNumDimAndSymbolVars(); i < e; ++i) { 1268bb901355SGroverkss dim[i] = 1; 12699fa59e76SBenjamin Kramer auto [min, max] = simplex.computeIntegerBounds(dim); 1270bb901355SGroverkss dim[i] = 0; 1271bb901355SGroverkss 1272bb901355SGroverkss assert((!min.isEmpty() && !max.isEmpty()) && 1273bb901355SGroverkss "Polytope should be rationally non-empty!"); 1274bb901355SGroverkss 1275bb901355SGroverkss // One of the dimensions is unbounded. Note this fact. We will return 1276bb901355SGroverkss // unbounded if none of the other dimensions makes the volume zero. 1277bb901355SGroverkss if (min.isUnbounded() || max.isUnbounded()) { 1278d95140a5SGroverkss hasUnboundedVar = true; 1279bb901355SGroverkss continue; 1280bb901355SGroverkss } 1281bb901355SGroverkss 1282bb901355SGroverkss // In this case there are no valid integer points and the volume is 1283bb901355SGroverkss // definitely zero. 1284bb901355SGroverkss if (min.getBoundedOptimum() > max.getBoundedOptimum()) 12851a0e67d7SRamkumar Ramachandra return DynamicAPInt(0); 1286bb901355SGroverkss 1287bb901355SGroverkss count *= (*max - *min + 1); 1288bb901355SGroverkss } 1289bb901355SGroverkss 1290bb901355SGroverkss if (count == 0) 12911a0e67d7SRamkumar Ramachandra return DynamicAPInt(0); 1292d95140a5SGroverkss if (hasUnboundedVar) 1293bb901355SGroverkss return {}; 1294bb901355SGroverkss return count; 1295bb901355SGroverkss } 1296bb901355SGroverkss 1297d95140a5SGroverkss void IntegerRelation::eliminateRedundantLocalVar(unsigned posA, unsigned posB) { 1298d95140a5SGroverkss assert(posA < getNumLocalVars() && "Invalid local var position"); 1299d95140a5SGroverkss assert(posB < getNumLocalVars() && "Invalid local var position"); 1300bb901355SGroverkss 1301d95140a5SGroverkss unsigned localOffset = getVarKindOffset(VarKind::Local); 1302bb901355SGroverkss posA += localOffset; 1303bb901355SGroverkss posB += localOffset; 1304bb901355SGroverkss inequalities.addToColumn(posB, posA, 1); 1305bb901355SGroverkss equalities.addToColumn(posB, posA, 1); 1306d95140a5SGroverkss removeVar(posB); 1307bb901355SGroverkss } 1308bb901355SGroverkss 13093eb9fd8aSBharathi Ramana Joshi /// mergeAndAlignSymbols's implementation can be broken down into two steps: 13103eb9fd8aSBharathi Ramana Joshi /// 1. Merge and align identifiers into `other` from `this. If an identifier 13113eb9fd8aSBharathi Ramana Joshi /// from `this` exists in `other` then we align it. Otherwise, we assume it is a 13123eb9fd8aSBharathi Ramana Joshi /// new identifier and insert it into `other` in the same position as `this`. 13133eb9fd8aSBharathi Ramana Joshi /// 2. Add identifiers that are in `other` but not `this to `this`. 13143eb9fd8aSBharathi Ramana Joshi void IntegerRelation::mergeAndAlignSymbols(IntegerRelation &other) { 13153eb9fd8aSBharathi Ramana Joshi assert(space.isUsingIds() && other.space.isUsingIds() && 13163eb9fd8aSBharathi Ramana Joshi "both relations need to have identifers to merge and align"); 13173eb9fd8aSBharathi Ramana Joshi 13183eb9fd8aSBharathi Ramana Joshi unsigned i = 0; 13193eb9fd8aSBharathi Ramana Joshi for (const Identifier identifier : space.getIds(VarKind::Symbol)) { 13203eb9fd8aSBharathi Ramana Joshi // Search in `other` starting at position `i` since the left of `i` is 13213eb9fd8aSBharathi Ramana Joshi // aligned. 13223eb9fd8aSBharathi Ramana Joshi const Identifier *findBegin = 13233eb9fd8aSBharathi Ramana Joshi other.space.getIds(VarKind::Symbol).begin() + i; 13243eb9fd8aSBharathi Ramana Joshi const Identifier *findEnd = other.space.getIds(VarKind::Symbol).end(); 13253eb9fd8aSBharathi Ramana Joshi const Identifier *itr = std::find(findBegin, findEnd, identifier); 13263eb9fd8aSBharathi Ramana Joshi if (itr != findEnd) { 13273eb9fd8aSBharathi Ramana Joshi other.swapVar(other.getVarKindOffset(VarKind::Symbol) + i, 13283eb9fd8aSBharathi Ramana Joshi other.getVarKindOffset(VarKind::Symbol) + i + 13293eb9fd8aSBharathi Ramana Joshi std::distance(findBegin, itr)); 13303eb9fd8aSBharathi Ramana Joshi } else { 13313eb9fd8aSBharathi Ramana Joshi other.insertVar(VarKind::Symbol, i); 133224da7fa0SBharathi Ramana Joshi other.space.setId(VarKind::Symbol, i, identifier); 13333eb9fd8aSBharathi Ramana Joshi } 13343eb9fd8aSBharathi Ramana Joshi ++i; 13353eb9fd8aSBharathi Ramana Joshi } 13363eb9fd8aSBharathi Ramana Joshi 13373eb9fd8aSBharathi Ramana Joshi for (unsigned e = other.getNumVarKind(VarKind::Symbol); i < e; ++i) { 13383eb9fd8aSBharathi Ramana Joshi insertVar(VarKind::Symbol, i); 133924da7fa0SBharathi Ramana Joshi space.setId(VarKind::Symbol, i, other.space.getId(VarKind::Symbol, i)); 13403eb9fd8aSBharathi Ramana Joshi } 13413eb9fd8aSBharathi Ramana Joshi } 13423eb9fd8aSBharathi Ramana Joshi 1343bb901355SGroverkss /// Adds additional local ids to the sets such that they both have the union 1344bb901355SGroverkss /// of the local ids in each set, without changing the set of points that 1345bb901355SGroverkss /// lie in `this` and `other`. 1346bb901355SGroverkss /// 13474ffd0b6fSGroverkss /// To detect local ids that always take the same value, each local id is 1348bb901355SGroverkss /// represented as a floordiv with constant denominator in terms of other ids. 13494ffd0b6fSGroverkss /// After extracting these divisions, local ids in `other` with the same 13504ffd0b6fSGroverkss /// division representation as some other local id in any set are considered 13514ffd0b6fSGroverkss /// duplicate and are merged. 13524ffd0b6fSGroverkss /// 13534ffd0b6fSGroverkss /// It is possible that division representation for some local id cannot be 13544ffd0b6fSGroverkss /// obtained, and thus these local ids are not considered for detecting 13554ffd0b6fSGroverkss /// duplicates. 1356d95140a5SGroverkss unsigned IntegerRelation::mergeLocalVars(IntegerRelation &other) { 1357bb901355SGroverkss IntegerRelation &relA = *this; 1358bb901355SGroverkss IntegerRelation &relB = other; 1359bb901355SGroverkss 1360d95140a5SGroverkss unsigned oldALocals = relA.getNumLocalVars(); 13614ffd0b6fSGroverkss 1362bb901355SGroverkss // Merge function that merges the local variables in both sets by treating 1363d95140a5SGroverkss // them as the same variable. 13644ffd0b6fSGroverkss auto merge = [&relA, &relB, oldALocals](unsigned i, unsigned j) -> bool { 13654ffd0b6fSGroverkss // We only merge from local at pos j to local at pos i, where j > i. 13664ffd0b6fSGroverkss if (i >= j) 13674ffd0b6fSGroverkss return false; 13684ffd0b6fSGroverkss 13694ffd0b6fSGroverkss // If i < oldALocals, we are trying to merge duplicate divs. Since we do not 13704ffd0b6fSGroverkss // want to merge duplicates in A, we ignore this call. 13714ffd0b6fSGroverkss if (j < oldALocals) 13724ffd0b6fSGroverkss return false; 13734ffd0b6fSGroverkss 13744ffd0b6fSGroverkss // Merge local at pos j into local at position i. 1375d95140a5SGroverkss relA.eliminateRedundantLocalVar(i, j); 1376d95140a5SGroverkss relB.eliminateRedundantLocalVar(i, j); 1377bb901355SGroverkss return true; 1378bb901355SGroverkss }; 1379bb901355SGroverkss 1380d95140a5SGroverkss presburger::mergeLocalVars(*this, other, merge); 13814ffd0b6fSGroverkss 13824ffd0b6fSGroverkss // Since we do not remove duplicate divisions in relA, this is guranteed to be 13834ffd0b6fSGroverkss // non-negative. 1384d95140a5SGroverkss return relA.getNumLocalVars() - oldALocals; 138531cb9995SArjun P } 138631cb9995SArjun P 13878a7ead69SArjun P bool IntegerRelation::hasOnlyDivLocals() const { 1388479c4f64SGroverkss return getLocalReprs().hasAllReprs(); 13898a7ead69SArjun P } 13908a7ead69SArjun P 139131cb9995SArjun P void IntegerRelation::removeDuplicateDivs() { 1392479c4f64SGroverkss DivisionRepr divs = getLocalReprs(); 139331cb9995SArjun P auto merge = [this](unsigned i, unsigned j) -> bool { 1394d95140a5SGroverkss eliminateRedundantLocalVar(i, j); 139531cb9995SArjun P return true; 139631cb9995SArjun P }; 1397479c4f64SGroverkss divs.removeDuplicateDivs(merge); 1398bb901355SGroverkss } 1399bb901355SGroverkss 140039b93955Sgilsaia void IntegerRelation::simplify() { 140139b93955Sgilsaia bool changed = true; 140239b93955Sgilsaia // Repeat until we reach a fixed point. 140339b93955Sgilsaia while (changed) { 140439b93955Sgilsaia if (isObviouslyEmpty()) 140539b93955Sgilsaia return; 140639b93955Sgilsaia changed = false; 140739b93955Sgilsaia normalizeConstraintsByGCD(); 140839b93955Sgilsaia changed |= gaussianEliminate(); 140939b93955Sgilsaia changed |= removeDuplicateConstraints(); 141039b93955Sgilsaia } 141139b93955Sgilsaia // Current set is not empty. 141239b93955Sgilsaia } 141339b93955Sgilsaia 1414bb901355SGroverkss /// Removes local variables using equalities. Each equality is checked if it 1415bb901355SGroverkss /// can be reduced to the form: `e = affine-expr`, where `e` is a local 1416bb901355SGroverkss /// variable and `affine-expr` is an affine expression not containing `e`. 1417bb901355SGroverkss /// If an equality satisfies this form, the local variable is replaced in 1418bb901355SGroverkss /// each constraint and then removed. The equality used to replace this local 1419bb901355SGroverkss /// variable is also removed. 1420bb901355SGroverkss void IntegerRelation::removeRedundantLocalVars() { 1421bb901355SGroverkss // Normalize the equality constraints to reduce coefficients of local 1422bb901355SGroverkss // variables to 1 wherever possible. 1423bb901355SGroverkss for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) 1424bb901355SGroverkss equalities.normalizeRow(i); 1425bb901355SGroverkss 1426bb901355SGroverkss while (true) { 1427bb901355SGroverkss unsigned i, e, j, f; 1428bb901355SGroverkss for (i = 0, e = getNumEqualities(); i < e; ++i) { 1429bb901355SGroverkss // Find a local variable to eliminate using ith equality. 1430d95140a5SGroverkss for (j = getNumDimAndSymbolVars(), f = getNumVars(); j < f; ++j) 14316d6f6c4dSArjun P if (abs(atEq(i, j)) == 1) 1432bb901355SGroverkss break; 1433bb901355SGroverkss 1434bb901355SGroverkss // Local variable can be eliminated using ith equality. 1435bb901355SGroverkss if (j < f) 1436bb901355SGroverkss break; 1437bb901355SGroverkss } 1438bb901355SGroverkss 1439bb901355SGroverkss // No equality can be used to eliminate a local variable. 1440bb901355SGroverkss if (i == e) 1441bb901355SGroverkss break; 1442bb901355SGroverkss 1443bb901355SGroverkss // Use the ith equality to simplify other equalities. If any changes 1444bb901355SGroverkss // are made to an equality constraint, it is normalized by GCD. 1445bb901355SGroverkss for (unsigned k = 0, t = getNumEqualities(); k < t; ++k) { 1446bb901355SGroverkss if (atEq(k, j) != 0) { 1447bb901355SGroverkss eliminateFromConstraint(this, k, i, j, j, /*isEq=*/true); 1448bb901355SGroverkss equalities.normalizeRow(k); 1449bb901355SGroverkss } 1450bb901355SGroverkss } 1451bb901355SGroverkss 1452bb901355SGroverkss // Use the ith equality to simplify inequalities. 1453bb901355SGroverkss for (unsigned k = 0, t = getNumInequalities(); k < t; ++k) 1454bb901355SGroverkss eliminateFromConstraint(this, k, i, j, j, /*isEq=*/false); 1455bb901355SGroverkss 1456bb901355SGroverkss // Remove the ith equality and the found local variable. 1457d95140a5SGroverkss removeVar(j); 1458bb901355SGroverkss removeEquality(i); 1459bb901355SGroverkss } 1460bb901355SGroverkss } 1461bb901355SGroverkss 1462d08522f5SArjun P void IntegerRelation::convertVarKind(VarKind srcKind, unsigned varStart, 1463d08522f5SArjun P unsigned varLimit, VarKind dstKind, 1464dac27da7SGroverkss unsigned pos) { 146524da7fa0SBharathi Ramana Joshi assert(varLimit <= getNumVarKind(srcKind) && "invalid id range"); 1466bb901355SGroverkss 1467d08522f5SArjun P if (varStart >= varLimit) 1468bb901355SGroverkss return; 1469bb901355SGroverkss 147024da7fa0SBharathi Ramana Joshi unsigned srcOffset = getVarKindOffset(srcKind); 147124da7fa0SBharathi Ramana Joshi unsigned dstOffset = getVarKindOffset(dstKind); 1472d08522f5SArjun P unsigned convertCount = varLimit - varStart; 147324da7fa0SBharathi Ramana Joshi int forwardMoveOffset = dstOffset > srcOffset ? -convertCount : 0; 1474bb901355SGroverkss 147524da7fa0SBharathi Ramana Joshi equalities.moveColumns(srcOffset + varStart, convertCount, 147624da7fa0SBharathi Ramana Joshi dstOffset + pos + forwardMoveOffset); 147724da7fa0SBharathi Ramana Joshi inequalities.moveColumns(srcOffset + varStart, convertCount, 147824da7fa0SBharathi Ramana Joshi dstOffset + pos + forwardMoveOffset); 1479bb901355SGroverkss 148024da7fa0SBharathi Ramana Joshi space.convertVarKind(srcKind, varStart, varLimit - varStart, dstKind, pos); 1481bb901355SGroverkss } 1482bb901355SGroverkss 14836d6f6c4dSArjun P void IntegerRelation::addBound(BoundType type, unsigned pos, 14841a0e67d7SRamkumar Ramachandra const DynamicAPInt &value) { 1485bb901355SGroverkss assert(pos < getNumCols()); 1486bb901355SGroverkss if (type == BoundType::EQ) { 1487bb901355SGroverkss unsigned row = equalities.appendExtraRow(); 1488bb901355SGroverkss equalities(row, pos) = 1; 1489bb901355SGroverkss equalities(row, getNumCols() - 1) = -value; 1490bb901355SGroverkss } else { 1491bb901355SGroverkss unsigned row = inequalities.appendExtraRow(); 1492bb901355SGroverkss inequalities(row, pos) = type == BoundType::LB ? 1 : -1; 1493bb901355SGroverkss inequalities(row, getNumCols() - 1) = 1494bb901355SGroverkss type == BoundType::LB ? -value : value; 1495bb901355SGroverkss } 1496bb901355SGroverkss } 1497bb901355SGroverkss 14981a0e67d7SRamkumar Ramachandra void IntegerRelation::addBound(BoundType type, ArrayRef<DynamicAPInt> expr, 14991a0e67d7SRamkumar Ramachandra const DynamicAPInt &value) { 1500bb901355SGroverkss assert(type != BoundType::EQ && "EQ not implemented"); 1501bb901355SGroverkss assert(expr.size() == getNumCols()); 1502bb901355SGroverkss unsigned row = inequalities.appendExtraRow(); 1503bb901355SGroverkss for (unsigned i = 0, e = expr.size(); i < e; ++i) 1504bb901355SGroverkss inequalities(row, i) = type == BoundType::LB ? expr[i] : -expr[i]; 1505bb901355SGroverkss inequalities(inequalities.getNumRows() - 1, getNumCols() - 1) += 1506bb901355SGroverkss type == BoundType::LB ? -value : value; 1507bb901355SGroverkss } 1508bb901355SGroverkss 1509d95140a5SGroverkss /// Adds a new local variable as the floordiv of an affine function of other 1510d95140a5SGroverkss /// variables, the coefficients of which are provided in 'dividend' and with 1511bb901355SGroverkss /// respect to a positive constant 'divisor'. Two constraints are added to the 1512bb901355SGroverkss /// system to capture equivalence with the floordiv. 1513bb901355SGroverkss /// q = expr floordiv c <=> c*q <= expr <= c*q + c - 1. 15141a0e67d7SRamkumar Ramachandra void IntegerRelation::addLocalFloorDiv(ArrayRef<DynamicAPInt> dividend, 15151a0e67d7SRamkumar Ramachandra const DynamicAPInt &divisor) { 1516bb901355SGroverkss assert(dividend.size() == getNumCols() && "incorrect dividend size"); 1517bb901355SGroverkss assert(divisor > 0 && "positive divisor expected"); 1518bb901355SGroverkss 1519d95140a5SGroverkss appendVar(VarKind::Local); 1520bb901355SGroverkss 15215262865aSKazu Hirata SmallVector<DynamicAPInt, 8> dividendCopy(dividend); 15221a0e67d7SRamkumar Ramachandra dividendCopy.insert(dividendCopy.end() - 1, DynamicAPInt(0)); 1523fd26d86fSArjun P addInequality( 1524fd26d86fSArjun P getDivLowerBound(dividendCopy, divisor, dividendCopy.size() - 2)); 1525fd26d86fSArjun P addInequality( 1526fd26d86fSArjun P getDivUpperBound(dividendCopy, divisor, dividendCopy.size() - 2)); 1527bb901355SGroverkss } 1528bb901355SGroverkss 1529d95140a5SGroverkss /// Finds an equality that equates the specified variable to a constant. 1530bb901355SGroverkss /// Returns the position of the equality row. If 'symbolic' is set to true, 1531bb901355SGroverkss /// symbols are also treated like a constant, i.e., an affine function of the 1532bb901355SGroverkss /// symbols is also treated like a constant. Returns -1 if such an equality 1533bb901355SGroverkss /// could not be found. 1534bb901355SGroverkss static int findEqualityToConstant(const IntegerRelation &cst, unsigned pos, 1535bb901355SGroverkss bool symbolic = false) { 1536d95140a5SGroverkss assert(pos < cst.getNumVars() && "invalid position"); 1537bb901355SGroverkss for (unsigned r = 0, e = cst.getNumEqualities(); r < e; r++) { 15381a0e67d7SRamkumar Ramachandra DynamicAPInt v = cst.atEq(r, pos); 1539bb901355SGroverkss if (v * v != 1) 1540bb901355SGroverkss continue; 1541bb901355SGroverkss unsigned c; 1542d95140a5SGroverkss unsigned f = symbolic ? cst.getNumDimVars() : cst.getNumVars(); 1543bb901355SGroverkss // This checks for zeros in all positions other than 'pos' in [0, f) 1544bb901355SGroverkss for (c = 0; c < f; c++) { 1545bb901355SGroverkss if (c == pos) 1546bb901355SGroverkss continue; 1547bb901355SGroverkss if (cst.atEq(r, c) != 0) { 1548d95140a5SGroverkss // Dependent on another variable. 1549bb901355SGroverkss break; 1550bb901355SGroverkss } 1551bb901355SGroverkss } 1552bb901355SGroverkss if (c == f) 1553d95140a5SGroverkss // Equality is free of other variables. 1554bb901355SGroverkss return r; 1555bb901355SGroverkss } 1556bb901355SGroverkss return -1; 1557bb901355SGroverkss } 1558bb901355SGroverkss 1559f819302aSRamkumar Ramachandra LogicalResult IntegerRelation::constantFoldVar(unsigned pos) { 1560d95140a5SGroverkss assert(pos < getNumVars() && "invalid position"); 1561bb901355SGroverkss int rowIdx; 1562bb901355SGroverkss if ((rowIdx = findEqualityToConstant(*this, pos)) == -1) 1563f819302aSRamkumar Ramachandra return failure(); 1564bb901355SGroverkss 1565bb901355SGroverkss // atEq(rowIdx, pos) is either -1 or 1. 1566bb901355SGroverkss assert(atEq(rowIdx, pos) * atEq(rowIdx, pos) == 1); 15671a0e67d7SRamkumar Ramachandra DynamicAPInt constVal = -atEq(rowIdx, getNumCols() - 1) / atEq(rowIdx, pos); 1568bb901355SGroverkss setAndEliminate(pos, constVal); 1569f819302aSRamkumar Ramachandra return success(); 1570bb901355SGroverkss } 1571bb901355SGroverkss 1572d95140a5SGroverkss void IntegerRelation::constantFoldVarRange(unsigned pos, unsigned num) { 1573bb901355SGroverkss for (unsigned s = pos, t = pos, e = pos + num; s < e; s++) { 1574f819302aSRamkumar Ramachandra if (constantFoldVar(t).failed()) 1575bb901355SGroverkss t++; 1576bb901355SGroverkss } 1577bb901355SGroverkss } 1578bb901355SGroverkss 1579bb901355SGroverkss /// Returns a non-negative constant bound on the extent (upper bound - lower 1580d95140a5SGroverkss /// bound) of the specified variable if it is found to be a constant; returns 15814f81805aSKazu Hirata /// std::nullopt if it's not a constant. This methods treats symbolic variables 1582bb901355SGroverkss /// specially, i.e., it looks for constant differences between affine 15834f81805aSKazu Hirata /// expressions involving only the symbolic variables. See comments at function 15844f81805aSKazu Hirata /// definition for example. 'lb', if provided, is set to the lower bound 15854f81805aSKazu Hirata /// associated with the constant difference. Note that 'lb' is purely symbolic 15864f81805aSKazu Hirata /// and thus will contain the coefficients of the symbolic variables and the 15874f81805aSKazu Hirata /// constant coefficient. 1588bb901355SGroverkss // Egs: 0 <= i <= 15, return 16. 1589bb901355SGroverkss // s0 + 2 <= i <= s0 + 17, returns 16. (s0 has to be a symbol) 1590bb901355SGroverkss // s0 + s1 + 16 <= d0 <= s0 + s1 + 31, returns 16. 1591bb901355SGroverkss // s0 - 7 <= 8*j <= s0 returns 1 with lb = s0, lbDivisor = 8 (since lb = 1592bb901355SGroverkss // ceil(s0 - 7 / 8) = floor(s0 / 8)). 15931a0e67d7SRamkumar Ramachandra std::optional<DynamicAPInt> IntegerRelation::getConstantBoundOnDimSize( 15941a0e67d7SRamkumar Ramachandra unsigned pos, SmallVectorImpl<DynamicAPInt> *lb, 15951a0e67d7SRamkumar Ramachandra DynamicAPInt *boundFloorDivisor, SmallVectorImpl<DynamicAPInt> *ub, 15961a0e67d7SRamkumar Ramachandra unsigned *minLbPos, unsigned *minUbPos) const { 1597d95140a5SGroverkss assert(pos < getNumDimVars() && "Invalid variable position"); 1598bb901355SGroverkss 1599d95140a5SGroverkss // Find an equality for 'pos'^th variable that equates it to some function 1600d95140a5SGroverkss // of the symbolic variables (+ constant). 1601bb901355SGroverkss int eqPos = findEqualityToConstant(*this, pos, /*symbolic=*/true); 1602bb901355SGroverkss if (eqPos != -1) { 1603bb901355SGroverkss auto eq = getEquality(eqPos); 1604bb901355SGroverkss // If the equality involves a local var, punt for now. 1605bb901355SGroverkss // TODO: this can be handled in the future by using the explicit 1606bb901355SGroverkss // representation of the local vars. 1607d95140a5SGroverkss if (!std::all_of(eq.begin() + getNumDimAndSymbolVars(), eq.end() - 1, 16081a0e67d7SRamkumar Ramachandra [](const DynamicAPInt &coeff) { return coeff == 0; })) 16091a36588eSKazu Hirata return std::nullopt; 1610bb901355SGroverkss 1611d95140a5SGroverkss // This variable can only take a single value. 1612bb901355SGroverkss if (lb) { 1613bb901355SGroverkss // Set lb to that symbolic value. 1614d95140a5SGroverkss lb->resize(getNumSymbolVars() + 1); 1615bb901355SGroverkss if (ub) 1616d95140a5SGroverkss ub->resize(getNumSymbolVars() + 1); 1617d95140a5SGroverkss for (unsigned c = 0, f = getNumSymbolVars() + 1; c < f; c++) { 16181a0e67d7SRamkumar Ramachandra DynamicAPInt v = atEq(eqPos, pos); 1619bb901355SGroverkss // atEq(eqRow, pos) is either -1 or 1. 1620bb901355SGroverkss assert(v * v == 1); 1621d95140a5SGroverkss (*lb)[c] = v < 0 ? atEq(eqPos, getNumDimVars() + c) / -v 1622d95140a5SGroverkss : -atEq(eqPos, getNumDimVars() + c) / v; 1623bb901355SGroverkss // Since this is an equality, ub = lb. 1624bb901355SGroverkss if (ub) 1625bb901355SGroverkss (*ub)[c] = (*lb)[c]; 1626bb901355SGroverkss } 1627bb901355SGroverkss assert(boundFloorDivisor && 1628bb901355SGroverkss "both lb and divisor or none should be provided"); 1629bb901355SGroverkss *boundFloorDivisor = 1; 1630bb901355SGroverkss } 1631bb901355SGroverkss if (minLbPos) 1632bb901355SGroverkss *minLbPos = eqPos; 1633bb901355SGroverkss if (minUbPos) 1634bb901355SGroverkss *minUbPos = eqPos; 16351a0e67d7SRamkumar Ramachandra return DynamicAPInt(1); 1636bb901355SGroverkss } 1637bb901355SGroverkss 1638d95140a5SGroverkss // Check if the variable appears at all in any of the inequalities. 1639bb901355SGroverkss unsigned r, e; 1640bb901355SGroverkss for (r = 0, e = getNumInequalities(); r < e; r++) { 1641bb901355SGroverkss if (atIneq(r, pos) != 0) 1642bb901355SGroverkss break; 1643bb901355SGroverkss } 1644bb901355SGroverkss if (r == e) 1645bb901355SGroverkss // If it doesn't, there isn't a bound on it. 16461a36588eSKazu Hirata return std::nullopt; 1647bb901355SGroverkss 1648bb901355SGroverkss // Positions of constraints that are lower/upper bounds on the variable. 1649bb901355SGroverkss SmallVector<unsigned, 4> lbIndices, ubIndices; 1650bb901355SGroverkss 1651bb901355SGroverkss // Gather all symbolic lower bounds and upper bounds of the variable, i.e., 1652d95140a5SGroverkss // the bounds can only involve symbolic (and local) variables. Since the 1653bb901355SGroverkss // canonical form c_1*x_1 + c_2*x_2 + ... + c_0 >= 0, a constraint is a lower 1654bb901355SGroverkss // bound for x_i if c_i >= 1, and an upper bound if c_i <= -1. 1655bb901355SGroverkss getLowerAndUpperBoundIndices(pos, &lbIndices, &ubIndices, 1656bb901355SGroverkss /*eqIndices=*/nullptr, /*offset=*/0, 1657d95140a5SGroverkss /*num=*/getNumDimVars()); 1658bb901355SGroverkss 16591a0e67d7SRamkumar Ramachandra std::optional<DynamicAPInt> minDiff; 1660bb901355SGroverkss unsigned minLbPosition = 0, minUbPosition = 0; 1661bb901355SGroverkss for (auto ubPos : ubIndices) { 1662bb901355SGroverkss for (auto lbPos : lbIndices) { 1663bb901355SGroverkss // Look for a lower bound and an upper bound that only differ by a 1664bb901355SGroverkss // constant, i.e., pairs of the form 0 <= c_pos - f(c_i's) <= diffConst. 1665bb901355SGroverkss // For example, if ii is the pos^th variable, we are looking for 1666bb901355SGroverkss // constraints like ii >= i, ii <= ii + 50, 50 being the difference. The 1667bb901355SGroverkss // minimum among all such constant differences is kept since that's the 1668bb901355SGroverkss // constant bounding the extent of the pos^th variable. 1669bb901355SGroverkss unsigned j, e; 1670bb901355SGroverkss for (j = 0, e = getNumCols() - 1; j < e; j++) 1671bb901355SGroverkss if (atIneq(ubPos, j) != -atIneq(lbPos, j)) { 1672bb901355SGroverkss break; 1673bb901355SGroverkss } 1674bb901355SGroverkss if (j < getNumCols() - 1) 1675bb901355SGroverkss continue; 16761a0e67d7SRamkumar Ramachandra DynamicAPInt diff = ceilDiv(atIneq(ubPos, getNumCols() - 1) + 1677bb901355SGroverkss atIneq(lbPos, getNumCols() - 1) + 1, 1678bb901355SGroverkss atIneq(lbPos, pos)); 1679bb901355SGroverkss // This bound is non-negative by definition. 16801a0e67d7SRamkumar Ramachandra diff = std::max<DynamicAPInt>(diff, DynamicAPInt(0)); 16811a36588eSKazu Hirata if (minDiff == std::nullopt || diff < minDiff) { 1682bb901355SGroverkss minDiff = diff; 1683bb901355SGroverkss minLbPosition = lbPos; 1684bb901355SGroverkss minUbPosition = ubPos; 1685bb901355SGroverkss } 1686bb901355SGroverkss } 1687bb901355SGroverkss } 1688037f0995SKazu Hirata if (lb && minDiff) { 1689bb901355SGroverkss // Set lb to the symbolic lower bound. 1690d95140a5SGroverkss lb->resize(getNumSymbolVars() + 1); 1691bb901355SGroverkss if (ub) 1692d95140a5SGroverkss ub->resize(getNumSymbolVars() + 1); 1693bb901355SGroverkss // The lower bound is the ceildiv of the lb constraint over the coefficient 1694bb901355SGroverkss // of the variable at 'pos'. We express the ceildiv equivalently as a floor 1695bb901355SGroverkss // for uniformity. For eg., if the lower bound constraint was: 32*d0 - N + 1696bb901355SGroverkss // 31 >= 0, the lower bound for d0 is ceil(N - 31, 32), i.e., floor(N, 32). 1697bb901355SGroverkss *boundFloorDivisor = atIneq(minLbPosition, pos); 1698bb901355SGroverkss assert(*boundFloorDivisor == -atIneq(minUbPosition, pos)); 1699d95140a5SGroverkss for (unsigned c = 0, e = getNumSymbolVars() + 1; c < e; c++) { 1700d95140a5SGroverkss (*lb)[c] = -atIneq(minLbPosition, getNumDimVars() + c); 1701bb901355SGroverkss } 1702bb901355SGroverkss if (ub) { 1703d95140a5SGroverkss for (unsigned c = 0, e = getNumSymbolVars() + 1; c < e; c++) 1704d95140a5SGroverkss (*ub)[c] = atIneq(minUbPosition, getNumDimVars() + c); 1705bb901355SGroverkss } 1706bb901355SGroverkss // The lower bound leads to a ceildiv while the upper bound is a floordiv 1707bb901355SGroverkss // whenever the coefficient at pos != 1. ceildiv (val / d) = floordiv (val + 1708bb901355SGroverkss // d - 1 / d); hence, the addition of 'atIneq(minLbPosition, pos) - 1' to 1709bb901355SGroverkss // the constant term for the lower bound. 1710d95140a5SGroverkss (*lb)[getNumSymbolVars()] += atIneq(minLbPosition, pos) - 1; 1711bb901355SGroverkss } 1712bb901355SGroverkss if (minLbPos) 1713bb901355SGroverkss *minLbPos = minLbPosition; 1714bb901355SGroverkss if (minUbPos) 1715bb901355SGroverkss *minUbPos = minUbPosition; 1716bb901355SGroverkss return minDiff; 1717bb901355SGroverkss } 1718bb901355SGroverkss 1719bb901355SGroverkss template <bool isLower> 17201a0e67d7SRamkumar Ramachandra std::optional<DynamicAPInt> 1721bb901355SGroverkss IntegerRelation::computeConstantLowerOrUpperBound(unsigned pos) { 1722d95140a5SGroverkss assert(pos < getNumVars() && "invalid position"); 1723bb901355SGroverkss // Project to 'pos'. 1724bb901355SGroverkss projectOut(0, pos); 1725d95140a5SGroverkss projectOut(1, getNumVars() - 1); 1726d95140a5SGroverkss // Check if there's an equality equating the '0'^th variable to a constant. 1727bb901355SGroverkss int eqRowIdx = findEqualityToConstant(*this, 0, /*symbolic=*/false); 1728bb901355SGroverkss if (eqRowIdx != -1) 1729bb901355SGroverkss // atEq(rowIdx, 0) is either -1 or 1. 1730bb901355SGroverkss return -atEq(eqRowIdx, getNumCols() - 1) / atEq(eqRowIdx, 0); 1731bb901355SGroverkss 1732d95140a5SGroverkss // Check if the variable appears at all in any of the inequalities. 1733bb901355SGroverkss unsigned r, e; 1734bb901355SGroverkss for (r = 0, e = getNumInequalities(); r < e; r++) { 1735bb901355SGroverkss if (atIneq(r, 0) != 0) 1736bb901355SGroverkss break; 1737bb901355SGroverkss } 1738bb901355SGroverkss if (r == e) 1739bb901355SGroverkss // If it doesn't, there isn't a bound on it. 17401a36588eSKazu Hirata return std::nullopt; 1741bb901355SGroverkss 17421a0e67d7SRamkumar Ramachandra std::optional<DynamicAPInt> minOrMaxConst; 1743bb901355SGroverkss 1744bb901355SGroverkss // Take the max across all const lower bounds (or min across all constant 1745bb901355SGroverkss // upper bounds). 1746bb901355SGroverkss for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 1747bb901355SGroverkss if (isLower) { 1748bb901355SGroverkss if (atIneq(r, 0) <= 0) 1749bb901355SGroverkss // Not a lower bound. 1750bb901355SGroverkss continue; 1751bb901355SGroverkss } else if (atIneq(r, 0) >= 0) { 1752bb901355SGroverkss // Not an upper bound. 1753bb901355SGroverkss continue; 1754bb901355SGroverkss } 1755bb901355SGroverkss unsigned c, f; 1756bb901355SGroverkss for (c = 0, f = getNumCols() - 1; c < f; c++) 1757bb901355SGroverkss if (c != 0 && atIneq(r, c) != 0) 1758bb901355SGroverkss break; 1759bb901355SGroverkss if (c < getNumCols() - 1) 1760bb901355SGroverkss // Not a constant bound. 1761bb901355SGroverkss continue; 1762bb901355SGroverkss 17631a0e67d7SRamkumar Ramachandra DynamicAPInt boundConst = 17646d6f6c4dSArjun P isLower ? ceilDiv(-atIneq(r, getNumCols() - 1), atIneq(r, 0)) 17656d6f6c4dSArjun P : floorDiv(atIneq(r, getNumCols() - 1), -atIneq(r, 0)); 1766bb901355SGroverkss if (isLower) { 17671a36588eSKazu Hirata if (minOrMaxConst == std::nullopt || boundConst > minOrMaxConst) 1768bb901355SGroverkss minOrMaxConst = boundConst; 1769bb901355SGroverkss } else { 17701a36588eSKazu Hirata if (minOrMaxConst == std::nullopt || boundConst < minOrMaxConst) 1771bb901355SGroverkss minOrMaxConst = boundConst; 1772bb901355SGroverkss } 1773bb901355SGroverkss } 1774bb901355SGroverkss return minOrMaxConst; 1775bb901355SGroverkss } 1776bb901355SGroverkss 17771a0e67d7SRamkumar Ramachandra std::optional<DynamicAPInt> 17781a0e67d7SRamkumar Ramachandra IntegerRelation::getConstantBound(BoundType type, unsigned pos) const { 1779bb901355SGroverkss if (type == BoundType::LB) 1780bb901355SGroverkss return IntegerRelation(*this) 1781bb901355SGroverkss .computeConstantLowerOrUpperBound</*isLower=*/true>(pos); 1782bb901355SGroverkss if (type == BoundType::UB) 1783bb901355SGroverkss return IntegerRelation(*this) 1784bb901355SGroverkss .computeConstantLowerOrUpperBound</*isLower=*/false>(pos); 1785bb901355SGroverkss 1786bb901355SGroverkss assert(type == BoundType::EQ && "expected EQ"); 17871a0e67d7SRamkumar Ramachandra std::optional<DynamicAPInt> lb = 1788bb901355SGroverkss IntegerRelation(*this).computeConstantLowerOrUpperBound</*isLower=*/true>( 1789bb901355SGroverkss pos); 17901a0e67d7SRamkumar Ramachandra std::optional<DynamicAPInt> ub = 1791bb901355SGroverkss IntegerRelation(*this) 1792bb901355SGroverkss .computeConstantLowerOrUpperBound</*isLower=*/false>(pos); 17931a0e67d7SRamkumar Ramachandra return (lb && ub && *lb == *ub) ? std::optional<DynamicAPInt>(*ub) 17941a0e67d7SRamkumar Ramachandra : std::nullopt; 1795bb901355SGroverkss } 1796bb901355SGroverkss 1797bb901355SGroverkss // A simple (naive and conservative) check for hyper-rectangularity. 1798bb901355SGroverkss bool IntegerRelation::isHyperRectangular(unsigned pos, unsigned num) const { 1799bb901355SGroverkss assert(pos < getNumCols() - 1); 1800bb901355SGroverkss // Check for two non-zero coefficients in the range [pos, pos + sum). 1801bb901355SGroverkss for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 1802bb901355SGroverkss unsigned sum = 0; 1803bb901355SGroverkss for (unsigned c = pos; c < pos + num; c++) { 1804bb901355SGroverkss if (atIneq(r, c) != 0) 1805bb901355SGroverkss sum++; 1806bb901355SGroverkss } 1807bb901355SGroverkss if (sum > 1) 1808bb901355SGroverkss return false; 1809bb901355SGroverkss } 1810bb901355SGroverkss for (unsigned r = 0, e = getNumEqualities(); r < e; r++) { 1811bb901355SGroverkss unsigned sum = 0; 1812bb901355SGroverkss for (unsigned c = pos; c < pos + num; c++) { 1813bb901355SGroverkss if (atEq(r, c) != 0) 1814bb901355SGroverkss sum++; 1815bb901355SGroverkss } 1816bb901355SGroverkss if (sum > 1) 1817bb901355SGroverkss return false; 1818bb901355SGroverkss } 1819bb901355SGroverkss return true; 1820bb901355SGroverkss } 1821bb901355SGroverkss 1822bb901355SGroverkss /// Removes duplicate constraints, trivially true constraints, and constraints 1823bb901355SGroverkss /// that can be detected as redundant as a result of differing only in their 1824bb901355SGroverkss /// constant term part. A constraint of the form <non-negative constant> >= 0 is 1825bb901355SGroverkss /// considered trivially true. 1826bb901355SGroverkss // Uses a DenseSet to hash and detect duplicates followed by a linear scan to 1827bb901355SGroverkss // remove duplicates in place. 1828bb901355SGroverkss void IntegerRelation::removeTrivialRedundancy() { 1829bb901355SGroverkss gcdTightenInequalities(); 1830bb901355SGroverkss normalizeConstraintsByGCD(); 1831bb901355SGroverkss 1832bb901355SGroverkss // A map used to detect redundancy stemming from constraints that only differ 1833bb901355SGroverkss // in their constant term. The value stored is <row position, const term> 1834bb901355SGroverkss // for a given row. 18351a0e67d7SRamkumar Ramachandra SmallDenseMap<ArrayRef<DynamicAPInt>, std::pair<unsigned, DynamicAPInt>> 1836bb901355SGroverkss rowsWithoutConstTerm; 1837bb901355SGroverkss // To unique rows. 18381a0e67d7SRamkumar Ramachandra SmallDenseSet<ArrayRef<DynamicAPInt>, 8> rowSet; 1839bb901355SGroverkss 1840bb901355SGroverkss // Check if constraint is of the form <non-negative-constant> >= 0. 1841bb901355SGroverkss auto isTriviallyValid = [&](unsigned r) -> bool { 1842bb901355SGroverkss for (unsigned c = 0, e = getNumCols() - 1; c < e; c++) { 1843bb901355SGroverkss if (atIneq(r, c) != 0) 1844bb901355SGroverkss return false; 1845bb901355SGroverkss } 1846bb901355SGroverkss return atIneq(r, getNumCols() - 1) >= 0; 1847bb901355SGroverkss }; 1848bb901355SGroverkss 1849bb901355SGroverkss // Detect and mark redundant constraints. 1850bb901355SGroverkss SmallVector<bool, 256> redunIneq(getNumInequalities(), false); 1851bb901355SGroverkss for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 18521a0e67d7SRamkumar Ramachandra DynamicAPInt *rowStart = &inequalities(r, 0); 18531a0e67d7SRamkumar Ramachandra auto row = ArrayRef<DynamicAPInt>(rowStart, getNumCols()); 1854bb901355SGroverkss if (isTriviallyValid(r) || !rowSet.insert(row).second) { 1855bb901355SGroverkss redunIneq[r] = true; 1856bb901355SGroverkss continue; 1857bb901355SGroverkss } 1858bb901355SGroverkss 1859bb901355SGroverkss // Among constraints that only differ in the constant term part, mark 1860bb901355SGroverkss // everything other than the one with the smallest constant term redundant. 1861bb901355SGroverkss // (eg: among i - 16j - 5 >= 0, i - 16j - 1 >=0, i - 16j - 7 >= 0, the 1862bb901355SGroverkss // former two are redundant). 18631a0e67d7SRamkumar Ramachandra DynamicAPInt constTerm = atIneq(r, getNumCols() - 1); 18641a0e67d7SRamkumar Ramachandra auto rowWithoutConstTerm = 18651a0e67d7SRamkumar Ramachandra ArrayRef<DynamicAPInt>(rowStart, getNumCols() - 1); 1866bb901355SGroverkss const auto &ret = 1867bb901355SGroverkss rowsWithoutConstTerm.insert({rowWithoutConstTerm, {r, constTerm}}); 1868bb901355SGroverkss if (!ret.second) { 1869bb901355SGroverkss // Check if the other constraint has a higher constant term. 1870bb901355SGroverkss auto &val = ret.first->second; 1871bb901355SGroverkss if (val.second > constTerm) { 1872bb901355SGroverkss // The stored row is redundant. Mark it so, and update with this one. 1873bb901355SGroverkss redunIneq[val.first] = true; 1874bb901355SGroverkss val = {r, constTerm}; 1875bb901355SGroverkss } else { 1876bb901355SGroverkss // The one stored makes this one redundant. 1877bb901355SGroverkss redunIneq[r] = true; 1878bb901355SGroverkss } 1879bb901355SGroverkss } 1880bb901355SGroverkss } 1881bb901355SGroverkss 1882bb901355SGroverkss // Scan to get rid of all rows marked redundant, in-place. 1883bb901355SGroverkss unsigned pos = 0; 1884bb901355SGroverkss for (unsigned r = 0, e = getNumInequalities(); r < e; r++) 1885bb901355SGroverkss if (!redunIneq[r]) 1886bb901355SGroverkss inequalities.copyRow(r, pos++); 1887bb901355SGroverkss 1888bb901355SGroverkss inequalities.resizeVertically(pos); 1889bb901355SGroverkss 1890bb901355SGroverkss // TODO: consider doing this for equalities as well, but probably not worth 1891bb901355SGroverkss // the savings. 1892bb901355SGroverkss } 1893bb901355SGroverkss 1894bb901355SGroverkss #undef DEBUG_TYPE 1895bb901355SGroverkss #define DEBUG_TYPE "fm" 1896bb901355SGroverkss 1897d95140a5SGroverkss /// Eliminates variable at the specified position using Fourier-Motzkin 1898bb901355SGroverkss /// variable elimination. This technique is exact for rational spaces but 1899bb901355SGroverkss /// conservative (in "rare" cases) for integer spaces. The operation corresponds 1900bb901355SGroverkss /// to a projection operation yielding the (convex) set of integer points 1901bb901355SGroverkss /// contained in the rational shadow of the set. An emptiness test that relies 1902bb901355SGroverkss /// on this method will guarantee emptiness, i.e., it disproves the existence of 1903bb901355SGroverkss /// a solution if it says it's empty. 1904bb901355SGroverkss /// If a non-null isResultIntegerExact is passed, it is set to true if the 1905bb901355SGroverkss /// result is also integer exact. If it's set to false, the obtained solution 1906bb901355SGroverkss /// *may* not be exact, i.e., it may contain integer points that do not have an 1907bb901355SGroverkss /// integer pre-image in the original set. 1908bb901355SGroverkss /// 1909bb901355SGroverkss /// Eg: 1910bb901355SGroverkss /// j >= 0, j <= i + 1 1911bb901355SGroverkss /// i >= 0, i <= N + 1 1912bb901355SGroverkss /// Eliminating i yields, 1913bb901355SGroverkss /// j >= 0, 0 <= N + 1, j - 1 <= N + 1 1914bb901355SGroverkss /// 1915bb901355SGroverkss /// If darkShadow = true, this method computes the dark shadow on elimination; 1916bb901355SGroverkss /// the dark shadow is a convex integer subset of the exact integer shadow. A 1917bb901355SGroverkss /// non-empty dark shadow proves the existence of an integer solution. The 1918bb901355SGroverkss /// elimination in such a case could however be an under-approximation, and thus 1919bb901355SGroverkss /// should not be used for scanning sets or used by itself for dependence 1920bb901355SGroverkss /// checking. 1921bb901355SGroverkss /// 1922bb901355SGroverkss /// Eg: 2-d set, * represents grid points, 'o' represents a point in the set. 1923bb901355SGroverkss /// ^ 1924bb901355SGroverkss /// | 1925bb901355SGroverkss /// | * * * * o o 1926bb901355SGroverkss /// i | * * o o o o 1927bb901355SGroverkss /// | o * * * * * 1928bb901355SGroverkss /// ---------------> 1929bb901355SGroverkss /// j -> 1930bb901355SGroverkss /// 1931bb901355SGroverkss /// Eliminating i from this system (projecting on the j dimension): 1932bb901355SGroverkss /// rational shadow / integer light shadow: 1 <= j <= 6 1933bb901355SGroverkss /// dark shadow: 3 <= j <= 6 1934bb901355SGroverkss /// exact integer shadow: j = 1 \union 3 <= j <= 6 1935bb901355SGroverkss /// holes/splinters: j = 2 1936bb901355SGroverkss /// 1937bb901355SGroverkss /// darkShadow = false, isResultIntegerExact = nullptr are default values. 1938bb901355SGroverkss // TODO: a slight modification to yield dark shadow version of FM (tightened), 1939bb901355SGroverkss // which can prove the existence of a solution if there is one. 1940bb901355SGroverkss void IntegerRelation::fourierMotzkinEliminate(unsigned pos, bool darkShadow, 1941bb901355SGroverkss bool *isResultIntegerExact) { 1942bb901355SGroverkss LLVM_DEBUG(llvm::dbgs() << "FM input (eliminate pos " << pos << "):\n"); 1943bb901355SGroverkss LLVM_DEBUG(dump()); 1944d95140a5SGroverkss assert(pos < getNumVars() && "invalid position"); 1945bb901355SGroverkss assert(hasConsistentState()); 1946bb901355SGroverkss 1947d95140a5SGroverkss // Check if this variable can be eliminated through a substitution. 1948bb901355SGroverkss for (unsigned r = 0, e = getNumEqualities(); r < e; r++) { 1949bb901355SGroverkss if (atEq(r, pos) != 0) { 1950bb901355SGroverkss // Use Gaussian elimination here (since we have an equality). 1951f819302aSRamkumar Ramachandra LogicalResult ret = gaussianEliminateVar(pos); 1952bb901355SGroverkss (void)ret; 1953f819302aSRamkumar Ramachandra assert(ret.succeeded() && "Gaussian elimination guaranteed to succeed"); 1954bb901355SGroverkss LLVM_DEBUG(llvm::dbgs() << "FM output (through Gaussian elimination):\n"); 1955bb901355SGroverkss LLVM_DEBUG(dump()); 1956bb901355SGroverkss return; 1957bb901355SGroverkss } 1958bb901355SGroverkss } 1959bb901355SGroverkss 1960bb901355SGroverkss // A fast linear time tightening. 1961bb901355SGroverkss gcdTightenInequalities(); 1962bb901355SGroverkss 1963d95140a5SGroverkss // Check if the variable appears at all in any of the inequalities. 1964bb901355SGroverkss if (isColZero(pos)) { 1965bb901355SGroverkss // If it doesn't appear, just remove the column and return. 1966bb901355SGroverkss // TODO: refactor removeColumns to use it from here. 1967d95140a5SGroverkss removeVar(pos); 1968bb901355SGroverkss LLVM_DEBUG(llvm::dbgs() << "FM output:\n"); 1969bb901355SGroverkss LLVM_DEBUG(dump()); 1970bb901355SGroverkss return; 1971bb901355SGroverkss } 1972bb901355SGroverkss 1973bb901355SGroverkss // Positions of constraints that are lower bounds on the variable. 1974bb901355SGroverkss SmallVector<unsigned, 4> lbIndices; 1975bb901355SGroverkss // Positions of constraints that are lower bounds on the variable. 1976bb901355SGroverkss SmallVector<unsigned, 4> ubIndices; 1977bb901355SGroverkss // Positions of constraints that do not involve the variable. 1978bb901355SGroverkss std::vector<unsigned> nbIndices; 1979bb901355SGroverkss nbIndices.reserve(getNumInequalities()); 1980bb901355SGroverkss 1981bb901355SGroverkss // Gather all lower bounds and upper bounds of the variable. Since the 1982bb901355SGroverkss // canonical form c_1*x_1 + c_2*x_2 + ... + c_0 >= 0, a constraint is a lower 1983bb901355SGroverkss // bound for x_i if c_i >= 1, and an upper bound if c_i <= -1. 1984bb901355SGroverkss for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 1985bb901355SGroverkss if (atIneq(r, pos) == 0) { 1986d95140a5SGroverkss // Var does not appear in bound. 1987266a5a9cSRamkumar Ramachandra nbIndices.emplace_back(r); 1988bb901355SGroverkss } else if (atIneq(r, pos) >= 1) { 1989bb901355SGroverkss // Lower bound. 1990266a5a9cSRamkumar Ramachandra lbIndices.emplace_back(r); 1991bb901355SGroverkss } else { 1992bb901355SGroverkss // Upper bound. 1993266a5a9cSRamkumar Ramachandra ubIndices.emplace_back(r); 1994bb901355SGroverkss } 1995bb901355SGroverkss } 1996bb901355SGroverkss 1997a5a598beSGroverkss PresburgerSpace newSpace = getSpace(); 1998d95140a5SGroverkss VarKind idKindRemove = newSpace.getVarKindAt(pos); 1999d95140a5SGroverkss unsigned relativePos = pos - newSpace.getVarKindOffset(idKindRemove); 2000d95140a5SGroverkss newSpace.removeVarRange(idKindRemove, relativePos, relativePos + 1); 2001bb901355SGroverkss 2002d95140a5SGroverkss /// Create the new system which has one variable less. 2003bb901355SGroverkss IntegerRelation newRel(lbIndices.size() * ubIndices.size() + nbIndices.size(), 2004a5a598beSGroverkss getNumEqualities(), getNumCols() - 1, newSpace); 2005bb901355SGroverkss 2006bb901355SGroverkss // This will be used to check if the elimination was integer exact. 2007bad95b72SArjun P bool allLCMsAreOne = true; 2008bb901355SGroverkss 2009bb901355SGroverkss // Let x be the variable we are eliminating. 2010bb901355SGroverkss // For each lower bound, lb <= c_l*x, and each upper bound c_u*x <= ub, (note 2011bb901355SGroverkss // that c_l, c_u >= 1) we have: 2012bb901355SGroverkss // lb*lcm(c_l, c_u)/c_l <= lcm(c_l, c_u)*x <= ub*lcm(c_l, c_u)/c_u 2013bb901355SGroverkss // We thus generate a constraint: 2014bb901355SGroverkss // lcm(c_l, c_u)/c_l*lb <= lcm(c_l, c_u)/c_u*ub. 2015bb901355SGroverkss // Note if c_l = c_u = 1, all integer points captured by the resulting 2016bb901355SGroverkss // constraint correspond to integer points in the original system (i.e., they 2017bb901355SGroverkss // have integer pre-images). Hence, if the lcm's are all 1, the elimination is 2018bb901355SGroverkss // integer exact. 2019bb901355SGroverkss for (auto ubPos : ubIndices) { 2020bb901355SGroverkss for (auto lbPos : lbIndices) { 20211a0e67d7SRamkumar Ramachandra SmallVector<DynamicAPInt, 4> ineq; 2022bb901355SGroverkss ineq.reserve(newRel.getNumCols()); 20231a0e67d7SRamkumar Ramachandra DynamicAPInt lbCoeff = atIneq(lbPos, pos); 2024bb901355SGroverkss // Note that in the comments above, ubCoeff is the negation of the 2025bb901355SGroverkss // coefficient in the canonical form as the view taken here is that of the 2026bb901355SGroverkss // term being moved to the other size of '>='. 20271a0e67d7SRamkumar Ramachandra DynamicAPInt ubCoeff = -atIneq(ubPos, pos); 2028bb901355SGroverkss // TODO: refactor this loop to avoid all branches inside. 2029bb901355SGroverkss for (unsigned l = 0, e = getNumCols(); l < e; l++) { 2030bb901355SGroverkss if (l == pos) 2031bb901355SGroverkss continue; 2032bb901355SGroverkss assert(lbCoeff >= 1 && ubCoeff >= 1 && "bounds wrongly identified"); 20331a0e67d7SRamkumar Ramachandra DynamicAPInt lcm = llvm::lcm(lbCoeff, ubCoeff); 2034266a5a9cSRamkumar Ramachandra ineq.emplace_back(atIneq(ubPos, l) * (lcm / ubCoeff) + 2035bb901355SGroverkss atIneq(lbPos, l) * (lcm / lbCoeff)); 2036bad95b72SArjun P assert(lcm > 0 && "lcm should be positive!"); 2037bad95b72SArjun P if (lcm != 1) 2038bad95b72SArjun P allLCMsAreOne = false; 2039bb901355SGroverkss } 2040bb901355SGroverkss if (darkShadow) { 2041bb901355SGroverkss // The dark shadow is a convex subset of the exact integer shadow. If 2042bb901355SGroverkss // there is a point here, it proves the existence of a solution. 2043bb901355SGroverkss ineq[ineq.size() - 1] += lbCoeff * ubCoeff - lbCoeff - ubCoeff + 1; 2044bb901355SGroverkss } 2045bb901355SGroverkss // TODO: we need to have a way to add inequalities in-place in 2046bb901355SGroverkss // IntegerRelation instead of creating and copying over. 2047bb901355SGroverkss newRel.addInequality(ineq); 2048bb901355SGroverkss } 2049bb901355SGroverkss } 2050bb901355SGroverkss 2051bad95b72SArjun P LLVM_DEBUG(llvm::dbgs() << "FM isResultIntegerExact: " << allLCMsAreOne 2052bb901355SGroverkss << "\n"); 2053bad95b72SArjun P if (allLCMsAreOne && isResultIntegerExact) 2054bb901355SGroverkss *isResultIntegerExact = true; 2055bb901355SGroverkss 2056bb901355SGroverkss // Copy over the constraints not involving this variable. 2057bb901355SGroverkss for (auto nbPos : nbIndices) { 20581a0e67d7SRamkumar Ramachandra SmallVector<DynamicAPInt, 4> ineq; 2059bb901355SGroverkss ineq.reserve(getNumCols() - 1); 2060bb901355SGroverkss for (unsigned l = 0, e = getNumCols(); l < e; l++) { 2061bb901355SGroverkss if (l == pos) 2062bb901355SGroverkss continue; 2063266a5a9cSRamkumar Ramachandra ineq.emplace_back(atIneq(nbPos, l)); 2064bb901355SGroverkss } 2065bb901355SGroverkss newRel.addInequality(ineq); 2066bb901355SGroverkss } 2067bb901355SGroverkss 2068bb901355SGroverkss assert(newRel.getNumConstraints() == 2069bb901355SGroverkss lbIndices.size() * ubIndices.size() + nbIndices.size()); 2070bb901355SGroverkss 2071bb901355SGroverkss // Copy over the equalities. 2072bb901355SGroverkss for (unsigned r = 0, e = getNumEqualities(); r < e; r++) { 20731a0e67d7SRamkumar Ramachandra SmallVector<DynamicAPInt, 4> eq; 2074bb901355SGroverkss eq.reserve(newRel.getNumCols()); 2075bb901355SGroverkss for (unsigned l = 0, e = getNumCols(); l < e; l++) { 2076bb901355SGroverkss if (l == pos) 2077bb901355SGroverkss continue; 2078266a5a9cSRamkumar Ramachandra eq.emplace_back(atEq(r, l)); 2079bb901355SGroverkss } 2080bb901355SGroverkss newRel.addEquality(eq); 2081bb901355SGroverkss } 2082bb901355SGroverkss 2083bb901355SGroverkss // GCD tightening and normalization allows detection of more trivially 2084bb901355SGroverkss // redundant constraints. 2085bb901355SGroverkss newRel.gcdTightenInequalities(); 2086bb901355SGroverkss newRel.normalizeConstraintsByGCD(); 2087bb901355SGroverkss newRel.removeTrivialRedundancy(); 2088bb901355SGroverkss clearAndCopyFrom(newRel); 2089bb901355SGroverkss LLVM_DEBUG(llvm::dbgs() << "FM output:\n"); 2090bb901355SGroverkss LLVM_DEBUG(dump()); 2091bb901355SGroverkss } 2092bb901355SGroverkss 2093bb901355SGroverkss #undef DEBUG_TYPE 2094bb901355SGroverkss #define DEBUG_TYPE "presburger" 2095bb901355SGroverkss 2096bb901355SGroverkss void IntegerRelation::projectOut(unsigned pos, unsigned num) { 2097bb901355SGroverkss if (num == 0) 2098bb901355SGroverkss return; 2099bb901355SGroverkss 2100bb901355SGroverkss // 'pos' can be at most getNumCols() - 2 if num > 0. 2101bb901355SGroverkss assert((getNumCols() < 2 || pos <= getNumCols() - 2) && "invalid position"); 2102bb901355SGroverkss assert(pos + num < getNumCols() && "invalid range"); 2103bb901355SGroverkss 2104d95140a5SGroverkss // Eliminate as many variables as possible using Gaussian elimination. 2105bb901355SGroverkss unsigned currentPos = pos; 2106bb901355SGroverkss unsigned numToEliminate = num; 2107bb901355SGroverkss unsigned numGaussianEliminated = 0; 2108bb901355SGroverkss 2109d95140a5SGroverkss while (currentPos < getNumVars()) { 2110bb901355SGroverkss unsigned curNumEliminated = 2111d95140a5SGroverkss gaussianEliminateVars(currentPos, currentPos + numToEliminate); 2112bb901355SGroverkss ++currentPos; 2113bb901355SGroverkss numToEliminate -= curNumEliminated + 1; 2114bb901355SGroverkss numGaussianEliminated += curNumEliminated; 2115bb901355SGroverkss } 2116bb901355SGroverkss 2117bb901355SGroverkss // Eliminate the remaining using Fourier-Motzkin. 2118bb901355SGroverkss for (unsigned i = 0; i < num - numGaussianEliminated; i++) { 2119bb901355SGroverkss unsigned numToEliminate = num - numGaussianEliminated - i; 2120bb901355SGroverkss fourierMotzkinEliminate( 2121d95140a5SGroverkss getBestVarToEliminate(*this, pos, pos + numToEliminate)); 2122bb901355SGroverkss } 2123bb901355SGroverkss 2124bb901355SGroverkss // Fast/trivial simplifications. 2125bb901355SGroverkss gcdTightenInequalities(); 2126bb901355SGroverkss // Normalize constraints after tightening since the latter impacts this, but 2127bb901355SGroverkss // not the other way round. 2128bb901355SGroverkss normalizeConstraintsByGCD(); 2129bb901355SGroverkss } 2130bb901355SGroverkss 2131bb901355SGroverkss namespace { 2132bb901355SGroverkss 2133bb901355SGroverkss enum BoundCmpResult { Greater, Less, Equal, Unknown }; 2134bb901355SGroverkss 2135bb901355SGroverkss /// Compares two affine bounds whose coefficients are provided in 'first' and 2136bb901355SGroverkss /// 'second'. The last coefficient is the constant term. 21371a0e67d7SRamkumar Ramachandra static BoundCmpResult compareBounds(ArrayRef<DynamicAPInt> a, 21381a0e67d7SRamkumar Ramachandra ArrayRef<DynamicAPInt> b) { 2139bb901355SGroverkss assert(a.size() == b.size()); 2140bb901355SGroverkss 2141d95140a5SGroverkss // For the bounds to be comparable, their corresponding variable 2142bb901355SGroverkss // coefficients should be equal; the constant terms are then compared to 2143bb901355SGroverkss // determine less/greater/equal. 2144bb901355SGroverkss 2145bb901355SGroverkss if (!std::equal(a.begin(), a.end() - 1, b.begin())) 2146bb901355SGroverkss return Unknown; 2147bb901355SGroverkss 2148bb901355SGroverkss if (a.back() == b.back()) 2149bb901355SGroverkss return Equal; 2150bb901355SGroverkss 2151bb901355SGroverkss return a.back() < b.back() ? Less : Greater; 2152bb901355SGroverkss } 2153bb901355SGroverkss } // namespace 2154bb901355SGroverkss 2155bb901355SGroverkss // Returns constraints that are common to both A & B. 2156bb901355SGroverkss static void getCommonConstraints(const IntegerRelation &a, 2157bb901355SGroverkss const IntegerRelation &b, IntegerRelation &c) { 2158a5a598beSGroverkss c = IntegerRelation(a.getSpace()); 2159bb901355SGroverkss // a naive O(n^2) check should be enough here given the input sizes. 2160bb901355SGroverkss for (unsigned r = 0, e = a.getNumInequalities(); r < e; ++r) { 2161bb901355SGroverkss for (unsigned s = 0, f = b.getNumInequalities(); s < f; ++s) { 2162bb901355SGroverkss if (a.getInequality(r) == b.getInequality(s)) { 2163bb901355SGroverkss c.addInequality(a.getInequality(r)); 2164bb901355SGroverkss break; 2165bb901355SGroverkss } 2166bb901355SGroverkss } 2167bb901355SGroverkss } 2168bb901355SGroverkss for (unsigned r = 0, e = a.getNumEqualities(); r < e; ++r) { 2169bb901355SGroverkss for (unsigned s = 0, f = b.getNumEqualities(); s < f; ++s) { 2170bb901355SGroverkss if (a.getEquality(r) == b.getEquality(s)) { 2171bb901355SGroverkss c.addEquality(a.getEquality(r)); 2172bb901355SGroverkss break; 2173bb901355SGroverkss } 2174bb901355SGroverkss } 2175bb901355SGroverkss } 2176bb901355SGroverkss } 2177bb901355SGroverkss 2178bb901355SGroverkss // Computes the bounding box with respect to 'other' by finding the min of the 2179bb901355SGroverkss // lower bounds and the max of the upper bounds along each of the dimensions. 2180f819302aSRamkumar Ramachandra LogicalResult 2181f819302aSRamkumar Ramachandra IntegerRelation::unionBoundingBox(const IntegerRelation &otherCst) { 218220aedb14SGroverkss assert(space.isEqual(otherCst.getSpace()) && "Spaces should match."); 2183d95140a5SGroverkss assert(getNumLocalVars() == 0 && "local ids not supported yet here"); 2184bb901355SGroverkss 2185bb901355SGroverkss // Get the constraints common to both systems; these will be added as is to 2186bb901355SGroverkss // the union. 2187a5a598beSGroverkss IntegerRelation commonCst(PresburgerSpace::getRelationSpace()); 2188bb901355SGroverkss getCommonConstraints(*this, otherCst, commonCst); 2189bb901355SGroverkss 21901a0e67d7SRamkumar Ramachandra std::vector<SmallVector<DynamicAPInt, 8>> boundingLbs; 21911a0e67d7SRamkumar Ramachandra std::vector<SmallVector<DynamicAPInt, 8>> boundingUbs; 2192d95140a5SGroverkss boundingLbs.reserve(2 * getNumDimVars()); 2193d95140a5SGroverkss boundingUbs.reserve(2 * getNumDimVars()); 2194bb901355SGroverkss 2195bb901355SGroverkss // To hold lower and upper bounds for each dimension. 21961a0e67d7SRamkumar Ramachandra SmallVector<DynamicAPInt, 4> lb, otherLb, ub, otherUb; 2197bb901355SGroverkss // To compute min of lower bounds and max of upper bounds for each dimension. 21981a0e67d7SRamkumar Ramachandra SmallVector<DynamicAPInt, 4> minLb(getNumSymbolVars() + 1); 21991a0e67d7SRamkumar Ramachandra SmallVector<DynamicAPInt, 4> maxUb(getNumSymbolVars() + 1); 2200bb901355SGroverkss // To compute final new lower and upper bounds for the union. 22011a0e67d7SRamkumar Ramachandra SmallVector<DynamicAPInt, 8> newLb(getNumCols()), newUb(getNumCols()); 2202bb901355SGroverkss 22031a0e67d7SRamkumar Ramachandra DynamicAPInt lbFloorDivisor, otherLbFloorDivisor; 2204d95140a5SGroverkss for (unsigned d = 0, e = getNumDimVars(); d < e; ++d) { 2205bb901355SGroverkss auto extent = getConstantBoundOnDimSize(d, &lb, &lbFloorDivisor, &ub); 2206491d2701SKazu Hirata if (!extent.has_value()) 2207bb901355SGroverkss // TODO: symbolic extents when necessary. 2208bb901355SGroverkss // TODO: handle union if a dimension is unbounded. 2209f819302aSRamkumar Ramachandra return failure(); 2210bb901355SGroverkss 2211bb901355SGroverkss auto otherExtent = otherCst.getConstantBoundOnDimSize( 2212bb901355SGroverkss d, &otherLb, &otherLbFloorDivisor, &otherUb); 2213491d2701SKazu Hirata if (!otherExtent.has_value() || lbFloorDivisor != otherLbFloorDivisor) 2214bb901355SGroverkss // TODO: symbolic extents when necessary. 2215f819302aSRamkumar Ramachandra return failure(); 2216bb901355SGroverkss 2217bb901355SGroverkss assert(lbFloorDivisor > 0 && "divisor always expected to be positive"); 2218bb901355SGroverkss 2219bb901355SGroverkss auto res = compareBounds(lb, otherLb); 2220bb901355SGroverkss // Identify min. 2221bb901355SGroverkss if (res == BoundCmpResult::Less || res == BoundCmpResult::Equal) { 2222bb901355SGroverkss minLb = lb; 2223bb901355SGroverkss // Since the divisor is for a floordiv, we need to convert to ceildiv, 2224bb901355SGroverkss // i.e., i >= expr floordiv div <=> i >= (expr - div + 1) ceildiv div <=> 2225bb901355SGroverkss // div * i >= expr - div + 1. 2226bb901355SGroverkss minLb.back() -= lbFloorDivisor - 1; 2227bb901355SGroverkss } else if (res == BoundCmpResult::Greater) { 2228bb901355SGroverkss minLb = otherLb; 2229bb901355SGroverkss minLb.back() -= otherLbFloorDivisor - 1; 2230bb901355SGroverkss } else { 2231bb901355SGroverkss // Uncomparable - check for constant lower/upper bounds. 2232bb901355SGroverkss auto constLb = getConstantBound(BoundType::LB, d); 2233bb901355SGroverkss auto constOtherLb = otherCst.getConstantBound(BoundType::LB, d); 2234491d2701SKazu Hirata if (!constLb.has_value() || !constOtherLb.has_value()) 2235f819302aSRamkumar Ramachandra return failure(); 2236bb901355SGroverkss std::fill(minLb.begin(), minLb.end(), 0); 2237cc037e17SFangrui Song minLb.back() = std::min(*constLb, *constOtherLb); 2238bb901355SGroverkss } 2239bb901355SGroverkss 2240bb901355SGroverkss // Do the same for ub's but max of upper bounds. Identify max. 2241bb901355SGroverkss auto uRes = compareBounds(ub, otherUb); 2242bb901355SGroverkss if (uRes == BoundCmpResult::Greater || uRes == BoundCmpResult::Equal) { 2243bb901355SGroverkss maxUb = ub; 2244bb901355SGroverkss } else if (uRes == BoundCmpResult::Less) { 2245bb901355SGroverkss maxUb = otherUb; 2246bb901355SGroverkss } else { 2247bb901355SGroverkss // Uncomparable - check for constant lower/upper bounds. 2248bb901355SGroverkss auto constUb = getConstantBound(BoundType::UB, d); 2249bb901355SGroverkss auto constOtherUb = otherCst.getConstantBound(BoundType::UB, d); 2250491d2701SKazu Hirata if (!constUb.has_value() || !constOtherUb.has_value()) 2251f819302aSRamkumar Ramachandra return failure(); 2252bb901355SGroverkss std::fill(maxUb.begin(), maxUb.end(), 0); 2253cc037e17SFangrui Song maxUb.back() = std::max(*constUb, *constOtherUb); 2254bb901355SGroverkss } 2255bb901355SGroverkss 2256bb901355SGroverkss std::fill(newLb.begin(), newLb.end(), 0); 2257bb901355SGroverkss std::fill(newUb.begin(), newUb.end(), 0); 2258bb901355SGroverkss 2259bb901355SGroverkss // The divisor for lb, ub, otherLb, otherUb at this point is lbDivisor, 2260bb901355SGroverkss // and so it's the divisor for newLb and newUb as well. 2261bb901355SGroverkss newLb[d] = lbFloorDivisor; 2262bb901355SGroverkss newUb[d] = -lbFloorDivisor; 2263bb901355SGroverkss // Copy over the symbolic part + constant term. 2264d95140a5SGroverkss std::copy(minLb.begin(), minLb.end(), newLb.begin() + getNumDimVars()); 2265d95140a5SGroverkss std::transform(newLb.begin() + getNumDimVars(), newLb.end(), 22661a0e67d7SRamkumar Ramachandra newLb.begin() + getNumDimVars(), 22671a0e67d7SRamkumar Ramachandra std::negate<DynamicAPInt>()); 2268d95140a5SGroverkss std::copy(maxUb.begin(), maxUb.end(), newUb.begin() + getNumDimVars()); 2269bb901355SGroverkss 2270266a5a9cSRamkumar Ramachandra boundingLbs.emplace_back(newLb); 2271266a5a9cSRamkumar Ramachandra boundingUbs.emplace_back(newUb); 2272bb901355SGroverkss } 2273bb901355SGroverkss 2274bb901355SGroverkss // Clear all constraints and add the lower/upper bounds for the bounding box. 2275bb901355SGroverkss clearConstraints(); 2276d95140a5SGroverkss for (unsigned d = 0, e = getNumDimVars(); d < e; ++d) { 2277bb901355SGroverkss addInequality(boundingLbs[d]); 2278bb901355SGroverkss addInequality(boundingUbs[d]); 2279bb901355SGroverkss } 2280bb901355SGroverkss 2281bb901355SGroverkss // Add the constraints that were common to both systems. 2282bb901355SGroverkss append(commonCst); 2283bb901355SGroverkss removeTrivialRedundancy(); 2284bb901355SGroverkss 2285bb901355SGroverkss // TODO: copy over pure symbolic constraints from this and 'other' over to the 2286bb901355SGroverkss // union (since the above are just the union along dimensions); we shouldn't 2287bb901355SGroverkss // be discarding any other constraints on the symbols. 2288bb901355SGroverkss 2289f819302aSRamkumar Ramachandra return success(); 2290bb901355SGroverkss } 2291bb901355SGroverkss 2292bb901355SGroverkss bool IntegerRelation::isColZero(unsigned pos) const { 2293bb901355SGroverkss unsigned rowPos; 2294bb901355SGroverkss return !findConstraintWithNonZeroAt(pos, /*isEq=*/false, &rowPos) && 2295bb901355SGroverkss !findConstraintWithNonZeroAt(pos, /*isEq=*/true, &rowPos); 2296bb901355SGroverkss } 2297bb901355SGroverkss 2298bb901355SGroverkss /// Find positions of inequalities and equalities that do not have a coefficient 2299d95140a5SGroverkss /// for [pos, pos + num) variables. 2300bb901355SGroverkss static void getIndependentConstraints(const IntegerRelation &cst, unsigned pos, 2301bb901355SGroverkss unsigned num, 2302bb901355SGroverkss SmallVectorImpl<unsigned> &nbIneqIndices, 2303bb901355SGroverkss SmallVectorImpl<unsigned> &nbEqIndices) { 2304d95140a5SGroverkss assert(pos < cst.getNumVars() && "invalid start position"); 2305d95140a5SGroverkss assert(pos + num <= cst.getNumVars() && "invalid limit"); 2306bb901355SGroverkss 2307bb901355SGroverkss for (unsigned r = 0, e = cst.getNumInequalities(); r < e; r++) { 2308bb901355SGroverkss // The bounds are to be independent of [offset, offset + num) columns. 2309bb901355SGroverkss unsigned c; 2310bb901355SGroverkss for (c = pos; c < pos + num; ++c) { 2311bb901355SGroverkss if (cst.atIneq(r, c) != 0) 2312bb901355SGroverkss break; 2313bb901355SGroverkss } 2314bb901355SGroverkss if (c == pos + num) 2315266a5a9cSRamkumar Ramachandra nbIneqIndices.emplace_back(r); 2316bb901355SGroverkss } 2317bb901355SGroverkss 2318bb901355SGroverkss for (unsigned r = 0, e = cst.getNumEqualities(); r < e; r++) { 2319bb901355SGroverkss // The bounds are to be independent of [offset, offset + num) columns. 2320bb901355SGroverkss unsigned c; 2321bb901355SGroverkss for (c = pos; c < pos + num; ++c) { 2322bb901355SGroverkss if (cst.atEq(r, c) != 0) 2323bb901355SGroverkss break; 2324bb901355SGroverkss } 2325bb901355SGroverkss if (c == pos + num) 2326266a5a9cSRamkumar Ramachandra nbEqIndices.emplace_back(r); 2327bb901355SGroverkss } 2328bb901355SGroverkss } 2329bb901355SGroverkss 2330bb901355SGroverkss void IntegerRelation::removeIndependentConstraints(unsigned pos, unsigned num) { 2331d95140a5SGroverkss assert(pos + num <= getNumVars() && "invalid range"); 2332bb901355SGroverkss 2333d95140a5SGroverkss // Remove constraints that are independent of these variables. 2334bb901355SGroverkss SmallVector<unsigned, 4> nbIneqIndices, nbEqIndices; 2335bb901355SGroverkss getIndependentConstraints(*this, /*pos=*/0, num, nbIneqIndices, nbEqIndices); 2336bb901355SGroverkss 2337bb901355SGroverkss // Iterate in reverse so that indices don't have to be updated. 2338bb901355SGroverkss // TODO: This method can be made more efficient (because removal of each 2339bb901355SGroverkss // inequality leads to much shifting/copying in the underlying buffer). 2340bb901355SGroverkss for (auto nbIndex : llvm::reverse(nbIneqIndices)) 2341bb901355SGroverkss removeInequality(nbIndex); 2342bb901355SGroverkss for (auto nbIndex : llvm::reverse(nbEqIndices)) 2343bb901355SGroverkss removeEquality(nbIndex); 2344bb901355SGroverkss } 2345bb901355SGroverkss 23463c057ac2SGroverkss IntegerPolyhedron IntegerRelation::getDomainSet() const { 23473c057ac2SGroverkss IntegerRelation copyRel = *this; 23483c057ac2SGroverkss 23493c057ac2SGroverkss // Convert Range variables to Local variables. 2350d95140a5SGroverkss copyRel.convertVarKind(VarKind::Range, 0, getNumVarKind(VarKind::Range), 2351d95140a5SGroverkss VarKind::Local); 23523c057ac2SGroverkss 23533c057ac2SGroverkss // Convert Domain variables to SetDim(Range) variables. 2354d95140a5SGroverkss copyRel.convertVarKind(VarKind::Domain, 0, getNumVarKind(VarKind::Domain), 2355d95140a5SGroverkss VarKind::SetDim); 23563c057ac2SGroverkss 23573c057ac2SGroverkss return IntegerPolyhedron(std::move(copyRel)); 23583c057ac2SGroverkss } 23593c057ac2SGroverkss 236039b93955Sgilsaia bool IntegerRelation::removeDuplicateConstraints() { 236139b93955Sgilsaia bool changed = false; 23621a0e67d7SRamkumar Ramachandra SmallDenseMap<ArrayRef<DynamicAPInt>, unsigned> hashTable; 236339b93955Sgilsaia unsigned ineqs = getNumInequalities(), cols = getNumCols(); 236439b93955Sgilsaia 236539b93955Sgilsaia if (ineqs <= 1) 236639b93955Sgilsaia return changed; 236739b93955Sgilsaia 236839b93955Sgilsaia // Check if the non-constant part of the constraint is the same. 23691a0e67d7SRamkumar Ramachandra ArrayRef<DynamicAPInt> row = getInequality(0).drop_back(); 237039b93955Sgilsaia hashTable.insert({row, 0}); 237139b93955Sgilsaia for (unsigned k = 1; k < ineqs; ++k) { 237239b93955Sgilsaia row = getInequality(k).drop_back(); 2373b2dbcf4dSKazu Hirata if (hashTable.try_emplace(row, k).second) 237439b93955Sgilsaia continue; 237539b93955Sgilsaia 237639b93955Sgilsaia // For identical cases, keep only the smaller part of the constant term. 237739b93955Sgilsaia unsigned l = hashTable[row]; 237839b93955Sgilsaia changed = true; 237939b93955Sgilsaia if (atIneq(k, cols - 1) <= atIneq(l, cols - 1)) 238039b93955Sgilsaia inequalities.swapRows(k, l); 238139b93955Sgilsaia removeInequality(k); 238239b93955Sgilsaia --k; 238339b93955Sgilsaia --ineqs; 238439b93955Sgilsaia } 238539b93955Sgilsaia 238639b93955Sgilsaia // Check the neg form of each inequality, need an extra vector to store it. 23871a0e67d7SRamkumar Ramachandra SmallVector<DynamicAPInt> negIneq(cols - 1); 238839b93955Sgilsaia for (unsigned k = 0; k < ineqs; ++k) { 238939b93955Sgilsaia row = getInequality(k).drop_back(); 239039b93955Sgilsaia negIneq.assign(row.begin(), row.end()); 23911a0e67d7SRamkumar Ramachandra for (DynamicAPInt &ele : negIneq) 239239b93955Sgilsaia ele = -ele; 239339b93955Sgilsaia if (!hashTable.contains(negIneq)) 239439b93955Sgilsaia continue; 239539b93955Sgilsaia 239639b93955Sgilsaia // For cases where the neg is the same as other inequalities, check that the 239739b93955Sgilsaia // sum of their constant terms is positive. 239839b93955Sgilsaia unsigned l = hashTable[row]; 239939b93955Sgilsaia auto sum = atIneq(l, cols - 1) + atIneq(k, cols - 1); 240039b93955Sgilsaia if (sum > 0 || l == k) 240139b93955Sgilsaia continue; 240239b93955Sgilsaia 240339b93955Sgilsaia // A sum of constant terms equal to zero combines two inequalities into one 240439b93955Sgilsaia // equation, less than zero means the set is empty. 240539b93955Sgilsaia changed = true; 240639b93955Sgilsaia if (k < l) 240739b93955Sgilsaia std::swap(l, k); 240839b93955Sgilsaia if (sum == 0) { 240939b93955Sgilsaia addEquality(getInequality(k)); 241039b93955Sgilsaia removeInequality(k); 241139b93955Sgilsaia removeInequality(l); 241239b93955Sgilsaia } else 241339b93955Sgilsaia *this = getEmpty(getSpace()); 241439b93955Sgilsaia break; 241539b93955Sgilsaia } 241639b93955Sgilsaia 241739b93955Sgilsaia return changed; 241839b93955Sgilsaia } 241939b93955Sgilsaia 24203c057ac2SGroverkss IntegerPolyhedron IntegerRelation::getRangeSet() const { 24213c057ac2SGroverkss IntegerRelation copyRel = *this; 24223c057ac2SGroverkss 24233c057ac2SGroverkss // Convert Domain variables to Local variables. 2424d95140a5SGroverkss copyRel.convertVarKind(VarKind::Domain, 0, getNumVarKind(VarKind::Domain), 2425d95140a5SGroverkss VarKind::Local); 24263c057ac2SGroverkss 24273c057ac2SGroverkss // We do not need to do anything to Range variables since they are already in 24283c057ac2SGroverkss // SetDim position. 24293c057ac2SGroverkss 24303c057ac2SGroverkss return IntegerPolyhedron(std::move(copyRel)); 24313c057ac2SGroverkss } 24323c057ac2SGroverkss 2433f168a659SGroverkss void IntegerRelation::intersectDomain(const IntegerPolyhedron &poly) { 2434f168a659SGroverkss assert(getDomainSet().getSpace().isCompatible(poly.getSpace()) && 2435f168a659SGroverkss "Domain set is not compatible with poly"); 2436f168a659SGroverkss 2437f168a659SGroverkss // Treating the poly as a relation, convert it from `0 -> R` to `R -> 0`. 2438f168a659SGroverkss IntegerRelation rel = poly; 2439f168a659SGroverkss rel.inverse(); 2440f168a659SGroverkss 2441f168a659SGroverkss // Append dummy range variables to make the spaces compatible. 2442d95140a5SGroverkss rel.appendVar(VarKind::Range, getNumRangeVars()); 2443f168a659SGroverkss 2444f168a659SGroverkss // Intersect in place. 2445d95140a5SGroverkss mergeLocalVars(rel); 2446f168a659SGroverkss append(rel); 2447f168a659SGroverkss } 2448f168a659SGroverkss 2449f168a659SGroverkss void IntegerRelation::intersectRange(const IntegerPolyhedron &poly) { 2450f168a659SGroverkss assert(getRangeSet().getSpace().isCompatible(poly.getSpace()) && 2451f168a659SGroverkss "Range set is not compatible with poly"); 2452f168a659SGroverkss 2453f168a659SGroverkss IntegerRelation rel = poly; 2454f168a659SGroverkss 2455f168a659SGroverkss // Append dummy domain variables to make the spaces compatible. 2456d95140a5SGroverkss rel.appendVar(VarKind::Domain, getNumDomainVars()); 2457f168a659SGroverkss 2458d95140a5SGroverkss mergeLocalVars(rel); 2459f168a659SGroverkss append(rel); 2460f168a659SGroverkss } 2461f168a659SGroverkss 2462fb857dedSGroverkss void IntegerRelation::inverse() { 2463d95140a5SGroverkss unsigned numRangeVars = getNumVarKind(VarKind::Range); 2464d95140a5SGroverkss convertVarKind(VarKind::Domain, 0, getVarKindEnd(VarKind::Domain), 2465d95140a5SGroverkss VarKind::Range); 2466d95140a5SGroverkss convertVarKind(VarKind::Range, 0, numRangeVars, VarKind::Domain); 2467fb857dedSGroverkss } 2468fb857dedSGroverkss 2469dac27da7SGroverkss void IntegerRelation::compose(const IntegerRelation &rel) { 2470dac27da7SGroverkss assert(getRangeSet().getSpace().isCompatible(rel.getDomainSet().getSpace()) && 2471dac27da7SGroverkss "Range of `this` should be compatible with Domain of `rel`"); 2472dac27da7SGroverkss 2473dac27da7SGroverkss IntegerRelation copyRel = rel; 2474dac27da7SGroverkss 2475dac27da7SGroverkss // Let relation `this` be R1: A -> B, and `rel` be R2: B -> C. 2476dac27da7SGroverkss // We convert R1 to A -> (B X C), and R2 to B X C then intersect the range of 2477dac27da7SGroverkss // R1 with R2. After this, we get R1: A -> C, by projecting out B. 2478dac27da7SGroverkss // TODO: Using nested spaces here would help, since we could directly 2479dac27da7SGroverkss // intersect the range with another relation. 2480d95140a5SGroverkss unsigned numBVars = getNumRangeVars(); 2481dac27da7SGroverkss 2482dac27da7SGroverkss // Convert R1 from A -> B to A -> (B X C). 2483d95140a5SGroverkss appendVar(VarKind::Range, copyRel.getNumRangeVars()); 2484dac27da7SGroverkss 2485dac27da7SGroverkss // Convert R2 to B X C. 2486d08522f5SArjun P copyRel.convertVarKind(VarKind::Domain, 0, numBVars, VarKind::Range, 0); 2487dac27da7SGroverkss 2488dac27da7SGroverkss // Intersect R2 to range of R1. 2489dac27da7SGroverkss intersectRange(IntegerPolyhedron(copyRel)); 2490dac27da7SGroverkss 2491dac27da7SGroverkss // Project out B in R1. 2492d95140a5SGroverkss convertVarKind(VarKind::Range, 0, numBVars, VarKind::Local); 2493dac27da7SGroverkss } 2494dac27da7SGroverkss 2495dac27da7SGroverkss void IntegerRelation::applyDomain(const IntegerRelation &rel) { 2496dac27da7SGroverkss inverse(); 2497dac27da7SGroverkss compose(rel); 2498dac27da7SGroverkss inverse(); 2499dac27da7SGroverkss } 2500dac27da7SGroverkss 2501dac27da7SGroverkss void IntegerRelation::applyRange(const IntegerRelation &rel) { compose(rel); } 2502dac27da7SGroverkss 2503bb901355SGroverkss void IntegerRelation::printSpace(raw_ostream &os) const { 250420aedb14SGroverkss space.print(os); 2505bb901355SGroverkss os << getNumConstraints() << " constraints\n"; 2506bb901355SGroverkss } 2507bb901355SGroverkss 2508562790f3SAbhinav271828 void IntegerRelation::removeTrivialEqualities() { 2509562790f3SAbhinav271828 for (int i = getNumEqualities() - 1; i >= 0; --i) 2510562790f3SAbhinav271828 if (rangeIsZero(getEquality(i))) 2511562790f3SAbhinav271828 removeEquality(i); 2512562790f3SAbhinav271828 } 2513562790f3SAbhinav271828 2514562790f3SAbhinav271828 bool IntegerRelation::isFullDim() { 2515562790f3SAbhinav271828 if (getNumVars() == 0) 2516562790f3SAbhinav271828 return true; 2517562790f3SAbhinav271828 if (isEmpty()) 2518562790f3SAbhinav271828 return false; 2519562790f3SAbhinav271828 2520562790f3SAbhinav271828 // If there is a non-trivial equality, the space cannot be full-dimensional. 2521562790f3SAbhinav271828 removeTrivialEqualities(); 2522562790f3SAbhinav271828 if (getNumEqualities() > 0) 2523562790f3SAbhinav271828 return false; 2524562790f3SAbhinav271828 2525562790f3SAbhinav271828 // The polytope is full-dimensional iff it is not flat along any of the 2526562790f3SAbhinav271828 // inequality directions. 2527562790f3SAbhinav271828 Simplex simplex(*this); 2528562790f3SAbhinav271828 return llvm::none_of(llvm::seq<int>(getNumInequalities()), [&](int i) { 2529562790f3SAbhinav271828 return simplex.isFlatAlong(getInequality(i)); 2530562790f3SAbhinav271828 }); 2531562790f3SAbhinav271828 } 2532562790f3SAbhinav271828 253324da7fa0SBharathi Ramana Joshi void IntegerRelation::mergeAndCompose(const IntegerRelation &other) { 253424da7fa0SBharathi Ramana Joshi assert(getNumDomainVars() == other.getNumRangeVars() && 253524da7fa0SBharathi Ramana Joshi "Domain of this and range of other do not match"); 253624da7fa0SBharathi Ramana Joshi // assert(std::equal(values.begin(), values.begin() + 253724da7fa0SBharathi Ramana Joshi // other.getNumDomainVars(), 253824da7fa0SBharathi Ramana Joshi // otherValues.begin() + other.getNumDomainVars()) && 253924da7fa0SBharathi Ramana Joshi // "Domain of this and range of other do not match"); 254024da7fa0SBharathi Ramana Joshi 254124da7fa0SBharathi Ramana Joshi IntegerRelation result = other; 254224da7fa0SBharathi Ramana Joshi 254324da7fa0SBharathi Ramana Joshi const unsigned thisDomain = getNumDomainVars(); 254424da7fa0SBharathi Ramana Joshi const unsigned thisRange = getNumRangeVars(); 254524da7fa0SBharathi Ramana Joshi const unsigned otherDomain = other.getNumDomainVars(); 254624da7fa0SBharathi Ramana Joshi const unsigned otherRange = other.getNumRangeVars(); 254724da7fa0SBharathi Ramana Joshi 254824da7fa0SBharathi Ramana Joshi // Add dimension variables temporarily to merge symbol and local vars. 254924da7fa0SBharathi Ramana Joshi // Convert `this` from 255024da7fa0SBharathi Ramana Joshi // [thisDomain] -> [thisRange] 255124da7fa0SBharathi Ramana Joshi // to 255224da7fa0SBharathi Ramana Joshi // [otherDomain thisDomain] -> [otherRange thisRange]. 255324da7fa0SBharathi Ramana Joshi // and `result` from 255424da7fa0SBharathi Ramana Joshi // [otherDomain] -> [otherRange] 255524da7fa0SBharathi Ramana Joshi // to 255624da7fa0SBharathi Ramana Joshi // [otherDomain thisDomain] -> [otherRange thisRange] 255724da7fa0SBharathi Ramana Joshi insertVar(VarKind::Domain, 0, otherDomain); 255824da7fa0SBharathi Ramana Joshi insertVar(VarKind::Range, 0, otherRange); 255924da7fa0SBharathi Ramana Joshi result.insertVar(VarKind::Domain, otherDomain, thisDomain); 256024da7fa0SBharathi Ramana Joshi result.insertVar(VarKind::Range, otherRange, thisRange); 256124da7fa0SBharathi Ramana Joshi 256224da7fa0SBharathi Ramana Joshi // Merge symbol and local variables. 256324da7fa0SBharathi Ramana Joshi mergeAndAlignSymbols(result); 256424da7fa0SBharathi Ramana Joshi mergeLocalVars(result); 256524da7fa0SBharathi Ramana Joshi 256624da7fa0SBharathi Ramana Joshi // Convert `result` from [otherDomain thisDomain] -> [otherRange thisRange] to 256724da7fa0SBharathi Ramana Joshi // [otherDomain] -> [thisRange] 256824da7fa0SBharathi Ramana Joshi result.removeVarRange(VarKind::Domain, otherDomain, otherDomain + thisDomain); 256924da7fa0SBharathi Ramana Joshi result.convertToLocal(VarKind::Range, 0, otherRange); 257024da7fa0SBharathi Ramana Joshi // Convert `this` from [otherDomain thisDomain] -> [otherRange thisRange] to 257124da7fa0SBharathi Ramana Joshi // [otherDomain] -> [thisRange] 257224da7fa0SBharathi Ramana Joshi convertToLocal(VarKind::Domain, otherDomain, otherDomain + thisDomain); 257324da7fa0SBharathi Ramana Joshi removeVarRange(VarKind::Range, 0, otherRange); 257424da7fa0SBharathi Ramana Joshi 257524da7fa0SBharathi Ramana Joshi // Add and match domain of `result` to domain of `this`. 257624da7fa0SBharathi Ramana Joshi for (unsigned i = 0, e = result.getNumDomainVars(); i < e; ++i) 257724da7fa0SBharathi Ramana Joshi if (result.getSpace().getId(VarKind::Domain, i).hasValue()) 257824da7fa0SBharathi Ramana Joshi space.setId(VarKind::Domain, i, 257924da7fa0SBharathi Ramana Joshi result.getSpace().getId(VarKind::Domain, i)); 258024da7fa0SBharathi Ramana Joshi // Add and match range of `this` to range of `result`. 258124da7fa0SBharathi Ramana Joshi for (unsigned i = 0, e = getNumRangeVars(); i < e; ++i) 258224da7fa0SBharathi Ramana Joshi if (space.getId(VarKind::Range, i).hasValue()) 258324da7fa0SBharathi Ramana Joshi result.space.setId(VarKind::Range, i, space.getId(VarKind::Range, i)); 258424da7fa0SBharathi Ramana Joshi 258524da7fa0SBharathi Ramana Joshi // Append `this` to `result` and simplify constraints. 258624da7fa0SBharathi Ramana Joshi result.append(*this); 258724da7fa0SBharathi Ramana Joshi result.removeRedundantLocalVars(); 258824da7fa0SBharathi Ramana Joshi 258924da7fa0SBharathi Ramana Joshi *this = result; 259024da7fa0SBharathi Ramana Joshi } 259124da7fa0SBharathi Ramana Joshi 2592bb901355SGroverkss void IntegerRelation::print(raw_ostream &os) const { 2593bb901355SGroverkss assert(hasConsistentState()); 2594bb901355SGroverkss printSpace(os); 2595*27402735SAmy Wang PrintTableMetrics ptm = {0, 0, "-"}; 2596*27402735SAmy Wang for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) 2597*27402735SAmy Wang for (unsigned j = 0, f = getNumCols(); j < f; ++j) 2598*27402735SAmy Wang updatePrintMetrics<DynamicAPInt>(atEq(i, j), ptm); 2599*27402735SAmy Wang for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) 2600*27402735SAmy Wang for (unsigned j = 0, f = getNumCols(); j < f; ++j) 2601*27402735SAmy Wang updatePrintMetrics<DynamicAPInt>(atIneq(i, j), ptm); 2602*27402735SAmy Wang // Print using PrintMetrics. 2603*27402735SAmy Wang unsigned MIN_SPACING = 1; 2604bb901355SGroverkss for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) { 2605bb901355SGroverkss for (unsigned j = 0, f = getNumCols(); j < f; ++j) { 2606*27402735SAmy Wang printWithPrintMetrics<DynamicAPInt>(os, atEq(i, j), MIN_SPACING, ptm); 2607bb901355SGroverkss } 2608bb901355SGroverkss os << " = 0\n"; 2609bb901355SGroverkss } 2610bb901355SGroverkss for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) { 2611bb901355SGroverkss for (unsigned j = 0, f = getNumCols(); j < f; ++j) { 2612*27402735SAmy Wang printWithPrintMetrics<DynamicAPInt>(os, atIneq(i, j), MIN_SPACING, ptm); 2613bb901355SGroverkss } 2614bb901355SGroverkss os << " >= 0\n"; 2615bb901355SGroverkss } 2616bb901355SGroverkss os << '\n'; 2617bb901355SGroverkss } 2618bb901355SGroverkss 2619bb901355SGroverkss void IntegerRelation::dump() const { print(llvm::errs()); } 262058966dd4SGroverkss 2621d95140a5SGroverkss unsigned IntegerPolyhedron::insertVar(VarKind kind, unsigned pos, 2622d95140a5SGroverkss unsigned num) { 2623d95140a5SGroverkss assert((kind != VarKind::Domain || num == 0) && 262458966dd4SGroverkss "Domain has to be zero in a set"); 2625d95140a5SGroverkss return IntegerRelation::insertVar(kind, pos, num); 262658966dd4SGroverkss } 2627a18f843fSGroverkss IntegerPolyhedron 2628a18f843fSGroverkss IntegerPolyhedron::intersect(const IntegerPolyhedron &other) const { 2629a18f843fSGroverkss return IntegerPolyhedron(IntegerRelation::intersect(other)); 2630a18f843fSGroverkss } 2631a18f843fSGroverkss 2632a18f843fSGroverkss PresburgerSet IntegerPolyhedron::subtract(const PresburgerSet &other) const { 2633a18f843fSGroverkss return PresburgerSet(IntegerRelation::subtract(other)); 2634a18f843fSGroverkss } 2635