1*d5a29442SArjun P //===- PWMAFunction.cpp - MLIR PWMAFunction Class -------------------------===// 2*d5a29442SArjun P // 3*d5a29442SArjun P // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*d5a29442SArjun P // See https://llvm.org/LICENSE.txt for license information. 5*d5a29442SArjun P // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*d5a29442SArjun P // 7*d5a29442SArjun P //===----------------------------------------------------------------------===// 8*d5a29442SArjun P 9*d5a29442SArjun P #include "mlir/Analysis/Presburger/PWMAFunction.h" 10*d5a29442SArjun P #include "mlir/Analysis/Presburger/Simplex.h" 11*d5a29442SArjun P 12*d5a29442SArjun P using namespace mlir; 13*d5a29442SArjun P 14*d5a29442SArjun P // Return the result of subtracting the two given vectors pointwise. 15*d5a29442SArjun P // The vectors must be of the same size. 16*d5a29442SArjun P // e.g., [3, 4, 6] - [2, 5, 1] = [1, -1, 5]. 17*d5a29442SArjun P static SmallVector<int64_t, 8> subtract(ArrayRef<int64_t> vecA, 18*d5a29442SArjun P ArrayRef<int64_t> vecB) { 19*d5a29442SArjun P assert(vecA.size() == vecB.size() && 20*d5a29442SArjun P "Cannot subtract vectors of differing lengths!"); 21*d5a29442SArjun P SmallVector<int64_t, 8> result; 22*d5a29442SArjun P result.reserve(vecA.size()); 23*d5a29442SArjun P for (unsigned i = 0, e = vecA.size(); i < e; ++i) 24*d5a29442SArjun P result.push_back(vecA[i] - vecB[i]); 25*d5a29442SArjun P return result; 26*d5a29442SArjun P } 27*d5a29442SArjun P 28*d5a29442SArjun P PresburgerSet PWMAFunction::getDomain() const { 29*d5a29442SArjun P PresburgerSet domain = 30*d5a29442SArjun P PresburgerSet::getEmptySet(getNumDimIds(), getNumSymbolIds()); 31*d5a29442SArjun P for (const MultiAffineFunction &piece : pieces) 32*d5a29442SArjun P domain.unionPolyInPlace(piece.getDomain()); 33*d5a29442SArjun P return domain; 34*d5a29442SArjun P } 35*d5a29442SArjun P 36*d5a29442SArjun P Optional<SmallVector<int64_t, 8>> 37*d5a29442SArjun P MultiAffineFunction::valueAt(ArrayRef<int64_t> point) const { 38*d5a29442SArjun P assert(getNumLocalIds() == 0 && "Local ids are not yet supported!"); 39*d5a29442SArjun P assert(point.size() == getNumIds() && "Point has incorrect dimensionality!"); 40*d5a29442SArjun P 41*d5a29442SArjun P if (!getDomain().containsPoint(point)) 42*d5a29442SArjun P return {}; 43*d5a29442SArjun P 44*d5a29442SArjun P // The point lies in the domain, so we need to compute the output value. 45*d5a29442SArjun P // The matrix `output` has an affine expression in the ith row, corresponding 46*d5a29442SArjun P // to the expression for the ith value in the output vector. The last column 47*d5a29442SArjun P // of the matrix contains the constant term. Let v be the input point with 48*d5a29442SArjun P // a 1 appended at the end. We can see that output * v gives the desired 49*d5a29442SArjun P // output vector. 50*d5a29442SArjun P SmallVector<int64_t, 8> pointHomogenous{llvm::to_vector(point)}; 51*d5a29442SArjun P pointHomogenous.push_back(1); 52*d5a29442SArjun P SmallVector<int64_t, 8> result = 53*d5a29442SArjun P output.postMultiplyWithColumn(pointHomogenous); 54*d5a29442SArjun P assert(result.size() == getNumOutputs()); 55*d5a29442SArjun P return result; 56*d5a29442SArjun P } 57*d5a29442SArjun P 58*d5a29442SArjun P Optional<SmallVector<int64_t, 8>> 59*d5a29442SArjun P PWMAFunction::valueAt(ArrayRef<int64_t> point) const { 60*d5a29442SArjun P assert(point.size() == getNumInputs() && 61*d5a29442SArjun P "Point has incorrect dimensionality!"); 62*d5a29442SArjun P for (const MultiAffineFunction &piece : pieces) 63*d5a29442SArjun P if (Optional<SmallVector<int64_t, 8>> output = piece.valueAt(point)) 64*d5a29442SArjun P return output; 65*d5a29442SArjun P return {}; 66*d5a29442SArjun P } 67*d5a29442SArjun P 68*d5a29442SArjun P void MultiAffineFunction::print(raw_ostream &os) const { 69*d5a29442SArjun P os << "Domain:"; 70*d5a29442SArjun P IntegerPolyhedron::print(os); 71*d5a29442SArjun P os << "Output:\n"; 72*d5a29442SArjun P output.print(os); 73*d5a29442SArjun P os << "\n"; 74*d5a29442SArjun P } 75*d5a29442SArjun P 76*d5a29442SArjun P void MultiAffineFunction::dump() const { print(llvm::errs()); } 77*d5a29442SArjun P 78*d5a29442SArjun P bool MultiAffineFunction::isEqual(const MultiAffineFunction &other) const { 79*d5a29442SArjun P return hasCompatibleDimensions(other) && 80*d5a29442SArjun P getDomain().isEqual(other.getDomain()) && 81*d5a29442SArjun P isEqualWhereDomainsOverlap(other); 82*d5a29442SArjun P } 83*d5a29442SArjun P 84*d5a29442SArjun P unsigned MultiAffineFunction::insertId(IdKind kind, unsigned pos, 85*d5a29442SArjun P unsigned num) { 86*d5a29442SArjun P unsigned absolutePos = getIdKindOffset(kind) + pos; 87*d5a29442SArjun P output.insertColumns(absolutePos, num); 88*d5a29442SArjun P return IntegerPolyhedron::insertId(kind, pos, num); 89*d5a29442SArjun P } 90*d5a29442SArjun P 91*d5a29442SArjun P void MultiAffineFunction::swapId(unsigned posA, unsigned posB) { 92*d5a29442SArjun P output.swapColumns(posA, posB); 93*d5a29442SArjun P IntegerPolyhedron::swapId(posA, posB); 94*d5a29442SArjun P } 95*d5a29442SArjun P 96*d5a29442SArjun P void MultiAffineFunction::removeIdRange(unsigned idStart, unsigned idLimit) { 97*d5a29442SArjun P output.removeColumns(idStart, idLimit - idStart); 98*d5a29442SArjun P IntegerPolyhedron::removeIdRange(idStart, idLimit); 99*d5a29442SArjun P } 100*d5a29442SArjun P 101*d5a29442SArjun P void MultiAffineFunction::eliminateRedundantLocalId(unsigned posA, 102*d5a29442SArjun P unsigned posB) { 103*d5a29442SArjun P output.addToColumn(posB, posA, /*scale=*/1); 104*d5a29442SArjun P IntegerPolyhedron::eliminateRedundantLocalId(posA, posB); 105*d5a29442SArjun P } 106*d5a29442SArjun P 107*d5a29442SArjun P bool MultiAffineFunction::isEqualWhereDomainsOverlap( 108*d5a29442SArjun P MultiAffineFunction other) const { 109*d5a29442SArjun P if (!hasCompatibleDimensions(other)) 110*d5a29442SArjun P return false; 111*d5a29442SArjun P 112*d5a29442SArjun P // `commonFunc` has the same output as `this`. 113*d5a29442SArjun P MultiAffineFunction commonFunc = *this; 114*d5a29442SArjun P // After this merge, `commonFunc` and `other` have the same local ids; they 115*d5a29442SArjun P // are merged. 116*d5a29442SArjun P commonFunc.mergeLocalIds(other); 117*d5a29442SArjun P // After this, the domain of `commonFunc` will be the intersection of the 118*d5a29442SArjun P // domains of `this` and `other`. 119*d5a29442SArjun P commonFunc.IntegerPolyhedron::append(other); 120*d5a29442SArjun P 121*d5a29442SArjun P // `commonDomainMatching` contains the subset of the common domain 122*d5a29442SArjun P // where the outputs of `this` and `other` match. 123*d5a29442SArjun P // 124*d5a29442SArjun P // We want to add constraints equating the outputs of `this` and `other`. 125*d5a29442SArjun P // However, `this` may have difference local ids from `other`, whereas we 126*d5a29442SArjun P // need both to have the same locals. Accordingly, we use `commonFunc.output` 127*d5a29442SArjun P // in place of `this->output`, since `commonFunc` has the same output but also 128*d5a29442SArjun P // has its locals merged. 129*d5a29442SArjun P IntegerPolyhedron commonDomainMatching = commonFunc.getDomain(); 130*d5a29442SArjun P for (unsigned row = 0, e = getNumOutputs(); row < e; ++row) 131*d5a29442SArjun P commonDomainMatching.addEquality( 132*d5a29442SArjun P subtract(commonFunc.output.getRow(row), other.output.getRow(row))); 133*d5a29442SArjun P 134*d5a29442SArjun P // If the whole common domain is a subset of commonDomainMatching, then they 135*d5a29442SArjun P // are equal and the two functions match on the whole common domain. 136*d5a29442SArjun P return commonFunc.getDomain().isSubsetOf(commonDomainMatching); 137*d5a29442SArjun P } 138*d5a29442SArjun P 139*d5a29442SArjun P /// Two PWMAFunctions are equal if they have the same dimensionalities, 140*d5a29442SArjun P /// the same domain, and take the same value at every point in the domain. 141*d5a29442SArjun P bool PWMAFunction::isEqual(const PWMAFunction &other) const { 142*d5a29442SArjun P if (!hasCompatibleDimensions(other)) 143*d5a29442SArjun P return false; 144*d5a29442SArjun P 145*d5a29442SArjun P if (!this->getDomain().isEqual(other.getDomain())) 146*d5a29442SArjun P return false; 147*d5a29442SArjun P 148*d5a29442SArjun P // Check if, whenever the domains of a piece of `this` and a piece of `other` 149*d5a29442SArjun P // overlap, they take the same output value. If `this` and `other` have the 150*d5a29442SArjun P // same domain (checked above), then this check passes iff the two functions 151*d5a29442SArjun P // have the same output at every point in the domain. 152*d5a29442SArjun P for (const MultiAffineFunction &aPiece : this->pieces) 153*d5a29442SArjun P for (const MultiAffineFunction &bPiece : other.pieces) 154*d5a29442SArjun P if (!aPiece.isEqualWhereDomainsOverlap(bPiece)) 155*d5a29442SArjun P return false; 156*d5a29442SArjun P return true; 157*d5a29442SArjun P } 158*d5a29442SArjun P 159*d5a29442SArjun P void PWMAFunction::addPiece(const MultiAffineFunction &piece) { 160*d5a29442SArjun P assert(hasCompatibleDimensions(piece) && 161*d5a29442SArjun P "Piece to be added is not compatible with this PWMAFunction!"); 162*d5a29442SArjun P assert(piece.isConsistent() && "Piece is internally inconsistent!"); 163*d5a29442SArjun P assert(this->getDomain() 164*d5a29442SArjun P .intersect(PresburgerSet(piece.getDomain())) 165*d5a29442SArjun P .isIntegerEmpty() && 166*d5a29442SArjun P "New piece's domain overlaps with that of existing pieces!"); 167*d5a29442SArjun P pieces.push_back(piece); 168*d5a29442SArjun P } 169*d5a29442SArjun P 170*d5a29442SArjun P void PWMAFunction::addPiece(const IntegerPolyhedron &domain, 171*d5a29442SArjun P const Matrix &output) { 172*d5a29442SArjun P addPiece(MultiAffineFunction(domain, output)); 173*d5a29442SArjun P } 174*d5a29442SArjun P 175*d5a29442SArjun P void PWMAFunction::print(raw_ostream &os) const { 176*d5a29442SArjun P os << pieces.size() << " pieces:\n"; 177*d5a29442SArjun P for (const MultiAffineFunction &piece : pieces) 178*d5a29442SArjun P piece.print(os); 179*d5a29442SArjun P } 180*d5a29442SArjun P 181*d5a29442SArjun P /// The hasCompatibleDimensions functions don't check the number of local ids; 182*d5a29442SArjun P /// functions are still compatible if they have differing number of locals. 183*d5a29442SArjun P bool MultiAffineFunction::hasCompatibleDimensions( 184*d5a29442SArjun P const MultiAffineFunction &f) const { 185*d5a29442SArjun P return getNumDimIds() == f.getNumDimIds() && 186*d5a29442SArjun P getNumSymbolIds() == f.getNumSymbolIds() && 187*d5a29442SArjun P getNumOutputs() == f.getNumOutputs(); 188*d5a29442SArjun P } 189*d5a29442SArjun P bool PWMAFunction::hasCompatibleDimensions(const MultiAffineFunction &f) const { 190*d5a29442SArjun P return getNumDimIds() == f.getNumDimIds() && 191*d5a29442SArjun P getNumSymbolIds() == f.getNumSymbolIds() && 192*d5a29442SArjun P getNumOutputs() == f.getNumOutputs(); 193*d5a29442SArjun P } 194*d5a29442SArjun P bool PWMAFunction::hasCompatibleDimensions(const PWMAFunction &f) const { 195*d5a29442SArjun P return getNumDimIds() == f.getNumDimIds() && 196*d5a29442SArjun P getNumSymbolIds() == f.getNumSymbolIds() && 197*d5a29442SArjun P getNumOutputs() == f.getNumOutputs(); 198*d5a29442SArjun P } 199