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