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 {
384418669fSArjun P   assert(point.size() == getNumDimAndSymbolIds() &&
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
504418669fSArjun P   // here. The function is not allowed to have local ids 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.
58d5a29442SArjun P   pointHomogenous.push_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:";
77d5a29442SArjun P   IntegerPolyhedron::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 {
86*20aedb14SGroverkss   return space.isCompatible(other.getSpace()) &&
87*20aedb14SGroverkss          getDomain().isEqual(other.getDomain()) &&
88d5a29442SArjun P          isEqualWhereDomainsOverlap(other);
89d5a29442SArjun P }
90d5a29442SArjun P 
91d5a29442SArjun P unsigned MultiAffineFunction::insertId(IdKind kind, unsigned pos,
92d5a29442SArjun P                                        unsigned num) {
9358966dd4SGroverkss   assert((kind != IdKind::Domain || num == 0) &&
9458966dd4SGroverkss          "Domain has to be zero in a set");
95d5a29442SArjun P   unsigned absolutePos = getIdKindOffset(kind) + pos;
96d5a29442SArjun P   output.insertColumns(absolutePos, num);
97d5a29442SArjun P   return IntegerPolyhedron::insertId(kind, pos, num);
98d5a29442SArjun P }
99d5a29442SArjun P 
100d5a29442SArjun P void MultiAffineFunction::swapId(unsigned posA, unsigned posB) {
101d5a29442SArjun P   output.swapColumns(posA, posB);
102d5a29442SArjun P   IntegerPolyhedron::swapId(posA, posB);
103d5a29442SArjun P }
104d5a29442SArjun P 
105c896e654SGroverkss void MultiAffineFunction::removeIdRange(IdKind kind, unsigned idStart,
106c896e654SGroverkss                                         unsigned idLimit) {
107c896e654SGroverkss   output.removeColumns(idStart + getIdKindOffset(kind), idLimit - idStart);
1087f611249SArjun P   IntegerPolyhedron::removeIdRange(kind, idStart, idLimit);
109d5a29442SArjun P }
110d5a29442SArjun P 
111d5a29442SArjun P void MultiAffineFunction::eliminateRedundantLocalId(unsigned posA,
112d5a29442SArjun P                                                     unsigned posB) {
113d81fa76fSArjun P   unsigned localOffset = getIdKindOffset(IdKind::Local);
114d81fa76fSArjun P   output.addToColumn(localOffset + posB, localOffset + posA, /*scale=*/1);
115d5a29442SArjun P   IntegerPolyhedron::eliminateRedundantLocalId(posA, posB);
116d5a29442SArjun P }
117d5a29442SArjun P 
11879ad5fb2SArjun P void MultiAffineFunction::truncateOutput(unsigned count) {
11979ad5fb2SArjun P   assert(count <= output.getNumRows());
12079ad5fb2SArjun P   output.resizeVertically(count);
12179ad5fb2SArjun P }
12279ad5fb2SArjun P 
12379ad5fb2SArjun P void PWMAFunction::truncateOutput(unsigned count) {
12479ad5fb2SArjun P   assert(count <= numOutputs);
12579ad5fb2SArjun P   for (MultiAffineFunction &piece : pieces)
12679ad5fb2SArjun P     piece.truncateOutput(count);
12779ad5fb2SArjun P   numOutputs = count;
12879ad5fb2SArjun P }
12979ad5fb2SArjun P 
130d5a29442SArjun P bool MultiAffineFunction::isEqualWhereDomainsOverlap(
131d5a29442SArjun P     MultiAffineFunction other) const {
132*20aedb14SGroverkss   if (!space.isCompatible(other.getSpace()))
133d5a29442SArjun P     return false;
134d5a29442SArjun P 
135d5a29442SArjun P   // `commonFunc` has the same output as `this`.
136d5a29442SArjun P   MultiAffineFunction commonFunc = *this;
137d5a29442SArjun P   // After this merge, `commonFunc` and `other` have the same local ids; they
138d5a29442SArjun P   // are merged.
139d5a29442SArjun P   commonFunc.mergeLocalIds(other);
140d5a29442SArjun P   // After this, the domain of `commonFunc` will be the intersection of the
141d5a29442SArjun P   // domains of `this` and `other`.
142d5a29442SArjun P   commonFunc.IntegerPolyhedron::append(other);
143d5a29442SArjun P 
144d5a29442SArjun P   // `commonDomainMatching` contains the subset of the common domain
145d5a29442SArjun P   // where the outputs of `this` and `other` match.
146d5a29442SArjun P   //
147d5a29442SArjun P   // We want to add constraints equating the outputs of `this` and `other`.
148d5a29442SArjun P   // However, `this` may have difference local ids from `other`, whereas we
149d5a29442SArjun P   // need both to have the same locals. Accordingly, we use `commonFunc.output`
150d5a29442SArjun P   // in place of `this->output`, since `commonFunc` has the same output but also
151d5a29442SArjun P   // has its locals merged.
152d5a29442SArjun P   IntegerPolyhedron commonDomainMatching = commonFunc.getDomain();
153d5a29442SArjun P   for (unsigned row = 0, e = getNumOutputs(); row < e; ++row)
154d5a29442SArjun P     commonDomainMatching.addEquality(
155d5a29442SArjun P         subtract(commonFunc.output.getRow(row), other.output.getRow(row)));
156d5a29442SArjun P 
157d5a29442SArjun P   // If the whole common domain is a subset of commonDomainMatching, then they
158d5a29442SArjun P   // are equal and the two functions match on the whole common domain.
159d5a29442SArjun P   return commonFunc.getDomain().isSubsetOf(commonDomainMatching);
160d5a29442SArjun P }
161d5a29442SArjun P 
162d5a29442SArjun P /// Two PWMAFunctions are equal if they have the same dimensionalities,
163d5a29442SArjun P /// the same domain, and take the same value at every point in the domain.
164d5a29442SArjun P bool PWMAFunction::isEqual(const PWMAFunction &other) const {
165*20aedb14SGroverkss   if (!space.isCompatible(other.space))
166d5a29442SArjun P     return false;
167d5a29442SArjun P 
168d5a29442SArjun P   if (!this->getDomain().isEqual(other.getDomain()))
169d5a29442SArjun P     return false;
170d5a29442SArjun P 
171d5a29442SArjun P   // Check if, whenever the domains of a piece of `this` and a piece of `other`
172d5a29442SArjun P   // overlap, they take the same output value. If `this` and `other` have the
173d5a29442SArjun P   // same domain (checked above), then this check passes iff the two functions
174d5a29442SArjun P   // have the same output at every point in the domain.
175d5a29442SArjun P   for (const MultiAffineFunction &aPiece : this->pieces)
176d5a29442SArjun P     for (const MultiAffineFunction &bPiece : other.pieces)
177d5a29442SArjun P       if (!aPiece.isEqualWhereDomainsOverlap(bPiece))
178d5a29442SArjun P         return false;
179d5a29442SArjun P   return true;
180d5a29442SArjun P }
181d5a29442SArjun P 
182d5a29442SArjun P void PWMAFunction::addPiece(const MultiAffineFunction &piece) {
183*20aedb14SGroverkss   assert(space.isCompatible(piece.getSpace()) &&
184d5a29442SArjun P          "Piece to be added is not compatible with this PWMAFunction!");
185d5a29442SArjun P   assert(piece.isConsistent() && "Piece is internally inconsistent!");
186d5a29442SArjun P   assert(this->getDomain()
187d5a29442SArjun P              .intersect(PresburgerSet(piece.getDomain()))
188d5a29442SArjun P              .isIntegerEmpty() &&
189d5a29442SArjun P          "New piece's domain overlaps with that of existing pieces!");
190d5a29442SArjun P   pieces.push_back(piece);
191d5a29442SArjun P }
192d5a29442SArjun P 
193d5a29442SArjun P void PWMAFunction::addPiece(const IntegerPolyhedron &domain,
194d5a29442SArjun P                             const Matrix &output) {
195d5a29442SArjun P   addPiece(MultiAffineFunction(domain, output));
196d5a29442SArjun P }
197d5a29442SArjun P 
198d5a29442SArjun P void PWMAFunction::print(raw_ostream &os) const {
199d5a29442SArjun P   os << pieces.size() << " pieces:\n";
200d5a29442SArjun P   for (const MultiAffineFunction &piece : pieces)
201d5a29442SArjun P     piece.print(os);
202d5a29442SArjun P }
203888894b6SArjun P 
204888894b6SArjun P void PWMAFunction::dump() const { print(llvm::errs()); }
205