1 //===- AffineStructures.h - MLIR Affine Structures 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 // Structures for affine/polyhedral analysis of ML functions.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef MLIR_DIALECT_AFFINE_ANALYSIS_AFFINESTRUCTURES_H
14 #define MLIR_DIALECT_AFFINE_ANALYSIS_AFFINESTRUCTURES_H
15 
16 #include "mlir/Analysis/Presburger/IntegerRelation.h"
17 #include "mlir/Analysis/Presburger/Matrix.h"
18 #include "mlir/IR/AffineExpr.h"
19 #include "mlir/IR/OpDefinition.h"
20 #include "mlir/Support/LogicalResult.h"
21 
22 namespace mlir {
23 
24 class AffineCondition;
25 class AffineForOp;
26 class AffineIfOp;
27 class AffineMap;
28 class AffineValueMap;
29 class IntegerSet;
30 class MLIRContext;
31 class Value;
32 class MemRefType;
33 struct MutableAffineMap;
34 
35 /// FlatAffineValueConstraints represents an extension of IntegerPolyhedron
36 /// where each non-local variable can have an SSA Value attached to it.
37 class FlatAffineValueConstraints : public presburger::IntegerPolyhedron {
38 public:
39   /// Constructs a constraint system reserving memory for the specified number
40   /// of constraints and variables.
41   FlatAffineValueConstraints(unsigned numReservedInequalities,
42                              unsigned numReservedEqualities,
43                              unsigned numReservedCols, unsigned numDims,
44                              unsigned numSymbols, unsigned numLocals,
45                              ArrayRef<Optional<Value>> valArgs = {})
IntegerPolyhedron(numReservedInequalities,numReservedEqualities,numReservedCols,presburger::PresburgerSpace::getSetSpace (numDims,numSymbols,numLocals))46       : IntegerPolyhedron(numReservedInequalities, numReservedEqualities,
47                           numReservedCols,
48                           presburger::PresburgerSpace::getSetSpace(
49                               numDims, numSymbols, numLocals)) {
50     assert(numReservedCols >= getNumVars() + 1);
51     assert(valArgs.empty() || valArgs.size() == getNumDimAndSymbolVars());
52     values.reserve(numReservedCols);
53     if (valArgs.empty())
54       values.resize(getNumDimAndSymbolVars(), None);
55     else
56       values.append(valArgs.begin(), valArgs.end());
57   }
58 
59   /// Constructs a constraint system with the specified number of
60   /// dimensions and symbols.
61   FlatAffineValueConstraints(unsigned numDims = 0, unsigned numSymbols = 0,
62                              unsigned numLocals = 0,
63                              ArrayRef<Optional<Value>> valArgs = {})
64       : FlatAffineValueConstraints(/*numReservedInequalities=*/0,
65                                    /*numReservedEqualities=*/0,
66                                    /*numReservedCols=*/numDims + numSymbols +
67                                        numLocals + 1,
68                                    numDims, numSymbols, numLocals, valArgs) {}
69 
70   FlatAffineValueConstraints(const IntegerPolyhedron &fac,
71                              ArrayRef<Optional<Value>> valArgs = {})
IntegerPolyhedron(fac)72       : IntegerPolyhedron(fac) {
73     assert(valArgs.empty() || valArgs.size() == getNumDimAndSymbolVars());
74     if (valArgs.empty())
75       values.resize(getNumDimAndSymbolVars(), None);
76     else
77       values.append(valArgs.begin(), valArgs.end());
78   }
79 
80   /// Create a flat affine constraint system from an AffineValueMap or a list of
81   /// these. The constructed system will only include equalities.
82   explicit FlatAffineValueConstraints(const AffineValueMap &avm);
83   explicit FlatAffineValueConstraints(ArrayRef<const AffineValueMap *> avmRef);
84 
85   /// Creates an affine constraint system from an IntegerSet.
86   explicit FlatAffineValueConstraints(IntegerSet set);
87 
88   FlatAffineValueConstraints(ArrayRef<const AffineValueMap *> avmRef,
89                              IntegerSet set);
90 
91   // Construct a hyperrectangular constraint set from ValueRanges that represent
92   // induction variables, lower and upper bounds. `ivs`, `lbs` and `ubs` are
93   // expected to match one to one. The order of variables and constraints is:
94   //
95   // ivs | lbs | ubs | eq/ineq
96   // ----+-----+-----+---------
97   //   1   -1     0      >= 0
98   // ----+-----+-----+---------
99   //  -1    0     1      >= 0
100   //
101   // All dimensions as set as VarKind::SetDim.
102   static FlatAffineValueConstraints
103   getHyperrectangular(ValueRange ivs, ValueRange lbs, ValueRange ubs);
104 
105   /// Return the kind of this FlatAffineConstraints.
getKind()106   Kind getKind() const override { return Kind::FlatAffineValueConstraints; }
107 
classof(const IntegerRelation * cst)108   static bool classof(const IntegerRelation *cst) {
109     return cst->getKind() == Kind::FlatAffineValueConstraints;
110   }
111 
112   /// Clears any existing data and reserves memory for the specified
113   /// constraints.
114   void reset(unsigned numReservedInequalities, unsigned numReservedEqualities,
115              unsigned numReservedCols, unsigned numDims, unsigned numSymbols,
116              unsigned numLocals = 0);
117   void reset(unsigned numDims = 0, unsigned numSymbols = 0,
118              unsigned numLocals = 0);
119   void reset(unsigned numReservedInequalities, unsigned numReservedEqualities,
120              unsigned numReservedCols, unsigned numDims, unsigned numSymbols,
121              unsigned numLocals, ArrayRef<Value> valArgs);
122   void reset(unsigned numDims, unsigned numSymbols, unsigned numLocals,
123              ArrayRef<Value> valArgs);
124 
125   /// Clones this object.
126   std::unique_ptr<FlatAffineValueConstraints> clone() const;
127 
128   /// Adds constraints (lower and upper bounds) for the specified 'affine.for'
129   /// operation's Value using IR information stored in its bound maps. The
130   /// right variable is first looked up using `forOp`'s Value. Asserts if the
131   /// Value corresponding to the 'affine.for' operation isn't found in the
132   /// constraint system. Returns failure for the yet unimplemented/unsupported
133   /// cases.  Any new variables that are found in the bound operands of the
134   /// 'affine.for' operation are added as trailing variables (either
135   /// dimensional or symbolic depending on whether the operand is a valid
136   /// symbol).
137   //  TODO: add support for non-unit strides.
138   LogicalResult addAffineForOpDomain(AffineForOp forOp);
139 
140   /// Adds constraints (lower and upper bounds) for each loop in the loop nest
141   /// described by the bound maps `lbMaps` and `ubMaps` of a computation slice.
142   /// Every pair (`lbMaps[i]`, `ubMaps[i]`) describes the bounds of a loop in
143   /// the nest, sorted outer-to-inner. `operands` contains the bound operands
144   /// for a single bound map. All the bound maps will use the same bound
145   /// operands. Note that some loops described by a computation slice might not
146   /// exist yet in the IR so the Value attached to those dimension variables
147   /// might be empty. For that reason, this method doesn't perform Value
148   /// look-ups to retrieve the dimension variable positions. Instead, it
149   /// assumes the position of the dim variables in the constraint system is
150   /// the same as the position of the loop in the loop nest.
151   LogicalResult addDomainFromSliceMaps(ArrayRef<AffineMap> lbMaps,
152                                        ArrayRef<AffineMap> ubMaps,
153                                        ArrayRef<Value> operands);
154 
155   /// Adds constraints imposed by the `affine.if` operation. These constraints
156   /// are collected from the IntegerSet attached to the given `affine.if`
157   /// instance argument (`ifOp`). It is asserted that:
158   /// 1) The IntegerSet of the given `affine.if` instance should not contain
159   /// semi-affine expressions,
160   /// 2) The columns of the constraint system created from `ifOp` should match
161   /// the columns in the current one regarding numbers and values.
162   void addAffineIfOpDomain(AffineIfOp ifOp);
163 
164   /// Adds a bound for the variable at the specified position with constraints
165   /// being drawn from the specified bound map. In case of an EQ bound, the
166   /// bound map is expected to have exactly one result. In case of a LB/UB, the
167   /// bound map may have more than one result, for each of which an inequality
168   /// is added.
169   ///
170   /// The bound can be added as open or closed by specifying isClosedBound. In
171   /// case of a LB/UB, isClosedBound = false means the bound is added internally
172   /// as a closed bound by +1/-1 respectively. In case of an EQ bound, it can
173   /// only be added as a closed bound.
174   ///
175   /// Note: The dimensions/symbols of this FlatAffineConstraints must match the
176   /// dimensions/symbols of the affine map.
177   LogicalResult addBound(BoundType type, unsigned pos, AffineMap boundMap,
178                          bool isClosedBound);
179 
180   /// Adds a bound for the variable at the specified position with constraints
181   /// being drawn from the specified bound map. In case of an EQ bound, the
182   /// bound map is expected to have exactly one result. In case of a LB/UB, the
183   /// bound map may have more than one result, for each of which an inequality
184   /// is added.
185   /// Note: The dimensions/symbols of this FlatAffineConstraints must match the
186   /// dimensions/symbols of the affine map. By default the lower bound is closed
187   /// and the upper bound is open.
188   LogicalResult addBound(BoundType type, unsigned pos, AffineMap boundMap);
189 
190   /// Adds a bound for the variable at the specified position with constraints
191   /// being drawn from the specified bound map and operands. In case of an
192   /// EQ bound, the  bound map is expected to have exactly one result. In case
193   /// of a LB/UB, the bound map may have more than one result, for each of which
194   /// an inequality is added.
195   LogicalResult addBound(BoundType type, unsigned pos, AffineMap boundMap,
196                          ValueRange operands);
197 
198   /// Adds a constant bound for the variable associated with the given Value.
199   void addBound(BoundType type, Value val, int64_t value);
200 
201   /// The `addBound` overload above hides the inherited overloads by default, so
202   /// we explicitly introduce them here.
203   using IntegerPolyhedron::addBound;
204 
205   /// Returns the constraint system as an integer set. Returns a null integer
206   /// set if the system has no constraints, or if an integer set couldn't be
207   /// constructed as a result of a local variable's explicit representation not
208   /// being known and such a local variable appearing in any of the constraints.
209   IntegerSet getAsIntegerSet(MLIRContext *context) const;
210 
211   /// Computes the lower and upper bounds of the first `num` dimensional
212   /// variables (starting at `offset`) as an affine map of the remaining
213   /// variables (dimensional and symbolic). This method is able to detect
214   /// variables as floordiv's and mod's of affine expressions of other
215   /// variables with respect to (positive) constants. Sets bound map to a
216   /// null AffineMap if such a bound can't be found (or yet unimplemented).
217   ///
218   /// By default the returned lower bounds are closed and upper bounds are open.
219   /// This can be changed by getClosedUB.
220   void getSliceBounds(unsigned offset, unsigned num, MLIRContext *context,
221                       SmallVectorImpl<AffineMap> *lbMaps,
222                       SmallVectorImpl<AffineMap> *ubMaps,
223                       bool getClosedUB = false);
224 
225   /// Composes an affine map whose dimensions and symbols match one to one with
226   /// the dimensions and symbols of this FlatAffineConstraints. The results of
227   /// the map `other` are added as the leading dimensions of this constraint
228   /// system. Returns failure if `other` is a semi-affine map.
229   LogicalResult composeMatchingMap(AffineMap other);
230 
231   /// Gets the lower and upper bound of the `offset` + `pos`th variable
232   /// treating [0, offset) U [offset + num, symStartPos) as dimensions and
233   /// [symStartPos, getNumDimAndSymbolVars) as symbols, and `pos` lies in
234   /// [0, num). The multi-dimensional maps in the returned pair represent the
235   /// max and min of potentially multiple affine expressions. The upper bound is
236   /// exclusive. `localExprs` holds pre-computed AffineExpr's for all local
237   /// variables in the system.
238   std::pair<AffineMap, AffineMap>
239   getLowerAndUpperBound(unsigned pos, unsigned offset, unsigned num,
240                         unsigned symStartPos, ArrayRef<AffineExpr> localExprs,
241                         MLIRContext *context) const;
242 
243   /// Returns the bound for the variable at `pos` from the inequality at
244   /// `ineqPos` as a 1-d affine value map (affine map + operands). The returned
245   /// affine value map can either be a lower bound or an upper bound depending
246   /// on the sign of atIneq(ineqPos, pos). Asserts if the row at `ineqPos` does
247   /// not involve the `pos`th variable.
248   void getIneqAsAffineValueMap(unsigned pos, unsigned ineqPos,
249                                AffineValueMap &vmap,
250                                MLIRContext *context) const;
251 
252   /// Adds slice lower bounds represented by lower bounds in `lbMaps` and upper
253   /// bounds in `ubMaps` to each variable in the constraint system which has
254   /// a value in `values`. Note that both lower/upper bounds share the same
255   /// operand list `operands`.
256   /// This function assumes `values.size` == `lbMaps.size` == `ubMaps.size`.
257   /// Note that both lower/upper bounds use operands from `operands`.
258   LogicalResult addSliceBounds(ArrayRef<Value> values,
259                                ArrayRef<AffineMap> lbMaps,
260                                ArrayRef<AffineMap> ubMaps,
261                                ArrayRef<Value> operands);
262 
263   /// Looks up the position of the variable with the specified Value. Returns
264   /// true if found (false otherwise). `pos` is set to the (column) position of
265   /// the variable.
266   bool findVar(Value val, unsigned *pos) const;
267 
268   /// Returns true if an variable with the specified Value exists, false
269   /// otherwise.
270   bool containsVar(Value mayBeVar) const;
271 
272   /// Swap the posA^th variable with the posB^th variable.
273   void swapVar(unsigned posA, unsigned posB) override;
274 
275   /// Insert variables of the specified kind at position `pos`. Positions are
276   /// relative to the kind of variable. The coefficient columns corresponding
277   /// to the added variables are initialized to zero. `vals` are the Values
278   /// corresponding to the variables. Values should not be used with
279   /// VarKind::Local since values can only be attached to non-local variables.
280   /// Return the absolute column position (i.e., not relative to the kind of
281   /// variable) of the first added variable.
282   ///
283   /// Note: Empty Values are allowed in `vals`.
284   unsigned insertDimVar(unsigned pos, unsigned num = 1) {
285     return insertVar(VarKind::SetDim, pos, num);
286   }
287   unsigned insertSymbolVar(unsigned pos, unsigned num = 1) {
288     return insertVar(VarKind::Symbol, pos, num);
289   }
290   unsigned insertLocalVar(unsigned pos, unsigned num = 1) {
291     return insertVar(VarKind::Local, pos, num);
292   }
293   unsigned insertDimVar(unsigned pos, ValueRange vals);
294   unsigned insertSymbolVar(unsigned pos, ValueRange vals);
295   unsigned insertVar(presburger::VarKind kind, unsigned pos,
296                      unsigned num = 1) override;
297   unsigned insertVar(presburger::VarKind kind, unsigned pos, ValueRange vals);
298 
299   /// Append variables of the specified kind after the last variable of that
300   /// kind. The coefficient columns corresponding to the added variables are
301   /// initialized to zero. `vals` are the Values corresponding to the
302   /// variables. Return the position of the first added column.
303   ///
304   /// Note: Empty Values are allowed in `vals`.
305   unsigned appendDimVar(ValueRange vals);
306   unsigned appendSymbolVar(ValueRange vals);
307   unsigned appendDimVar(unsigned num = 1) {
308     return appendVar(VarKind::SetDim, num);
309   }
310   unsigned appendSymbolVar(unsigned num = 1) {
311     return appendVar(VarKind::Symbol, num);
312   }
313   unsigned appendLocalVar(unsigned num = 1) {
314     return appendVar(VarKind::Local, num);
315   }
316 
317   /// Removes variables in the column range [varStart, varLimit), and copies any
318   /// remaining valid data into place, updates member variables, and resizes
319   /// arrays as needed.
320   void removeVarRange(presburger::VarKind kind, unsigned varStart,
321                       unsigned varLimit) override;
322   using IntegerPolyhedron::removeVarRange;
323 
324   /// Add the specified values as a dim or symbol var depending on its nature,
325   /// if it already doesn't exist in the system. `val` has to be either a
326   /// terminal symbol or a loop IV, i.e., it cannot be the result affine.apply
327   /// of any symbols or loop IVs. The variable is added to the end of the
328   /// existing dims or symbols. Additional information on the variable is
329   /// extracted from the IR and added to the constraint system.
330   void addInductionVarOrTerminalSymbol(Value val);
331 
332   /// Align `map` with this constraint system based on `operands`. Each operand
333   /// must already have a corresponding dim/symbol in this constraint system.
334   AffineMap computeAlignedMap(AffineMap map, ValueRange operands) const;
335 
336   /// Composes the affine value map with this FlatAffineValueConstrains, adding
337   /// the results of the map as dimensions at the front
338   /// [0, vMap->getNumResults()) and with the dimensions set to the equalities
339   /// specified by the value map.
340   ///
341   /// Returns failure if the composition fails (when vMap is a semi-affine map).
342   /// The vMap's operand Value's are used to look up the right positions in
343   /// the FlatAffineConstraints with which to associate. Every operand of vMap
344   /// should have a matching dim/symbol column in this constraint system (with
345   /// the same associated Value).
346   LogicalResult composeMap(const AffineValueMap *vMap);
347 
348   /// Projects out the variable that is associate with Value.
349   void projectOut(Value val);
350   using IntegerPolyhedron::projectOut;
351 
352   /// Changes all symbol variables which are loop IVs to dim variables.
353   void convertLoopIVSymbolsToDims();
354 
355   /// Updates the constraints to be the smallest bounding (enclosing) box that
356   /// contains the points of `this` set and that of `other`, with the symbols
357   /// being treated specially. For each of the dimensions, the min of the lower
358   /// bounds (symbolic) and the max of the upper bounds (symbolic) is computed
359   /// to determine such a bounding box. `other` is expected to have the same
360   /// dimensional variables as this constraint system (in the same order).
361   ///
362   /// E.g.:
363   /// 1) this   = {0 <= d0 <= 127},
364   ///    other  = {16 <= d0 <= 192},
365   ///    output = {0 <= d0 <= 192}
366   /// 2) this   = {s0 + 5 <= d0 <= s0 + 20},
367   ///    other  = {s0 + 1 <= d0 <= s0 + 9},
368   ///    output = {s0 + 1 <= d0 <= s0 + 20}
369   /// 3) this   = {0 <= d0 <= 5, 1 <= d1 <= 9}
370   ///    other  = {2 <= d0 <= 6, 5 <= d1 <= 15},
371   ///    output = {0 <= d0 <= 6, 1 <= d1 <= 15}
372   LogicalResult unionBoundingBox(const FlatAffineValueConstraints &other);
373   using IntegerPolyhedron::unionBoundingBox;
374 
375   /// Merge and align the variables of `this` and `other` starting at
376   /// `offset`, so that both constraint systems get the union of the contained
377   /// variables that is dimension-wise and symbol-wise unique; both
378   /// constraint systems are updated so that they have the union of all
379   /// variables, with `this`'s original variables appearing first followed
380   /// by any of `other`'s variables that didn't appear in `this`. Local
381   /// variables in `other` that have the same division representation as local
382   /// variables in `this` are merged into one.
383   //  E.g.: Input: `this`  has (%i, %j) [%M, %N]
384   //               `other` has (%k, %j) [%P, %N, %M]
385   //        Output: both `this`, `other` have (%i, %j, %k) [%M, %N, %P]
386   //
387   void mergeAndAlignVarsWithOther(unsigned offset,
388                                   FlatAffineValueConstraints *other);
389 
390   /// Returns true if this constraint system and `other` are in the same
391   /// space, i.e., if they are associated with the same set of variables,
392   /// appearing in the same order. Returns false otherwise.
393   bool areVarsAlignedWithOther(const FlatAffineValueConstraints &other);
394 
395   /// Replaces the contents of this FlatAffineValueConstraints with `other`.
396   void clearAndCopyFrom(const IntegerRelation &other) override;
397 
398   /// Returns the Value associated with the pos^th variable. Asserts if
399   /// no Value variable was associated.
getValue(unsigned pos)400   inline Value getValue(unsigned pos) const {
401     assert(pos < getNumDimAndSymbolVars() && "Invalid position");
402     assert(hasValue(pos) && "variable's Value not set");
403     return values[pos].value();
404   }
405 
406   /// Returns true if the pos^th variable has an associated Value.
hasValue(unsigned pos)407   inline bool hasValue(unsigned pos) const {
408     assert(pos < getNumDimAndSymbolVars() && "Invalid position");
409     return values[pos].has_value();
410   }
411 
412   /// Returns true if at least one variable has an associated Value.
413   bool hasValues() const;
414 
415   /// Returns the Values associated with variables in range [start, end).
416   /// Asserts if no Value was associated with one of these variables.
getValues(unsigned start,unsigned end,SmallVectorImpl<Value> * values)417   inline void getValues(unsigned start, unsigned end,
418                         SmallVectorImpl<Value> *values) const {
419     assert(end <= getNumDimAndSymbolVars() && "invalid end position");
420     assert(start <= end && "invalid start position");
421     values->clear();
422     values->reserve(end - start);
423     for (unsigned i = start; i < end; i++)
424       values->push_back(getValue(i));
425   }
getAllValues(SmallVectorImpl<Value> * values)426   inline void getAllValues(SmallVectorImpl<Value> *values) const {
427     getValues(0, getNumDimAndSymbolVars(), values);
428   }
429 
getMaybeValues()430   inline ArrayRef<Optional<Value>> getMaybeValues() const {
431     return {values.data(), values.size()};
432   }
433 
434   inline ArrayRef<Optional<Value>>
getMaybeValues(presburger::VarKind kind)435   getMaybeValues(presburger::VarKind kind) const {
436     assert(kind != VarKind::Local &&
437            "Local variables do not have any value attached to them.");
438     return {values.data() + getVarKindOffset(kind), getNumVarKind(kind)};
439   }
440 
441   /// Sets the Value associated with the pos^th variable.
setValue(unsigned pos,Value val)442   inline void setValue(unsigned pos, Value val) {
443     assert(pos < getNumDimAndSymbolVars() && "invalid var position");
444     values[pos] = val;
445   }
446 
447   /// Sets the Values associated with the variables in the range [start, end).
448   /// The range must contain only dim and symbol variables.
setValues(unsigned start,unsigned end,ArrayRef<Value> values)449   void setValues(unsigned start, unsigned end, ArrayRef<Value> values) {
450     assert(end <= getNumVars() && "invalid end position");
451     assert(start <= end && "invalid start position");
452     assert(values.size() == end - start &&
453            "value should be provided for each variable in the range.");
454     for (unsigned i = start; i < end; ++i)
455       setValue(i, values[i - start]);
456   }
457 
458   /// Merge and align symbols of `this` and `other` such that both get union of
459   /// of symbols that are unique. Symbols in `this` and `other` should be
460   /// unique. Symbols with Value as `None` are considered to be inequal to all
461   /// other symbols.
462   void mergeSymbolVars(FlatAffineValueConstraints &other);
463 
464 protected:
465   using VarKind = presburger::VarKind;
466 
467   /// Returns false if the fields corresponding to various variable counts, or
468   /// equality/inequality buffer sizes aren't consistent; true otherwise. This
469   /// is meant to be used within an assert internally.
470   bool hasConsistentState() const override;
471 
472   /// Given an affine map that is aligned with this constraint system:
473   /// * Flatten the map.
474   /// * Add newly introduced local columns at the beginning of this constraint
475   ///   system (local column pos 0).
476   /// * Add equalities that define the new local columns to this constraint
477   ///   system.
478   /// * Return the flattened expressions via `flattenedExprs`.
479   ///
480   /// Note: This is a shared helper function of `addLowerOrUpperBound` and
481   ///       `composeMatchingMap`.
482   LogicalResult flattenAlignedMapAndMergeLocals(
483       AffineMap map, std::vector<SmallVector<int64_t, 8>> *flattenedExprs);
484 
485   /// Eliminates the variable at the specified position using Fourier-Motzkin
486   /// variable elimination, but uses Gaussian elimination if there is an
487   /// equality involving that variable. If the result of the elimination is
488   /// integer exact, `*isResultIntegerExact` is set to true. If `darkShadow` is
489   /// set to true, a potential under approximation (subset) of the rational
490   /// shadow / exact integer shadow is computed.
491   // See implementation comments for more details.
492   void fourierMotzkinEliminate(unsigned pos, bool darkShadow = false,
493                                bool *isResultIntegerExact = nullptr) override;
494 
495   /// Prints the number of constraints, dimensions, symbols and locals in the
496   /// FlatAffineConstraints. Also, prints for each variable whether there is
497   /// an SSA Value attached to it.
498   void printSpace(raw_ostream &os) const override;
499 
500   /// Values corresponding to the (column) non-local variables of this
501   /// constraint system appearing in the order the variables correspond to
502   /// columns. Variables that aren't associated with any Value are set to
503   /// None.
504   SmallVector<Optional<Value>, 8> values;
505 };
506 
507 /// A FlatAffineRelation represents a set of ordered pairs (domain -> range)
508 /// where "domain" and "range" are tuples of variables. The relation is
509 /// represented as a FlatAffineValueConstraints with separation of dimension
510 /// variables into domain and  range. The variables are stored as:
511 /// [domainVars, rangeVars, symbolVars, localVars, constant].
512 class FlatAffineRelation : public FlatAffineValueConstraints {
513 public:
514   FlatAffineRelation(unsigned numReservedInequalities,
515                      unsigned numReservedEqualities, unsigned numReservedCols,
516                      unsigned numDomainDims, unsigned numRangeDims,
517                      unsigned numSymbols, unsigned numLocals,
518                      ArrayRef<Optional<Value>> valArgs = {})
519       : FlatAffineValueConstraints(
520             numReservedInequalities, numReservedEqualities, numReservedCols,
521             numDomainDims + numRangeDims, numSymbols, numLocals, valArgs),
522         numDomainDims(numDomainDims), numRangeDims(numRangeDims) {}
523 
524   FlatAffineRelation(unsigned numDomainDims = 0, unsigned numRangeDims = 0,
525                      unsigned numSymbols = 0, unsigned numLocals = 0)
526       : FlatAffineValueConstraints(numDomainDims + numRangeDims, numSymbols,
527                                    numLocals),
528         numDomainDims(numDomainDims), numRangeDims(numRangeDims) {}
529 
FlatAffineRelation(unsigned numDomainDims,unsigned numRangeDims,FlatAffineValueConstraints & fac)530   FlatAffineRelation(unsigned numDomainDims, unsigned numRangeDims,
531                      FlatAffineValueConstraints &fac)
532       : FlatAffineValueConstraints(fac), numDomainDims(numDomainDims),
533         numRangeDims(numRangeDims) {}
534 
FlatAffineRelation(unsigned numDomainDims,unsigned numRangeDims,IntegerPolyhedron & fac)535   FlatAffineRelation(unsigned numDomainDims, unsigned numRangeDims,
536                      IntegerPolyhedron &fac)
537       : FlatAffineValueConstraints(fac), numDomainDims(numDomainDims),
538         numRangeDims(numRangeDims) {}
539 
540   /// Returns a set corresponding to the domain/range of the affine relation.
541   FlatAffineValueConstraints getDomainSet() const;
542   FlatAffineValueConstraints getRangeSet() const;
543 
544   /// Returns the number of variables corresponding to domain/range of
545   /// relation.
getNumDomainDims()546   inline unsigned getNumDomainDims() const { return numDomainDims; }
getNumRangeDims()547   inline unsigned getNumRangeDims() const { return numRangeDims; }
548 
549   /// Given affine relation `other: (domainOther -> rangeOther)`, this operation
550   /// takes the composition of `other` on `this: (domainThis -> rangeThis)`.
551   /// The resulting relation represents tuples of the form: `domainOther ->
552   /// rangeThis`.
553   void compose(const FlatAffineRelation &other);
554 
555   /// Swap domain and range of the relation.
556   /// `(domain -> range)` is converted to `(range -> domain)`.
557   void inverse();
558 
559   /// Insert `num` variables of the specified kind after the `pos` variable
560   /// of that kind. The coefficient columns corresponding to the added
561   /// variables are initialized to zero.
562   void insertDomainVar(unsigned pos, unsigned num = 1);
563   void insertRangeVar(unsigned pos, unsigned num = 1);
564 
565   /// Append `num` variables of the specified kind after the last variable
566   /// of that kind. The coefficient columns corresponding to the added
567   /// variables are initialized to zero.
568   void appendDomainVar(unsigned num = 1);
569   void appendRangeVar(unsigned num = 1);
570 
571   /// Removes variables in the column range [varStart, varLimit), and copies any
572   /// remaining valid data into place, updates member variables, and resizes
573   /// arrays as needed.
574   void removeVarRange(VarKind kind, unsigned varStart,
575                       unsigned varLimit) override;
576   using IntegerRelation::removeVarRange;
577 
578 protected:
579   // Number of dimension variables corresponding to domain variables.
580   unsigned numDomainDims;
581 
582   // Number of dimension variables corresponding to range variables.
583   unsigned numRangeDims;
584 };
585 
586 /// Flattens 'expr' into 'flattenedExpr', which contains the coefficients of the
587 /// dimensions, symbols, and additional variables that represent floor divisions
588 /// of dimensions, symbols, and in turn other floor divisions.  Returns failure
589 /// if 'expr' could not be flattened (i.e., semi-affine is not yet handled).
590 /// 'cst' contains constraints that connect newly introduced local variables
591 /// to existing dimensional and symbolic variables. See documentation for
592 /// AffineExprFlattener on how mod's and div's are flattened.
593 LogicalResult getFlattenedAffineExpr(AffineExpr expr, unsigned numDims,
594                                      unsigned numSymbols,
595                                      SmallVectorImpl<int64_t> *flattenedExpr,
596                                      FlatAffineValueConstraints *cst = nullptr);
597 
598 /// Flattens the result expressions of the map to their corresponding flattened
599 /// forms and set in 'flattenedExprs'. Returns failure if any expression in the
600 /// map could not be flattened (i.e., semi-affine is not yet handled). 'cst'
601 /// contains constraints that connect newly introduced local variables to
602 /// existing dimensional and / symbolic variables. See documentation for
603 /// AffineExprFlattener on how mod's and div's are flattened. For all affine
604 /// expressions that share the same operands (like those of an affine map), this
605 /// method should be used instead of repeatedly calling getFlattenedAffineExpr
606 /// since local variables added to deal with div's and mod's will be reused
607 /// across expressions.
608 LogicalResult
609 getFlattenedAffineExprs(AffineMap map,
610                         std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
611                         FlatAffineValueConstraints *cst = nullptr);
612 LogicalResult
613 getFlattenedAffineExprs(IntegerSet set,
614                         std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
615                         FlatAffineValueConstraints *cst = nullptr);
616 
617 /// Re-indexes the dimensions and symbols of an affine map with given `operands`
618 /// values to align with `dims` and `syms` values.
619 ///
620 /// Each dimension/symbol of the map, bound to an operand `o`, is replaced with
621 /// dimension `i`, where `i` is the position of `o` within `dims`. If `o` is not
622 /// in `dims`, replace it with symbol `i`, where `i` is the position of `o`
623 /// within `syms`. If `o` is not in `syms` either, replace it with a new symbol.
624 ///
625 /// Note: If a value appears multiple times as a dimension/symbol (or both), all
626 /// corresponding dim/sym expressions are replaced with the first dimension
627 /// bound to that value (or first symbol if no such dimension exists).
628 ///
629 /// The resulting affine map has `dims.size()` many dimensions and at least
630 /// `syms.size()` many symbols.
631 ///
632 /// The SSA values of the symbols of the resulting map are optionally returned
633 /// via `newSyms`. This is a concatenation of `syms` with the SSA values of the
634 /// newly added symbols.
635 ///
636 /// Note: As part of this re-indexing, dimensions may turn into symbols, or vice
637 /// versa.
638 AffineMap alignAffineMapWithValues(AffineMap map, ValueRange operands,
639                                    ValueRange dims, ValueRange syms,
640                                    SmallVector<Value> *newSyms = nullptr);
641 
642 /// Builds a relation from the given AffineMap/AffineValueMap `map`, containing
643 /// all pairs of the form `operands -> result` that satisfy `map`. `rel` is set
644 /// to the relation built. For example, give the AffineMap:
645 ///
646 ///   (d0, d1)[s0] -> (d0 + s0, d0 - s0)
647 ///
648 /// the resulting relation formed is:
649 ///
650 ///   (d0, d1) -> (r1, r2)
651 ///   [d0  d1  r1  r2  s0  const]
652 ///    1   0   -1   0  1     0     = 0
653 ///    0   1    0  -1  -1    0     = 0
654 ///
655 /// For AffineValueMap, the domain and symbols have Value set corresponding to
656 /// the Value in `map`. Returns failure if the AffineMap could not be flattened
657 /// (i.e., semi-affine is not yet handled).
658 LogicalResult getRelationFromMap(AffineMap &map, FlatAffineRelation &rel);
659 LogicalResult getRelationFromMap(const AffineValueMap &map,
660                                  FlatAffineRelation &rel);
661 
662 } // namespace mlir.
663 
664 #endif // MLIR_DIALECT_AFFINE_ANALYSIS_AFFINESTRUCTURES_H
665