1 //===- AffineStructures.cpp - MLIR Affine Structures Class-----------------===//
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 affine dialect ops.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "mlir/Dialect/Affine/Analysis/AffineStructures.h"
14 #include "mlir/Analysis/Presburger/LinearTransform.h"
15 #include "mlir/Analysis/Presburger/Simplex.h"
16 #include "mlir/Analysis/Presburger/Utils.h"
17 #include "mlir/Dialect/Affine/IR/AffineOps.h"
18 #include "mlir/Dialect/Affine/IR/AffineValueMap.h"
19 #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
20 #include "mlir/Dialect/StandardOps/IR/Ops.h"
21 #include "mlir/IR/AffineExprVisitor.h"
22 #include "mlir/IR/IntegerSet.h"
23 #include "mlir/Support/LLVM.h"
24 #include "mlir/Support/MathExtras.h"
25 #include "llvm/ADT/STLExtras.h"
26 #include "llvm/ADT/SmallPtrSet.h"
27 #include "llvm/ADT/SmallVector.h"
28 #include "llvm/Support/Debug.h"
29 #include "llvm/Support/raw_ostream.h"
30 
31 #define DEBUG_TYPE "affine-structures"
32 
33 using namespace mlir;
34 using namespace presburger_utils;
35 
36 namespace {
37 
38 // See comments for SimpleAffineExprFlattener.
39 // An AffineExprFlattener extends a SimpleAffineExprFlattener by recording
40 // constraint information associated with mod's, floordiv's, and ceildiv's
41 // in FlatAffineConstraints 'localVarCst'.
42 struct AffineExprFlattener : public SimpleAffineExprFlattener {
43 public:
44   // Constraints connecting newly introduced local variables (for mod's and
45   // div's) to existing (dimensional and symbolic) ones. These are always
46   // inequalities.
47   FlatAffineConstraints localVarCst;
48 
49   AffineExprFlattener(unsigned nDims, unsigned nSymbols)
50       : SimpleAffineExprFlattener(nDims, nSymbols) {
51     localVarCst.reset(nDims, nSymbols, /*numLocals=*/0);
52   }
53 
54 private:
55   // Add a local identifier (needed to flatten a mod, floordiv, ceildiv expr).
56   // The local identifier added is always a floordiv of a pure add/mul affine
57   // function of other identifiers, coefficients of which are specified in
58   // `dividend' and with respect to the positive constant `divisor'. localExpr
59   // is the simplified tree expression (AffineExpr) corresponding to the
60   // quantifier.
61   void addLocalFloorDivId(ArrayRef<int64_t> dividend, int64_t divisor,
62                           AffineExpr localExpr) override {
63     SimpleAffineExprFlattener::addLocalFloorDivId(dividend, divisor, localExpr);
64     // Update localVarCst.
65     localVarCst.addLocalFloorDiv(dividend, divisor);
66   }
67 };
68 
69 } // namespace
70 
71 // Flattens the expressions in map. Returns failure if 'expr' was unable to be
72 // flattened (i.e., semi-affine expressions not handled yet).
73 static LogicalResult
74 getFlattenedAffineExprs(ArrayRef<AffineExpr> exprs, unsigned numDims,
75                         unsigned numSymbols,
76                         std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
77                         FlatAffineConstraints *localVarCst) {
78   if (exprs.empty()) {
79     localVarCst->reset(numDims, numSymbols);
80     return success();
81   }
82 
83   AffineExprFlattener flattener(numDims, numSymbols);
84   // Use the same flattener to simplify each expression successively. This way
85   // local identifiers / expressions are shared.
86   for (auto expr : exprs) {
87     if (!expr.isPureAffine())
88       return failure();
89 
90     flattener.walkPostOrder(expr);
91   }
92 
93   assert(flattener.operandExprStack.size() == exprs.size());
94   flattenedExprs->clear();
95   flattenedExprs->assign(flattener.operandExprStack.begin(),
96                          flattener.operandExprStack.end());
97 
98   if (localVarCst)
99     localVarCst->clearAndCopyFrom(flattener.localVarCst);
100 
101   return success();
102 }
103 
104 // Flattens 'expr' into 'flattenedExpr'. Returns failure if 'expr' was unable to
105 // be flattened (semi-affine expressions not handled yet).
106 LogicalResult
107 mlir::getFlattenedAffineExpr(AffineExpr expr, unsigned numDims,
108                              unsigned numSymbols,
109                              SmallVectorImpl<int64_t> *flattenedExpr,
110                              FlatAffineConstraints *localVarCst) {
111   std::vector<SmallVector<int64_t, 8>> flattenedExprs;
112   LogicalResult ret = ::getFlattenedAffineExprs({expr}, numDims, numSymbols,
113                                                 &flattenedExprs, localVarCst);
114   *flattenedExpr = flattenedExprs[0];
115   return ret;
116 }
117 
118 /// Flattens the expressions in map. Returns failure if 'expr' was unable to be
119 /// flattened (i.e., semi-affine expressions not handled yet).
120 LogicalResult mlir::getFlattenedAffineExprs(
121     AffineMap map, std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
122     FlatAffineConstraints *localVarCst) {
123   if (map.getNumResults() == 0) {
124     localVarCst->reset(map.getNumDims(), map.getNumSymbols());
125     return success();
126   }
127   return ::getFlattenedAffineExprs(map.getResults(), map.getNumDims(),
128                                    map.getNumSymbols(), flattenedExprs,
129                                    localVarCst);
130 }
131 
132 LogicalResult mlir::getFlattenedAffineExprs(
133     IntegerSet set, std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
134     FlatAffineConstraints *localVarCst) {
135   if (set.getNumConstraints() == 0) {
136     localVarCst->reset(set.getNumDims(), set.getNumSymbols());
137     return success();
138   }
139   return ::getFlattenedAffineExprs(set.getConstraints(), set.getNumDims(),
140                                    set.getNumSymbols(), flattenedExprs,
141                                    localVarCst);
142 }
143 
144 //===----------------------------------------------------------------------===//
145 // FlatAffineConstraints / FlatAffineValueConstraints.
146 //===----------------------------------------------------------------------===//
147 
148 // Clones this object.
149 std::unique_ptr<FlatAffineConstraints> FlatAffineConstraints::clone() const {
150   return std::make_unique<FlatAffineConstraints>(*this);
151 }
152 
153 std::unique_ptr<FlatAffineValueConstraints>
154 FlatAffineValueConstraints::clone() const {
155   return std::make_unique<FlatAffineValueConstraints>(*this);
156 }
157 
158 // Construct from an IntegerSet.
159 FlatAffineConstraints::FlatAffineConstraints(IntegerSet set)
160     : IntegerPolyhedron(set.getNumInequalities(), set.getNumEqualities(),
161                         set.getNumDims() + set.getNumSymbols() + 1,
162                         set.getNumDims(), set.getNumSymbols(),
163                         /*numLocals=*/0) {
164 
165   // Flatten expressions and add them to the constraint system.
166   std::vector<SmallVector<int64_t, 8>> flatExprs;
167   FlatAffineConstraints localVarCst;
168   if (failed(getFlattenedAffineExprs(set, &flatExprs, &localVarCst))) {
169     assert(false && "flattening unimplemented for semi-affine integer sets");
170     return;
171   }
172   assert(flatExprs.size() == set.getNumConstraints());
173   appendLocalId(/*num=*/localVarCst.getNumLocalIds());
174 
175   for (unsigned i = 0, e = flatExprs.size(); i < e; ++i) {
176     const auto &flatExpr = flatExprs[i];
177     assert(flatExpr.size() == getNumCols());
178     if (set.getEqFlags()[i]) {
179       addEquality(flatExpr);
180     } else {
181       addInequality(flatExpr);
182     }
183   }
184   // Add the other constraints involving local id's from flattening.
185   append(localVarCst);
186 }
187 
188 // Construct from an IntegerSet.
189 FlatAffineValueConstraints::FlatAffineValueConstraints(IntegerSet set)
190     : FlatAffineConstraints(set) {
191   values.resize(numIds, None);
192 }
193 
194 // Construct a hyperrectangular constraint set from ValueRanges that represent
195 // induction variables, lower and upper bounds. `ivs`, `lbs` and `ubs` are
196 // expected to match one to one. The order of variables and constraints is:
197 //
198 // ivs | lbs | ubs | eq/ineq
199 // ----+-----+-----+---------
200 //   1   -1     0      >= 0
201 // ----+-----+-----+---------
202 //  -1    0     1      >= 0
203 //
204 // All dimensions as set as DimId.
205 FlatAffineValueConstraints
206 FlatAffineValueConstraints::getHyperrectangular(ValueRange ivs, ValueRange lbs,
207                                                 ValueRange ubs) {
208   FlatAffineValueConstraints res;
209   unsigned nIvs = ivs.size();
210   assert(nIvs == lbs.size() && "expected as many lower bounds as ivs");
211   assert(nIvs == ubs.size() && "expected as many upper bounds as ivs");
212 
213   if (nIvs == 0)
214     return res;
215 
216   res.appendDimId(ivs);
217   unsigned lbsStart = res.appendDimId(lbs);
218   unsigned ubsStart = res.appendDimId(ubs);
219 
220   MLIRContext *ctx = ivs.front().getContext();
221   for (int ivIdx = 0, e = nIvs; ivIdx < e; ++ivIdx) {
222     // iv - lb >= 0
223     AffineMap lb = AffineMap::get(/*dimCount=*/3 * nIvs, /*symbolCount=*/0,
224                                   getAffineDimExpr(lbsStart + ivIdx, ctx));
225     if (failed(res.addBound(BoundType::LB, ivIdx, lb)))
226       llvm_unreachable("Unexpected FlatAffineValueConstraints creation error");
227     // -iv + ub >= 0
228     AffineMap ub = AffineMap::get(/*dimCount=*/3 * nIvs, /*symbolCount=*/0,
229                                   getAffineDimExpr(ubsStart + ivIdx, ctx));
230     if (failed(res.addBound(BoundType::UB, ivIdx, ub)))
231       llvm_unreachable("Unexpected FlatAffineValueConstraints creation error");
232   }
233   return res;
234 }
235 
236 void FlatAffineValueConstraints::reset(unsigned numReservedInequalities,
237                                        unsigned numReservedEqualities,
238                                        unsigned newNumReservedCols,
239                                        unsigned newNumDims,
240                                        unsigned newNumSymbols,
241                                        unsigned newNumLocals) {
242   reset(numReservedInequalities, numReservedEqualities, newNumReservedCols,
243         newNumDims, newNumSymbols, newNumLocals, /*valArgs=*/{});
244 }
245 
246 void FlatAffineValueConstraints::reset(
247     unsigned numReservedInequalities, unsigned numReservedEqualities,
248     unsigned newNumReservedCols, unsigned newNumDims, unsigned newNumSymbols,
249     unsigned newNumLocals, ArrayRef<Value> valArgs) {
250   assert(newNumReservedCols >= newNumDims + newNumSymbols + newNumLocals + 1 &&
251          "minimum 1 column");
252   SmallVector<Optional<Value>, 8> newVals;
253   if (!valArgs.empty())
254     newVals.assign(valArgs.begin(), valArgs.end());
255 
256   *this = FlatAffineValueConstraints(
257       numReservedInequalities, numReservedEqualities, newNumReservedCols,
258       newNumDims, newNumSymbols, newNumLocals, newVals);
259 }
260 
261 void FlatAffineValueConstraints::reset(unsigned newNumDims,
262                                        unsigned newNumSymbols,
263                                        unsigned newNumLocals,
264                                        ArrayRef<Value> valArgs) {
265   reset(0, 0, newNumDims + newNumSymbols + newNumLocals + 1, newNumDims,
266         newNumSymbols, newNumLocals, valArgs);
267 }
268 
269 unsigned FlatAffineValueConstraints::appendDimId(ValueRange vals) {
270   unsigned pos = getNumDimIds();
271   insertId(IdKind::Dimension, pos, vals);
272   return pos;
273 }
274 
275 unsigned FlatAffineValueConstraints::appendSymbolId(ValueRange vals) {
276   unsigned pos = getNumSymbolIds();
277   insertId(IdKind::Symbol, pos, vals);
278   return pos;
279 }
280 
281 unsigned FlatAffineValueConstraints::insertDimId(unsigned pos,
282                                                  ValueRange vals) {
283   return insertId(IdKind::Dimension, pos, vals);
284 }
285 
286 unsigned FlatAffineValueConstraints::insertSymbolId(unsigned pos,
287                                                     ValueRange vals) {
288   return insertId(IdKind::Symbol, pos, vals);
289 }
290 
291 unsigned FlatAffineValueConstraints::insertId(IdKind kind, unsigned pos,
292                                               unsigned num) {
293   unsigned absolutePos = FlatAffineConstraints::insertId(kind, pos, num);
294   values.insert(values.begin() + absolutePos, num, None);
295   assert(values.size() == getNumIds());
296   return absolutePos;
297 }
298 
299 unsigned FlatAffineValueConstraints::insertId(IdKind kind, unsigned pos,
300                                               ValueRange vals) {
301   assert(!vals.empty() && "expected ValueRange with Values");
302   unsigned num = vals.size();
303   unsigned absolutePos = FlatAffineConstraints::insertId(kind, pos, num);
304 
305   // If a Value is provided, insert it; otherwise use None.
306   for (unsigned i = 0; i < num; ++i)
307     values.insert(values.begin() + absolutePos + i,
308                   vals[i] ? Optional<Value>(vals[i]) : None);
309 
310   assert(values.size() == getNumIds());
311   return absolutePos;
312 }
313 
314 bool FlatAffineValueConstraints::hasValues() const {
315   return llvm::find_if(values, [](Optional<Value> id) {
316            return id.hasValue();
317          }) != values.end();
318 }
319 
320 /// Checks if two constraint systems are in the same space, i.e., if they are
321 /// associated with the same set of identifiers, appearing in the same order.
322 static bool areIdsAligned(const FlatAffineValueConstraints &a,
323                           const FlatAffineValueConstraints &b) {
324   return a.getNumDimIds() == b.getNumDimIds() &&
325          a.getNumSymbolIds() == b.getNumSymbolIds() &&
326          a.getNumIds() == b.getNumIds() &&
327          a.getMaybeValues().equals(b.getMaybeValues());
328 }
329 
330 /// Calls areIdsAligned to check if two constraint systems have the same set
331 /// of identifiers in the same order.
332 bool FlatAffineValueConstraints::areIdsAlignedWithOther(
333     const FlatAffineValueConstraints &other) {
334   return areIdsAligned(*this, other);
335 }
336 
337 /// Checks if the SSA values associated with `cst`'s identifiers in range
338 /// [start, end) are unique.
339 static bool LLVM_ATTRIBUTE_UNUSED areIdsUnique(
340     const FlatAffineValueConstraints &cst, unsigned start, unsigned end) {
341 
342   assert(start <= cst.getNumIds() && "Start position out of bounds");
343   assert(end <= cst.getNumIds() && "End position out of bounds");
344 
345   if (start >= end)
346     return true;
347 
348   SmallPtrSet<Value, 8> uniqueIds;
349   ArrayRef<Optional<Value>> maybeValues = cst.getMaybeValues();
350   for (Optional<Value> val : maybeValues) {
351     if (val.hasValue() && !uniqueIds.insert(val.getValue()).second)
352       return false;
353   }
354   return true;
355 }
356 
357 /// Checks if the SSA values associated with `cst`'s identifiers are unique.
358 static bool LLVM_ATTRIBUTE_UNUSED
359 areIdsUnique(const FlatAffineConstraints &cst) {
360   return areIdsUnique(cst, 0, cst.getNumIds());
361 }
362 
363 /// Checks if the SSA values associated with `cst`'s identifiers of kind `kind`
364 /// are unique.
365 static bool LLVM_ATTRIBUTE_UNUSED areIdsUnique(
366     const FlatAffineValueConstraints &cst, FlatAffineConstraints::IdKind kind) {
367 
368   if (kind == FlatAffineConstraints::IdKind::Dimension)
369     return areIdsUnique(cst, 0, cst.getNumDimIds());
370   if (kind == FlatAffineConstraints::IdKind::Symbol)
371     return areIdsUnique(cst, cst.getNumDimIds(), cst.getNumDimAndSymbolIds());
372   if (kind == FlatAffineConstraints::IdKind::Local)
373     return areIdsUnique(cst, cst.getNumDimAndSymbolIds(), cst.getNumIds());
374   llvm_unreachable("Unexpected IdKind");
375 }
376 
377 /// Merge and align the identifiers of A and B starting at 'offset', so that
378 /// both constraint systems get the union of the contained identifiers that is
379 /// dimension-wise and symbol-wise unique; both constraint systems are updated
380 /// so that they have the union of all identifiers, with A's original
381 /// identifiers appearing first followed by any of B's identifiers that didn't
382 /// appear in A. Local identifiers in B that have the same division
383 /// representation as local identifiers in A are merged into one.
384 //  E.g.: Input: A has ((%i, %j) [%M, %N]) and B has (%k, %j) [%P, %N, %M])
385 //        Output: both A, B have (%i, %j, %k) [%M, %N, %P]
386 static void mergeAndAlignIds(unsigned offset, FlatAffineValueConstraints *a,
387                              FlatAffineValueConstraints *b) {
388   assert(offset <= a->getNumDimIds() && offset <= b->getNumDimIds());
389   // A merge/align isn't meaningful if a cst's ids aren't distinct.
390   assert(areIdsUnique(*a) && "A's values aren't unique");
391   assert(areIdsUnique(*b) && "B's values aren't unique");
392 
393   assert(std::all_of(a->getMaybeValues().begin() + offset,
394                      a->getMaybeValues().begin() + a->getNumDimAndSymbolIds(),
395                      [](Optional<Value> id) { return id.hasValue(); }));
396 
397   assert(std::all_of(b->getMaybeValues().begin() + offset,
398                      b->getMaybeValues().begin() + b->getNumDimAndSymbolIds(),
399                      [](Optional<Value> id) { return id.hasValue(); }));
400 
401   SmallVector<Value, 4> aDimValues;
402   a->getValues(offset, a->getNumDimIds(), &aDimValues);
403 
404   {
405     // Merge dims from A into B.
406     unsigned d = offset;
407     for (auto aDimValue : aDimValues) {
408       unsigned loc;
409       if (b->findId(aDimValue, &loc)) {
410         assert(loc >= offset && "A's dim appears in B's aligned range");
411         assert(loc < b->getNumDimIds() &&
412                "A's dim appears in B's non-dim position");
413         b->swapId(d, loc);
414       } else {
415         b->insertDimId(d, aDimValue);
416       }
417       d++;
418     }
419     // Dimensions that are in B, but not in A, are added at the end.
420     for (unsigned t = a->getNumDimIds(), e = b->getNumDimIds(); t < e; t++) {
421       a->appendDimId(b->getValue(t));
422     }
423     assert(a->getNumDimIds() == b->getNumDimIds() &&
424            "expected same number of dims");
425   }
426 
427   // Merge and align symbols of A and B
428   a->mergeSymbolIds(*b);
429   // Merge and align local ids of A and B
430   a->mergeLocalIds(*b);
431 
432   assert(areIdsAligned(*a, *b) && "IDs expected to be aligned");
433 }
434 
435 // Call 'mergeAndAlignIds' to align constraint systems of 'this' and 'other'.
436 void FlatAffineValueConstraints::mergeAndAlignIdsWithOther(
437     unsigned offset, FlatAffineValueConstraints *other) {
438   mergeAndAlignIds(offset, this, other);
439 }
440 
441 LogicalResult
442 FlatAffineValueConstraints::composeMap(const AffineValueMap *vMap) {
443   return composeMatchingMap(
444       computeAlignedMap(vMap->getAffineMap(), vMap->getOperands()));
445 }
446 
447 // Similar to `composeMap` except that no Values need be associated with the
448 // constraint system nor are they looked at -- the dimensions and symbols of
449 // `other` are expected to correspond 1:1 to `this` system.
450 LogicalResult FlatAffineConstraints::composeMatchingMap(AffineMap other) {
451   assert(other.getNumDims() == getNumDimIds() && "dim mismatch");
452   assert(other.getNumSymbols() == getNumSymbolIds() && "symbol mismatch");
453 
454   std::vector<SmallVector<int64_t, 8>> flatExprs;
455   if (failed(flattenAlignedMapAndMergeLocals(other, &flatExprs)))
456     return failure();
457   assert(flatExprs.size() == other.getNumResults());
458 
459   // Add dimensions corresponding to the map's results.
460   insertDimId(/*pos=*/0, /*num=*/other.getNumResults());
461 
462   // We add one equality for each result connecting the result dim of the map to
463   // the other identifiers.
464   // E.g.: if the expression is 16*i0 + i1, and this is the r^th
465   // iteration/result of the value map, we are adding the equality:
466   // d_r - 16*i0 - i1 = 0. Similarly, when flattening (i0 + 1, i0 + 8*i2), we
467   // add two equalities: d_0 - i0 - 1 == 0, d1 - i0 - 8*i2 == 0.
468   for (unsigned r = 0, e = flatExprs.size(); r < e; r++) {
469     const auto &flatExpr = flatExprs[r];
470     assert(flatExpr.size() >= other.getNumInputs() + 1);
471 
472     SmallVector<int64_t, 8> eqToAdd(getNumCols(), 0);
473     // Set the coefficient for this result to one.
474     eqToAdd[r] = 1;
475 
476     // Dims and symbols.
477     for (unsigned i = 0, f = other.getNumInputs(); i < f; i++) {
478       // Negate `eq[r]` since the newly added dimension will be set to this one.
479       eqToAdd[e + i] = -flatExpr[i];
480     }
481     // Local columns of `eq` are at the beginning.
482     unsigned j = getNumDimIds() + getNumSymbolIds();
483     unsigned end = flatExpr.size() - 1;
484     for (unsigned i = other.getNumInputs(); i < end; i++, j++) {
485       eqToAdd[j] = -flatExpr[i];
486     }
487 
488     // Constant term.
489     eqToAdd[getNumCols() - 1] = -flatExpr[flatExpr.size() - 1];
490 
491     // Add the equality connecting the result of the map to this constraint set.
492     addEquality(eqToAdd);
493   }
494 
495   return success();
496 }
497 
498 // Turn a symbol into a dimension.
499 static void turnSymbolIntoDim(FlatAffineValueConstraints *cst, Value id) {
500   unsigned pos;
501   if (cst->findId(id, &pos) && pos >= cst->getNumDimIds() &&
502       pos < cst->getNumDimAndSymbolIds()) {
503     cst->swapId(pos, cst->getNumDimIds());
504     cst->setDimSymbolSeparation(cst->getNumSymbolIds() - 1);
505   }
506 }
507 
508 /// Merge and align symbols of `this` and `other` such that both get union of
509 /// of symbols that are unique. Symbols in `this` and `other` should be
510 /// unique. Symbols with Value as `None` are considered to be inequal to all
511 /// other symbols.
512 void FlatAffineValueConstraints::mergeSymbolIds(
513     FlatAffineValueConstraints &other) {
514 
515   assert(areIdsUnique(*this, IdKind::Symbol) && "Symbol ids are not unique");
516   assert(areIdsUnique(other, IdKind::Symbol) && "Symbol ids are not unique");
517 
518   SmallVector<Value, 4> aSymValues;
519   getValues(getNumDimIds(), getNumDimAndSymbolIds(), &aSymValues);
520 
521   // Merge symbols: merge symbols into `other` first from `this`.
522   unsigned s = other.getNumDimIds();
523   for (Value aSymValue : aSymValues) {
524     unsigned loc;
525     // If the id is a symbol in `other`, then align it, otherwise assume that
526     // it is a new symbol
527     if (other.findId(aSymValue, &loc) && loc >= other.getNumDimIds() &&
528         loc < other.getNumDimAndSymbolIds())
529       other.swapId(s, loc);
530     else
531       other.insertSymbolId(s - other.getNumDimIds(), aSymValue);
532     s++;
533   }
534 
535   // Symbols that are in other, but not in this, are added at the end.
536   for (unsigned t = other.getNumDimIds() + getNumSymbolIds(),
537                 e = other.getNumDimAndSymbolIds();
538        t < e; t++)
539     insertSymbolId(getNumSymbolIds(), other.getValue(t));
540 
541   assert(getNumSymbolIds() == other.getNumSymbolIds() &&
542          "expected same number of symbols");
543   assert(areIdsUnique(*this, IdKind::Symbol) && "Symbol ids are not unique");
544   assert(areIdsUnique(other, IdKind::Symbol) && "Symbol ids are not unique");
545 }
546 
547 // Changes all symbol identifiers which are loop IVs to dim identifiers.
548 void FlatAffineValueConstraints::convertLoopIVSymbolsToDims() {
549   // Gather all symbols which are loop IVs.
550   SmallVector<Value, 4> loopIVs;
551   for (unsigned i = getNumDimIds(), e = getNumDimAndSymbolIds(); i < e; i++) {
552     if (hasValue(i) && getForInductionVarOwner(getValue(i)))
553       loopIVs.push_back(getValue(i));
554   }
555   // Turn each symbol in 'loopIVs' into a dim identifier.
556   for (auto iv : loopIVs) {
557     turnSymbolIntoDim(this, iv);
558   }
559 }
560 
561 void FlatAffineValueConstraints::addInductionVarOrTerminalSymbol(Value val) {
562   if (containsId(val))
563     return;
564 
565   // Caller is expected to fully compose map/operands if necessary.
566   assert((isTopLevelValue(val) || isForInductionVar(val)) &&
567          "non-terminal symbol / loop IV expected");
568   // Outer loop IVs could be used in forOp's bounds.
569   if (auto loop = getForInductionVarOwner(val)) {
570     appendDimId(val);
571     if (failed(this->addAffineForOpDomain(loop)))
572       LLVM_DEBUG(
573           loop.emitWarning("failed to add domain info to constraint system"));
574     return;
575   }
576   // Add top level symbol.
577   appendSymbolId(val);
578   // Check if the symbol is a constant.
579   if (auto constOp = val.getDefiningOp<arith::ConstantIndexOp>())
580     addBound(BoundType::EQ, val, constOp.value());
581 }
582 
583 LogicalResult
584 FlatAffineValueConstraints::addAffineForOpDomain(AffineForOp forOp) {
585   unsigned pos;
586   // Pre-condition for this method.
587   if (!findId(forOp.getInductionVar(), &pos)) {
588     assert(false && "Value not found");
589     return failure();
590   }
591 
592   int64_t step = forOp.getStep();
593   if (step != 1) {
594     if (!forOp.hasConstantLowerBound())
595       LLVM_DEBUG(forOp.emitWarning("domain conservatively approximated"));
596     else {
597       // Add constraints for the stride.
598       // (iv - lb) % step = 0 can be written as:
599       // (iv - lb) - step * q = 0 where q = (iv - lb) / step.
600       // Add local variable 'q' and add the above equality.
601       // The first constraint is q = (iv - lb) floordiv step
602       SmallVector<int64_t, 8> dividend(getNumCols(), 0);
603       int64_t lb = forOp.getConstantLowerBound();
604       dividend[pos] = 1;
605       dividend.back() -= lb;
606       addLocalFloorDiv(dividend, step);
607       // Second constraint: (iv - lb) - step * q = 0.
608       SmallVector<int64_t, 8> eq(getNumCols(), 0);
609       eq[pos] = 1;
610       eq.back() -= lb;
611       // For the local var just added above.
612       eq[getNumCols() - 2] = -step;
613       addEquality(eq);
614     }
615   }
616 
617   if (forOp.hasConstantLowerBound()) {
618     addBound(BoundType::LB, pos, forOp.getConstantLowerBound());
619   } else {
620     // Non-constant lower bound case.
621     if (failed(addBound(BoundType::LB, pos, forOp.getLowerBoundMap(),
622                         forOp.getLowerBoundOperands())))
623       return failure();
624   }
625 
626   if (forOp.hasConstantUpperBound()) {
627     addBound(BoundType::UB, pos, forOp.getConstantUpperBound() - 1);
628     return success();
629   }
630   // Non-constant upper bound case.
631   return addBound(BoundType::UB, pos, forOp.getUpperBoundMap(),
632                   forOp.getUpperBoundOperands());
633 }
634 
635 LogicalResult
636 FlatAffineValueConstraints::addDomainFromSliceMaps(ArrayRef<AffineMap> lbMaps,
637                                                    ArrayRef<AffineMap> ubMaps,
638                                                    ArrayRef<Value> operands) {
639   assert(lbMaps.size() == ubMaps.size());
640   assert(lbMaps.size() <= getNumDimIds());
641 
642   for (unsigned i = 0, e = lbMaps.size(); i < e; ++i) {
643     AffineMap lbMap = lbMaps[i];
644     AffineMap ubMap = ubMaps[i];
645     assert(!lbMap || lbMap.getNumInputs() == operands.size());
646     assert(!ubMap || ubMap.getNumInputs() == operands.size());
647 
648     // Check if this slice is just an equality along this dimension. If so,
649     // retrieve the existing loop it equates to and add it to the system.
650     if (lbMap && ubMap && lbMap.getNumResults() == 1 &&
651         ubMap.getNumResults() == 1 &&
652         lbMap.getResult(0) + 1 == ubMap.getResult(0) &&
653         // The condition above will be true for maps describing a single
654         // iteration (e.g., lbMap.getResult(0) = 0, ubMap.getResult(0) = 1).
655         // Make sure we skip those cases by checking that the lb result is not
656         // just a constant.
657         !lbMap.getResult(0).isa<AffineConstantExpr>()) {
658       // Limited support: we expect the lb result to be just a loop dimension.
659       // Not supported otherwise for now.
660       AffineDimExpr result = lbMap.getResult(0).dyn_cast<AffineDimExpr>();
661       if (!result)
662         return failure();
663 
664       AffineForOp loop =
665           getForInductionVarOwner(operands[result.getPosition()]);
666       if (!loop)
667         return failure();
668 
669       if (failed(addAffineForOpDomain(loop)))
670         return failure();
671       continue;
672     }
673 
674     // This slice refers to a loop that doesn't exist in the IR yet. Add its
675     // bounds to the system assuming its dimension identifier position is the
676     // same as the position of the loop in the loop nest.
677     if (lbMap && failed(addBound(BoundType::LB, i, lbMap, operands)))
678       return failure();
679     if (ubMap && failed(addBound(BoundType::UB, i, ubMap, operands)))
680       return failure();
681   }
682   return success();
683 }
684 
685 void FlatAffineValueConstraints::addAffineIfOpDomain(AffineIfOp ifOp) {
686   // Create the base constraints from the integer set attached to ifOp.
687   FlatAffineValueConstraints cst(ifOp.getIntegerSet());
688 
689   // Bind ids in the constraints to ifOp operands.
690   SmallVector<Value, 4> operands = ifOp.getOperands();
691   cst.setValues(0, cst.getNumDimAndSymbolIds(), operands);
692 
693   // Merge the constraints from ifOp to the current domain. We need first merge
694   // and align the IDs from both constraints, and then append the constraints
695   // from the ifOp into the current one.
696   mergeAndAlignIdsWithOther(0, &cst);
697   append(cst);
698 }
699 
700 bool FlatAffineValueConstraints::hasConsistentState() const {
701   return FlatAffineConstraints::hasConsistentState() &&
702          values.size() == getNumIds();
703 }
704 
705 void FlatAffineValueConstraints::removeIdRange(unsigned idStart,
706                                                unsigned idLimit) {
707   FlatAffineConstraints::removeIdRange(idStart, idLimit);
708   values.erase(values.begin() + idStart, values.begin() + idLimit);
709 }
710 
711 // Determine whether the identifier at 'pos' (say id_r) can be expressed as
712 // modulo of another known identifier (say id_n) w.r.t a constant. For example,
713 // if the following constraints hold true:
714 // ```
715 // 0 <= id_r <= divisor - 1
716 // id_n - (divisor * q_expr) = id_r
717 // ```
718 // where `id_n` is a known identifier (called dividend), and `q_expr` is an
719 // `AffineExpr` (called the quotient expression), `id_r` can be written as:
720 //
721 // `id_r = id_n mod divisor`.
722 //
723 // Additionally, in a special case of the above constaints where `q_expr` is an
724 // identifier itself that is not yet known (say `id_q`), it can be written as a
725 // floordiv in the following way:
726 //
727 // `id_q = id_n floordiv divisor`.
728 //
729 // Returns true if the above mod or floordiv are detected, updating 'memo' with
730 // these new expressions. Returns false otherwise.
731 static bool detectAsMod(const FlatAffineConstraints &cst, unsigned pos,
732                         int64_t lbConst, int64_t ubConst,
733                         SmallVectorImpl<AffineExpr> &memo,
734                         MLIRContext *context) {
735   assert(pos < cst.getNumIds() && "invalid position");
736 
737   // Check if a divisor satisfying the condition `0 <= id_r <= divisor - 1` can
738   // be determined.
739   if (lbConst != 0 || ubConst < 1)
740     return false;
741   int64_t divisor = ubConst + 1;
742 
743   // Check for the aforementioned conditions in each equality.
744   for (unsigned curEquality = 0, numEqualities = cst.getNumEqualities();
745        curEquality < numEqualities; curEquality++) {
746     int64_t coefficientAtPos = cst.atEq(curEquality, pos);
747     // If current equality does not involve `id_r`, continue to the next
748     // equality.
749     if (coefficientAtPos == 0)
750       continue;
751 
752     // Constant term should be 0 in this equality.
753     if (cst.atEq(curEquality, cst.getNumCols() - 1) != 0)
754       continue;
755 
756     // Traverse through the equality and construct the dividend expression
757     // `dividendExpr`, to contain all the identifiers which are known and are
758     // not divisible by `(coefficientAtPos * divisor)`. Hope here is that the
759     // `dividendExpr` gets simplified into a single identifier `id_n` discussed
760     // above.
761     auto dividendExpr = getAffineConstantExpr(0, context);
762 
763     // Track the terms that go into quotient expression, later used to detect
764     // additional floordiv.
765     unsigned quotientCount = 0;
766     int quotientPosition = -1;
767     int quotientSign = 1;
768 
769     // Consider each term in the current equality.
770     unsigned curId, e;
771     for (curId = 0, e = cst.getNumDimAndSymbolIds(); curId < e; ++curId) {
772       // Ignore id_r.
773       if (curId == pos)
774         continue;
775       int64_t coefficientOfCurId = cst.atEq(curEquality, curId);
776       // Ignore ids that do not contribute to the current equality.
777       if (coefficientOfCurId == 0)
778         continue;
779       // Check if the current id goes into the quotient expression.
780       if (coefficientOfCurId % (divisor * coefficientAtPos) == 0) {
781         quotientCount++;
782         quotientPosition = curId;
783         quotientSign = (coefficientOfCurId * coefficientAtPos) > 0 ? 1 : -1;
784         continue;
785       }
786       // Identifiers that are part of dividendExpr should be known.
787       if (!memo[curId])
788         break;
789       // Append the current identifier to the dividend expression.
790       dividendExpr = dividendExpr + memo[curId] * coefficientOfCurId;
791     }
792 
793     // Can't construct expression as it depends on a yet uncomputed id.
794     if (curId < e)
795       continue;
796 
797     // Express `id_r` in terms of the other ids collected so far.
798     if (coefficientAtPos > 0)
799       dividendExpr = (-dividendExpr).floorDiv(coefficientAtPos);
800     else
801       dividendExpr = dividendExpr.floorDiv(-coefficientAtPos);
802 
803     // Simplify the expression.
804     dividendExpr = simplifyAffineExpr(dividendExpr, cst.getNumDimIds(),
805                                       cst.getNumSymbolIds());
806     // Only if the final dividend expression is just a single id (which we call
807     // `id_n`), we can proceed.
808     // TODO: Handle AffineSymbolExpr as well. There is no reason to restrict it
809     // to dims themselves.
810     auto dimExpr = dividendExpr.dyn_cast<AffineDimExpr>();
811     if (!dimExpr)
812       continue;
813 
814     // Express `id_r` as `id_n % divisor` and store the expression in `memo`.
815     if (quotientCount >= 1) {
816       auto ub = cst.getConstantBound(FlatAffineConstraints::BoundType::UB,
817                                      dimExpr.getPosition());
818       // If `id_n` has an upperbound that is less than the divisor, mod can be
819       // eliminated altogether.
820       if (ub.hasValue() && ub.getValue() < divisor)
821         memo[pos] = dimExpr;
822       else
823         memo[pos] = dimExpr % divisor;
824       // If a unique quotient `id_q` was seen, it can be expressed as
825       // `id_n floordiv divisor`.
826       if (quotientCount == 1 && !memo[quotientPosition])
827         memo[quotientPosition] = dimExpr.floorDiv(divisor) * quotientSign;
828 
829       return true;
830     }
831   }
832   return false;
833 }
834 
835 /// Check if the pos^th identifier can be expressed as a floordiv of an affine
836 /// function of other identifiers (where the divisor is a positive constant)
837 /// given the initial set of expressions in `exprs`. If it can be, the
838 /// corresponding position in `exprs` is set as the detected affine expr. For
839 /// eg: 4q <= i + j <= 4q + 3   <=>   q = (i + j) floordiv 4. An equality can
840 /// also yield a floordiv: eg.  4q = i + j <=> q = (i + j) floordiv 4. 32q + 28
841 /// <= i <= 32q + 31 => q = i floordiv 32.
842 static bool detectAsFloorDiv(const FlatAffineConstraints &cst, unsigned pos,
843                              MLIRContext *context,
844                              SmallVectorImpl<AffineExpr> &exprs) {
845   assert(pos < cst.getNumIds() && "invalid position");
846 
847   // Get upper-lower bound pair for this variable.
848   SmallVector<bool, 8> foundRepr(cst.getNumIds(), false);
849   for (unsigned i = 0, e = cst.getNumIds(); i < e; ++i)
850     if (exprs[i])
851       foundRepr[i] = true;
852 
853   SmallVector<int64_t, 8> dividend;
854   unsigned divisor;
855   auto ulPair = computeSingleVarRepr(cst, foundRepr, pos, dividend, divisor);
856 
857   // No upper-lower bound pair found for this var.
858   if (ulPair.kind == ReprKind::None || ulPair.kind == ReprKind::Equality)
859     return false;
860 
861   // Construct the dividend expression.
862   auto dividendExpr = getAffineConstantExpr(dividend.back(), context);
863   for (unsigned c = 0, f = cst.getNumIds(); c < f; c++)
864     if (dividend[c] != 0)
865       dividendExpr = dividendExpr + dividend[c] * exprs[c];
866 
867   // Successfully detected the floordiv.
868   exprs[pos] = dividendExpr.floorDiv(divisor);
869   return true;
870 }
871 
872 std::pair<AffineMap, AffineMap> FlatAffineConstraints::getLowerAndUpperBound(
873     unsigned pos, unsigned offset, unsigned num, unsigned symStartPos,
874     ArrayRef<AffineExpr> localExprs, MLIRContext *context) const {
875   assert(pos + offset < getNumDimIds() && "invalid dim start pos");
876   assert(symStartPos >= (pos + offset) && "invalid sym start pos");
877   assert(getNumLocalIds() == localExprs.size() &&
878          "incorrect local exprs count");
879 
880   SmallVector<unsigned, 4> lbIndices, ubIndices, eqIndices;
881   getLowerAndUpperBoundIndices(pos + offset, &lbIndices, &ubIndices, &eqIndices,
882                                offset, num);
883 
884   /// Add to 'b' from 'a' in set [0, offset) U [offset + num, symbStartPos).
885   auto addCoeffs = [&](ArrayRef<int64_t> a, SmallVectorImpl<int64_t> &b) {
886     b.clear();
887     for (unsigned i = 0, e = a.size(); i < e; ++i) {
888       if (i < offset || i >= offset + num)
889         b.push_back(a[i]);
890     }
891   };
892 
893   SmallVector<int64_t, 8> lb, ub;
894   SmallVector<AffineExpr, 4> lbExprs;
895   unsigned dimCount = symStartPos - num;
896   unsigned symCount = getNumDimAndSymbolIds() - symStartPos;
897   lbExprs.reserve(lbIndices.size() + eqIndices.size());
898   // Lower bound expressions.
899   for (auto idx : lbIndices) {
900     auto ineq = getInequality(idx);
901     // Extract the lower bound (in terms of other coeff's + const), i.e., if
902     // i - j + 1 >= 0 is the constraint, 'pos' is for i the lower bound is j
903     // - 1.
904     addCoeffs(ineq, lb);
905     std::transform(lb.begin(), lb.end(), lb.begin(), std::negate<int64_t>());
906     auto expr =
907         getAffineExprFromFlatForm(lb, dimCount, symCount, localExprs, context);
908     // expr ceildiv divisor is (expr + divisor - 1) floordiv divisor
909     int64_t divisor = std::abs(ineq[pos + offset]);
910     expr = (expr + divisor - 1).floorDiv(divisor);
911     lbExprs.push_back(expr);
912   }
913 
914   SmallVector<AffineExpr, 4> ubExprs;
915   ubExprs.reserve(ubIndices.size() + eqIndices.size());
916   // Upper bound expressions.
917   for (auto idx : ubIndices) {
918     auto ineq = getInequality(idx);
919     // Extract the upper bound (in terms of other coeff's + const).
920     addCoeffs(ineq, ub);
921     auto expr =
922         getAffineExprFromFlatForm(ub, dimCount, symCount, localExprs, context);
923     expr = expr.floorDiv(std::abs(ineq[pos + offset]));
924     // Upper bound is exclusive.
925     ubExprs.push_back(expr + 1);
926   }
927 
928   // Equalities. It's both a lower and a upper bound.
929   SmallVector<int64_t, 4> b;
930   for (auto idx : eqIndices) {
931     auto eq = getEquality(idx);
932     addCoeffs(eq, b);
933     if (eq[pos + offset] > 0)
934       std::transform(b.begin(), b.end(), b.begin(), std::negate<int64_t>());
935 
936     // Extract the upper bound (in terms of other coeff's + const).
937     auto expr =
938         getAffineExprFromFlatForm(b, dimCount, symCount, localExprs, context);
939     expr = expr.floorDiv(std::abs(eq[pos + offset]));
940     // Upper bound is exclusive.
941     ubExprs.push_back(expr + 1);
942     // Lower bound.
943     expr =
944         getAffineExprFromFlatForm(b, dimCount, symCount, localExprs, context);
945     expr = expr.ceilDiv(std::abs(eq[pos + offset]));
946     lbExprs.push_back(expr);
947   }
948 
949   auto lbMap = AffineMap::get(dimCount, symCount, lbExprs, context);
950   auto ubMap = AffineMap::get(dimCount, symCount, ubExprs, context);
951 
952   return {lbMap, ubMap};
953 }
954 
955 /// Computes the lower and upper bounds of the first 'num' dimensional
956 /// identifiers (starting at 'offset') as affine maps of the remaining
957 /// identifiers (dimensional and symbolic identifiers). Local identifiers are
958 /// themselves explicitly computed as affine functions of other identifiers in
959 /// this process if needed.
960 void FlatAffineConstraints::getSliceBounds(unsigned offset, unsigned num,
961                                            MLIRContext *context,
962                                            SmallVectorImpl<AffineMap> *lbMaps,
963                                            SmallVectorImpl<AffineMap> *ubMaps) {
964   assert(num < getNumDimIds() && "invalid range");
965 
966   // Basic simplification.
967   normalizeConstraintsByGCD();
968 
969   LLVM_DEBUG(llvm::dbgs() << "getSliceBounds for first " << num
970                           << " identifiers\n");
971   LLVM_DEBUG(dump());
972 
973   // Record computed/detected identifiers.
974   SmallVector<AffineExpr, 8> memo(getNumIds());
975   // Initialize dimensional and symbolic identifiers.
976   for (unsigned i = 0, e = getNumDimIds(); i < e; i++) {
977     if (i < offset)
978       memo[i] = getAffineDimExpr(i, context);
979     else if (i >= offset + num)
980       memo[i] = getAffineDimExpr(i - num, context);
981   }
982   for (unsigned i = getNumDimIds(), e = getNumDimAndSymbolIds(); i < e; i++)
983     memo[i] = getAffineSymbolExpr(i - getNumDimIds(), context);
984 
985   bool changed;
986   do {
987     changed = false;
988     // Identify yet unknown identifiers as constants or mod's / floordiv's of
989     // other identifiers if possible.
990     for (unsigned pos = 0; pos < getNumIds(); pos++) {
991       if (memo[pos])
992         continue;
993 
994       auto lbConst = getConstantBound(BoundType::LB, pos);
995       auto ubConst = getConstantBound(BoundType::UB, pos);
996       if (lbConst.hasValue() && ubConst.hasValue()) {
997         // Detect equality to a constant.
998         if (lbConst.getValue() == ubConst.getValue()) {
999           memo[pos] = getAffineConstantExpr(lbConst.getValue(), context);
1000           changed = true;
1001           continue;
1002         }
1003 
1004         // Detect an identifier as modulo of another identifier w.r.t a
1005         // constant.
1006         if (detectAsMod(*this, pos, lbConst.getValue(), ubConst.getValue(),
1007                         memo, context)) {
1008           changed = true;
1009           continue;
1010         }
1011       }
1012 
1013       // Detect an identifier as a floordiv of an affine function of other
1014       // identifiers (divisor is a positive constant).
1015       if (detectAsFloorDiv(*this, pos, context, memo)) {
1016         changed = true;
1017         continue;
1018       }
1019 
1020       // Detect an identifier as an expression of other identifiers.
1021       unsigned idx;
1022       if (!findConstraintWithNonZeroAt(pos, /*isEq=*/true, &idx)) {
1023         continue;
1024       }
1025 
1026       // Build AffineExpr solving for identifier 'pos' in terms of all others.
1027       auto expr = getAffineConstantExpr(0, context);
1028       unsigned j, e;
1029       for (j = 0, e = getNumIds(); j < e; ++j) {
1030         if (j == pos)
1031           continue;
1032         int64_t c = atEq(idx, j);
1033         if (c == 0)
1034           continue;
1035         // If any of the involved IDs hasn't been found yet, we can't proceed.
1036         if (!memo[j])
1037           break;
1038         expr = expr + memo[j] * c;
1039       }
1040       if (j < e)
1041         // Can't construct expression as it depends on a yet uncomputed
1042         // identifier.
1043         continue;
1044 
1045       // Add constant term to AffineExpr.
1046       expr = expr + atEq(idx, getNumIds());
1047       int64_t vPos = atEq(idx, pos);
1048       assert(vPos != 0 && "expected non-zero here");
1049       if (vPos > 0)
1050         expr = (-expr).floorDiv(vPos);
1051       else
1052         // vPos < 0.
1053         expr = expr.floorDiv(-vPos);
1054       // Successfully constructed expression.
1055       memo[pos] = expr;
1056       changed = true;
1057     }
1058     // This loop is guaranteed to reach a fixed point - since once an
1059     // identifier's explicit form is computed (in memo[pos]), it's not updated
1060     // again.
1061   } while (changed);
1062 
1063   // Set the lower and upper bound maps for all the identifiers that were
1064   // computed as affine expressions of the rest as the "detected expr" and
1065   // "detected expr + 1" respectively; set the undetected ones to null.
1066   Optional<FlatAffineConstraints> tmpClone;
1067   for (unsigned pos = 0; pos < num; pos++) {
1068     unsigned numMapDims = getNumDimIds() - num;
1069     unsigned numMapSymbols = getNumSymbolIds();
1070     AffineExpr expr = memo[pos + offset];
1071     if (expr)
1072       expr = simplifyAffineExpr(expr, numMapDims, numMapSymbols);
1073 
1074     AffineMap &lbMap = (*lbMaps)[pos];
1075     AffineMap &ubMap = (*ubMaps)[pos];
1076 
1077     if (expr) {
1078       lbMap = AffineMap::get(numMapDims, numMapSymbols, expr);
1079       ubMap = AffineMap::get(numMapDims, numMapSymbols, expr + 1);
1080     } else {
1081       // TODO: Whenever there are local identifiers in the dependence
1082       // constraints, we'll conservatively over-approximate, since we don't
1083       // always explicitly compute them above (in the while loop).
1084       if (getNumLocalIds() == 0) {
1085         // Work on a copy so that we don't update this constraint system.
1086         if (!tmpClone) {
1087           tmpClone.emplace(FlatAffineConstraints(*this));
1088           // Removing redundant inequalities is necessary so that we don't get
1089           // redundant loop bounds.
1090           tmpClone->removeRedundantInequalities();
1091         }
1092         std::tie(lbMap, ubMap) = tmpClone->getLowerAndUpperBound(
1093             pos, offset, num, getNumDimIds(), /*localExprs=*/{}, context);
1094       }
1095 
1096       // If the above fails, we'll just use the constant lower bound and the
1097       // constant upper bound (if they exist) as the slice bounds.
1098       // TODO: being conservative for the moment in cases that
1099       // lead to multiple bounds - until getConstDifference in LoopFusion.cpp is
1100       // fixed (b/126426796).
1101       if (!lbMap || lbMap.getNumResults() > 1) {
1102         LLVM_DEBUG(llvm::dbgs()
1103                    << "WARNING: Potentially over-approximating slice lb\n");
1104         auto lbConst = getConstantBound(BoundType::LB, pos + offset);
1105         if (lbConst.hasValue()) {
1106           lbMap = AffineMap::get(
1107               numMapDims, numMapSymbols,
1108               getAffineConstantExpr(lbConst.getValue(), context));
1109         }
1110       }
1111       if (!ubMap || ubMap.getNumResults() > 1) {
1112         LLVM_DEBUG(llvm::dbgs()
1113                    << "WARNING: Potentially over-approximating slice ub\n");
1114         auto ubConst = getConstantBound(BoundType::UB, pos + offset);
1115         if (ubConst.hasValue()) {
1116           (ubMap) = AffineMap::get(
1117               numMapDims, numMapSymbols,
1118               getAffineConstantExpr(ubConst.getValue() + 1, context));
1119         }
1120       }
1121     }
1122     LLVM_DEBUG(llvm::dbgs()
1123                << "lb map for pos = " << Twine(pos + offset) << ", expr: ");
1124     LLVM_DEBUG(lbMap.dump(););
1125     LLVM_DEBUG(llvm::dbgs()
1126                << "ub map for pos = " << Twine(pos + offset) << ", expr: ");
1127     LLVM_DEBUG(ubMap.dump(););
1128   }
1129 }
1130 
1131 LogicalResult FlatAffineConstraints::flattenAlignedMapAndMergeLocals(
1132     AffineMap map, std::vector<SmallVector<int64_t, 8>> *flattenedExprs) {
1133   FlatAffineConstraints localCst;
1134   if (failed(getFlattenedAffineExprs(map, flattenedExprs, &localCst))) {
1135     LLVM_DEBUG(llvm::dbgs()
1136                << "composition unimplemented for semi-affine maps\n");
1137     return failure();
1138   }
1139 
1140   // Add localCst information.
1141   if (localCst.getNumLocalIds() > 0) {
1142     unsigned numLocalIds = getNumLocalIds();
1143     // Insert local dims of localCst at the beginning.
1144     insertLocalId(/*pos=*/0, /*num=*/localCst.getNumLocalIds());
1145     // Insert local dims of `this` at the end of localCst.
1146     localCst.appendLocalId(/*num=*/numLocalIds);
1147     // Dimensions of localCst and this constraint set match. Append localCst to
1148     // this constraint set.
1149     append(localCst);
1150   }
1151 
1152   return success();
1153 }
1154 
1155 LogicalResult FlatAffineConstraints::addBound(BoundType type, unsigned pos,
1156                                               AffineMap boundMap) {
1157   assert(boundMap.getNumDims() == getNumDimIds() && "dim mismatch");
1158   assert(boundMap.getNumSymbols() == getNumSymbolIds() && "symbol mismatch");
1159   assert(pos < getNumDimAndSymbolIds() && "invalid position");
1160 
1161   // Equality follows the logic of lower bound except that we add an equality
1162   // instead of an inequality.
1163   assert((type != BoundType::EQ || boundMap.getNumResults() == 1) &&
1164          "single result expected");
1165   bool lower = type == BoundType::LB || type == BoundType::EQ;
1166 
1167   std::vector<SmallVector<int64_t, 8>> flatExprs;
1168   if (failed(flattenAlignedMapAndMergeLocals(boundMap, &flatExprs)))
1169     return failure();
1170   assert(flatExprs.size() == boundMap.getNumResults());
1171 
1172   // Add one (in)equality for each result.
1173   for (const auto &flatExpr : flatExprs) {
1174     SmallVector<int64_t> ineq(getNumCols(), 0);
1175     // Dims and symbols.
1176     for (unsigned j = 0, e = boundMap.getNumInputs(); j < e; j++) {
1177       ineq[j] = lower ? -flatExpr[j] : flatExpr[j];
1178     }
1179     // Invalid bound: pos appears in `boundMap`.
1180     // TODO: This should be an assertion. Fix `addDomainFromSliceMaps` and/or
1181     // its callers to prevent invalid bounds from being added.
1182     if (ineq[pos] != 0)
1183       continue;
1184     ineq[pos] = lower ? 1 : -1;
1185     // Local columns of `ineq` are at the beginning.
1186     unsigned j = getNumDimIds() + getNumSymbolIds();
1187     unsigned end = flatExpr.size() - 1;
1188     for (unsigned i = boundMap.getNumInputs(); i < end; i++, j++) {
1189       ineq[j] = lower ? -flatExpr[i] : flatExpr[i];
1190     }
1191     // Constant term.
1192     ineq[getNumCols() - 1] =
1193         lower ? -flatExpr[flatExpr.size() - 1]
1194               // Upper bound in flattenedExpr is an exclusive one.
1195               : flatExpr[flatExpr.size() - 1] - 1;
1196     type == BoundType::EQ ? addEquality(ineq) : addInequality(ineq);
1197   }
1198 
1199   return success();
1200 }
1201 
1202 AffineMap
1203 FlatAffineValueConstraints::computeAlignedMap(AffineMap map,
1204                                               ValueRange operands) const {
1205   assert(map.getNumInputs() == operands.size() && "number of inputs mismatch");
1206 
1207   SmallVector<Value> dims, syms;
1208 #ifndef NDEBUG
1209   SmallVector<Value> newSyms;
1210   SmallVector<Value> *newSymsPtr = &newSyms;
1211 #else
1212   SmallVector<Value> *newSymsPtr = nullptr;
1213 #endif // NDEBUG
1214 
1215   dims.reserve(numDims);
1216   syms.reserve(numSymbols);
1217   for (unsigned i = 0; i < numDims; ++i)
1218     dims.push_back(values[i] ? *values[i] : Value());
1219   for (unsigned i = numDims, e = numDims + numSymbols; i < e; ++i)
1220     syms.push_back(values[i] ? *values[i] : Value());
1221 
1222   AffineMap alignedMap =
1223       alignAffineMapWithValues(map, operands, dims, syms, newSymsPtr);
1224   // All symbols are already part of this FlatAffineConstraints.
1225   assert(syms.size() == newSymsPtr->size() && "unexpected new/missing symbols");
1226   assert(std::equal(syms.begin(), syms.end(), newSymsPtr->begin()) &&
1227          "unexpected new/missing symbols");
1228   return alignedMap;
1229 }
1230 
1231 LogicalResult FlatAffineValueConstraints::addBound(BoundType type, unsigned pos,
1232                                                    AffineMap boundMap,
1233                                                    ValueRange boundOperands) {
1234   // Fully compose map and operands; canonicalize and simplify so that we
1235   // transitively get to terminal symbols or loop IVs.
1236   auto map = boundMap;
1237   SmallVector<Value, 4> operands(boundOperands.begin(), boundOperands.end());
1238   fullyComposeAffineMapAndOperands(&map, &operands);
1239   map = simplifyAffineMap(map);
1240   canonicalizeMapAndOperands(&map, &operands);
1241   for (auto operand : operands)
1242     addInductionVarOrTerminalSymbol(operand);
1243   return addBound(type, pos, computeAlignedMap(map, operands));
1244 }
1245 
1246 // Adds slice lower bounds represented by lower bounds in 'lbMaps' and upper
1247 // bounds in 'ubMaps' to each value in `values' that appears in the constraint
1248 // system. Note that both lower/upper bounds share the same operand list
1249 // 'operands'.
1250 // This function assumes 'values.size' == 'lbMaps.size' == 'ubMaps.size', and
1251 // skips any null AffineMaps in 'lbMaps' or 'ubMaps'.
1252 // Note that both lower/upper bounds use operands from 'operands'.
1253 // Returns failure for unimplemented cases such as semi-affine expressions or
1254 // expressions with mod/floordiv.
1255 LogicalResult FlatAffineValueConstraints::addSliceBounds(
1256     ArrayRef<Value> values, ArrayRef<AffineMap> lbMaps,
1257     ArrayRef<AffineMap> ubMaps, ArrayRef<Value> operands) {
1258   assert(values.size() == lbMaps.size());
1259   assert(lbMaps.size() == ubMaps.size());
1260 
1261   for (unsigned i = 0, e = lbMaps.size(); i < e; ++i) {
1262     unsigned pos;
1263     if (!findId(values[i], &pos))
1264       continue;
1265 
1266     AffineMap lbMap = lbMaps[i];
1267     AffineMap ubMap = ubMaps[i];
1268     assert(!lbMap || lbMap.getNumInputs() == operands.size());
1269     assert(!ubMap || ubMap.getNumInputs() == operands.size());
1270 
1271     // Check if this slice is just an equality along this dimension.
1272     if (lbMap && ubMap && lbMap.getNumResults() == 1 &&
1273         ubMap.getNumResults() == 1 &&
1274         lbMap.getResult(0) + 1 == ubMap.getResult(0)) {
1275       if (failed(addBound(BoundType::EQ, pos, lbMap, operands)))
1276         return failure();
1277       continue;
1278     }
1279 
1280     // If lower or upper bound maps are null or provide no results, it implies
1281     // that the source loop was not at all sliced, and the entire loop will be a
1282     // part of the slice.
1283     if (lbMap && lbMap.getNumResults() != 0 && ubMap &&
1284         ubMap.getNumResults() != 0) {
1285       if (failed(addBound(BoundType::LB, pos, lbMap, operands)))
1286         return failure();
1287       if (failed(addBound(BoundType::UB, pos, ubMap, operands)))
1288         return failure();
1289     } else {
1290       auto loop = getForInductionVarOwner(values[i]);
1291       if (failed(this->addAffineForOpDomain(loop)))
1292         return failure();
1293     }
1294   }
1295   return success();
1296 }
1297 
1298 bool FlatAffineValueConstraints::findId(Value val, unsigned *pos) const {
1299   unsigned i = 0;
1300   for (const auto &mayBeId : values) {
1301     if (mayBeId.hasValue() && mayBeId.getValue() == val) {
1302       *pos = i;
1303       return true;
1304     }
1305     i++;
1306   }
1307   return false;
1308 }
1309 
1310 bool FlatAffineValueConstraints::containsId(Value val) const {
1311   return llvm::any_of(values, [&](const Optional<Value> &mayBeId) {
1312     return mayBeId.hasValue() && mayBeId.getValue() == val;
1313   });
1314 }
1315 
1316 void FlatAffineValueConstraints::swapId(unsigned posA, unsigned posB) {
1317   FlatAffineConstraints::swapId(posA, posB);
1318   std::swap(values[posA], values[posB]);
1319 }
1320 
1321 void FlatAffineValueConstraints::addBound(BoundType type, Value val,
1322                                           int64_t value) {
1323   unsigned pos;
1324   if (!findId(val, &pos))
1325     // This is a pre-condition for this method.
1326     assert(0 && "id not found");
1327   addBound(type, pos, value);
1328 }
1329 
1330 void FlatAffineConstraints::printSpace(raw_ostream &os) const {
1331   IntegerPolyhedron::printSpace(os);
1332   os << "(";
1333   for (unsigned i = 0, e = getNumIds(); i < e; i++) {
1334     if (auto *valueCstr = dyn_cast<const FlatAffineValueConstraints>(this)) {
1335       if (valueCstr->hasValue(i))
1336         os << "Value ";
1337       else
1338         os << "None ";
1339     } else {
1340       os << "None ";
1341     }
1342   }
1343   os << " const)\n";
1344 }
1345 
1346 void FlatAffineConstraints::clearAndCopyFrom(const IntegerPolyhedron &other) {
1347   if (auto *otherValueSet = dyn_cast<const FlatAffineValueConstraints>(&other))
1348     assert(!otherValueSet->hasValues() &&
1349            "cannot copy associated Values into FlatAffineConstraints");
1350 
1351   // Note: Assigment operator does not vtable pointer, so kind does not
1352   // change.
1353   if (auto *otherValueSet = dyn_cast<const FlatAffineConstraints>(&other))
1354     *this = *otherValueSet;
1355   else
1356     *static_cast<IntegerPolyhedron *>(this) = other;
1357 }
1358 
1359 void FlatAffineValueConstraints::clearAndCopyFrom(
1360     const IntegerPolyhedron &other) {
1361 
1362   if (auto *otherValueSet =
1363           dyn_cast<const FlatAffineValueConstraints>(&other)) {
1364     *this = *otherValueSet;
1365     return;
1366   }
1367 
1368   if (auto *otherValueSet = dyn_cast<const FlatAffineValueConstraints>(&other))
1369     *static_cast<FlatAffineConstraints *>(this) = *otherValueSet;
1370   else
1371     *static_cast<IntegerPolyhedron *>(this) = other;
1372 
1373   values.clear();
1374   values.resize(numIds, None);
1375 }
1376 
1377 void FlatAffineValueConstraints::fourierMotzkinEliminate(
1378     unsigned pos, bool darkShadow, bool *isResultIntegerExact) {
1379   SmallVector<Optional<Value>, 8> newVals;
1380   newVals.reserve(numIds - 1);
1381   newVals.append(values.begin(), values.begin() + pos);
1382   newVals.append(values.begin() + pos + 1, values.end());
1383   // Note: Base implementation discards all associated Values.
1384   FlatAffineConstraints::fourierMotzkinEliminate(pos, darkShadow,
1385                                                  isResultIntegerExact);
1386   values = newVals;
1387   assert(values.size() == getNumIds());
1388 }
1389 
1390 void FlatAffineValueConstraints::projectOut(Value val) {
1391   unsigned pos;
1392   bool ret = findId(val, &pos);
1393   assert(ret);
1394   (void)ret;
1395   fourierMotzkinEliminate(pos);
1396 }
1397 
1398 LogicalResult FlatAffineValueConstraints::unionBoundingBox(
1399     const FlatAffineValueConstraints &otherCst) {
1400   assert(otherCst.getNumDimIds() == numDims && "dims mismatch");
1401   assert(otherCst.getMaybeValues()
1402              .slice(0, getNumDimIds())
1403              .equals(getMaybeValues().slice(0, getNumDimIds())) &&
1404          "dim values mismatch");
1405   assert(otherCst.getNumLocalIds() == 0 && "local ids not supported here");
1406   assert(getNumLocalIds() == 0 && "local ids not supported yet here");
1407 
1408   // Align `other` to this.
1409   if (!areIdsAligned(*this, otherCst)) {
1410     FlatAffineValueConstraints otherCopy(otherCst);
1411     mergeAndAlignIds(/*offset=*/numDims, this, &otherCopy);
1412     return FlatAffineConstraints::unionBoundingBox(otherCopy);
1413   }
1414 
1415   return FlatAffineConstraints::unionBoundingBox(otherCst);
1416 }
1417 
1418 /// Compute an explicit representation for local vars. For all systems coming
1419 /// from MLIR integer sets, maps, or expressions where local vars were
1420 /// introduced to model floordivs and mods, this always succeeds.
1421 static LogicalResult computeLocalVars(const FlatAffineConstraints &cst,
1422                                       SmallVectorImpl<AffineExpr> &memo,
1423                                       MLIRContext *context) {
1424   unsigned numDims = cst.getNumDimIds();
1425   unsigned numSyms = cst.getNumSymbolIds();
1426 
1427   // Initialize dimensional and symbolic identifiers.
1428   for (unsigned i = 0; i < numDims; i++)
1429     memo[i] = getAffineDimExpr(i, context);
1430   for (unsigned i = numDims, e = numDims + numSyms; i < e; i++)
1431     memo[i] = getAffineSymbolExpr(i - numDims, context);
1432 
1433   bool changed;
1434   do {
1435     // Each time `changed` is true at the end of this iteration, one or more
1436     // local vars would have been detected as floordivs and set in memo; so the
1437     // number of null entries in memo[...] strictly reduces; so this converges.
1438     changed = false;
1439     for (unsigned i = 0, e = cst.getNumLocalIds(); i < e; ++i)
1440       if (!memo[numDims + numSyms + i] &&
1441           detectAsFloorDiv(cst, /*pos=*/numDims + numSyms + i, context, memo))
1442         changed = true;
1443   } while (changed);
1444 
1445   ArrayRef<AffineExpr> localExprs =
1446       ArrayRef<AffineExpr>(memo).take_back(cst.getNumLocalIds());
1447   return success(
1448       llvm::all_of(localExprs, [](AffineExpr expr) { return expr; }));
1449 }
1450 
1451 void FlatAffineValueConstraints::getIneqAsAffineValueMap(
1452     unsigned pos, unsigned ineqPos, AffineValueMap &vmap,
1453     MLIRContext *context) const {
1454   unsigned numDims = getNumDimIds();
1455   unsigned numSyms = getNumSymbolIds();
1456 
1457   assert(pos < numDims && "invalid position");
1458   assert(ineqPos < getNumInequalities() && "invalid inequality position");
1459 
1460   // Get expressions for local vars.
1461   SmallVector<AffineExpr, 8> memo(getNumIds(), AffineExpr());
1462   if (failed(computeLocalVars(*this, memo, context)))
1463     assert(false &&
1464            "one or more local exprs do not have an explicit representation");
1465   auto localExprs = ArrayRef<AffineExpr>(memo).take_back(getNumLocalIds());
1466 
1467   // Compute the AffineExpr lower/upper bound for this inequality.
1468   ArrayRef<int64_t> inequality = getInequality(ineqPos);
1469   SmallVector<int64_t, 8> bound;
1470   bound.reserve(getNumCols() - 1);
1471   // Everything other than the coefficient at `pos`.
1472   bound.append(inequality.begin(), inequality.begin() + pos);
1473   bound.append(inequality.begin() + pos + 1, inequality.end());
1474 
1475   if (inequality[pos] > 0)
1476     // Lower bound.
1477     std::transform(bound.begin(), bound.end(), bound.begin(),
1478                    std::negate<int64_t>());
1479   else
1480     // Upper bound (which is exclusive).
1481     bound.back() += 1;
1482 
1483   // Convert to AffineExpr (tree) form.
1484   auto boundExpr = getAffineExprFromFlatForm(bound, numDims - 1, numSyms,
1485                                              localExprs, context);
1486 
1487   // Get the values to bind to this affine expr (all dims and symbols).
1488   SmallVector<Value, 4> operands;
1489   getValues(0, pos, &operands);
1490   SmallVector<Value, 4> trailingOperands;
1491   getValues(pos + 1, getNumDimAndSymbolIds(), &trailingOperands);
1492   operands.append(trailingOperands.begin(), trailingOperands.end());
1493   vmap.reset(AffineMap::get(numDims - 1, numSyms, boundExpr), operands);
1494 }
1495 
1496 IntegerSet FlatAffineConstraints::getAsIntegerSet(MLIRContext *context) const {
1497   if (getNumConstraints() == 0)
1498     // Return universal set (always true): 0 == 0.
1499     return IntegerSet::get(getNumDimIds(), getNumSymbolIds(),
1500                            getAffineConstantExpr(/*constant=*/0, context),
1501                            /*eqFlags=*/true);
1502 
1503   // Construct local references.
1504   SmallVector<AffineExpr, 8> memo(getNumIds(), AffineExpr());
1505 
1506   if (failed(computeLocalVars(*this, memo, context))) {
1507     // Check if the local variables without an explicit representation have
1508     // zero coefficients everywhere.
1509     SmallVector<unsigned> noLocalRepVars;
1510     unsigned numDimsSymbols = getNumDimAndSymbolIds();
1511     for (unsigned i = numDimsSymbols, e = getNumIds(); i < e; ++i) {
1512       if (!memo[i] && !isColZero(/*pos=*/i))
1513         noLocalRepVars.push_back(i - numDimsSymbols);
1514     }
1515     if (!noLocalRepVars.empty()) {
1516       LLVM_DEBUG({
1517         llvm::dbgs() << "local variables at position(s) ";
1518         llvm::interleaveComma(noLocalRepVars, llvm::dbgs());
1519         llvm::dbgs() << " do not have an explicit representation in:\n";
1520         this->dump();
1521       });
1522       return IntegerSet();
1523     }
1524   }
1525 
1526   ArrayRef<AffineExpr> localExprs =
1527       ArrayRef<AffineExpr>(memo).take_back(getNumLocalIds());
1528 
1529   // Construct the IntegerSet from the equalities/inequalities.
1530   unsigned numDims = getNumDimIds();
1531   unsigned numSyms = getNumSymbolIds();
1532 
1533   SmallVector<bool, 16> eqFlags(getNumConstraints());
1534   std::fill(eqFlags.begin(), eqFlags.begin() + getNumEqualities(), true);
1535   std::fill(eqFlags.begin() + getNumEqualities(), eqFlags.end(), false);
1536 
1537   SmallVector<AffineExpr, 8> exprs;
1538   exprs.reserve(getNumConstraints());
1539 
1540   for (unsigned i = 0, e = getNumEqualities(); i < e; ++i)
1541     exprs.push_back(getAffineExprFromFlatForm(getEquality(i), numDims, numSyms,
1542                                               localExprs, context));
1543   for (unsigned i = 0, e = getNumInequalities(); i < e; ++i)
1544     exprs.push_back(getAffineExprFromFlatForm(getInequality(i), numDims,
1545                                               numSyms, localExprs, context));
1546   return IntegerSet::get(numDims, numSyms, exprs, eqFlags);
1547 }
1548 
1549 AffineMap mlir::alignAffineMapWithValues(AffineMap map, ValueRange operands,
1550                                          ValueRange dims, ValueRange syms,
1551                                          SmallVector<Value> *newSyms) {
1552   assert(operands.size() == map.getNumInputs() &&
1553          "expected same number of operands and map inputs");
1554   MLIRContext *ctx = map.getContext();
1555   Builder builder(ctx);
1556   SmallVector<AffineExpr> dimReplacements(map.getNumDims(), {});
1557   unsigned numSymbols = syms.size();
1558   SmallVector<AffineExpr> symReplacements(map.getNumSymbols(), {});
1559   if (newSyms) {
1560     newSyms->clear();
1561     newSyms->append(syms.begin(), syms.end());
1562   }
1563 
1564   for (const auto &operand : llvm::enumerate(operands)) {
1565     // Compute replacement dim/sym of operand.
1566     AffineExpr replacement;
1567     auto dimIt = std::find(dims.begin(), dims.end(), operand.value());
1568     auto symIt = std::find(syms.begin(), syms.end(), operand.value());
1569     if (dimIt != dims.end()) {
1570       replacement =
1571           builder.getAffineDimExpr(std::distance(dims.begin(), dimIt));
1572     } else if (symIt != syms.end()) {
1573       replacement =
1574           builder.getAffineSymbolExpr(std::distance(syms.begin(), symIt));
1575     } else {
1576       // This operand is neither a dimension nor a symbol. Add it as a new
1577       // symbol.
1578       replacement = builder.getAffineSymbolExpr(numSymbols++);
1579       if (newSyms)
1580         newSyms->push_back(operand.value());
1581     }
1582     // Add to corresponding replacements vector.
1583     if (operand.index() < map.getNumDims()) {
1584       dimReplacements[operand.index()] = replacement;
1585     } else {
1586       symReplacements[operand.index() - map.getNumDims()] = replacement;
1587     }
1588   }
1589 
1590   return map.replaceDimsAndSymbols(dimReplacements, symReplacements,
1591                                    dims.size(), numSymbols);
1592 }
1593 
1594 FlatAffineValueConstraints FlatAffineRelation::getDomainSet() const {
1595   FlatAffineValueConstraints domain = *this;
1596   // Convert all range variables to local variables.
1597   domain.convertDimToLocal(getNumDomainDims(),
1598                            getNumDomainDims() + getNumRangeDims());
1599   return domain;
1600 }
1601 
1602 FlatAffineValueConstraints FlatAffineRelation::getRangeSet() const {
1603   FlatAffineValueConstraints range = *this;
1604   // Convert all domain variables to local variables.
1605   range.convertDimToLocal(0, getNumDomainDims());
1606   return range;
1607 }
1608 
1609 void FlatAffineRelation::compose(const FlatAffineRelation &other) {
1610   assert(getNumDomainDims() == other.getNumRangeDims() &&
1611          "Domain of this and range of other do not match");
1612   assert(std::equal(values.begin(), values.begin() + getNumDomainDims(),
1613                     other.values.begin() + other.getNumDomainDims()) &&
1614          "Domain of this and range of other do not match");
1615 
1616   FlatAffineRelation rel = other;
1617 
1618   // Convert `rel` from
1619   //    [otherDomain] -> [otherRange]
1620   // to
1621   //    [otherDomain] -> [otherRange thisRange]
1622   // and `this` from
1623   //    [thisDomain] -> [thisRange]
1624   // to
1625   //    [otherDomain thisDomain] -> [thisRange].
1626   unsigned removeDims = rel.getNumRangeDims();
1627   insertDomainId(0, rel.getNumDomainDims());
1628   rel.appendRangeId(getNumRangeDims());
1629 
1630   // Merge symbol and local identifiers.
1631   mergeSymbolIds(rel);
1632   mergeLocalIds(rel);
1633 
1634   // Convert `rel` from [otherDomain] -> [otherRange thisRange] to
1635   // [otherDomain] -> [thisRange] by converting first otherRange range ids
1636   // to local ids.
1637   rel.convertDimToLocal(rel.getNumDomainDims(),
1638                         rel.getNumDomainDims() + removeDims);
1639   // Convert `this` from [otherDomain thisDomain] -> [thisRange] to
1640   // [otherDomain] -> [thisRange] by converting last thisDomain domain ids
1641   // to local ids.
1642   convertDimToLocal(getNumDomainDims() - removeDims, getNumDomainDims());
1643 
1644   auto thisMaybeValues = getMaybeDimValues();
1645   auto relMaybeValues = rel.getMaybeDimValues();
1646 
1647   // Add and match domain of `rel` to domain of `this`.
1648   for (unsigned i = 0, e = rel.getNumDomainDims(); i < e; ++i)
1649     if (relMaybeValues[i].hasValue())
1650       setValue(i, relMaybeValues[i].getValue());
1651   // Add and match range of `this` to range of `rel`.
1652   for (unsigned i = 0, e = getNumRangeDims(); i < e; ++i) {
1653     unsigned rangeIdx = rel.getNumDomainDims() + i;
1654     if (thisMaybeValues[rangeIdx].hasValue())
1655       rel.setValue(rangeIdx, thisMaybeValues[rangeIdx].getValue());
1656   }
1657 
1658   // Append `this` to `rel` and simplify constraints.
1659   rel.append(*this);
1660   rel.removeRedundantLocalVars();
1661 
1662   *this = rel;
1663 }
1664 
1665 void FlatAffineRelation::inverse() {
1666   unsigned oldDomain = getNumDomainDims();
1667   unsigned oldRange = getNumRangeDims();
1668   // Add new range ids.
1669   appendRangeId(oldDomain);
1670   // Swap new ids with domain.
1671   for (unsigned i = 0; i < oldDomain; ++i)
1672     swapId(i, oldDomain + oldRange + i);
1673   // Remove the swapped domain.
1674   removeIdRange(0, oldDomain);
1675   // Set domain and range as inverse.
1676   numDomainDims = oldRange;
1677   numRangeDims = oldDomain;
1678 }
1679 
1680 void FlatAffineRelation::insertDomainId(unsigned pos, unsigned num) {
1681   assert(pos <= getNumDomainDims() &&
1682          "Id cannot be inserted at invalid position");
1683   insertDimId(pos, num);
1684   numDomainDims += num;
1685 }
1686 
1687 void FlatAffineRelation::insertRangeId(unsigned pos, unsigned num) {
1688   assert(pos <= getNumRangeDims() &&
1689          "Id cannot be inserted at invalid position");
1690   insertDimId(getNumDomainDims() + pos, num);
1691   numRangeDims += num;
1692 }
1693 
1694 void FlatAffineRelation::appendDomainId(unsigned num) {
1695   insertDimId(getNumDomainDims(), num);
1696   numDomainDims += num;
1697 }
1698 
1699 void FlatAffineRelation::appendRangeId(unsigned num) {
1700   insertDimId(getNumDimIds(), num);
1701   numRangeDims += num;
1702 }
1703 
1704 void FlatAffineRelation::removeIdRange(unsigned idStart, unsigned idLimit) {
1705   if (idStart >= idLimit)
1706     return;
1707 
1708   // Compute number of domain and range identifiers to remove. This is done by
1709   // intersecting the range of domain/range ids with range of ids to remove.
1710   unsigned intersectDomainLHS = std::min(idLimit, getNumDomainDims());
1711   unsigned intersectDomainRHS = idStart;
1712   unsigned intersectRangeLHS = std::min(idLimit, getNumDimIds());
1713   unsigned intersectRangeRHS = std::max(idStart, getNumDomainDims());
1714 
1715   FlatAffineValueConstraints::removeIdRange(idStart, idLimit);
1716 
1717   if (intersectDomainLHS > intersectDomainRHS)
1718     numDomainDims -= intersectDomainLHS - intersectDomainRHS;
1719   if (intersectRangeLHS > intersectRangeRHS)
1720     numRangeDims -= intersectRangeLHS - intersectRangeRHS;
1721 }
1722 
1723 LogicalResult mlir::getRelationFromMap(AffineMap &map,
1724                                        FlatAffineRelation &rel) {
1725   // Get flattened affine expressions.
1726   std::vector<SmallVector<int64_t, 8>> flatExprs;
1727   FlatAffineConstraints localVarCst;
1728   if (failed(getFlattenedAffineExprs(map, &flatExprs, &localVarCst)))
1729     return failure();
1730 
1731   unsigned oldDimNum = localVarCst.getNumDimIds();
1732   unsigned oldCols = localVarCst.getNumCols();
1733   unsigned numRangeIds = map.getNumResults();
1734   unsigned numDomainIds = map.getNumDims();
1735 
1736   // Add range as the new expressions.
1737   localVarCst.appendDimId(numRangeIds);
1738 
1739   // Add equalities between source and range.
1740   SmallVector<int64_t, 8> eq(localVarCst.getNumCols());
1741   for (unsigned i = 0, e = map.getNumResults(); i < e; ++i) {
1742     // Zero fill.
1743     std::fill(eq.begin(), eq.end(), 0);
1744     // Fill equality.
1745     for (unsigned j = 0, f = oldDimNum; j < f; ++j)
1746       eq[j] = flatExprs[i][j];
1747     for (unsigned j = oldDimNum, f = oldCols; j < f; ++j)
1748       eq[j + numRangeIds] = flatExprs[i][j];
1749     // Set this dimension to -1 to equate lhs and rhs and add equality.
1750     eq[numDomainIds + i] = -1;
1751     localVarCst.addEquality(eq);
1752   }
1753 
1754   // Create relation and return success.
1755   rel = FlatAffineRelation(numDomainIds, numRangeIds, localVarCst);
1756   return success();
1757 }
1758 
1759 LogicalResult mlir::getRelationFromMap(const AffineValueMap &map,
1760                                        FlatAffineRelation &rel) {
1761 
1762   AffineMap affineMap = map.getAffineMap();
1763   if (failed(getRelationFromMap(affineMap, rel)))
1764     return failure();
1765 
1766   // Set symbol values for domain dimensions and symbols.
1767   for (unsigned i = 0, e = rel.getNumDomainDims(); i < e; ++i)
1768     rel.setValue(i, map.getOperand(i));
1769   for (unsigned i = rel.getNumDimIds(), e = rel.getNumDimAndSymbolIds(); i < e;
1770        ++i)
1771     rel.setValue(i, map.getOperand(i - rel.getNumRangeDims()));
1772 
1773   return success();
1774 }
1775