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; 130c1f6865SGroverkss 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 { 30a5a598beSGroverkss PresburgerSet domain = PresburgerSet::getEmpty(getSpace()); 31d5a29442SArjun P for (const MultiAffineFunction &piece : pieces) 32abc2c230SGroverkss domain.unionInPlace(piece.getDomain()); 33d5a29442SArjun P return domain; 34d5a29442SArjun P } 35d5a29442SArjun P 36d5a29442SArjun P Optional<SmallVector<int64_t, 8>> 37d5a29442SArjun P MultiAffineFunction::valueAt(ArrayRef<int64_t> point) const { 38*d95140a5SGroverkss assert(point.size() == domainSet.getNumDimAndSymbolVars() && 394418669fSArjun P "Point has incorrect dimensionality!"); 40d5a29442SArjun P 414418669fSArjun P Optional<SmallVector<int64_t, 8>> maybeLocalValues = 424418669fSArjun P getDomain().containsPointNoLocal(point); 434418669fSArjun P if (!maybeLocalValues) 44d5a29442SArjun P return {}; 45d5a29442SArjun P 46d5a29442SArjun P // The point lies in the domain, so we need to compute the output value. 474418669fSArjun P SmallVector<int64_t, 8> pointHomogenous{llvm::to_vector(point)}; 484418669fSArjun P // The given point didn't include the values of locals which the output is a 494418669fSArjun P // function of; we have computed one possible set of values and use them 50*d95140a5SGroverkss // here. The function is not allowed to have local vars that take more than 514418669fSArjun P // one possible value. 524418669fSArjun P pointHomogenous.append(*maybeLocalValues); 53d5a29442SArjun P // The matrix `output` has an affine expression in the ith row, corresponding 54d5a29442SArjun P // to the expression for the ith value in the output vector. The last column 55d5a29442SArjun P // of the matrix contains the constant term. Let v be the input point with 56d5a29442SArjun P // a 1 appended at the end. We can see that output * v gives the desired 57d5a29442SArjun P // output vector. 58094ad066SArjun P pointHomogenous.emplace_back(1); 59d5a29442SArjun P SmallVector<int64_t, 8> result = 60d5a29442SArjun P output.postMultiplyWithColumn(pointHomogenous); 61d5a29442SArjun P assert(result.size() == getNumOutputs()); 62d5a29442SArjun P return result; 63d5a29442SArjun P } 64d5a29442SArjun P 65d5a29442SArjun P Optional<SmallVector<int64_t, 8>> 66d5a29442SArjun P PWMAFunction::valueAt(ArrayRef<int64_t> point) const { 67d5a29442SArjun P assert(point.size() == getNumInputs() && 68d5a29442SArjun P "Point has incorrect dimensionality!"); 69d5a29442SArjun P for (const MultiAffineFunction &piece : pieces) 70d5a29442SArjun P if (Optional<SmallVector<int64_t, 8>> output = piece.valueAt(point)) 71d5a29442SArjun P return output; 72d5a29442SArjun P return {}; 73d5a29442SArjun P } 74d5a29442SArjun P 75d5a29442SArjun P void MultiAffineFunction::print(raw_ostream &os) const { 76d5a29442SArjun P os << "Domain:"; 7715650b32SGroverkss domainSet.print(os); 78d5a29442SArjun P os << "Output:\n"; 79d5a29442SArjun P output.print(os); 80d5a29442SArjun P os << "\n"; 81d5a29442SArjun P } 82d5a29442SArjun P 83d5a29442SArjun P void MultiAffineFunction::dump() const { print(llvm::errs()); } 84d5a29442SArjun P 85d5a29442SArjun P bool MultiAffineFunction::isEqual(const MultiAffineFunction &other) const { 8615650b32SGroverkss return getDomainSpace().isCompatible(other.getDomainSpace()) && 8720aedb14SGroverkss getDomain().isEqual(other.getDomain()) && 88d5a29442SArjun P isEqualWhereDomainsOverlap(other); 89d5a29442SArjun P } 90d5a29442SArjun P 91*d95140a5SGroverkss unsigned MultiAffineFunction::insertVar(VarKind kind, unsigned pos, 92d5a29442SArjun P unsigned num) { 93*d95140a5SGroverkss assert(kind != VarKind::Domain && "Domain has to be zero in a set"); 94*d95140a5SGroverkss unsigned absolutePos = domainSet.getVarKindOffset(kind) + pos; 95d5a29442SArjun P output.insertColumns(absolutePos, num); 96*d95140a5SGroverkss return domainSet.insertVar(kind, pos, num); 97d5a29442SArjun P } 98d5a29442SArjun P 99*d95140a5SGroverkss void MultiAffineFunction::removeVarRange(VarKind kind, unsigned varStart, 100*d95140a5SGroverkss unsigned varLimit) { 101*d95140a5SGroverkss output.removeColumns(varStart + domainSet.getVarKindOffset(kind), 102*d95140a5SGroverkss varLimit - varStart); 103*d95140a5SGroverkss domainSet.removeVarRange(kind, varStart, varLimit); 104d5a29442SArjun P } 105d5a29442SArjun P 10679ad5fb2SArjun P void MultiAffineFunction::truncateOutput(unsigned count) { 10779ad5fb2SArjun P assert(count <= output.getNumRows()); 10879ad5fb2SArjun P output.resizeVertically(count); 10979ad5fb2SArjun P } 11079ad5fb2SArjun P 11179ad5fb2SArjun P void PWMAFunction::truncateOutput(unsigned count) { 11279ad5fb2SArjun P assert(count <= numOutputs); 11379ad5fb2SArjun P for (MultiAffineFunction &piece : pieces) 11479ad5fb2SArjun P piece.truncateOutput(count); 11579ad5fb2SArjun P numOutputs = count; 11679ad5fb2SArjun P } 11779ad5fb2SArjun P 118*d95140a5SGroverkss void MultiAffineFunction::mergeLocalVars(MultiAffineFunction &other) { 119*d95140a5SGroverkss // Merge output local vars of both functions without using division 120*d95140a5SGroverkss // information i.e. append local vars of `other` to `this` and insert 121*d95140a5SGroverkss // local vars of `this` to `other` at the start of it's local vars. 122*d95140a5SGroverkss output.insertColumns(domainSet.getVarKindEnd(VarKind::Local), 123*d95140a5SGroverkss other.domainSet.getNumLocalVars()); 124*d95140a5SGroverkss other.output.insertColumns(other.domainSet.getVarKindOffset(VarKind::Local), 125*d95140a5SGroverkss domainSet.getNumLocalVars()); 12615650b32SGroverkss 12715650b32SGroverkss auto merge = [this, &other](unsigned i, unsigned j) -> bool { 12815650b32SGroverkss // Merge local at position j into local at position i in function domain. 129*d95140a5SGroverkss domainSet.eliminateRedundantLocalVar(i, j); 130*d95140a5SGroverkss other.domainSet.eliminateRedundantLocalVar(i, j); 13115650b32SGroverkss 132*d95140a5SGroverkss unsigned localOffset = domainSet.getVarKindOffset(VarKind::Local); 13315650b32SGroverkss 13415650b32SGroverkss // Merge local at position j into local at position i in output domain. 13515650b32SGroverkss output.addToColumn(localOffset + j, localOffset + i, 1); 13615650b32SGroverkss output.removeColumn(localOffset + j); 13715650b32SGroverkss other.output.addToColumn(localOffset + j, localOffset + i, 1); 13815650b32SGroverkss other.output.removeColumn(localOffset + j); 13915650b32SGroverkss 14015650b32SGroverkss return true; 14115650b32SGroverkss }; 14215650b32SGroverkss 143*d95140a5SGroverkss presburger::mergeLocalVars(domainSet, other.domainSet, merge); 14415650b32SGroverkss } 14515650b32SGroverkss 146d5a29442SArjun P bool MultiAffineFunction::isEqualWhereDomainsOverlap( 147d5a29442SArjun P MultiAffineFunction other) const { 14815650b32SGroverkss if (!getDomainSpace().isCompatible(other.getDomainSpace())) 149d5a29442SArjun P return false; 150d5a29442SArjun P 151d5a29442SArjun P // `commonFunc` has the same output as `this`. 152d5a29442SArjun P MultiAffineFunction commonFunc = *this; 153*d95140a5SGroverkss // After this merge, `commonFunc` and `other` have the same local vars; they 154d5a29442SArjun P // are merged. 155*d95140a5SGroverkss commonFunc.mergeLocalVars(other); 156d5a29442SArjun P // After this, the domain of `commonFunc` will be the intersection of the 157d5a29442SArjun P // domains of `this` and `other`. 15815650b32SGroverkss commonFunc.domainSet.append(other.domainSet); 159d5a29442SArjun P 160d5a29442SArjun P // `commonDomainMatching` contains the subset of the common domain 161d5a29442SArjun P // where the outputs of `this` and `other` match. 162d5a29442SArjun P // 163d5a29442SArjun P // We want to add constraints equating the outputs of `this` and `other`. 164*d95140a5SGroverkss // However, `this` may have difference local vars from `other`, whereas we 165d5a29442SArjun P // need both to have the same locals. Accordingly, we use `commonFunc.output` 166d5a29442SArjun P // in place of `this->output`, since `commonFunc` has the same output but also 167d5a29442SArjun P // has its locals merged. 168d5a29442SArjun P IntegerPolyhedron commonDomainMatching = commonFunc.getDomain(); 169d5a29442SArjun P for (unsigned row = 0, e = getNumOutputs(); row < e; ++row) 170d5a29442SArjun P commonDomainMatching.addEquality( 171d5a29442SArjun P subtract(commonFunc.output.getRow(row), other.output.getRow(row))); 172d5a29442SArjun P 173d5a29442SArjun P // If the whole common domain is a subset of commonDomainMatching, then they 174d5a29442SArjun P // are equal and the two functions match on the whole common domain. 175d5a29442SArjun P return commonFunc.getDomain().isSubsetOf(commonDomainMatching); 176d5a29442SArjun P } 177d5a29442SArjun P 178d5a29442SArjun P /// Two PWMAFunctions are equal if they have the same dimensionalities, 179d5a29442SArjun P /// the same domain, and take the same value at every point in the domain. 180d5a29442SArjun P bool PWMAFunction::isEqual(const PWMAFunction &other) const { 18120aedb14SGroverkss if (!space.isCompatible(other.space)) 182d5a29442SArjun P return false; 183d5a29442SArjun P 184d5a29442SArjun P if (!this->getDomain().isEqual(other.getDomain())) 185d5a29442SArjun P return false; 186d5a29442SArjun P 187d5a29442SArjun P // Check if, whenever the domains of a piece of `this` and a piece of `other` 188d5a29442SArjun P // overlap, they take the same output value. If `this` and `other` have the 189d5a29442SArjun P // same domain (checked above), then this check passes iff the two functions 190d5a29442SArjun P // have the same output at every point in the domain. 191d5a29442SArjun P for (const MultiAffineFunction &aPiece : this->pieces) 192d5a29442SArjun P for (const MultiAffineFunction &bPiece : other.pieces) 193d5a29442SArjun P if (!aPiece.isEqualWhereDomainsOverlap(bPiece)) 194d5a29442SArjun P return false; 195d5a29442SArjun P return true; 196d5a29442SArjun P } 197d5a29442SArjun P 198d5a29442SArjun P void PWMAFunction::addPiece(const MultiAffineFunction &piece) { 19915650b32SGroverkss assert(space.isCompatible(piece.getDomainSpace()) && 200d5a29442SArjun P "Piece to be added is not compatible with this PWMAFunction!"); 201d5a29442SArjun P assert(piece.isConsistent() && "Piece is internally inconsistent!"); 202d5a29442SArjun P assert(this->getDomain() 203d5a29442SArjun P .intersect(PresburgerSet(piece.getDomain())) 204d5a29442SArjun P .isIntegerEmpty() && 205d5a29442SArjun P "New piece's domain overlaps with that of existing pieces!"); 206d5a29442SArjun P pieces.push_back(piece); 207d5a29442SArjun P } 208d5a29442SArjun P 209d5a29442SArjun P void PWMAFunction::addPiece(const IntegerPolyhedron &domain, 210d5a29442SArjun P const Matrix &output) { 211d5a29442SArjun P addPiece(MultiAffineFunction(domain, output)); 212d5a29442SArjun P } 213d5a29442SArjun P 214d5a29442SArjun P void PWMAFunction::print(raw_ostream &os) const { 215d5a29442SArjun P os << pieces.size() << " pieces:\n"; 216d5a29442SArjun P for (const MultiAffineFunction &piece : pieces) 217d5a29442SArjun P piece.print(os); 218d5a29442SArjun P } 219888894b6SArjun P 220888894b6SArjun P void PWMAFunction::dump() const { print(llvm::errs()); } 221