1 //===- PWMAFunction.h - MLIR PWMAFunction Class------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Support for piece-wise multi-affine functions. These are functions that are
10 // defined on a domain that is a union of IntegerPolyhedrons, and on each domain
11 // the value of the function is a tuple of integers, with each value in the
12 // tuple being an affine expression in the vars of the IntegerPolyhedron.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #ifndef MLIR_ANALYSIS_PRESBURGER_PWMAFUNCTION_H
17 #define MLIR_ANALYSIS_PRESBURGER_PWMAFUNCTION_H
18 
19 #include "mlir/Analysis/Presburger/IntegerRelation.h"
20 #include "mlir/Analysis/Presburger/PresburgerRelation.h"
21 
22 namespace mlir {
23 namespace presburger {
24 
25 /// This class represents a multi-affine function whose domain is given by an
26 /// IntegerPolyhedron. This can be thought of as an IntegerPolyhedron with a
27 /// tuple of integer values attached to every point in the polyhedron, with the
28 /// value of each element of the tuple given by an affine expression in the vars
29 /// of the polyhedron. For example we could have the domain
30 ///
31 /// (x, y) : (x >= 5, y >= x)
32 ///
33 /// and a tuple of three integers defined at every point in the polyhedron:
34 ///
35 /// (x, y) -> (x + 2, 2*x - 3y + 5, 2*x + y).
36 ///
37 /// In this way every point in the polyhedron has a tuple of integers associated
38 /// with it. If the integer polyhedron has local vars, then the output
39 /// expressions can use them as well. The output expressions are represented as
40 /// a matrix with one row for every element in the output vector one column for
41 /// each var, and an extra column at the end for the constant term.
42 ///
43 /// Checking equality of two such functions is supported, as well as finding the
44 /// value of the function at a specified point.
45 class MultiAffineFunction {
46 public:
MultiAffineFunction(const IntegerPolyhedron & domain,const Matrix & output)47   MultiAffineFunction(const IntegerPolyhedron &domain, const Matrix &output)
48       : domainSet(domain), output(output) {}
MultiAffineFunction(const Matrix & output,const PresburgerSpace & space)49   MultiAffineFunction(const Matrix &output, const PresburgerSpace &space)
50       : domainSet(space), output(output) {}
51 
getNumInputs()52   unsigned getNumInputs() const { return domainSet.getNumDimAndSymbolVars(); }
getNumOutputs()53   unsigned getNumOutputs() const { return output.getNumRows(); }
isConsistent()54   bool isConsistent() const {
55     return output.getNumColumns() == domainSet.getNumVars() + 1;
56   }
57 
58   /// Get the space of the input domain of this function.
getDomainSpace()59   const PresburgerSpace &getDomainSpace() const { return domainSet.getSpace(); }
60   /// Get the input domain of this function.
getDomain()61   const IntegerPolyhedron &getDomain() const { return domainSet; }
62   /// Get a matrix with each row representing row^th output expression.
getOutputMatrix()63   const Matrix &getOutputMatrix() const { return output; }
64   /// Get the `i^th` output expression.
getOutputExpr(unsigned i)65   ArrayRef<int64_t> getOutputExpr(unsigned i) const { return output.getRow(i); }
66 
67   /// Insert `num` variables of the specified kind at position `pos`.
68   /// Positions are relative to the kind of variable. The coefficient columns
69   /// corresponding to the added variables are initialized to zero. Return the
70   /// absolute column position (i.e., not relative to the kind of variable)
71   /// of the first added variable.
72   unsigned insertVar(VarKind kind, unsigned pos, unsigned num = 1);
73 
74   /// Remove the specified range of vars.
75   void removeVarRange(VarKind kind, unsigned varStart, unsigned varLimit);
76 
77   /// Given a MAF `other`, merges local variables such that both funcitons
78   /// have union of local vars, without changing the set of points in domain or
79   /// the output.
80   void mergeLocalVars(MultiAffineFunction &other);
81 
82   /// Return whether the outputs of `this` and `other` agree wherever both
83   /// functions are defined, i.e., the outputs should be equal for all points in
84   /// the intersection of the domains.
85   bool isEqualWhereDomainsOverlap(MultiAffineFunction other) const;
86 
87   /// Return whether the `this` and `other` are equal. This is the case if
88   /// they lie in the same space, i.e. have the same dimensions, and their
89   /// domains are identical and their outputs are equal on their domain.
90   bool isEqual(const MultiAffineFunction &other) const;
91 
92   /// Get the value of the function at the specified point. If the point lies
93   /// outside the domain, an empty optional is returned.
94   Optional<SmallVector<int64_t, 8>> valueAt(ArrayRef<int64_t> point) const;
95 
96   /// Truncate the output dimensions to the first `count` dimensions.
97   ///
98   /// TODO: refactor so that this can be accomplished through removeVarRange.
99   void truncateOutput(unsigned count);
100 
101   void print(raw_ostream &os) const;
102   void dump() const;
103 
104 private:
105   /// The IntegerPolyhedron representing the domain over which the function is
106   /// defined.
107   IntegerPolyhedron domainSet;
108 
109   /// The function's output is a tuple of integers, with the ith element of the
110   /// tuple defined by the affine expression given by the ith row of this output
111   /// matrix.
112   Matrix output;
113 };
114 
115 /// This class represents a piece-wise MultiAffineFunction. This can be thought
116 /// of as a list of MultiAffineFunction with disjoint domains, with each having
117 /// their own affine expressions for their output tuples. For example, we could
118 /// have a function with two input variables (x, y), defined as
119 ///
120 /// f(x, y) = (2*x + y, y - 4)  if x >= 0, y >= 0
121 ///         = (-2*x + y, y + 4) if x < 0,  y < 0
122 ///         = (4, 1)            if x < 0,  y >= 0
123 ///
124 /// Note that the domains all have to be *disjoint*. Otherwise, the behaviour of
125 /// this class is undefined. The domains need not cover all possible points;
126 /// this represents a partial function and so could be undefined at some points.
127 ///
128 /// As in PresburgerSets, the input vars are partitioned into dimension vars and
129 /// symbolic vars.
130 ///
131 /// Support is provided to compare equality of two such functions as well as
132 /// finding the value of the function at a point.
133 class PWMAFunction {
134 public:
PWMAFunction(const PresburgerSpace & space,unsigned numOutputs)135   PWMAFunction(const PresburgerSpace &space, unsigned numOutputs)
136       : space(space), numOutputs(numOutputs) {
137     assert(space.getNumDomainVars() == 0 &&
138            "Set type space should have zero domain vars.");
139     assert(space.getNumLocalVars() == 0 &&
140            "PWMAFunction cannot have local vars.");
141     assert(numOutputs >= 1 && "The function must output something!");
142   }
143 
getSpace()144   const PresburgerSpace &getSpace() const { return space; }
145 
146   void addPiece(const MultiAffineFunction &piece);
147   void addPiece(const IntegerPolyhedron &domain, const Matrix &output);
148   void addPiece(const PresburgerSet &domain, const Matrix &output);
149 
getPiece(unsigned i)150   const MultiAffineFunction &getPiece(unsigned i) const { return pieces[i]; }
getNumPieces()151   unsigned getNumPieces() const { return pieces.size(); }
getNumOutputs()152   unsigned getNumOutputs() const { return numOutputs; }
getNumInputs()153   unsigned getNumInputs() const { return space.getNumVars(); }
getPiece(unsigned i)154   MultiAffineFunction &getPiece(unsigned i) { return pieces[i]; }
155 
156   /// Return the domain of this piece-wise MultiAffineFunction. This is the
157   /// union of the domains of all the pieces.
158   PresburgerSet getDomain() const;
159 
160   /// Return the value at the specified point and an empty optional if the
161   /// point does not lie in the domain.
162   Optional<SmallVector<int64_t, 8>> valueAt(ArrayRef<int64_t> point) const;
163 
164   /// Return whether `this` and `other` are equal as PWMAFunctions, i.e. whether
165   /// they have the same dimensions, the same domain and they take the same
166   /// value at every point in the domain.
167   bool isEqual(const PWMAFunction &other) const;
168 
169   /// Truncate the output dimensions to the first `count` dimensions.
170   ///
171   /// TODO: refactor so that this can be accomplished through removeVarRange.
172   void truncateOutput(unsigned count);
173 
174   /// Return a function defined on the union of the domains of this and func,
175   /// such that when only one of the functions is defined, it outputs the same
176   /// as that function, and if both are defined, it outputs the lexmax/lexmin of
177   /// the two outputs. On points where neither function is defined, the returned
178   /// function is not defined either.
179   ///
180   /// Currently this does not support PWMAFunctions which have pieces containing
181   /// local variables.
182   /// TODO: Support local variables in peices.
183   PWMAFunction unionLexMin(const PWMAFunction &func);
184   PWMAFunction unionLexMax(const PWMAFunction &func);
185 
186   void print(raw_ostream &os) const;
187   void dump() const;
188 
189 private:
190   /// Return a function defined on the union of the domains of `this` and
191   /// `func`, such that when only one of the functions is defined, it outputs
192   /// the same as that function, and if neither is defined, the returned
193   /// function is not defined either.
194   ///
195   /// The provided `tiebreak` function determines which of the two functions'
196   /// output should be used on inputs where both the functions are defined. More
197   /// precisely, given two `MultiAffineFunction`s `mafA` and `mafB`, `tiebreak`
198   /// returns the subset of the intersection of the two functions' domains where
199   /// the output of `mafA` should be used.
200   ///
201   /// The PresburgerSet returned by `tiebreak` should be disjoint.
202   /// TODO: Remove this constraint of returning disjoint set.
203   PWMAFunction
204   unionFunction(const PWMAFunction &func,
205                 llvm::function_ref<PresburgerSet(MultiAffineFunction mafA,
206                                                  MultiAffineFunction mafB)>
207                     tiebreak) const;
208 
209   PresburgerSpace space;
210 
211   /// The list of pieces in this piece-wise MultiAffineFunction.
212   SmallVector<MultiAffineFunction, 4> pieces;
213 
214   /// The number of output vars.
215   unsigned numOutputs;
216 };
217 
218 } // namespace presburger
219 } // namespace mlir
220 
221 #endif // MLIR_ANALYSIS_PRESBURGER_PWMAFUNCTION_H
222