Coverage Report

Created: 2020-06-26 05:44

/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