/home/arjun/llvm-project/mlir/include/mlir/Analysis/AffineStructures.h
Line | Count | Source (jump to first uncovered line) |
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_ANALYSIS_AFFINE_STRUCTURES_H |
14 | | #define MLIR_ANALYSIS_AFFINE_STRUCTURES_H |
15 | | |
16 | | #include "mlir/IR/AffineExpr.h" |
17 | | #include "mlir/IR/OpDefinition.h" |
18 | | #include "mlir/Support/LogicalResult.h" |
19 | | |
20 | | namespace mlir { |
21 | | |
22 | | class AffineCondition; |
23 | | class AffineForOp; |
24 | | class AffineMap; |
25 | | class AffineValueMap; |
26 | | class IntegerSet; |
27 | | class MLIRContext; |
28 | | class Value; |
29 | | class MemRefType; |
30 | | struct MutableAffineMap; |
31 | | |
32 | | /// A flat list of affine equalities and inequalities in the form. |
33 | | /// Inequality: c_0*x_0 + c_1*x_1 + .... + c_{n-1}*x_{n-1} >= 0 |
34 | | /// Equality: c_0*x_0 + c_1*x_1 + .... + c_{n-1}*x_{n-1} == 0 |
35 | | /// |
36 | | /// FlatAffineConstraints stores coefficients in a contiguous buffer (one buffer |
37 | | /// for equalities and one for inequalities). The size of each buffer is |
38 | | /// numReservedCols * number of inequalities (or equalities). The reserved size |
39 | | /// is numReservedCols * numReservedInequalities (or numReservedEqualities). A |
40 | | /// coefficient (r, c) lives at the location numReservedCols * r + c in the |
41 | | /// buffer. The extra space between getNumCols() and numReservedCols exists to |
42 | | /// prevent frequent movement of data when adding columns, especially at the |
43 | | /// end. |
44 | | /// |
45 | | /// The identifiers x_0, x_1, ... appear in the order: dimensional identifiers, |
46 | | /// symbolic identifiers, and local identifiers. The local identifiers |
47 | | /// correspond to local/internal variables created when converting from |
48 | | /// AffineExpr's containing mod's and div's; they are thus needed to increase |
49 | | /// representational power. Each local identifier is always (by construction) a |
50 | | /// floordiv of a pure add/mul affine function of dimensional, symbolic, and |
51 | | /// other local identifiers, in a non-mutually recursive way. Hence, every local |
52 | | /// identifier can ultimately always be recovered as an affine function of |
53 | | /// dimensional and symbolic identifiers (involving floordiv's); note however |
54 | | /// that some floordiv combinations are converted to mod's by AffineExpr |
55 | | /// construction. |
56 | | /// |
57 | | class FlatAffineConstraints { |
58 | | public: |
59 | | enum IdKind { Dimension, Symbol, Local }; |
60 | | |
61 | | /// Constructs a constraint system reserving memory for the specified number |
62 | | /// of constraints and identifiers.. |
63 | | FlatAffineConstraints(unsigned numReservedInequalities, |
64 | | unsigned numReservedEqualities, |
65 | | unsigned numReservedCols, unsigned numDims = 0, |
66 | | unsigned numSymbols = 0, unsigned numLocals = 0, |
67 | | ArrayRef<Optional<Value>> idArgs = {}) |
68 | | : numReservedCols(numReservedCols), numDims(numDims), |
69 | 31 | numSymbols(numSymbols) { |
70 | 31 | assert(numReservedCols >= numDims + numSymbols + 1); |
71 | 31 | assert(idArgs.empty() || idArgs.size() == numDims + numSymbols + numLocals); |
72 | 31 | equalities.reserve(numReservedCols * numReservedEqualities); |
73 | 31 | inequalities.reserve(numReservedCols * numReservedInequalities); |
74 | 31 | numIds = numDims + numSymbols + numLocals; |
75 | 31 | ids.reserve(numReservedCols); |
76 | 31 | if (idArgs.empty()) |
77 | 31 | ids.resize(numIds, None); |
78 | 0 | else |
79 | 0 | ids.append(idArgs.begin(), idArgs.end()); |
80 | 31 | } |
81 | | |
82 | | /// Constructs a constraint system with the specified number of |
83 | | /// dimensions and symbols. |
84 | | FlatAffineConstraints(unsigned numDims = 0, unsigned numSymbols = 0, |
85 | | unsigned numLocals = 0, |
86 | | ArrayRef<Optional<Value>> idArgs = {}) |
87 | | : numReservedCols(numDims + numSymbols + numLocals + 1), numDims(numDims), |
88 | 0 | numSymbols(numSymbols) { |
89 | 0 | assert(numReservedCols >= numDims + numSymbols + 1); |
90 | 0 | assert(idArgs.empty() || idArgs.size() == numDims + numSymbols + numLocals); |
91 | 0 | numIds = numDims + numSymbols + numLocals; |
92 | 0 | ids.reserve(numIds); |
93 | 0 | if (idArgs.empty()) |
94 | 0 | ids.resize(numIds, None); |
95 | 0 | else |
96 | 0 | ids.append(idArgs.begin(), idArgs.end()); |
97 | 0 | } |
98 | | |
99 | | /// Create a flat affine constraint system from an AffineValueMap or a list of |
100 | | /// these. The constructed system will only include equalities. |
101 | | // TODO(bondhugula) |
102 | | explicit FlatAffineConstraints(const AffineValueMap &avm); |
103 | | explicit FlatAffineConstraints(ArrayRef<const AffineValueMap *> avmRef); |
104 | | |
105 | | /// Creates an affine constraint system from an IntegerSet. |
106 | | explicit FlatAffineConstraints(IntegerSet set); |
107 | | |
108 | | FlatAffineConstraints(const FlatAffineConstraints &other); |
109 | | |
110 | | FlatAffineConstraints(ArrayRef<const AffineValueMap *> avmRef, |
111 | | IntegerSet set); |
112 | | |
113 | | FlatAffineConstraints(const MutableAffineMap &map); |
114 | | |
115 | 31 | ~FlatAffineConstraints() {} |
116 | | |
117 | | // Clears any existing data and reserves memory for the specified constraints. |
118 | | void reset(unsigned numReservedInequalities, unsigned numReservedEqualities, |
119 | | unsigned numReservedCols, unsigned numDims, unsigned numSymbols, |
120 | | unsigned numLocals = 0, ArrayRef<Value> idArgs = {}); |
121 | | |
122 | | void reset(unsigned numDims = 0, unsigned numSymbols = 0, |
123 | | unsigned numLocals = 0, ArrayRef<Value> idArgs = {}); |
124 | | |
125 | | /// Appends constraints from 'other' into this. This is equivalent to an |
126 | | /// intersection with no simplification of any sort attempted. |
127 | | void append(const FlatAffineConstraints &other); |
128 | | |
129 | | /// Checks for emptiness by performing variable elimination on all |
130 | | /// identifiers, running the GCD test on each equality constraint, and |
131 | | /// checking for invalid constraints. Returns true if the GCD test fails for |
132 | | /// any equality, or if any invalid constraints are discovered on any row. |
133 | | /// Returns false otherwise. |
134 | | bool isEmpty() const; |
135 | | |
136 | | /// Runs the GCD test on all equality constraints. Returns 'true' if this test |
137 | | /// fails on any equality. Returns 'false' otherwise. |
138 | | /// This test can be used to disprove the existence of a solution. If it |
139 | | /// returns true, no integer solution to the equality constraints can exist. |
140 | | bool isEmptyByGCDTest() const; |
141 | | |
142 | | /// Runs the GCD test heuristic. If it proves inconclusive, falls back to |
143 | | /// generalized basis reduction if the set is bounded. |
144 | | /// |
145 | | /// Returns true if the set of constraints is found to have no solution, |
146 | | /// false if a solution exists or all tests were inconclusive. |
147 | | bool isIntegerEmpty() const; |
148 | | |
149 | | /// Find a sample point satisfying the constraints. This uses a branch and |
150 | | /// bound algorithm with generalized basis reduction, which always works if |
151 | | /// the set is bounded. This should not be called for unbounded sets. |
152 | | /// |
153 | | /// Returns such a point if one exists, or an empty Optional otherwise. |
154 | | Optional<SmallVector<int64_t, 8>> findIntegerSample() const; |
155 | | |
156 | | // Clones this object. |
157 | | std::unique_ptr<FlatAffineConstraints> clone() const; |
158 | | |
159 | | /// Returns the value at the specified equality row and column. |
160 | 30 | inline int64_t atEq(unsigned i, unsigned j) const { |
161 | 30 | return equalities[i * numReservedCols + j]; |
162 | 30 | } |
163 | 0 | inline int64_t &atEq(unsigned i, unsigned j) { |
164 | 0 | return equalities[i * numReservedCols + j]; |
165 | 0 | } |
166 | | |
167 | 0 | inline int64_t atIneq(unsigned i, unsigned j) const { |
168 | 0 | return inequalities[i * numReservedCols + j]; |
169 | 0 | } |
170 | | |
171 | 0 | inline int64_t &atIneq(unsigned i, unsigned j) { |
172 | 0 | return inequalities[i * numReservedCols + j]; |
173 | 0 | } |
174 | | |
175 | | /// Returns the number of columns in the constraint system. |
176 | 293 | inline unsigned getNumCols() const { return numIds + 1; } |
177 | | |
178 | 62 | inline unsigned getNumEqualities() const { |
179 | 62 | assert(equalities.size() % numReservedCols == 0 && |
180 | 62 | "inconsistent equality buffer size"); |
181 | 62 | return equalities.size() / numReservedCols; |
182 | 62 | } |
183 | | |
184 | 97 | inline unsigned getNumInequalities() const { |
185 | 97 | assert(inequalities.size() % numReservedCols == 0 && |
186 | 97 | "inconsistent inequality buffer size"); |
187 | 97 | return inequalities.size() / numReservedCols; |
188 | 97 | } |
189 | | |
190 | 0 | inline unsigned getNumReservedEqualities() const { |
191 | 0 | return equalities.capacity() / numReservedCols; |
192 | 0 | } |
193 | | |
194 | 0 | inline unsigned getNumReservedInequalities() const { |
195 | 0 | return inequalities.capacity() / numReservedCols; |
196 | 0 | } |
197 | | |
198 | 21 | inline ArrayRef<int64_t> getEquality(unsigned idx) const { |
199 | 21 | return ArrayRef<int64_t>(&equalities[idx * numReservedCols], getNumCols()); |
200 | 21 | } |
201 | | |
202 | 148 | inline ArrayRef<int64_t> getInequality(unsigned idx) const { |
203 | 148 | return ArrayRef<int64_t>(&inequalities[idx * numReservedCols], |
204 | 148 | getNumCols()); |
205 | 148 | } |
206 | | |
207 | | /// Adds constraints (lower and upper bounds) for the specified 'affine.for' |
208 | | /// operation's Value using IR information stored in its bound maps. The |
209 | | /// right identifier is first looked up using forOp's Value. Asserts if the |
210 | | /// Value corresponding to the 'affine.for' operation isn't found in the |
211 | | /// constraint system. Returns failure for the yet unimplemented/unsupported |
212 | | /// cases. Any new identifiers that are found in the bound operands of the |
213 | | /// 'affine.for' operation are added as trailing identifiers (either |
214 | | /// dimensional or symbolic depending on whether the operand is a valid |
215 | | /// symbol). |
216 | | // TODO(bondhugula): add support for non-unit strides. |
217 | | LogicalResult addAffineForOpDomain(AffineForOp forOp); |
218 | | |
219 | | /// Adds a lower or an upper bound for the identifier at the specified |
220 | | /// position with constraints being drawn from the specified bound map and |
221 | | /// operands. If `eq` is true, add a single equality equal to the bound map's |
222 | | /// first result expr. |
223 | | LogicalResult addLowerOrUpperBound(unsigned pos, AffineMap boundMap, |
224 | | ValueRange operands, bool eq, |
225 | | bool lower = true); |
226 | | |
227 | | /// Returns the bound for the identifier at `pos` from the inequality at |
228 | | /// `ineqPos` as a 1-d affine value map (affine map + operands). The returned |
229 | | /// affine value map can either be a lower bound or an upper bound depending |
230 | | /// on the sign of atIneq(ineqPos, pos). Asserts if the row at `ineqPos` does |
231 | | /// not involve the `pos`th identifier. |
232 | | void getIneqAsAffineValueMap(unsigned pos, unsigned ineqPos, |
233 | | AffineValueMap &vmap, |
234 | | MLIRContext *context) const; |
235 | | |
236 | | /// Returns the constraint system as an integer set. Returns a null integer |
237 | | /// set if the system has no constraints, or if an integer set couldn't be |
238 | | /// constructed as a result of a local variable's explicit representation not |
239 | | /// being known and such a local variable appearing in any of the constraints. |
240 | | IntegerSet getAsIntegerSet(MLIRContext *context) const; |
241 | | |
242 | | /// Computes the lower and upper bounds of the first 'num' dimensional |
243 | | /// identifiers (starting at 'offset') as an affine map of the remaining |
244 | | /// identifiers (dimensional and symbolic). This method is able to detect |
245 | | /// identifiers as floordiv's and mod's of affine expressions of other |
246 | | /// identifiers with respect to (positive) constants. Sets bound map to a |
247 | | /// null AffineMap if such a bound can't be found (or yet unimplemented). |
248 | | void getSliceBounds(unsigned offset, unsigned num, MLIRContext *context, |
249 | | SmallVectorImpl<AffineMap> *lbMaps, |
250 | | SmallVectorImpl<AffineMap> *ubMaps); |
251 | | |
252 | | /// Adds slice lower bounds represented by lower bounds in 'lbMaps' and upper |
253 | | /// bounds in 'ubMaps' to each identifier 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 | | // Adds an inequality (>= 0) from the coefficients specified in inEq. |
264 | | void addInequality(ArrayRef<int64_t> inEq); |
265 | | // Adds an equality from the coefficients specified in eq. |
266 | | void addEquality(ArrayRef<int64_t> eq); |
267 | | |
268 | | /// Adds a constant lower bound constraint for the specified identifier. |
269 | | void addConstantLowerBound(unsigned pos, int64_t lb); |
270 | | /// Adds a constant upper bound constraint for the specified identifier. |
271 | | void addConstantUpperBound(unsigned pos, int64_t ub); |
272 | | |
273 | | /// Adds a new local identifier as the floordiv of an affine function of other |
274 | | /// identifiers, the coefficients of which are provided in 'dividend' and with |
275 | | /// respect to a positive constant 'divisor'. Two constraints are added to the |
276 | | /// system to capture equivalence with the floordiv: |
277 | | /// q = dividend floordiv c <=> c*q <= dividend <= c*q + c - 1. |
278 | | void addLocalFloorDiv(ArrayRef<int64_t> dividend, int64_t divisor); |
279 | | |
280 | | /// Adds a constant lower bound constraint for the specified expression. |
281 | | void addConstantLowerBound(ArrayRef<int64_t> expr, int64_t lb); |
282 | | /// Adds a constant upper bound constraint for the specified expression. |
283 | | void addConstantUpperBound(ArrayRef<int64_t> expr, int64_t ub); |
284 | | |
285 | | /// Sets the identifier at the specified position to a constant. |
286 | | void setIdToConstant(unsigned pos, int64_t val); |
287 | | |
288 | | /// Sets the identifier corresponding to the specified Value id to a |
289 | | /// constant. Asserts if the 'id' is not found. |
290 | | void setIdToConstant(Value id, int64_t val); |
291 | | |
292 | | /// Looks up the position of the identifier with the specified Value. Returns |
293 | | /// true if found (false otherwise). `pos' is set to the (column) position of |
294 | | /// the identifier. |
295 | | bool findId(Value id, unsigned *pos) const; |
296 | | |
297 | | /// Returns true if an identifier with the specified Value exists, false |
298 | | /// otherwise. |
299 | | bool containsId(Value id) const; |
300 | | |
301 | | // Add identifiers of the specified kind - specified positions are relative to |
302 | | // the kind of identifier. The coefficient column corresponding to the added |
303 | | // identifier is initialized to zero. 'id' is the Value corresponding to the |
304 | | // identifier that can optionally be provided. |
305 | | void addDimId(unsigned pos, Value id = nullptr); |
306 | | void addSymbolId(unsigned pos, Value id = nullptr); |
307 | | void addLocalId(unsigned pos); |
308 | | void addId(IdKind kind, unsigned pos, Value id = nullptr); |
309 | | |
310 | | /// Add the specified values as a dim or symbol id depending on its nature, if |
311 | | /// it already doesn't exist in the system. `id' has to be either a terminal |
312 | | /// symbol or a loop IV, i.e., it cannot be the result affine.apply of any |
313 | | /// symbols or loop IVs. The identifier is added to the end of the existing |
314 | | /// dims or symbols. Additional information on the identifier is extracted |
315 | | /// from the IR and added to the constraint system. |
316 | | void addInductionVarOrTerminalSymbol(Value id); |
317 | | |
318 | | /// Composes the affine value map with this FlatAffineConstrains, adding the |
319 | | /// results of the map as dimensions at the front [0, vMap->getNumResults()) |
320 | | /// and with the dimensions set to the equalities specified by the value map. |
321 | | /// Returns failure if the composition fails (when vMap is a semi-affine map). |
322 | | /// The vMap's operand Value's are used to look up the right positions in |
323 | | /// the FlatAffineConstraints with which to associate. The dimensional and |
324 | | /// symbolic operands of vMap should match 1:1 (in the same order) with those |
325 | | /// of this constraint system, but the latter could have additional trailing |
326 | | /// operands. |
327 | | LogicalResult composeMap(const AffineValueMap *vMap); |
328 | | |
329 | | /// Composes an affine map whose dimensions match one to one to the |
330 | | /// dimensions of this FlatAffineConstraints. The results of the map 'other' |
331 | | /// are added as the leading dimensions of this constraint system. Returns |
332 | | /// failure if 'other' is a semi-affine map. |
333 | | LogicalResult composeMatchingMap(AffineMap other); |
334 | | |
335 | | /// Projects out (aka eliminates) 'num' identifiers starting at position |
336 | | /// 'pos'. The resulting constraint system is the shadow along the dimensions |
337 | | /// that still exist. This method may not always be integer exact. |
338 | | // TODO(bondhugula): deal with integer exactness when necessary - can return a |
339 | | // value to mark exactness for example. |
340 | | void projectOut(unsigned pos, unsigned num); |
341 | 0 | inline void projectOut(unsigned pos) { return projectOut(pos, 1); } |
342 | | |
343 | | /// Projects out the identifier that is associate with Value . |
344 | | void projectOut(Value id); |
345 | | |
346 | | /// Removes the specified identifier from the system. |
347 | | void removeId(unsigned pos); |
348 | | |
349 | | void removeEquality(unsigned pos); |
350 | | void removeInequality(unsigned pos); |
351 | | |
352 | | /// Changes the partition between dimensions and symbols. Depending on the new |
353 | | /// symbol count, either a chunk of trailing dimensional identifiers becomes |
354 | | /// symbols, or some of the leading symbols become dimensions. |
355 | | void setDimSymbolSeparation(unsigned newSymbolCount); |
356 | | |
357 | | /// Changes all symbol identifiers which are loop IVs to dim identifiers. |
358 | | void convertLoopIVSymbolsToDims(); |
359 | | |
360 | | /// Sets the specified identifier to a constant and removes it. |
361 | | void setAndEliminate(unsigned pos, int64_t constVal); |
362 | | |
363 | | /// Tries to fold the specified identifier to a constant using a trivial |
364 | | /// equality detection; if successful, the constant is substituted for the |
365 | | /// identifier everywhere in the constraint system and then removed from the |
366 | | /// system. |
367 | | LogicalResult constantFoldId(unsigned pos); |
368 | | |
369 | | /// This method calls constantFoldId for the specified range of identifiers, |
370 | | /// 'num' identifiers starting at position 'pos'. |
371 | | void constantFoldIdRange(unsigned pos, unsigned num); |
372 | | |
373 | | /// Updates the constraints to be the smallest bounding (enclosing) box that |
374 | | /// contains the points of 'this' set and that of 'other', with the symbols |
375 | | /// being treated specially. For each of the dimensions, the min of the lower |
376 | | /// bounds (symbolic) and the max of the upper bounds (symbolic) is computed |
377 | | /// to determine such a bounding box. `other' is expected to have the same |
378 | | /// dimensional identifiers as this constraint system (in the same order). |
379 | | /// |
380 | | /// Eg: if 'this' is {0 <= d0 <= 127}, 'other' is {16 <= d0 <= 192}, the |
381 | | /// output is {0 <= d0 <= 192}. |
382 | | /// 2) 'this' = {s0 + 5 <= d0 <= s0 + 20}, 'other' is {s0 + 1 <= d0 <= s0 + |
383 | | /// 9}, output = {s0 + 1 <= d0 <= s0 + 20}. |
384 | | /// 3) 'this' = {0 <= d0 <= 5, 1 <= d1 <= 9}, 'other' = {2 <= d0 <= 6, 5 <= d1 |
385 | | /// <= 15}, output = {0 <= d0 <= 6, 1 <= d1 <= 15}. |
386 | | LogicalResult unionBoundingBox(const FlatAffineConstraints &other); |
387 | | |
388 | | /// Returns 'true' if this constraint system and 'other' are in the same |
389 | | /// space, i.e., if they are associated with the same set of identifiers, |
390 | | /// appearing in the same order. Returns 'false' otherwise. |
391 | | bool areIdsAlignedWithOther(const FlatAffineConstraints &other); |
392 | | |
393 | | /// Merge and align the identifiers of 'this' and 'other' starting at |
394 | | /// 'offset', so that both constraint systems get the union of the contained |
395 | | /// identifiers that is dimension-wise and symbol-wise unique; both |
396 | | /// constraint systems are updated so that they have the union of all |
397 | | /// identifiers, with this's original identifiers appearing first followed by |
398 | | /// any of other's identifiers that didn't appear in 'this'. Local |
399 | | /// identifiers of each system are by design separate/local and are placed |
400 | | /// one after other (this's followed by other's). |
401 | | // Eg: Input: 'this' has ((%i %j) [%M %N]) |
402 | | // 'other' has (%k, %j) [%P, %N, %M]) |
403 | | // Output: both 'this', 'other' have (%i, %j, %k) [%M, %N, %P] |
404 | | // |
405 | | void mergeAndAlignIdsWithOther(unsigned offset, FlatAffineConstraints *other); |
406 | | |
407 | 0 | unsigned getNumConstraints() const { |
408 | 0 | return getNumInequalities() + getNumEqualities(); |
409 | 0 | } |
410 | 37 | inline unsigned getNumIds() const { return numIds; } |
411 | 0 | inline unsigned getNumDimIds() const { return numDims; } |
412 | 0 | inline unsigned getNumSymbolIds() const { return numSymbols; } |
413 | 0 | inline unsigned getNumDimAndSymbolIds() const { return numDims + numSymbols; } |
414 | 0 | inline unsigned getNumLocalIds() const { |
415 | 0 | return numIds - numDims - numSymbols; |
416 | 0 | } |
417 | | |
418 | 0 | inline ArrayRef<Optional<Value>> getIds() const { |
419 | 0 | return {ids.data(), ids.size()}; |
420 | 0 | } |
421 | 0 | inline MutableArrayRef<Optional<Value>> getIds() { |
422 | 0 | return {ids.data(), ids.size()}; |
423 | 0 | } |
424 | | |
425 | | /// Returns the optional Value corresponding to the pos^th identifier. |
426 | 0 | inline Optional<Value> getId(unsigned pos) const { return ids[pos]; } |
427 | 0 | inline Optional<Value> &getId(unsigned pos) { return ids[pos]; } |
428 | | |
429 | | /// Returns the Value associated with the pos^th identifier. Asserts if |
430 | | /// no Value identifier was associated. |
431 | 0 | inline Value getIdValue(unsigned pos) const { |
432 | 0 | assert(ids[pos].hasValue() && "identifier's Value not set"); |
433 | 0 | return ids[pos].getValue(); |
434 | 0 | } |
435 | | |
436 | | /// Returns the Values associated with identifiers in range [start, end). |
437 | | /// Asserts if no Value was associated with one of these identifiers. |
438 | | void getIdValues(unsigned start, unsigned end, |
439 | | SmallVectorImpl<Value> *values) const { |
440 | | assert((start < numIds || start == end) && "invalid start position"); |
441 | | assert(end <= numIds && "invalid end position"); |
442 | | values->clear(); |
443 | | values->reserve(end - start); |
444 | | for (unsigned i = start; i < end; i++) { |
445 | | values->push_back(getIdValue(i)); |
446 | | } |
447 | | } |
448 | 0 | inline void getAllIdValues(SmallVectorImpl<Value> *values) const { |
449 | 0 | getIdValues(0, numIds, values); |
450 | 0 | } |
451 | | |
452 | | /// Sets Value associated with the pos^th identifier. |
453 | 0 | inline void setIdValue(unsigned pos, Value val) { |
454 | 0 | assert(pos < numIds && "invalid id position"); |
455 | 0 | ids[pos] = val; |
456 | 0 | } |
457 | | /// Sets Values associated with identifiers in the range [start, end). |
458 | | void setIdValues(unsigned start, unsigned end, ArrayRef<Value> values) { |
459 | | assert((start < numIds || end == start) && "invalid start position"); |
460 | | assert(end <= numIds && "invalid end position"); |
461 | | assert(values.size() == end - start); |
462 | | for (unsigned i = start; i < end; ++i) |
463 | | ids[i] = values[i - start]; |
464 | | } |
465 | | |
466 | | /// Clears this list of constraints and copies other into it. |
467 | | void clearAndCopyFrom(const FlatAffineConstraints &other); |
468 | | |
469 | | /// Returns the smallest known constant bound for the extent of the specified |
470 | | /// identifier (pos^th), i.e., the smallest known constant that is greater |
471 | | /// than or equal to 'exclusive upper bound' - 'lower bound' of the |
472 | | /// identifier. Returns None if it's not a constant. This method employs |
473 | | /// trivial (low complexity / cost) checks and detection. Symbolic identifiers |
474 | | /// are treated specially, i.e., it looks for constant differences between |
475 | | /// affine expressions involving only the symbolic identifiers. `lb` and |
476 | | /// `ub` (along with the `boundFloorDivisor`) are set to represent the lower |
477 | | /// and upper bound associated with the constant difference: `lb`, `ub` have |
478 | | /// the coefficients, and boundFloorDivisor, their divisor. `minLbPos` and |
479 | | /// `minUbPos` if non-null are set to the position of the constant lower bound |
480 | | /// and upper bound respectively (to the same if they are from an equality). |
481 | | /// Ex: if the lower bound is [(s0 + s2 - 1) floordiv 32] for a system with |
482 | | /// three symbolic identifiers, *lb = [1, 0, 1], lbDivisor = 32. See comments |
483 | | /// at function definition for examples. |
484 | | Optional<int64_t> getConstantBoundOnDimSize( |
485 | | unsigned pos, SmallVectorImpl<int64_t> *lb = nullptr, |
486 | | int64_t *boundFloorDivisor = nullptr, |
487 | | SmallVectorImpl<int64_t> *ub = nullptr, unsigned *minLbPos = nullptr, |
488 | | unsigned *minUbPos = nullptr) const; |
489 | | |
490 | | /// Returns the constant lower bound for the pos^th identifier if there is |
491 | | /// one; None otherwise. |
492 | | Optional<int64_t> getConstantLowerBound(unsigned pos) const; |
493 | | |
494 | | /// Returns the constant upper bound for the pos^th identifier if there is |
495 | | /// one; None otherwise. |
496 | | Optional<int64_t> getConstantUpperBound(unsigned pos) const; |
497 | | |
498 | | /// Gets the lower and upper bound of the `offset` + `pos`th identifier |
499 | | /// treating [0, offset) U [offset + num, symStartPos) as dimensions and |
500 | | /// [symStartPos, getNumDimAndSymbolIds) as symbols, and `pos` lies in |
501 | | /// [0, num). The multi-dimensional maps in the returned pair represent the |
502 | | /// max and min of potentially multiple affine expressions. The upper bound is |
503 | | /// exclusive. `localExprs` holds pre-computed AffineExpr's for all local |
504 | | /// identifiers in the system. |
505 | | std::pair<AffineMap, AffineMap> |
506 | | getLowerAndUpperBound(unsigned pos, unsigned offset, unsigned num, |
507 | | unsigned symStartPos, ArrayRef<AffineExpr> localExprs, |
508 | | MLIRContext *context) const; |
509 | | |
510 | | /// Gather positions of all lower and upper bounds of the identifier at `pos`, |
511 | | /// and optionally any equalities on it. In addition, the bounds are to be |
512 | | /// independent of identifiers in position range [`offset`, `offset` + `num`). |
513 | | void |
514 | | getLowerAndUpperBoundIndices(unsigned pos, |
515 | | SmallVectorImpl<unsigned> *lbIndices, |
516 | | SmallVectorImpl<unsigned> *ubIndices, |
517 | | SmallVectorImpl<unsigned> *eqIndices = nullptr, |
518 | | unsigned offset = 0, unsigned num = 0) const; |
519 | | |
520 | | /// Removes constraints that are independent of (i.e., do not have a |
521 | | /// coefficient for) for identifiers in the range [pos, pos + num). |
522 | | void removeIndependentConstraints(unsigned pos, unsigned num); |
523 | | |
524 | | /// Returns true if the set can be trivially detected as being |
525 | | /// hyper-rectangular on the specified contiguous set of identifiers. |
526 | | bool isHyperRectangular(unsigned pos, unsigned num) const; |
527 | | |
528 | | /// Removes duplicate constraints, trivially true constraints, and constraints |
529 | | /// that can be detected as redundant as a result of differing only in their |
530 | | /// constant term part. A constraint of the form <non-negative constant> >= 0 |
531 | | /// is considered trivially true. This method is a linear time method on the |
532 | | /// constraints, does a single scan, and updates in place. It also normalizes |
533 | | /// constraints by their GCD and performs GCD tightening on inequalities. |
534 | | void removeTrivialRedundancy(); |
535 | | |
536 | | /// A more expensive check to detect redundant inequalities thatn |
537 | | /// removeTrivialRedundancy. |
538 | | void removeRedundantInequalities(); |
539 | | |
540 | | // Removes all equalities and inequalities. |
541 | | void clearConstraints(); |
542 | | |
543 | | void print(raw_ostream &os) const; |
544 | | void dump() const; |
545 | | |
546 | | private: |
547 | | /// Returns false if the fields corresponding to various identifier counts, or |
548 | | /// equality/inequality buffer sizes aren't consistent; true otherwise. This |
549 | | /// is meant to be used within an assert internally. |
550 | | bool hasConsistentState() const; |
551 | | |
552 | | /// Checks all rows of equality/inequality constraints for trivial |
553 | | /// contradictions (for example: 1 == 0, 0 >= 1), which may have surfaced |
554 | | /// after elimination. Returns 'true' if an invalid constraint is found; |
555 | | /// 'false'otherwise. |
556 | | bool hasInvalidConstraint() const; |
557 | | |
558 | | /// Returns the constant lower bound bound if isLower is true, and the upper |
559 | | /// bound if isLower is false. |
560 | | template <bool isLower> |
561 | | Optional<int64_t> computeConstantLowerOrUpperBound(unsigned pos); |
562 | | |
563 | | // Eliminates a single identifier at 'position' from equality and inequality |
564 | | // constraints. Returns 'success' if the identifier was eliminated, and |
565 | | // 'failure' otherwise. |
566 | 0 | inline LogicalResult gaussianEliminateId(unsigned position) { |
567 | 0 | return success(gaussianEliminateIds(position, position + 1) == 1); |
568 | 0 | } |
569 | | |
570 | | // Eliminates identifiers from equality and inequality constraints |
571 | | // in column range [posStart, posLimit). |
572 | | // Returns the number of variables eliminated. |
573 | | unsigned gaussianEliminateIds(unsigned posStart, unsigned posLimit); |
574 | | |
575 | | /// Eliminates identifier at the specified position using Fourier-Motzkin |
576 | | /// variable elimination, but uses Gaussian elimination if there is an |
577 | | /// equality involving that identifier. If the result of the elimination is |
578 | | /// integer exact, *isResultIntegerExact is set to true. If 'darkShadow' is |
579 | | /// set to true, a potential under approximation (subset) of the rational |
580 | | /// shadow / exact integer shadow is computed. |
581 | | // See implementation comments for more details. |
582 | | void FourierMotzkinEliminate(unsigned pos, bool darkShadow = false, |
583 | | bool *isResultIntegerExact = nullptr); |
584 | | |
585 | | /// Tightens inequalities given that we are dealing with integer spaces. This |
586 | | /// is similar to the GCD test but applied to inequalities. The constant term |
587 | | /// can be reduced to the preceding multiple of the GCD of the coefficients, |
588 | | /// i.e., |
589 | | /// 64*i - 100 >= 0 => 64*i - 128 >= 0 (since 'i' is an integer). This is a |
590 | | /// fast method (linear in the number of coefficients). |
591 | | void GCDTightenInequalities(); |
592 | | |
593 | | /// Normalized each constraints by the GCD of its coefficients. |
594 | | void normalizeConstraintsByGCD(); |
595 | | |
596 | | /// Removes identifiers in the column range [idStart, idLimit), and copies any |
597 | | /// remaining valid data into place, updates member variables, and resizes |
598 | | /// arrays as needed. |
599 | | void removeIdRange(unsigned idStart, unsigned idLimit); |
600 | | |
601 | | /// Coefficients of affine equalities (in == 0 form). |
602 | | SmallVector<int64_t, 64> equalities; |
603 | | |
604 | | /// Coefficients of affine inequalities (in >= 0 form). |
605 | | SmallVector<int64_t, 64> inequalities; |
606 | | |
607 | | /// Number of columns reserved. Actual ones in used are returned by |
608 | | /// getNumCols(). |
609 | | unsigned numReservedCols; |
610 | | |
611 | | /// Total number of identifiers. |
612 | | unsigned numIds; |
613 | | |
614 | | /// Number of identifiers corresponding to real dimensions. |
615 | | unsigned numDims; |
616 | | |
617 | | /// Number of identifiers corresponding to symbols (unknown but constant for |
618 | | /// analysis). |
619 | | unsigned numSymbols; |
620 | | |
621 | | /// Values corresponding to the (column) identifiers of this constraint |
622 | | /// system appearing in the order the identifiers correspond to columns. |
623 | | /// Temporary ones or those that aren't associated to any Value are set to |
624 | | /// None. |
625 | | SmallVector<Optional<Value>, 8> ids; |
626 | | |
627 | | /// A parameter that controls detection of an unrealistic number of |
628 | | /// constraints. If the number of constraints is this many times the number of |
629 | | /// variables, we consider such a system out of line with the intended use |
630 | | /// case of FlatAffineConstraints. |
631 | | // The rationale for 32 is that in the typical simplest of cases, an |
632 | | // identifier is expected to have one lower bound and one upper bound |
633 | | // constraint. With a level of tiling or a connection to another identifier |
634 | | // through a div or mod, an extra pair of bounds gets added. As a limit, we |
635 | | // don't expect an identifier to have more than 32 lower/upper/equality |
636 | | // constraints. This is conservatively set low and can be raised if needed. |
637 | | constexpr static unsigned kExplosionFactor = 32; |
638 | | }; |
639 | | |
640 | | /// Flattens 'expr' into 'flattenedExpr', which contains the coefficients of the |
641 | | /// dimensions, symbols, and additional variables that represent floor divisions |
642 | | /// of dimensions, symbols, and in turn other floor divisions. Returns failure |
643 | | /// if 'expr' could not be flattened (i.e., semi-affine is not yet handled). |
644 | | /// 'cst' contains constraints that connect newly introduced local identifiers |
645 | | /// to existing dimensional and symbolic identifiers. See documentation for |
646 | | /// AffineExprFlattener on how mod's and div's are flattened. |
647 | | LogicalResult getFlattenedAffineExpr(AffineExpr expr, unsigned numDims, |
648 | | unsigned numSymbols, |
649 | | SmallVectorImpl<int64_t> *flattenedExpr, |
650 | | FlatAffineConstraints *cst = nullptr); |
651 | | |
652 | | /// Flattens the result expressions of the map to their corresponding flattened |
653 | | /// forms and set in 'flattenedExprs'. Returns failure if any expression in the |
654 | | /// map could not be flattened (i.e., semi-affine is not yet handled). 'cst' |
655 | | /// contains constraints that connect newly introduced local identifiers to |
656 | | /// existing dimensional and / symbolic identifiers. See documentation for |
657 | | /// AffineExprFlattener on how mod's and div's are flattened. For all affine |
658 | | /// expressions that share the same operands (like those of an affine map), this |
659 | | /// method should be used instead of repeatedly calling getFlattenedAffineExpr |
660 | | /// since local variables added to deal with div's and mod's will be reused |
661 | | /// across expressions. |
662 | | LogicalResult |
663 | | getFlattenedAffineExprs(AffineMap map, |
664 | | std::vector<SmallVector<int64_t, 8>> *flattenedExprs, |
665 | | FlatAffineConstraints *cst = nullptr); |
666 | | LogicalResult |
667 | | getFlattenedAffineExprs(IntegerSet set, |
668 | | std::vector<SmallVector<int64_t, 8>> *flattenedExprs, |
669 | | FlatAffineConstraints *cst = nullptr); |
670 | | |
671 | | } // end namespace mlir. |
672 | | |
673 | | #endif // MLIR_ANALYSIS_AFFINE_STRUCTURES_H |