1 //===- AffineMap.h - MLIR Affine Map Class ----------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Affine maps are mathematical functions which map a list of dimension
10 // identifiers and symbols, to multidimensional affine expressions.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef MLIR_IR_AFFINEMAP_H
15 #define MLIR_IR_AFFINEMAP_H
16 
17 #include "mlir/IR/AffineExpr.h"
18 #include "mlir/Support/LLVM.h"
19 #include "llvm/ADT/ArrayRef.h"
20 #include "llvm/ADT/DenseMapInfo.h"
21 #include "llvm/ADT/SmallBitVector.h"
22 
23 namespace llvm {
24 class SmallBitVector;
25 } // namespace llvm
26 
27 namespace mlir {
28 
29 namespace detail {
30 struct AffineMapStorage;
31 } // namespace detail
32 
33 class Attribute;
34 struct LogicalResult;
35 class MLIRContext;
36 
37 /// A multi-dimensional affine map
38 /// Affine map's are immutable like Type's, and they are uniqued.
39 /// Eg: (d0, d1) -> (d0/128, d0 mod 128, d1)
40 /// The names used (d0, d1) don't matter - it's the mathematical function that
41 /// is unique to this affine map.
42 class AffineMap {
43 public:
44   using ImplType = detail::AffineMapStorage;
45 
46   constexpr AffineMap() = default;
AffineMap(ImplType * map)47   explicit AffineMap(ImplType *map) : map(map) {}
48 
49   /// Returns a zero result affine map with no dimensions or symbols: () -> ().
50   static AffineMap get(MLIRContext *context);
51 
52   /// Returns a zero result affine map with `dimCount` dimensions and
53   /// `symbolCount` symbols, e.g.: `(...) -> ()`.
54   static AffineMap get(unsigned dimCount, unsigned symbolCount,
55                        MLIRContext *context);
56 
57   /// Returns an affine map with `dimCount` dimensions and `symbolCount` mapping
58   /// to a single output dimension
59   static AffineMap get(unsigned dimCount, unsigned symbolCount,
60                        AffineExpr result);
61 
62   /// Returns an affine map with `dimCount` dimensions and `symbolCount` mapping
63   /// to the given results.
64   static AffineMap get(unsigned dimCount, unsigned symbolCount,
65                        ArrayRef<AffineExpr> results, MLIRContext *context);
66 
67   /// Returns a single constant result affine map.
68   static AffineMap getConstantMap(int64_t val, MLIRContext *context);
69 
70   /// Returns an AffineMap with 'numDims' identity result dim exprs.
71   static AffineMap getMultiDimIdentityMap(unsigned numDims,
72                                           MLIRContext *context);
73 
74   /// Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most
75   /// minor dimensions.
76   static AffineMap getMinorIdentityMap(unsigned dims, unsigned results,
77                                        MLIRContext *context);
78 
79   /// Returns an AffineMap representing a permutation.
80   /// The permutation is expressed as a non-empty vector of integers.
81   /// E.g. the permutation `(i,j,k) -> (j,k,i)` will be expressed with
82   /// `permutation = [1,2,0]`. All values in `permutation` must be
83   /// integers, in the range 0..`permutation.size()-1` without duplications
84   /// (i.e. `[1,1,2]` is an invalid permutation).
85   static AffineMap getPermutationMap(ArrayRef<unsigned> permutation,
86                                      MLIRContext *context);
87 
88   /// Returns a vector of AffineMaps; each with as many results as
89   /// `exprs.size()`, as many dims as the largest dim in `exprs` and as many
90   /// symbols as the largest symbol in `exprs`.
91   static SmallVector<AffineMap, 4>
92   inferFromExprList(ArrayRef<ArrayRef<AffineExpr>> exprsList);
93   static SmallVector<AffineMap, 4>
94   inferFromExprList(ArrayRef<SmallVector<AffineExpr, 4>> exprsList);
95 
96   MLIRContext *getContext() const;
97 
98   explicit operator bool() const { return map != nullptr; }
99   bool operator==(AffineMap other) const { return other.map == map; }
100   bool operator!=(AffineMap other) const { return !(other.map == map); }
101 
102   /// Returns true if this affine map is an identity affine map.
103   /// An identity affine map corresponds to an identity affine function on the
104   /// dimensional identifiers.
105   bool isIdentity() const;
106 
107   /// Returns true if this affine map is a minor identity, i.e. an identity
108   /// affine map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions.
109   bool isMinorIdentity() const;
110 
111   /// Returns true if this affine map is a minor identity up to broadcasted
112   /// dimensions which are indicated by value 0 in the result. If
113   /// `broadcastedDims` is not null, it will be populated with the indices of
114   /// the broadcasted dimensions in the result array.
115   /// Example: affine_map<(d0, d1, d2, d3, d4) -> (0, d2, 0, d4)>
116   ///          (`broadcastedDims` will contain [0, 2])
117   bool isMinorIdentityWithBroadcasting(
118       SmallVectorImpl<unsigned> *broadcastedDims = nullptr) const;
119 
120   /// Return true if this affine map can be converted to a minor identity with
121   /// broadcast by doing a permute. Return a permutation (there may be
122   /// several) to apply to get to a minor identity with broadcasts.
123   /// Ex:
124   ///  * (d0, d1, d2) -> (0, d1) maps to minor identity (d1, 0 = d2) with
125   ///  perm = [1, 0] and broadcast d2
126   ///  * (d0, d1, d2) -> (d0, 0) cannot be mapped to a minor identity by
127   ///  permutation + broadcast
128   ///  * (d0, d1, d2, d3) -> (0, d1, d3) maps to minor identity (d1, 0 = d2, d3)
129   ///  with perm = [1, 0, 2] and broadcast d2
130   ///  * (d0, d1) -> (d1, 0, 0, d0) maps to minor identity (d0, d1) with extra
131   ///  leading broadcat dimensions. The map returned would be (0, 0, d0, d1)
132   ///  with perm = [3, 0, 1, 2]
133   bool isPermutationOfMinorIdentityWithBroadcasting(
134       SmallVectorImpl<unsigned> &permutedDims) const;
135 
136   /// Returns true if this affine map is an empty map, i.e., () -> ().
137   bool isEmpty() const;
138 
139   /// Returns true if this affine map is a single result constant function.
140   bool isSingleConstant() const;
141 
142   /// Returns true if this affine map has only constant results.
143   bool isConstant() const;
144 
145   /// Returns the constant result of this map. This methods asserts that the map
146   /// has a single constant result.
147   int64_t getSingleConstantResult() const;
148 
149   /// Returns the constant results of this map. This method asserts that the map
150   /// has all constant results.
151   SmallVector<int64_t> getConstantResults() const;
152 
153   // Prints affine map to 'os'.
154   void print(raw_ostream &os) const;
155   void dump() const;
156 
157   unsigned getNumDims() const;
158   unsigned getNumSymbols() const;
159   unsigned getNumResults() const;
160   unsigned getNumInputs() const;
161 
162   ArrayRef<AffineExpr> getResults() const;
163   AffineExpr getResult(unsigned idx) const;
164 
165   /// Extracts the position of the dimensional expression at the given result,
166   /// when the caller knows it is safe to do so.
167   unsigned getDimPosition(unsigned idx) const;
168 
169   /// Extracts the permuted position where given input index resides.
170   /// Fails when called on a non-permutation.
171   unsigned getPermutedPosition(unsigned input) const;
172 
173   /// Return true if any affine expression involves AffineDimExpr `position`.
isFunctionOfDim(unsigned position)174   bool isFunctionOfDim(unsigned position) const {
175     return llvm::any_of(getResults(), [&](AffineExpr e) {
176       return e.isFunctionOfDim(position);
177     });
178   }
179 
180   /// Return true if any affine expression involves AffineSymbolExpr `position`.
isFunctionOfSymbol(unsigned position)181   bool isFunctionOfSymbol(unsigned position) const {
182     return llvm::any_of(getResults(), [&](AffineExpr e) {
183       return e.isFunctionOfSymbol(position);
184     });
185   }
186 
187   /// Walk all of the AffineExpr's in this mapping. Each node in an expression
188   /// tree is visited in postorder.
189   void walkExprs(llvm::function_ref<void(AffineExpr)> callback) const;
190 
191   /// This method substitutes any uses of dimensions and symbols (e.g.
192   /// dim#0 with dimReplacements[0]) in subexpressions and returns the modified
193   /// expression mapping.  Because this can be used to eliminate dims and
194   /// symbols, the client needs to specify the number of dims and symbols in
195   /// the result.  The returned map always has the same number of results.
196   AffineMap replaceDimsAndSymbols(ArrayRef<AffineExpr> dimReplacements,
197                                   ArrayRef<AffineExpr> symReplacements,
198                                   unsigned numResultDims,
199                                   unsigned numResultSyms) const;
200 
201   /// Sparse replace method. Apply AffineExpr::replace(`expr`, `replacement`) to
202   /// each of the results and return a new AffineMap with the new results and
203   /// with the specified number of dims and symbols.
204   AffineMap replace(AffineExpr expr, AffineExpr replacement,
205                     unsigned numResultDims, unsigned numResultSyms) const;
206 
207   /// Sparse replace method. Apply AffineExpr::replace(`map`) to each of the
208   /// results and return a new AffineMap with the new results and with inferred
209   /// number of dims and symbols.
210   AffineMap replace(const DenseMap<AffineExpr, AffineExpr> &map) const;
211 
212   /// Sparse replace method. Apply AffineExpr::replace(`map`) to each of the
213   /// results and return a new AffineMap with the new results and with the
214   /// specified number of dims and symbols.
215   AffineMap replace(const DenseMap<AffineExpr, AffineExpr> &map,
216                     unsigned numResultDims, unsigned numResultSyms) const;
217 
218   /// Replace dims[offset ... numDims)
219   /// by dims[offset + shift ... shift + numDims).
220   AffineMap shiftDims(unsigned shift, unsigned offset = 0) const {
221     assert(offset <= getNumDims());
222     return AffineMap::get(getNumDims() + shift, getNumSymbols(),
223                           llvm::to_vector<4>(llvm::map_range(
224                               getResults(),
225                               [&](AffineExpr e) {
226                                 return e.shiftDims(getNumDims(), shift, offset);
227                               })),
228                           getContext());
229   }
230 
231   /// Replace symbols[offset ... numSymbols)
232   /// by symbols[offset + shift ... shift + numSymbols).
233   AffineMap shiftSymbols(unsigned shift, unsigned offset = 0) const {
234     return AffineMap::get(getNumDims(), getNumSymbols() + shift,
235                           llvm::to_vector<4>(llvm::map_range(
236                               getResults(),
237                               [&](AffineExpr e) {
238                                 return e.shiftSymbols(getNumSymbols(), shift,
239                                                       offset);
240                               })),
241                           getContext());
242   }
243 
244   /// Returns a new AffineMap with the same number of dims and symbols and one
245   /// less result at `pos`, dropped.
dropResult(unsigned pos)246   AffineMap dropResult(unsigned pos) {
247     auto exprs = llvm::to_vector<4>(getResults());
248     exprs.erase(exprs.begin() + pos);
249     return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext());
250   }
251 
252   /// Returns a new AffineMap with the same number of dims and symbols and an
253   /// extra result inserted at `pos`.
insertResult(AffineExpr expr,unsigned pos)254   AffineMap insertResult(AffineExpr expr, unsigned pos) {
255     auto exprs = llvm::to_vector<4>(getResults());
256     exprs.insert(exprs.begin() + pos, expr);
257     return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext());
258   }
259 
260   /// Folds the results of the application of an affine map on the provided
261   /// operands to a constant if possible.
262   LogicalResult constantFold(ArrayRef<Attribute> operandConstants,
263                              SmallVectorImpl<Attribute> &results) const;
264 
265   /// Propagates the constant operands into this affine map. Operands are
266   /// allowed to be null, at which point they are treated as non-constant. This
267   /// does not change the number of symbols and dimensions. Returns a new map,
268   /// which may be equal to the old map if no folding happened. If `results` is
269   /// provided and if all expressions in the map were folded to constants,
270   /// `results` will contain the values of these constants.
271   AffineMap
272   partialConstantFold(ArrayRef<Attribute> operandConstants,
273                       SmallVectorImpl<int64_t> *results = nullptr) const;
274 
275   /// Returns the AffineMap resulting from composing `this` with `map`.
276   /// The resulting AffineMap has as many AffineDimExpr as `map` and as many
277   /// AffineSymbolExpr as the concatenation of `this` and `map` (in which case
278   /// the symbols of `this` map come first).
279   ///
280   /// Prerequisites:
281   /// The maps are composable, i.e. that the number of AffineDimExpr of `this`
282   /// matches the number of results of `map`.
283   ///
284   /// Example:
285   ///   map1: `(d0, d1)[s0, s1] -> (d0 + 1 + s1, d1 - 1 - s0)`
286   ///   map2: `(d0)[s0] -> (d0 + s0, d0 - s0)`
287   ///   map1.compose(map2):
288   ///     `(d0)[s0, s1, s2] -> (d0 + s1 + s2 + 1, d0 - s0 - s2 - 1)`
289   AffineMap compose(AffineMap map) const;
290 
291   /// Applies composition by the dims of `this` to the integer `values` and
292   /// returns the resulting values. `this` must be symbol-less.
293   SmallVector<int64_t, 4> compose(ArrayRef<int64_t> values) const;
294 
295   /// Returns true if the AffineMap represents a subset (i.e. a projection) of a
296   /// symbol-less permutation map. `allowZeroInResults` allows projected
297   /// permutation maps with constant zero result expressions.
298   /// TODO: Remove `allowZeroInResults` when constant zero result expressions
299   /// are broadly supported.
300   bool isProjectedPermutation(bool allowZeroInResults = false) const;
301 
302   /// Returns true if the AffineMap represents a symbol-less permutation map.
303   bool isPermutation() const;
304 
305   /// Returns the map consisting of the `resultPos` subset.
306   AffineMap getSubMap(ArrayRef<unsigned> resultPos) const;
307 
308   /// Returns the map consisting of `length` expressions starting from `start`.
309   AffineMap getSliceMap(unsigned start, unsigned length) const;
310 
311   /// Returns the map consisting of the most major `numResults` results.
312   /// Returns the null AffineMap if `numResults` == 0.
313   /// Returns `*this` if `numResults` >= `this->getNumResults()`.
314   AffineMap getMajorSubMap(unsigned numResults) const;
315 
316   /// Returns the map consisting of the most minor `numResults` results.
317   /// Returns the null AffineMap if `numResults` == 0.
318   /// Returns `*this` if `numResults` >= `this->getNumResults()`.
319   AffineMap getMinorSubMap(unsigned numResults) const;
320 
321   friend ::llvm::hash_code hash_value(AffineMap arg);
322 
323   /// Methods supporting C API.
getAsOpaquePointer()324   const void *getAsOpaquePointer() const {
325     return static_cast<const void *>(map);
326   }
getFromOpaquePointer(const void * pointer)327   static AffineMap getFromOpaquePointer(const void *pointer) {
328     return AffineMap(reinterpret_cast<ImplType *>(const_cast<void *>(pointer)));
329   }
330 
331 private:
332   ImplType *map{nullptr};
333 
334   static AffineMap getImpl(unsigned dimCount, unsigned symbolCount,
335                            ArrayRef<AffineExpr> results, MLIRContext *context);
336 };
337 
338 // Make AffineExpr hashable.
hash_value(AffineMap arg)339 inline ::llvm::hash_code hash_value(AffineMap arg) {
340   return ::llvm::hash_value(arg.map);
341 }
342 
343 /// A mutable affine map. Its affine expressions are however unique.
344 struct MutableAffineMap {
345 public:
346   MutableAffineMap() = default;
347   MutableAffineMap(AffineMap map);
348 
getResultsMutableAffineMap349   ArrayRef<AffineExpr> getResults() const { return results; }
getResultMutableAffineMap350   AffineExpr getResult(unsigned idx) const { return results[idx]; }
setResultMutableAffineMap351   void setResult(unsigned idx, AffineExpr result) { results[idx] = result; }
getNumResultsMutableAffineMap352   unsigned getNumResults() const { return results.size(); }
getNumDimsMutableAffineMap353   unsigned getNumDims() const { return numDims; }
setNumDimsMutableAffineMap354   void setNumDims(unsigned d) { numDims = d; }
getNumSymbolsMutableAffineMap355   unsigned getNumSymbols() const { return numSymbols; }
setNumSymbolsMutableAffineMap356   void setNumSymbols(unsigned d) { numSymbols = d; }
getContextMutableAffineMap357   MLIRContext *getContext() const { return context; }
358 
359   /// Returns true if the idx'th result expression is a multiple of factor.
360   bool isMultipleOf(unsigned idx, int64_t factor) const;
361 
362   /// Resets this MutableAffineMap with 'map'.
363   void reset(AffineMap map);
364 
365   /// Simplify the (result) expressions in this map using analysis (used by
366   //-simplify-affine-expr pass).
367   void simplify();
368   /// Get the AffineMap corresponding to this MutableAffineMap. Note that an
369   /// AffineMap will be uniqued and stored in context, while a mutable one
370   /// isn't.
371   AffineMap getAffineMap() const;
372 
373 private:
374   // Same meaning as AffineMap's fields.
375   SmallVector<AffineExpr, 8> results;
376   unsigned numDims = 0;
377   unsigned numSymbols = 0;
378   /// A pointer to the IR's context to store all newly created
379   /// AffineExprStorage's.
380   MLIRContext *context = nullptr;
381 };
382 
383 /// Simplifies an affine map by simplifying its underlying AffineExpr results.
384 AffineMap simplifyAffineMap(AffineMap map);
385 
386 /// Drop the dims that are not used.
387 AffineMap compressUnusedDims(AffineMap map);
388 
389 /// Drop the dims that are not used by any of the individual maps in `maps`.
390 /// Asserts that all maps in `maps` are normalized to the same number of
391 /// dims and symbols.
392 SmallVector<AffineMap> compressUnusedDims(ArrayRef<AffineMap> maps);
393 
394 /// Drop the dims that are not listed in `unusedDims`.
395 AffineMap compressDims(AffineMap map, const llvm::SmallBitVector &unusedDims);
396 
397 /// Drop the symbols that are not used.
398 AffineMap compressUnusedSymbols(AffineMap map);
399 
400 /// Drop the symbols that are not used by any of the individual maps in `maps`.
401 /// Asserts that all maps in `maps` are normalized to the same number of
402 /// dims and symbols.
403 SmallVector<AffineMap> compressUnusedSymbols(ArrayRef<AffineMap> maps);
404 
405 /// Drop the symbols that are not listed in `unusedSymbols`.
406 AffineMap compressSymbols(AffineMap map,
407                           const llvm::SmallBitVector &unusedSymbols);
408 
409 /// Returns a map with the same dimension and symbol count as `map`, but whose
410 /// results are the unique affine expressions of `map`.
411 AffineMap removeDuplicateExprs(AffineMap map);
412 
413 /// Returns a map of codomain to domain dimensions such that the first codomain
414 /// dimension for a particular domain dimension is selected.
415 /// Returns an empty map if the input map is empty.
416 /// Returns null map (not empty map) if `map` is not invertible (i.e. `map` does
417 /// not contain a subset that is a permutation of full domain rank).
418 ///
419 /// Prerequisites:
420 ///   1. `map` has no symbols.
421 ///
422 /// Example 1:
423 ///
424 /// ```mlir
425 ///    (d0, d1, d2) -> (d1, d1, d0, d2, d1, d2, d1, d0)
426 ///                      0       2   3
427 /// ```
428 ///
429 /// returns:
430 ///
431 /// ```mlir
432 ///    (d0, d1, d2, d3, d4, d5, d6, d7) -> (d2, d0, d3)
433 /// ```
434 ///
435 /// Example 2:
436 ///
437 /// ```mlir
438 ///    (d0, d1, d2) -> (d1, d0 + d1, d0, d2, d1, d2, d1, d0)
439 ///                      0            2   3
440 /// ```
441 ///
442 /// returns:
443 ///
444 /// ```mlir
445 ///    (d0, d1, d2, d3, d4, d5, d6, d7) -> (d2, d0, d3)
446 /// ```
447 AffineMap inversePermutation(AffineMap map);
448 
449 /// Return the reverse map of a projected permutation where the projected
450 /// dimensions are transformed into 0s.
451 ///
452 /// Prerequisites: `map` must be a projected permuation.
453 ///
454 /// Example 1:
455 ///
456 /// ```mlir
457 ///    affine_map<(d0, d1, d2, d3) -> (d2, d0)>
458 /// ```
459 ///
460 /// returns:
461 ///
462 /// ```mlir
463 ///    affine_map<(d0, d1) -> (d1, 0, d0, 0)>
464 /// ```
465 ///
466 /// Example 2:
467 ///
468 /// ```mlir
469 ///    affine_map<(d0, d1, d2, d3) -> (d0, d3)>
470 /// ```
471 ///
472 /// returns:
473 ///
474 /// ```mlir
475 ///    affine_map<(d0, d1) -> (d0, 0, 0, d1)>
476 /// ```
477 ///
478 /// Example 3:
479 ///
480 /// ```mlir
481 ///    affine_map<(d0, d1, d2, d3) -> (d2)>
482 /// ```
483 ///
484 /// returns:
485 ///
486 /// ```mlir
487 ///    affine_map<(d0) -> (0, 0, d0, 0)>
488 /// ```
489 /// Example 4:
490 ///
491 /// ```mlir
492 ///    affine_map<(d0, d1, d2) -> (d0, 0)>
493 /// ```
494 ///
495 /// returns:
496 ///
497 /// ```mlir
498 ///    affine_map<(d0, d1) -> (d0, 0, 0)>
499 /// ```
500 AffineMap inverseAndBroadcastProjectedPermutation(AffineMap map);
501 
502 /// Concatenates a list of `maps` into a single AffineMap, stepping over
503 /// potentially empty maps. Assumes each of the underlying map has 0 symbols.
504 /// The resulting map has a number of dims equal to the max of `maps`' dims and
505 /// the concatenated results as its results.
506 /// Returns an empty map if all input `maps` are empty.
507 ///
508 /// Example:
509 /// When applied to the following list of 3 affine maps,
510 ///
511 /// ```mlir
512 ///    {
513 ///      (i, j, k) -> (i, k),
514 ///      (i, j, k) -> (k, j),
515 ///      (i, j, k) -> (i, j)
516 ///    }
517 /// ```
518 ///
519 /// Returns the map:
520 ///
521 /// ```mlir
522 ///     (i, j, k) -> (i, k, k, j, i, j)
523 /// ```
524 AffineMap concatAffineMaps(ArrayRef<AffineMap> maps);
525 
526 /// Returns the map that results from projecting out the dimensions specified in
527 /// `projectedDimensions`. The projected dimensions are set to 0.
528 ///
529 /// Example:
530 /// 1) map                  : affine_map<(d0, d1, d2) -> (d0, d1)>
531 ///    projected_dimensions : {2}
532 ///    result               : affine_map<(d0, d1) -> (d0, d1)>
533 ///
534 /// 2) map                  : affine_map<(d0, d1) -> (d0 + d1)>
535 ///    projected_dimensions : {1}
536 ///    result               : affine_map<(d0) -> (d0)>
537 ///
538 /// 3) map                  : affine_map<(d0, d1, d2) -> (d0, d1)>
539 ///    projected_dimensions : {1}
540 ///    result               : affine_map<(d0, d1) -> (d0, 0)>
541 ///
542 /// This function also compresses unused symbols away.
543 AffineMap getProjectedMap(AffineMap map,
544                           const llvm::SmallBitVector &projectedDimensions);
545 
546 /// Apply a permutation from `map` to `source` and return the result.
547 template <typename T>
applyPermutationMap(AffineMap map,llvm::ArrayRef<T> source)548 SmallVector<T> applyPermutationMap(AffineMap map, llvm::ArrayRef<T> source) {
549   assert(map.isProjectedPermutation());
550   assert(map.getNumInputs() == source.size());
551   SmallVector<T> result;
552   result.reserve(map.getNumResults());
553   for (AffineExpr expr : map.getResults()) {
554     if (auto dimExpr = expr.dyn_cast<AffineDimExpr>()) {
555       result.push_back(source[dimExpr.getPosition()]);
556     } else if (auto constExpr = expr.dyn_cast<AffineConstantExpr>()) {
557       assert(constExpr.getValue() == 0 &&
558              "Unexpected constant in projected permutation map");
559       result.push_back(0);
560     } else {
561       llvm_unreachable("Unexpected result in projected permutation map");
562     }
563   }
564   return result;
565 }
566 
567 /// Calculates maxmimum dimension and symbol positions from the expressions
568 /// in `exprsLists` and stores them in `maxDim` and `maxSym` respectively.
569 template <typename AffineExprContainer>
getMaxDimAndSymbol(ArrayRef<AffineExprContainer> exprsList,int64_t & maxDim,int64_t & maxSym)570 static void getMaxDimAndSymbol(ArrayRef<AffineExprContainer> exprsList,
571                                int64_t &maxDim, int64_t &maxSym) {
572   for (const auto &exprs : exprsList) {
573     for (auto expr : exprs) {
574       expr.walk([&maxDim, &maxSym](AffineExpr e) {
575         if (auto d = e.dyn_cast<AffineDimExpr>())
576           maxDim = std::max(maxDim, static_cast<int64_t>(d.getPosition()));
577         if (auto s = e.dyn_cast<AffineSymbolExpr>())
578           maxSym = std::max(maxSym, static_cast<int64_t>(s.getPosition()));
579       });
580     }
581   }
582 }
583 
584 inline raw_ostream &operator<<(raw_ostream &os, AffineMap map) {
585   map.print(os);
586   return os;
587 }
588 
589 // Return a bitvector where each bit set indicates a dimension that is not used
590 // by any of the maps in the input array `maps`.
591 llvm::SmallBitVector getUnusedDimsBitVector(ArrayRef<AffineMap> maps);
592 
593 } // namespace mlir
594 
595 namespace llvm {
596 
597 // AffineExpr hash just like pointers
598 template <>
599 struct DenseMapInfo<mlir::AffineMap> {
600   static mlir::AffineMap getEmptyKey() {
601     auto *pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
602     return mlir::AffineMap(static_cast<mlir::AffineMap::ImplType *>(pointer));
603   }
604   static mlir::AffineMap getTombstoneKey() {
605     auto *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
606     return mlir::AffineMap(static_cast<mlir::AffineMap::ImplType *>(pointer));
607   }
608   static unsigned getHashValue(mlir::AffineMap val) {
609     return mlir::hash_value(val);
610   }
611   static bool isEqual(mlir::AffineMap LHS, mlir::AffineMap RHS) {
612     return LHS == RHS;
613   }
614 };
615 
616 } // namespace llvm
617 
618 #endif // MLIR_IR_AFFINEMAP_H
619