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