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