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