1 //===- AffineMap.cpp - MLIR Affine Map Classes ----------------------------===//
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 #include "mlir/IR/AffineMap.h"
10 #include "AffineMapDetail.h"
11 #include "mlir/IR/BuiltinAttributes.h"
12 #include "mlir/IR/BuiltinTypes.h"
13 #include "mlir/Support/LogicalResult.h"
14 #include "mlir/Support/MathExtras.h"
15 #include "llvm/ADT/SmallBitVector.h"
16 #include "llvm/ADT/SmallSet.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/Support/raw_ostream.h"
19 
20 using namespace mlir;
21 
22 namespace {
23 
24 // AffineExprConstantFolder evaluates an affine expression using constant
25 // operands passed in 'operandConsts'. Returns an IntegerAttr attribute
26 // representing the constant value of the affine expression evaluated on
27 // constant 'operandConsts', or nullptr if it can't be folded.
28 class AffineExprConstantFolder {
29 public:
30   AffineExprConstantFolder(unsigned numDims, ArrayRef<Attribute> operandConsts)
31       : numDims(numDims), operandConsts(operandConsts) {}
32 
33   /// Attempt to constant fold the specified affine expr, or return null on
34   /// failure.
35   IntegerAttr constantFold(AffineExpr expr) {
36     if (auto result = constantFoldImpl(expr))
37       return IntegerAttr::get(IndexType::get(expr.getContext()), *result);
38     return nullptr;
39   }
40 
41 private:
42   Optional<int64_t> constantFoldImpl(AffineExpr expr) {
43     switch (expr.getKind()) {
44     case AffineExprKind::Add:
45       return constantFoldBinExpr(
46           expr, [](int64_t lhs, int64_t rhs) { return lhs + rhs; });
47     case AffineExprKind::Mul:
48       return constantFoldBinExpr(
49           expr, [](int64_t lhs, int64_t rhs) { return lhs * rhs; });
50     case AffineExprKind::Mod:
51       return constantFoldBinExpr(
52           expr, [](int64_t lhs, int64_t rhs) { return mod(lhs, rhs); });
53     case AffineExprKind::FloorDiv:
54       return constantFoldBinExpr(
55           expr, [](int64_t lhs, int64_t rhs) { return floorDiv(lhs, rhs); });
56     case AffineExprKind::CeilDiv:
57       return constantFoldBinExpr(
58           expr, [](int64_t lhs, int64_t rhs) { return ceilDiv(lhs, rhs); });
59     case AffineExprKind::Constant:
60       return expr.cast<AffineConstantExpr>().getValue();
61     case AffineExprKind::DimId:
62       if (auto attr = operandConsts[expr.cast<AffineDimExpr>().getPosition()]
63                           .dyn_cast_or_null<IntegerAttr>())
64         return attr.getInt();
65       return llvm::None;
66     case AffineExprKind::SymbolId:
67       if (auto attr = operandConsts[numDims +
68                                     expr.cast<AffineSymbolExpr>().getPosition()]
69                           .dyn_cast_or_null<IntegerAttr>())
70         return attr.getInt();
71       return llvm::None;
72     }
73     llvm_unreachable("Unknown AffineExpr");
74   }
75 
76   // TODO: Change these to operate on APInts too.
77   Optional<int64_t> constantFoldBinExpr(AffineExpr expr,
78                                         int64_t (*op)(int64_t, int64_t)) {
79     auto binOpExpr = expr.cast<AffineBinaryOpExpr>();
80     if (auto lhs = constantFoldImpl(binOpExpr.getLHS()))
81       if (auto rhs = constantFoldImpl(binOpExpr.getRHS()))
82         return op(*lhs, *rhs);
83     return llvm::None;
84   }
85 
86   // The number of dimension operands in AffineMap containing this expression.
87   unsigned numDims;
88   // The constant valued operands used to evaluate this AffineExpr.
89   ArrayRef<Attribute> operandConsts;
90 };
91 
92 } // end anonymous namespace
93 
94 /// Returns a single constant result affine map.
95 AffineMap AffineMap::getConstantMap(int64_t val, MLIRContext *context) {
96   return get(/*dimCount=*/0, /*symbolCount=*/0,
97              {getAffineConstantExpr(val, context)});
98 }
99 
100 /// Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most
101 /// minor dimensions.
102 AffineMap AffineMap::getMinorIdentityMap(unsigned dims, unsigned results,
103                                          MLIRContext *context) {
104   assert(dims >= results && "Dimension mismatch");
105   auto id = AffineMap::getMultiDimIdentityMap(dims, context);
106   return AffineMap::get(dims, 0, id.getResults().take_back(results), context);
107 }
108 
109 bool AffineMap::isMinorIdentity() const {
110   return getNumDims() >= getNumResults() &&
111          *this ==
112              getMinorIdentityMap(getNumDims(), getNumResults(), getContext());
113 }
114 
115 /// Returns true if this affine map is a minor identity up to broadcasted
116 /// dimensions which are indicated by value 0 in the result.
117 bool AffineMap::isMinorIdentityWithBroadcasting(
118     SmallVectorImpl<unsigned> *broadcastedDims) const {
119   if (broadcastedDims)
120     broadcastedDims->clear();
121   if (getNumDims() < getNumResults())
122     return false;
123   unsigned suffixStart = getNumDims() - getNumResults();
124   for (auto idxAndExpr : llvm::enumerate(getResults())) {
125     unsigned resIdx = idxAndExpr.index();
126     AffineExpr expr = idxAndExpr.value();
127     if (auto constExpr = expr.dyn_cast<AffineConstantExpr>()) {
128       // Each result may be either a constant 0 (broadcasted dimension).
129       if (constExpr.getValue() != 0)
130         return false;
131       if (broadcastedDims)
132         broadcastedDims->push_back(resIdx);
133     } else if (auto dimExpr = expr.dyn_cast<AffineDimExpr>()) {
134       // Or it may be the input dimension corresponding to this result position.
135       if (dimExpr.getPosition() != suffixStart + resIdx)
136         return false;
137     } else {
138       return false;
139     }
140   }
141   return true;
142 }
143 
144 /// Return true if this affine map can be converted to a minor identity with
145 /// broadcast by doing a permute. Return a permutation (there may be
146 /// several) to apply to get to a minor identity with broadcasts.
147 /// Ex:
148 ///  * (d0, d1, d2) -> (0, d1) maps to minor identity (d1, 0 = d2) with
149 ///  perm = [1, 0] and broadcast d2
150 ///  * (d0, d1, d2) -> (d0, 0) cannot be mapped to a minor identity by
151 ///  permutation + broadcast
152 ///  * (d0, d1, d2, d3) -> (0, d1, d3) maps to minor identity (d1, 0 = d2, d3)
153 ///  with perm = [1, 0, 2] and broadcast d2
154 ///  * (d0, d1) -> (d1, 0, 0, d0) maps to minor identity (d0, d1) with extra
155 ///  leading broadcat dimensions. The map returned would be (0, 0, d0, d1) with
156 ///  perm = [3, 0, 1, 2]
157 bool AffineMap::isPermutationOfMinorIdentityWithBroadcasting(
158     SmallVectorImpl<unsigned> &permutedDims) const {
159   unsigned projectionStart =
160       getNumResults() < getNumInputs() ? getNumInputs() - getNumResults() : 0;
161   permutedDims.clear();
162   SmallVector<unsigned> broadcastDims;
163   permutedDims.resize(getNumResults(), 0);
164   // If there are more results than input dimensions we want the new map to
165   // start with broadcast dimensions in order to be a minor identity with
166   // broadcasting.
167   unsigned leadingBroadcast =
168       getNumResults() > getNumInputs() ? getNumResults() - getNumInputs() : 0;
169   llvm::SmallBitVector dimFound(std::max(getNumInputs(), getNumResults()),
170                                 false);
171   for (auto idxAndExpr : llvm::enumerate(getResults())) {
172     unsigned resIdx = idxAndExpr.index();
173     AffineExpr expr = idxAndExpr.value();
174     // Each result may be either a constant 0 (broadcast dimension) or a
175     // dimension.
176     if (auto constExpr = expr.dyn_cast<AffineConstantExpr>()) {
177       if (constExpr.getValue() != 0)
178         return false;
179       broadcastDims.push_back(resIdx);
180     } else if (auto dimExpr = expr.dyn_cast<AffineDimExpr>()) {
181       if (dimExpr.getPosition() < projectionStart)
182         return false;
183       unsigned newPosition =
184           dimExpr.getPosition() - projectionStart + leadingBroadcast;
185       permutedDims[resIdx] = newPosition;
186       dimFound[newPosition] = true;
187     } else {
188       return false;
189     }
190   }
191   // Find a permuation for the broadcast dimension. Since they are broadcasted
192   // any valid permutation is acceptable. We just permute the dim into a slot
193   // without an existing dimension.
194   unsigned pos = 0;
195   for (auto dim : broadcastDims) {
196     while (pos < dimFound.size() && dimFound[pos]) {
197       pos++;
198     }
199     permutedDims[dim] = pos++;
200   }
201   return true;
202 }
203 
204 /// Returns an AffineMap representing a permutation.
205 AffineMap AffineMap::getPermutationMap(ArrayRef<unsigned> permutation,
206                                        MLIRContext *context) {
207   assert(!permutation.empty() &&
208          "Cannot create permutation map from empty permutation vector");
209   SmallVector<AffineExpr, 4> affExprs;
210   for (auto index : permutation)
211     affExprs.push_back(getAffineDimExpr(index, context));
212   auto m = std::max_element(permutation.begin(), permutation.end());
213   auto permutationMap = AffineMap::get(*m + 1, 0, affExprs, context);
214   assert(permutationMap.isPermutation() && "Invalid permutation vector");
215   return permutationMap;
216 }
217 
218 template <typename AffineExprContainer>
219 static void getMaxDimAndSymbol(ArrayRef<AffineExprContainer> exprsList,
220                                int64_t &maxDim, int64_t &maxSym) {
221   for (const auto &exprs : exprsList) {
222     for (auto expr : exprs) {
223       expr.walk([&maxDim, &maxSym](AffineExpr e) {
224         if (auto d = e.dyn_cast<AffineDimExpr>())
225           maxDim = std::max(maxDim, static_cast<int64_t>(d.getPosition()));
226         if (auto s = e.dyn_cast<AffineSymbolExpr>())
227           maxSym = std::max(maxSym, static_cast<int64_t>(s.getPosition()));
228       });
229     }
230   }
231 }
232 
233 template <typename AffineExprContainer>
234 static SmallVector<AffineMap, 4>
235 inferFromExprList(ArrayRef<AffineExprContainer> exprsList) {
236   assert(!exprsList.empty());
237   assert(!exprsList[0].empty());
238   auto context = exprsList[0][0].getContext();
239   int64_t maxDim = -1, maxSym = -1;
240   getMaxDimAndSymbol(exprsList, maxDim, maxSym);
241   SmallVector<AffineMap, 4> maps;
242   maps.reserve(exprsList.size());
243   for (const auto &exprs : exprsList)
244     maps.push_back(AffineMap::get(/*dimCount=*/maxDim + 1,
245                                   /*symbolCount=*/maxSym + 1, exprs, context));
246   return maps;
247 }
248 
249 SmallVector<AffineMap, 4>
250 AffineMap::inferFromExprList(ArrayRef<ArrayRef<AffineExpr>> exprsList) {
251   return ::inferFromExprList(exprsList);
252 }
253 
254 SmallVector<AffineMap, 4>
255 AffineMap::inferFromExprList(ArrayRef<SmallVector<AffineExpr, 4>> exprsList) {
256   return ::inferFromExprList(exprsList);
257 }
258 
259 AffineMap AffineMap::getMultiDimIdentityMap(unsigned numDims,
260                                             MLIRContext *context) {
261   SmallVector<AffineExpr, 4> dimExprs;
262   dimExprs.reserve(numDims);
263   for (unsigned i = 0; i < numDims; ++i)
264     dimExprs.push_back(mlir::getAffineDimExpr(i, context));
265   return get(/*dimCount=*/numDims, /*symbolCount=*/0, dimExprs, context);
266 }
267 
268 MLIRContext *AffineMap::getContext() const { return map->context; }
269 
270 bool AffineMap::isIdentity() const {
271   if (getNumDims() != getNumResults())
272     return false;
273   ArrayRef<AffineExpr> results = getResults();
274   for (unsigned i = 0, numDims = getNumDims(); i < numDims; ++i) {
275     auto expr = results[i].dyn_cast<AffineDimExpr>();
276     if (!expr || expr.getPosition() != i)
277       return false;
278   }
279   return true;
280 }
281 
282 bool AffineMap::isEmpty() const {
283   return getNumDims() == 0 && getNumSymbols() == 0 && getNumResults() == 0;
284 }
285 
286 bool AffineMap::isSingleConstant() const {
287   return getNumResults() == 1 && getResult(0).isa<AffineConstantExpr>();
288 }
289 
290 bool AffineMap::isConstant() const {
291   return llvm::all_of(getResults(), [](AffineExpr expr) {
292     return expr.isa<AffineConstantExpr>();
293   });
294 }
295 
296 int64_t AffineMap::getSingleConstantResult() const {
297   assert(isSingleConstant() && "map must have a single constant result");
298   return getResult(0).cast<AffineConstantExpr>().getValue();
299 }
300 
301 SmallVector<int64_t> AffineMap::getConstantResults() const {
302   assert(isConstant() && "map must have only constant results");
303   SmallVector<int64_t> result;
304   for (auto expr : getResults())
305     result.emplace_back(expr.cast<AffineConstantExpr>().getValue());
306   return result;
307 }
308 
309 unsigned AffineMap::getNumDims() const {
310   assert(map && "uninitialized map storage");
311   return map->numDims;
312 }
313 unsigned AffineMap::getNumSymbols() const {
314   assert(map && "uninitialized map storage");
315   return map->numSymbols;
316 }
317 unsigned AffineMap::getNumResults() const {
318   assert(map && "uninitialized map storage");
319   return map->results.size();
320 }
321 unsigned AffineMap::getNumInputs() const {
322   assert(map && "uninitialized map storage");
323   return map->numDims + map->numSymbols;
324 }
325 
326 ArrayRef<AffineExpr> AffineMap::getResults() const {
327   assert(map && "uninitialized map storage");
328   return map->results;
329 }
330 AffineExpr AffineMap::getResult(unsigned idx) const {
331   assert(map && "uninitialized map storage");
332   return map->results[idx];
333 }
334 
335 unsigned AffineMap::getDimPosition(unsigned idx) const {
336   return getResult(idx).cast<AffineDimExpr>().getPosition();
337 }
338 
339 unsigned AffineMap::getPermutedPosition(unsigned input) const {
340   assert(isPermutation() && "invalid permutation request");
341   for (unsigned i = 0, numResults = getNumResults(); i < numResults; i++)
342     if (getDimPosition(i) == input)
343       return i;
344   llvm_unreachable("incorrect permutation request");
345 }
346 
347 /// Folds the results of the application of an affine map on the provided
348 /// operands to a constant if possible. Returns false if the folding happens,
349 /// true otherwise.
350 LogicalResult
351 AffineMap::constantFold(ArrayRef<Attribute> operandConstants,
352                         SmallVectorImpl<Attribute> &results) const {
353   // Attempt partial folding.
354   SmallVector<int64_t, 2> integers;
355   partialConstantFold(operandConstants, &integers);
356 
357   // If all expressions folded to a constant, populate results with attributes
358   // containing those constants.
359   if (integers.empty())
360     return failure();
361 
362   auto range = llvm::map_range(integers, [this](int64_t i) {
363     return IntegerAttr::get(IndexType::get(getContext()), i);
364   });
365   results.append(range.begin(), range.end());
366   return success();
367 }
368 
369 AffineMap
370 AffineMap::partialConstantFold(ArrayRef<Attribute> operandConstants,
371                                SmallVectorImpl<int64_t> *results) const {
372   assert(getNumInputs() == operandConstants.size());
373 
374   // Fold each of the result expressions.
375   AffineExprConstantFolder exprFolder(getNumDims(), operandConstants);
376   SmallVector<AffineExpr, 4> exprs;
377   exprs.reserve(getNumResults());
378 
379   for (auto expr : getResults()) {
380     auto folded = exprFolder.constantFold(expr);
381     // If did not fold to a constant, keep the original expression, and clear
382     // the integer results vector.
383     if (folded) {
384       exprs.push_back(
385           getAffineConstantExpr(folded.getInt(), folded.getContext()));
386       if (results)
387         results->push_back(folded.getInt());
388     } else {
389       exprs.push_back(expr);
390       if (results) {
391         results->clear();
392         results = nullptr;
393       }
394     }
395   }
396 
397   return get(getNumDims(), getNumSymbols(), exprs, getContext());
398 }
399 
400 /// Walk all of the AffineExpr's in this mapping. Each node in an expression
401 /// tree is visited in postorder.
402 void AffineMap::walkExprs(std::function<void(AffineExpr)> callback) const {
403   for (auto expr : getResults())
404     expr.walk(callback);
405 }
406 
407 /// This method substitutes any uses of dimensions and symbols (e.g.
408 /// dim#0 with dimReplacements[0]) in subexpressions and returns the modified
409 /// expression mapping.  Because this can be used to eliminate dims and
410 /// symbols, the client needs to specify the number of dims and symbols in
411 /// the result.  The returned map always has the same number of results.
412 AffineMap AffineMap::replaceDimsAndSymbols(ArrayRef<AffineExpr> dimReplacements,
413                                            ArrayRef<AffineExpr> symReplacements,
414                                            unsigned numResultDims,
415                                            unsigned numResultSyms) const {
416   SmallVector<AffineExpr, 8> results;
417   results.reserve(getNumResults());
418   for (auto expr : getResults())
419     results.push_back(
420         expr.replaceDimsAndSymbols(dimReplacements, symReplacements));
421   return get(numResultDims, numResultSyms, results, getContext());
422 }
423 
424 /// Sparse replace method. Apply AffineExpr::replace(`expr`, `replacement`) to
425 /// each of the results and return a new AffineMap with the new results and
426 /// with the specified number of dims and symbols.
427 AffineMap AffineMap::replace(AffineExpr expr, AffineExpr replacement,
428                              unsigned numResultDims,
429                              unsigned numResultSyms) const {
430   SmallVector<AffineExpr, 4> newResults;
431   newResults.reserve(getNumResults());
432   for (AffineExpr e : getResults())
433     newResults.push_back(e.replace(expr, replacement));
434   return AffineMap::get(numResultDims, numResultSyms, newResults, getContext());
435 }
436 
437 /// Sparse replace method. Apply AffineExpr::replace(`map`) to each of the
438 /// results and return a new AffineMap with the new results and with the
439 /// specified number of dims and symbols.
440 AffineMap AffineMap::replace(const DenseMap<AffineExpr, AffineExpr> &map,
441                              unsigned numResultDims,
442                              unsigned numResultSyms) const {
443   SmallVector<AffineExpr, 4> newResults;
444   newResults.reserve(getNumResults());
445   for (AffineExpr e : getResults())
446     newResults.push_back(e.replace(map));
447   return AffineMap::get(numResultDims, numResultSyms, newResults, getContext());
448 }
449 
450 AffineMap
451 AffineMap::replace(const DenseMap<AffineExpr, AffineExpr> &map) const {
452   SmallVector<AffineExpr, 4> newResults;
453   newResults.reserve(getNumResults());
454   for (AffineExpr e : getResults())
455     newResults.push_back(e.replace(map));
456   return AffineMap::inferFromExprList(newResults).front();
457 }
458 
459 AffineMap AffineMap::compose(AffineMap map) const {
460   assert(getNumDims() == map.getNumResults() && "Number of results mismatch");
461   // Prepare `map` by concatenating the symbols and rewriting its exprs.
462   unsigned numDims = map.getNumDims();
463   unsigned numSymbolsThisMap = getNumSymbols();
464   unsigned numSymbols = numSymbolsThisMap + map.getNumSymbols();
465   SmallVector<AffineExpr, 8> newDims(numDims);
466   for (unsigned idx = 0; idx < numDims; ++idx) {
467     newDims[idx] = getAffineDimExpr(idx, getContext());
468   }
469   SmallVector<AffineExpr, 8> newSymbols(numSymbols - numSymbolsThisMap);
470   for (unsigned idx = numSymbolsThisMap; idx < numSymbols; ++idx) {
471     newSymbols[idx - numSymbolsThisMap] =
472         getAffineSymbolExpr(idx, getContext());
473   }
474   auto newMap =
475       map.replaceDimsAndSymbols(newDims, newSymbols, numDims, numSymbols);
476   SmallVector<AffineExpr, 8> exprs;
477   exprs.reserve(getResults().size());
478   for (auto expr : getResults())
479     exprs.push_back(expr.compose(newMap));
480   return AffineMap::get(numDims, numSymbols, exprs, map.getContext());
481 }
482 
483 SmallVector<int64_t, 4> AffineMap::compose(ArrayRef<int64_t> values) const {
484   assert(getNumSymbols() == 0 && "Expected symbol-less map");
485   SmallVector<AffineExpr, 4> exprs;
486   exprs.reserve(values.size());
487   MLIRContext *ctx = getContext();
488   for (auto v : values)
489     exprs.push_back(getAffineConstantExpr(v, ctx));
490   auto resMap = compose(AffineMap::get(0, 0, exprs, ctx));
491   SmallVector<int64_t, 4> res;
492   res.reserve(resMap.getNumResults());
493   for (auto e : resMap.getResults())
494     res.push_back(e.cast<AffineConstantExpr>().getValue());
495   return res;
496 }
497 
498 bool AffineMap::isProjectedPermutation(bool allowZeroInResults) const {
499   if (getNumSymbols() > 0)
500     return false;
501 
502   // Having more results than inputs means that results have duplicated dims or
503   // zeros that can't be mapped to input dims.
504   if (getNumResults() > getNumInputs())
505     return false;
506 
507   SmallVector<bool, 8> seen(getNumInputs(), false);
508   // A projected permutation can have, at most, only one instance of each input
509   // dimension in the result expressions. Zeros are allowed as long as the
510   // number of result expressions is lower or equal than the number of input
511   // expressions.
512   for (auto expr : getResults()) {
513     if (auto dim = expr.dyn_cast<AffineDimExpr>()) {
514       if (seen[dim.getPosition()])
515         return false;
516       seen[dim.getPosition()] = true;
517     } else {
518       auto constExpr = expr.dyn_cast<AffineConstantExpr>();
519       if (!allowZeroInResults || !constExpr || constExpr.getValue() != 0)
520         return false;
521     }
522   }
523 
524   // Results are either dims or zeros and zeros can be mapped to input dims.
525   return true;
526 }
527 
528 bool AffineMap::isPermutation() const {
529   if (getNumDims() != getNumResults())
530     return false;
531   return isProjectedPermutation();
532 }
533 
534 AffineMap AffineMap::getSubMap(ArrayRef<unsigned> resultPos) const {
535   SmallVector<AffineExpr, 4> exprs;
536   exprs.reserve(resultPos.size());
537   for (auto idx : resultPos)
538     exprs.push_back(getResult(idx));
539   return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext());
540 }
541 
542 AffineMap AffineMap::getSliceMap(unsigned start, unsigned length) const {
543   return AffineMap::get(getNumDims(), getNumSymbols(),
544                         getResults().slice(start, length), getContext());
545 }
546 
547 AffineMap AffineMap::getMajorSubMap(unsigned numResults) const {
548   if (numResults == 0)
549     return AffineMap();
550   if (numResults > getNumResults())
551     return *this;
552   return getSubMap(llvm::to_vector<4>(llvm::seq<unsigned>(0, numResults)));
553 }
554 
555 AffineMap AffineMap::getMinorSubMap(unsigned numResults) const {
556   if (numResults == 0)
557     return AffineMap();
558   if (numResults > getNumResults())
559     return *this;
560   return getSubMap(llvm::to_vector<4>(
561       llvm::seq<unsigned>(getNumResults() - numResults, getNumResults())));
562 }
563 
564 AffineMap mlir::compressDims(AffineMap map,
565                              const llvm::SmallDenseSet<unsigned> &unusedDims) {
566   unsigned numDims = 0;
567   SmallVector<AffineExpr> dimReplacements;
568   dimReplacements.reserve(map.getNumDims());
569   MLIRContext *context = map.getContext();
570   for (unsigned dim = 0, e = map.getNumDims(); dim < e; ++dim) {
571     if (unusedDims.contains(dim))
572       dimReplacements.push_back(getAffineConstantExpr(0, context));
573     else
574       dimReplacements.push_back(getAffineDimExpr(numDims++, context));
575   }
576   SmallVector<AffineExpr> resultExprs;
577   resultExprs.reserve(map.getNumResults());
578   for (auto e : map.getResults())
579     resultExprs.push_back(e.replaceDims(dimReplacements));
580   return AffineMap::get(numDims, map.getNumSymbols(), resultExprs, context);
581 }
582 
583 AffineMap mlir::compressUnusedDims(AffineMap map) {
584   llvm::SmallDenseSet<unsigned> usedDims;
585   map.walkExprs([&](AffineExpr expr) {
586     if (auto dimExpr = expr.dyn_cast<AffineDimExpr>())
587       usedDims.insert(dimExpr.getPosition());
588   });
589   llvm::SmallDenseSet<unsigned> unusedDims;
590   for (unsigned d = 0, e = map.getNumDims(); d != e; ++d)
591     if (!usedDims.contains(d))
592       unusedDims.insert(d);
593   return compressDims(map, unusedDims);
594 }
595 
596 static SmallVector<AffineMap>
597 compressUnusedImpl(ArrayRef<AffineMap> maps,
598                    llvm::function_ref<AffineMap(AffineMap)> compressionFun) {
599   if (maps.empty())
600     return SmallVector<AffineMap>();
601   SmallVector<AffineExpr> allExprs;
602   allExprs.reserve(maps.size() * maps.front().getNumResults());
603   unsigned numDims = maps.front().getNumDims(),
604            numSymbols = maps.front().getNumSymbols();
605   for (auto m : maps) {
606     assert(numDims == m.getNumDims() && numSymbols == m.getNumSymbols() &&
607            "expected maps with same num dims and symbols");
608     llvm::append_range(allExprs, m.getResults());
609   }
610   AffineMap unifiedMap = compressionFun(
611       AffineMap::get(numDims, numSymbols, allExprs, maps.front().getContext()));
612   unsigned unifiedNumDims = unifiedMap.getNumDims(),
613            unifiedNumSymbols = unifiedMap.getNumSymbols();
614   ArrayRef<AffineExpr> unifiedResults = unifiedMap.getResults();
615   SmallVector<AffineMap> res;
616   res.reserve(maps.size());
617   for (auto m : maps) {
618     res.push_back(AffineMap::get(unifiedNumDims, unifiedNumSymbols,
619                                  unifiedResults.take_front(m.getNumResults()),
620                                  m.getContext()));
621     unifiedResults = unifiedResults.drop_front(m.getNumResults());
622   }
623   return res;
624 }
625 
626 SmallVector<AffineMap> mlir::compressUnusedDims(ArrayRef<AffineMap> maps) {
627   return compressUnusedImpl(maps,
628                             [](AffineMap m) { return compressUnusedDims(m); });
629 }
630 
631 AffineMap
632 mlir::compressSymbols(AffineMap map,
633                       const llvm::SmallDenseSet<unsigned> &unusedSymbols) {
634   unsigned numSymbols = 0;
635   SmallVector<AffineExpr> symReplacements;
636   symReplacements.reserve(map.getNumSymbols());
637   MLIRContext *context = map.getContext();
638   for (unsigned sym = 0, e = map.getNumSymbols(); sym < e; ++sym) {
639     if (unusedSymbols.contains(sym))
640       symReplacements.push_back(getAffineConstantExpr(0, context));
641     else
642       symReplacements.push_back(getAffineSymbolExpr(numSymbols++, context));
643   }
644   SmallVector<AffineExpr> resultExprs;
645   resultExprs.reserve(map.getNumResults());
646   for (auto e : map.getResults())
647     resultExprs.push_back(e.replaceSymbols(symReplacements));
648   return AffineMap::get(map.getNumDims(), numSymbols, resultExprs, context);
649 }
650 
651 AffineMap mlir::compressUnusedSymbols(AffineMap map) {
652   llvm::SmallDenseSet<unsigned> usedSymbols;
653   map.walkExprs([&](AffineExpr expr) {
654     if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>())
655       usedSymbols.insert(symExpr.getPosition());
656   });
657   llvm::SmallDenseSet<unsigned> unusedSymbols;
658   for (unsigned d = 0, e = map.getNumSymbols(); d != e; ++d)
659     if (!usedSymbols.contains(d))
660       unusedSymbols.insert(d);
661   return compressSymbols(map, unusedSymbols);
662 }
663 
664 SmallVector<AffineMap> mlir::compressUnusedSymbols(ArrayRef<AffineMap> maps) {
665   return compressUnusedImpl(
666       maps, [](AffineMap m) { return compressUnusedSymbols(m); });
667 }
668 
669 AffineMap mlir::simplifyAffineMap(AffineMap map) {
670   SmallVector<AffineExpr, 8> exprs;
671   for (auto e : map.getResults()) {
672     exprs.push_back(
673         simplifyAffineExpr(e, map.getNumDims(), map.getNumSymbols()));
674   }
675   return AffineMap::get(map.getNumDims(), map.getNumSymbols(), exprs,
676                         map.getContext());
677 }
678 
679 AffineMap mlir::removeDuplicateExprs(AffineMap map) {
680   auto results = map.getResults();
681   SmallVector<AffineExpr, 4> uniqueExprs(results.begin(), results.end());
682   uniqueExprs.erase(std::unique(uniqueExprs.begin(), uniqueExprs.end()),
683                     uniqueExprs.end());
684   return AffineMap::get(map.getNumDims(), map.getNumSymbols(), uniqueExprs,
685                         map.getContext());
686 }
687 
688 AffineMap mlir::inversePermutation(AffineMap map) {
689   if (map.isEmpty())
690     return map;
691   assert(map.getNumSymbols() == 0 && "expected map without symbols");
692   SmallVector<AffineExpr, 4> exprs(map.getNumDims());
693   for (auto en : llvm::enumerate(map.getResults())) {
694     auto expr = en.value();
695     // Skip non-permutations.
696     if (auto d = expr.dyn_cast<AffineDimExpr>()) {
697       if (exprs[d.getPosition()])
698         continue;
699       exprs[d.getPosition()] = getAffineDimExpr(en.index(), d.getContext());
700     }
701   }
702   SmallVector<AffineExpr, 4> seenExprs;
703   seenExprs.reserve(map.getNumDims());
704   for (auto expr : exprs)
705     if (expr)
706       seenExprs.push_back(expr);
707   if (seenExprs.size() != map.getNumInputs())
708     return AffineMap();
709   return AffineMap::get(map.getNumResults(), 0, seenExprs, map.getContext());
710 }
711 
712 AffineMap mlir::inverseAndBroadcastProjectedPermuation(AffineMap map) {
713   assert(map.isProjectedPermutation(/*allowZeroInResults=*/true));
714   MLIRContext *context = map.getContext();
715   AffineExpr zero = mlir::getAffineConstantExpr(0, context);
716   // Start with all the results as 0.
717   SmallVector<AffineExpr, 4> exprs(map.getNumInputs(), zero);
718   for (unsigned i : llvm::seq(unsigned(0), map.getNumResults())) {
719     // Skip zeros from input map. 'exprs' is already initialized to zero.
720     if (auto constExpr = map.getResult(i).dyn_cast<AffineConstantExpr>()) {
721       assert(constExpr.getValue() == 0 &&
722              "Unexpected constant in projected permutation");
723       (void)constExpr;
724       continue;
725     }
726 
727     // Reverse each dimension existing in the original map result.
728     exprs[map.getDimPosition(i)] = getAffineDimExpr(i, context);
729   }
730   return AffineMap::get(map.getNumResults(), /*symbolCount=*/0, exprs, context);
731 }
732 
733 AffineMap mlir::concatAffineMaps(ArrayRef<AffineMap> maps) {
734   unsigned numResults = 0, numDims = 0, numSymbols = 0;
735   for (auto m : maps)
736     numResults += m.getNumResults();
737   SmallVector<AffineExpr, 8> results;
738   results.reserve(numResults);
739   for (auto m : maps) {
740     for (auto res : m.getResults())
741       results.push_back(res.shiftSymbols(m.getNumSymbols(), numSymbols));
742 
743     numSymbols += m.getNumSymbols();
744     numDims = std::max(m.getNumDims(), numDims);
745   }
746   return AffineMap::get(numDims, numSymbols, results,
747                         maps.front().getContext());
748 }
749 
750 AffineMap
751 mlir::getProjectedMap(AffineMap map,
752                       const llvm::SmallDenseSet<unsigned> &unusedDims) {
753   return compressUnusedSymbols(compressDims(map, unusedDims));
754 }
755 
756 //===----------------------------------------------------------------------===//
757 // MutableAffineMap.
758 //===----------------------------------------------------------------------===//
759 
760 MutableAffineMap::MutableAffineMap(AffineMap map)
761     : numDims(map.getNumDims()), numSymbols(map.getNumSymbols()),
762       context(map.getContext()) {
763   for (auto result : map.getResults())
764     results.push_back(result);
765 }
766 
767 void MutableAffineMap::reset(AffineMap map) {
768   results.clear();
769   numDims = map.getNumDims();
770   numSymbols = map.getNumSymbols();
771   context = map.getContext();
772   for (auto result : map.getResults())
773     results.push_back(result);
774 }
775 
776 bool MutableAffineMap::isMultipleOf(unsigned idx, int64_t factor) const {
777   if (results[idx].isMultipleOf(factor))
778     return true;
779 
780   // TODO: use simplifyAffineExpr and FlatAffineConstraints to
781   // complete this (for a more powerful analysis).
782   return false;
783 }
784 
785 // Simplifies the result affine expressions of this map. The expressions have to
786 // be pure for the simplification implemented.
787 void MutableAffineMap::simplify() {
788   // Simplify each of the results if possible.
789   // TODO: functional-style map
790   for (unsigned i = 0, e = getNumResults(); i < e; i++) {
791     results[i] = simplifyAffineExpr(getResult(i), numDims, numSymbols);
792   }
793 }
794 
795 AffineMap MutableAffineMap::getAffineMap() const {
796   return AffineMap::get(numDims, numSymbols, results, context);
797 }
798