/home/arjun/llvm-project/mlir/include/mlir/IR/AffineMap.h
Line | Count | Source (jump to first uncovered line) |
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_AFFINE_MAP_H |
15 | | #define MLIR_IR_AFFINE_MAP_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 | | |
22 | | namespace mlir { |
23 | | |
24 | | namespace detail { |
25 | | struct AffineMapStorage; |
26 | | } // end namespace detail |
27 | | |
28 | | class Attribute; |
29 | | struct LogicalResult; |
30 | | class MLIRContext; |
31 | | |
32 | | /// A multi-dimensional affine map |
33 | | /// Affine map's are immutable like Type's, and they are uniqued. |
34 | | /// Eg: (d0, d1) -> (d0/128, d0 mod 128, d1) |
35 | | /// The names used (d0, d1) don't matter - it's the mathematical function that |
36 | | /// is unique to this affine map. |
37 | | class AffineMap { |
38 | | public: |
39 | | using ImplType = detail::AffineMapStorage; |
40 | | |
41 | 0 | constexpr AffineMap() : map(nullptr) {} |
42 | 0 | explicit AffineMap(ImplType *map) : map(map) {} |
43 | | |
44 | | /// Returns a zero result affine map with no dimensions or symbols: () -> (). |
45 | | static AffineMap get(MLIRContext *context); |
46 | | |
47 | | /// Returns a zero result affine map with `dimCount` dimensions and |
48 | | /// `symbolCount` symbols, e.g.: `(...) -> ()`. |
49 | | static AffineMap get(unsigned dimCount, unsigned symbolCount, |
50 | | MLIRContext *context); |
51 | | |
52 | | /// Returns an affine map with `dimCount` dimensions and `symbolCount` mapping |
53 | | /// to a single output dimension |
54 | | static AffineMap get(unsigned dimCount, unsigned symbolCount, |
55 | | AffineExpr result); |
56 | | |
57 | | /// Returns an affine map with `dimCount` dimensions and `symbolCount` mapping |
58 | | /// to the given results. |
59 | | static AffineMap get(unsigned dimCount, unsigned symbolCount, |
60 | | ArrayRef<AffineExpr> results, MLIRContext *context); |
61 | | |
62 | | /// Returns a single constant result affine map. |
63 | | static AffineMap getConstantMap(int64_t val, MLIRContext *context); |
64 | | |
65 | | /// Returns an AffineMap with 'numDims' identity result dim exprs. |
66 | | static AffineMap getMultiDimIdentityMap(unsigned numDims, |
67 | | MLIRContext *context); |
68 | | |
69 | | /// Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most |
70 | | /// minor dimensions. |
71 | | static AffineMap getMinorIdentityMap(unsigned dims, unsigned results, |
72 | | MLIRContext *context); |
73 | | |
74 | | /// Returns an AffineMap representing a permutation. |
75 | | /// The permutation is expressed as a non-empty vector of integers. |
76 | | /// E.g. the permutation `(i,j,k) -> (j,k,i)` will be expressed with |
77 | | /// `permutation = [1,2,0]`. All values in `permutation` must be |
78 | | /// integers, in the range 0..`permutation.size()-1` without duplications |
79 | | /// (i.e. `[1,1,2]` is an invalid permutation). |
80 | | static AffineMap getPermutationMap(ArrayRef<unsigned> permutation, |
81 | | MLIRContext *context); |
82 | | |
83 | | /// Returns a vector of AffineMaps; each with as many results as |
84 | | /// `exprs.size()`, as many dims as the largest dim in `exprs` and as many |
85 | | /// symbols as the largest symbol in `exprs`. |
86 | | static SmallVector<AffineMap, 4> |
87 | | inferFromExprList(ArrayRef<ArrayRef<AffineExpr>> exprsList); |
88 | | static SmallVector<AffineMap, 4> |
89 | | inferFromExprList(ArrayRef<SmallVector<AffineExpr, 4>> exprsList); |
90 | | |
91 | | MLIRContext *getContext() const; |
92 | | |
93 | 0 | explicit operator bool() { return map != nullptr; } |
94 | 0 | bool operator==(AffineMap other) const { return other.map == map; } |
95 | 0 | bool operator!=(AffineMap other) const { return !(other.map == map); } |
96 | | |
97 | | /// Returns true if this affine map is an identity affine map. |
98 | | /// An identity affine map corresponds to an identity affine function on the |
99 | | /// dimensional identifiers. |
100 | | bool isIdentity() const; |
101 | | |
102 | | /// Returns true if the map is a minor identity map, i.e. an identity affine |
103 | | /// map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions. |
104 | | static bool isMinorIdentity(AffineMap map); |
105 | | |
106 | | /// Returns true if this affine map is an empty map, i.e., () -> (). |
107 | | bool isEmpty() const; |
108 | | |
109 | | /// Returns true if this affine map is a single result constant function. |
110 | | bool isSingleConstant() const; |
111 | | |
112 | | /// Returns the constant result of this map. This methods asserts that the map |
113 | | /// has a single constant result. |
114 | | int64_t getSingleConstantResult() const; |
115 | | |
116 | | // Prints affine map to 'os'. |
117 | | void print(raw_ostream &os) const; |
118 | | void dump() const; |
119 | | |
120 | | unsigned getNumDims() const; |
121 | | unsigned getNumSymbols() const; |
122 | | unsigned getNumResults() const; |
123 | | unsigned getNumInputs() const; |
124 | | |
125 | | ArrayRef<AffineExpr> getResults() const; |
126 | | AffineExpr getResult(unsigned idx) const; |
127 | | |
128 | | /// Walk all of the AffineExpr's in this mapping. Each node in an expression |
129 | | /// tree is visited in postorder. |
130 | | void walkExprs(std::function<void(AffineExpr)> callback) const; |
131 | | |
132 | | /// This method substitutes any uses of dimensions and symbols (e.g. |
133 | | /// dim#0 with dimReplacements[0]) in subexpressions and returns the modified |
134 | | /// expression mapping. Because this can be used to eliminate dims and |
135 | | /// symbols, the client needs to specify the number of dims and symbols in |
136 | | /// the result. The returned map always has the same number of results. |
137 | | AffineMap replaceDimsAndSymbols(ArrayRef<AffineExpr> dimReplacements, |
138 | | ArrayRef<AffineExpr> symReplacements, |
139 | | unsigned numResultDims, |
140 | | unsigned numResultSyms) const; |
141 | | |
142 | | /// Folds the results of the application of an affine map on the provided |
143 | | /// operands to a constant if possible. |
144 | | LogicalResult constantFold(ArrayRef<Attribute> operandConstants, |
145 | | SmallVectorImpl<Attribute> &results) const; |
146 | | |
147 | | /// Propagates the constant operands into this affine map. Operands are |
148 | | /// allowed to be null, at which point they are treated as non-constant. This |
149 | | /// does not change the number of symbols and dimensions. Returns a new map, |
150 | | /// which may be equal to the old map if no folding happened. If `results` is |
151 | | /// provided and if all expressions in the map were folded to constants, |
152 | | /// `results` will contain the values of these constants. |
153 | | AffineMap |
154 | | partialConstantFold(ArrayRef<Attribute> operandConstants, |
155 | | SmallVectorImpl<int64_t> *results = nullptr) const; |
156 | | |
157 | | /// Returns the AffineMap resulting from composing `this` with `map`. |
158 | | /// The resulting AffineMap has as many AffineDimExpr as `map` and as many |
159 | | /// AffineSymbolExpr as the concatenation of `this` and `map` (in which case |
160 | | /// the symbols of `this` map come first). |
161 | | /// |
162 | | /// Prerequisites: |
163 | | /// The maps are composable, i.e. that the number of AffineDimExpr of `this` |
164 | | /// matches the number of results of `map`. |
165 | | /// |
166 | | /// Example: |
167 | | /// map1: `(d0, d1)[s0, s1] -> (d0 + 1 + s1, d1 - 1 - s0)` |
168 | | /// map2: `(d0)[s0] -> (d0 + s0, d0 - s0)` |
169 | | /// map1.compose(map2): |
170 | | /// `(d0)[s0, s1, s2] -> (d0 + s1 + s2 + 1, d0 - s0 - s2 - 1)` |
171 | | AffineMap compose(AffineMap map); |
172 | | |
173 | | /// Returns true if the AffineMap represents a subset (i.e. a projection) of a |
174 | | /// symbol-less permutation map. |
175 | | bool isProjectedPermutation(); |
176 | | |
177 | | /// Returns true if the AffineMap represents a symbol-less permutation map. |
178 | | bool isPermutation(); |
179 | | |
180 | | /// Returns the map consisting of the `resultPos` subset. |
181 | | AffineMap getSubMap(ArrayRef<unsigned> resultPos); |
182 | | |
183 | | friend ::llvm::hash_code hash_value(AffineMap arg); |
184 | | |
185 | | private: |
186 | | ImplType *map; |
187 | | |
188 | | static AffineMap getImpl(unsigned dimCount, unsigned symbolCount, |
189 | | ArrayRef<AffineExpr> results, MLIRContext *context); |
190 | | }; |
191 | | |
192 | | // Make AffineExpr hashable. |
193 | 0 | inline ::llvm::hash_code hash_value(AffineMap arg) { |
194 | 0 | return ::llvm::hash_value(arg.map); |
195 | 0 | } |
196 | | |
197 | | /// A mutable affine map. Its affine expressions are however unique. |
198 | | struct MutableAffineMap { |
199 | | public: |
200 | 0 | MutableAffineMap() {} |
201 | | MutableAffineMap(AffineMap map); |
202 | | |
203 | 0 | ArrayRef<AffineExpr> getResults() const { return results; } |
204 | 0 | AffineExpr getResult(unsigned idx) const { return results[idx]; } |
205 | 0 | void setResult(unsigned idx, AffineExpr result) { results[idx] = result; } |
206 | 0 | unsigned getNumResults() const { return results.size(); } |
207 | 0 | unsigned getNumDims() const { return numDims; } |
208 | 0 | void setNumDims(unsigned d) { numDims = d; } |
209 | 0 | unsigned getNumSymbols() const { return numSymbols; } |
210 | 0 | void setNumSymbols(unsigned d) { numSymbols = d; } |
211 | 0 | MLIRContext *getContext() const { return context; } |
212 | | |
213 | | /// Returns true if the idx'th result expression is a multiple of factor. |
214 | | bool isMultipleOf(unsigned idx, int64_t factor) const; |
215 | | |
216 | | /// Resets this MutableAffineMap with 'map'. |
217 | | void reset(AffineMap map); |
218 | | |
219 | | /// Simplify the (result) expressions in this map using analysis (used by |
220 | | //-simplify-affine-expr pass). |
221 | | void simplify(); |
222 | | /// Get the AffineMap corresponding to this MutableAffineMap. Note that an |
223 | | /// AffineMap will be uniqued and stored in context, while a mutable one |
224 | | /// isn't. |
225 | | AffineMap getAffineMap() const; |
226 | | |
227 | | private: |
228 | | // Same meaning as AffineMap's fields. |
229 | | SmallVector<AffineExpr, 8> results; |
230 | | unsigned numDims; |
231 | | unsigned numSymbols; |
232 | | /// A pointer to the IR's context to store all newly created |
233 | | /// AffineExprStorage's. |
234 | | MLIRContext *context; |
235 | | }; |
236 | | |
237 | | /// Simplifies an affine map by simplifying its underlying AffineExpr results. |
238 | | AffineMap simplifyAffineMap(AffineMap map); |
239 | | |
240 | | /// Returns a map with the same dimension and symbol count as `map`, but whose |
241 | | /// results are the unique affine expressions of `map`. |
242 | | AffineMap removeDuplicateExprs(AffineMap map); |
243 | | |
244 | | /// Returns a map of codomain to domain dimensions such that the first codomain |
245 | | /// dimension for a particular domain dimension is selected. |
246 | | /// Returns an empty map if the input map is empty. |
247 | | /// Returns null map (not empty map) if `map` is not invertible (i.e. `map` does |
248 | | /// not contain a subset that is a permutation of full domain rank). |
249 | | /// |
250 | | /// Prerequisites: |
251 | | /// 1. `map` has no symbols. |
252 | | /// |
253 | | /// Example 1: |
254 | | /// |
255 | | /// ```mlir |
256 | | /// (d0, d1, d2) -> (d1, d1, d0, d2, d1, d2, d1, d0) |
257 | | /// 0 2 3 |
258 | | /// ``` |
259 | | /// |
260 | | /// returns: |
261 | | /// |
262 | | /// ```mlir |
263 | | /// (d0, d1, d2, d3, d4, d5, d6, d7) -> (d2, d0, d3) |
264 | | /// ``` |
265 | | /// |
266 | | /// Example 2: |
267 | | /// |
268 | | /// ```mlir |
269 | | /// (d0, d1, d2) -> (d1, d0 + d1, d0, d2, d1, d2, d1, d0) |
270 | | /// 0 2 3 |
271 | | /// ``` |
272 | | /// |
273 | | /// returns: |
274 | | /// |
275 | | /// ```mlir |
276 | | /// (d0, d1, d2, d3, d4, d5, d6, d7) -> (d2, d0, d3) |
277 | | /// ``` |
278 | | AffineMap inversePermutation(AffineMap map); |
279 | | |
280 | | /// Concatenates a list of `maps` into a single AffineMap, stepping over |
281 | | /// potentially empty maps. Assumes each of the underlying map has 0 symbols. |
282 | | /// The resulting map has a number of dims equal to the max of `maps`' dims and |
283 | | /// the concatenated results as its results. |
284 | | /// Returns an empty map if all input `maps` are empty. |
285 | | /// |
286 | | /// Example: |
287 | | /// When applied to the following list of 3 affine maps, |
288 | | /// |
289 | | /// ```mlir |
290 | | /// { |
291 | | /// (i, j, k) -> (i, k), |
292 | | /// (i, j, k) -> (k, j), |
293 | | /// (i, j, k) -> (i, j) |
294 | | /// } |
295 | | /// ``` |
296 | | /// |
297 | | /// Returns the map: |
298 | | /// |
299 | | /// ```mlir |
300 | | /// (i, j, k) -> (i, k, k, j, i, j) |
301 | | /// ``` |
302 | | AffineMap concatAffineMaps(ArrayRef<AffineMap> maps); |
303 | | |
304 | 0 | inline raw_ostream &operator<<(raw_ostream &os, AffineMap map) { |
305 | 0 | map.print(os); |
306 | 0 | return os; |
307 | 0 | } |
308 | | } // end namespace mlir |
309 | | |
310 | | namespace llvm { |
311 | | |
312 | | // AffineExpr hash just like pointers |
313 | | template <> struct DenseMapInfo<mlir::AffineMap> { |
314 | 0 | static mlir::AffineMap getEmptyKey() { |
315 | 0 | auto pointer = llvm::DenseMapInfo<void *>::getEmptyKey(); |
316 | 0 | return mlir::AffineMap(static_cast<mlir::AffineMap::ImplType *>(pointer)); |
317 | 0 | } |
318 | 0 | static mlir::AffineMap getTombstoneKey() { |
319 | 0 | auto pointer = llvm::DenseMapInfo<void *>::getTombstoneKey(); |
320 | 0 | return mlir::AffineMap(static_cast<mlir::AffineMap::ImplType *>(pointer)); |
321 | 0 | } |
322 | 0 | static unsigned getHashValue(mlir::AffineMap val) { |
323 | 0 | return mlir::hash_value(val); |
324 | 0 | } |
325 | 0 | static bool isEqual(mlir::AffineMap LHS, mlir::AffineMap RHS) { |
326 | 0 | return LHS == RHS; |
327 | 0 | } |
328 | | }; |
329 | | |
330 | | } // namespace llvm |
331 | | |
332 | | #endif // MLIR_IR_AFFINE_MAP_H |