//===- AffineMap.h - MLIR Affine Map Class ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Affine maps are mathematical functions which map a list of dimension // identifiers and symbols, to multidimensional affine expressions. // //===----------------------------------------------------------------------===// #ifndef MLIR_IR_AFFINEMAP_H #define MLIR_IR_AFFINEMAP_H #include "mlir/IR/AffineExpr.h" #include "mlir/IR/Value.h" #include "mlir/Support/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallVectorExtras.h" #include namespace llvm { class SmallBitVector; } // namespace llvm namespace mlir { namespace detail { struct AffineMapStorage; } // namespace detail class Attribute; class Builder; class OpFoldResult; class MLIRContext; /// A multi-dimensional affine map /// Affine map's are immutable like Type's, and they are uniqued. /// Eg: (d0, d1) -> (d0/128, d0 mod 128, d1) /// The names used (d0, d1) don't matter - it's the mathematical function that /// is unique to this affine map. class AffineMap { public: using ImplType = detail::AffineMapStorage; constexpr AffineMap() = default; explicit AffineMap(ImplType *map) : map(map) {} /// Returns a zero result affine map with no dimensions or symbols: () -> (). static AffineMap get(MLIRContext *context); /// Returns a zero result affine map with `dimCount` dimensions and /// `symbolCount` symbols, e.g.: `(...) -> ()`. static AffineMap get(unsigned dimCount, unsigned symbolCount, MLIRContext *context); /// Returns an affine map with `dimCount` dimensions and `symbolCount` mapping /// to a single output dimension static AffineMap get(unsigned dimCount, unsigned symbolCount, AffineExpr result); /// Returns an affine map with `dimCount` dimensions and `symbolCount` mapping /// to the given results. static AffineMap get(unsigned dimCount, unsigned symbolCount, ArrayRef results, MLIRContext *context); /// Returns a single constant result affine map. static AffineMap getConstantMap(int64_t val, MLIRContext *context); /// Returns an AffineMap with 'numDims' identity result dim exprs. static AffineMap getMultiDimIdentityMap(unsigned numDims, MLIRContext *context); /// Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most /// minor dimensions. static AffineMap getMinorIdentityMap(unsigned dims, unsigned results, MLIRContext *context); /// Returns an identity affine map with `numDims` input dimensions and /// filtered results using `keepDimFilter`. If `keepDimFilter` returns true /// for a dimension, the dimension is kept in the affine map results. /// Otherwise, the dimension is dropped from the results. /// /// Examples: /// * getFilteredIdentityMap(4, [false, true, false, true]) /// -> affine_map<(d0, d1, d2, d3) -> (d1, d3)> /// * getFilteredIdentityMap(3, [false, false, true]) /// -> affine_map<(d0, d1, d2) -> (d2)> static AffineMap getFilteredIdentityMap(MLIRContext *ctx, unsigned numDims, llvm::function_ref keepDimFilter); /// Returns an AffineMap representing a permutation. /// The permutation is expressed as a non-empty vector of integers. /// E.g. the permutation `(i,j,k) -> (j,k,i)` will be expressed with /// `permutation = [1,2,0]`. All values in `permutation` must be /// integers, in the range 0..`permutation.size()-1` without duplications /// (i.e. `[1,1,2]` is an invalid permutation). static AffineMap getPermutationMap(ArrayRef permutation, MLIRContext *context); static AffineMap getPermutationMap(ArrayRef permutation, MLIRContext *context); /// Returns an affine map with `numDims` input dimensions and results /// specified by `targets`. /// /// Examples: /// * getMultiDimMapWithTargets(3, [0, 2, 1]) /// -> affine_map<(d0, d1, d2) -> (d0, d2, d1)> /// * getMultiDimMapWithTargets(3, [2, 1]) /// -> affine_map<(d0, d1, d2) -> (d2, d1)> static AffineMap getMultiDimMapWithTargets(unsigned numDims, ArrayRef targets, MLIRContext *context); /// Returns a vector of AffineMaps; each with as many results as /// `exprs.size()`, as many dims as the largest dim in `exprs` and as many /// symbols as the largest symbol in `exprs`. static SmallVector inferFromExprList(ArrayRef> exprsList, MLIRContext *context); static SmallVector inferFromExprList(ArrayRef> exprsList, MLIRContext *context); MLIRContext *getContext() const; explicit operator bool() const { return map != nullptr; } bool operator==(AffineMap other) const { return other.map == map; } bool operator!=(AffineMap other) const { return !(other.map == map); } /// Returns true if this affine map is an identity affine map. /// An identity affine map corresponds to an identity affine function on the /// dimensional identifiers. bool isIdentity() const; /// Returns true if this affine map is an identity affine map on the symbol /// identifiers. bool isSymbolIdentity() const; /// Returns true if this affine map is a minor identity, i.e. an identity /// affine map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions. bool isMinorIdentity() const; /// Returns the list of broadcast dimensions (i.e. dims indicated by value 0 /// in the result). /// Ex: /// * (d0, d1, d2) -> (0, d1) gives [0] /// * (d0, d1, d2) -> (d2, d1) gives [] /// * (d0, d1, d2, d4) -> (d0, 0, d1, 0) gives [1, 3] SmallVector getBroadcastDims() const; /// Returns true if this affine map is a minor identity up to broadcasted /// dimensions which are indicated by value 0 in the result. If /// `broadcastedDims` is not null, it will be populated with the indices of /// the broadcasted dimensions in the result array. /// Example: affine_map<(d0, d1, d2, d3, d4) -> (0, d2, 0, d4)> /// (`broadcastedDims` will contain [0, 2]) bool isMinorIdentityWithBroadcasting( SmallVectorImpl *broadcastedDims = nullptr) const; /// Return true if this affine map can be converted to a minor identity with /// broadcast by doing a permute. Return a permutation (there may be /// several) to apply to get to a minor identity with broadcasts. /// Ex: /// * (d0, d1, d2) -> (0, d1) maps to minor identity (d1, 0 = d2) with /// perm = [1, 0] and broadcast d2 /// * (d0, d1, d2) -> (d0, 0) cannot be mapped to a minor identity by /// permutation + broadcast /// * (d0, d1, d2, d3) -> (0, d1, d3) maps to minor identity (d1, 0 = d2, d3) /// with perm = [1, 0, 2] and broadcast d2 /// * (d0, d1) -> (d1, 0, 0, d0) maps to minor identity (d0, d1) with extra /// leading broadcat dimensions. The map returned would be (0, 0, d0, d1) /// with perm = [3, 0, 1, 2] bool isPermutationOfMinorIdentityWithBroadcasting( SmallVectorImpl &permutedDims) const; /// Returns true if this affine map is an empty map, i.e., () -> (). bool isEmpty() const; /// Returns true if this affine map is a single result constant function. bool isSingleConstant() const; /// Returns true if this affine map has only constant results. bool isConstant() const; /// Returns the constant result of this map. This methods asserts that the map /// has a single constant result. int64_t getSingleConstantResult() const; /// Returns the constant results of this map. This method asserts that the map /// has all constant results. SmallVector getConstantResults() const; // Prints affine map to 'os'. void print(raw_ostream &os) const; void dump() const; unsigned getNumDims() const; unsigned getNumSymbols() const; unsigned getNumResults() const; unsigned getNumInputs() const; ArrayRef getResults() const; AffineExpr getResult(unsigned idx) const; /// Extracts the position of the dimensional expression at the given result, /// when the caller knows it is safe to do so. unsigned getDimPosition(unsigned idx) const; /// Extracts the first result position where `input` dimension resides. /// Returns `std::nullopt` if `input` is not a dimension expression or cannot /// be found in results. std::optional getResultPosition(AffineExpr input) const; /// Return true if any affine expression involves AffineDimExpr `position`. bool isFunctionOfDim(unsigned position) const { return llvm::any_of(getResults(), [&](AffineExpr e) { return e.isFunctionOfDim(position); }); } /// Return true if any affine expression involves AffineSymbolExpr `position`. bool isFunctionOfSymbol(unsigned position) const { return llvm::any_of(getResults(), [&](AffineExpr e) { return e.isFunctionOfSymbol(position); }); } /// Walk all of the AffineExpr's in this mapping. Each node in an expression /// tree is visited in postorder. void walkExprs(llvm::function_ref callback) const; /// This method substitutes any uses of dimensions and symbols (e.g. /// dim#0 with dimReplacements[0]) in subexpressions and returns the modified /// expression mapping. Because this can be used to eliminate dims and /// symbols, the client needs to specify the number of dims and symbols in /// the result. The returned map always has the same number of results. AffineMap replaceDimsAndSymbols(ArrayRef dimReplacements, ArrayRef symReplacements, unsigned numResultDims, unsigned numResultSyms) const; /// Sparse replace method. Apply AffineExpr::replace(`expr`, `replacement`) to /// each of the results and return a new AffineMap with the new results and /// with the specified number of dims and symbols. AffineMap replace(AffineExpr expr, AffineExpr replacement, unsigned numResultDims, unsigned numResultSyms) const; /// Sparse replace method. Apply AffineExpr::replace(`map`) to each of the /// results and return a new AffineMap with the new results and with inferred /// number of dims and symbols. AffineMap replace(const DenseMap &map) const; /// Sparse replace method. Apply AffineExpr::replace(`map`) to each of the /// results and return a new AffineMap with the new results and with the /// specified number of dims and symbols. AffineMap replace(const DenseMap &map, unsigned numResultDims, unsigned numResultSyms) const; /// Replace dims[offset ... numDims) /// by dims[offset + shift ... shift + numDims). AffineMap shiftDims(unsigned shift, unsigned offset = 0) const { assert(offset <= getNumDims()); return AffineMap::get(getNumDims() + shift, getNumSymbols(), llvm::map_to_vector<4>( getResults(), [&](AffineExpr e) { return e.shiftDims(getNumDims(), shift, offset); }), getContext()); } /// Replace symbols[offset ... numSymbols) /// by symbols[offset + shift ... shift + numSymbols). AffineMap shiftSymbols(unsigned shift, unsigned offset = 0) const { return AffineMap::get(getNumDims(), getNumSymbols() + shift, llvm::map_to_vector<4>(getResults(), [&](AffineExpr e) { return e.shiftSymbols( getNumSymbols(), shift, offset); }), getContext()); } /// Returns a new AffineMap with the same number of dims and symbols and one /// less result at `pos`, dropped. AffineMap dropResult(int64_t pos) const { return dropResults(ArrayRef({pos})); } // Returns a new AffineMap with the same number of dims and symbols, but all // results in `positions` dropped. AffineMap dropResults(ArrayRef positions) const { SmallVector reverse_sorted_positions = llvm::to_vector(positions); llvm::sort(reverse_sorted_positions, std::greater()); auto exprs = llvm::to_vector<4>(getResults()); for (int64_t pos : reverse_sorted_positions) exprs.erase(exprs.begin() + pos); return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext()); } // Returns a new AffineMap with the same number of dims and symbols, but all // results in `positions` dropped. AffineMap dropResults(const llvm::SmallBitVector &positions) const; /// Returns a new AffineMap with the same number of dims and symbols and an /// extra result inserted at `pos`. AffineMap insertResult(AffineExpr expr, unsigned pos) const { auto exprs = llvm::to_vector<4>(getResults()); exprs.insert(exprs.begin() + pos, expr); return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext()); } /// Folds the results of the application of an affine map on the provided /// operands to a constant if possible. LogicalResult constantFold(ArrayRef operandConstants, SmallVectorImpl &results, bool *hasPoison = nullptr) const; /// Propagates the constant operands into this affine map. Operands are /// allowed to be null, at which point they are treated as non-constant. This /// does not change the number of symbols and dimensions. Returns a new map, /// which may be equal to the old map if no folding happened. If `results` is /// provided and if all expressions in the map were folded to constants, /// `results` will contain the values of these constants. AffineMap partialConstantFold(ArrayRef operandConstants, SmallVectorImpl *results = nullptr, bool *hasPoison = nullptr) const; /// Returns the AffineMap resulting from composing `this` with `map`. /// The resulting AffineMap has as many AffineDimExpr as `map` and as many /// AffineSymbolExpr as the concatenation of `this` and `map` (in which case /// the symbols of `this` map come first). /// /// Prerequisites: /// The maps are composable, i.e. that the number of AffineDimExpr of `this` /// matches the number of results of `map`. /// /// Example: /// map1: `(d0, d1)[s0, s1] -> (d0 + 1 + s1, d1 - 1 - s0)` /// map2: `(d0)[s0] -> (d0 + s0, d0 - s0)` /// map1.compose(map2): /// `(d0)[s0, s1, s2] -> (d0 + s1 + s2 + 1, d0 - s0 - s2 - 1)` AffineMap compose(AffineMap map) const; /// Applies composition by the dims of `this` to the integer `values` and /// returns the resulting values. `this` must be symbol-less. SmallVector compose(ArrayRef values) const; /// Returns the number of "zero" results (constant values == 0) in this map. /// /// Example: /// * For `(d0, d1) -> (d0, d1, 0)` returns 1 /// * For `(d0, d1, d2) -> (d0, d1)` returns 0 /// * For `(d0, d1, d2) -> (d0, 0, d1, 0, d2)` returns 2 size_t getNumOfZeroResults() const; /// Returns the AffineMap resulting from removing "zero" results (constant /// values == 0) from this map. /// /// Example: /// * For `(d0, d1) -> (d0, d1, 0)` returns `(d0, d1) -> (d0, d1)` /// * For `(d0, d1, d2) -> (d0, d1)` returns `(d0, d1, d2) -> (d0, d1)` /// * For `(d0, d1, d2) -> (d0, 0, d1, 0, d2)` returns /// `(d0, d1, d2) -> (d0, d1, d2)` AffineMap dropZeroResults(); /// Returns true if the AffineMap represents a subset (i.e. a projection) of a /// symbol-less permutation map. `allowZeroInResults` allows projected /// permutation maps with constant zero result expressions. /// TODO: Remove `allowZeroInResults` when constant zero result expressions /// are broadly supported. bool isProjectedPermutation(bool allowZeroInResults = false) const; /// Returns true if the AffineMap represents a symbol-less permutation map. bool isPermutation() const; /// Returns the map consisting of the `resultPos` subset. AffineMap getSubMap(ArrayRef resultPos) const; /// Returns the map consisting of `length` expressions starting from `start`. AffineMap getSliceMap(unsigned start, unsigned length) const; /// Returns the map consisting of the most major `numResults` results. /// Returns the null AffineMap if `numResults` == 0. /// Returns `*this` if `numResults` >= `this->getNumResults()`. AffineMap getMajorSubMap(unsigned numResults) const; /// Returns the map consisting of the most minor `numResults` results. /// Returns the null AffineMap if `numResults` == 0. /// Returns `*this` if `numResults` >= `this->getNumResults()`. AffineMap getMinorSubMap(unsigned numResults) const; /// Get the largest known divisor of all map expressions. /// For eg: for (d0, d1) -> (8*d0 + 4, 4*d1 + 2), the result is 2. /// In the case of maps with no expressions or all zero constant expressions, /// the largest known divisor is trivially the max uint64_t value. uint64_t getLargestKnownDivisorOfMapExprs(); friend ::llvm::hash_code hash_value(AffineMap arg); /// Methods supporting C API. const void *getAsOpaquePointer() const { return static_cast(map); } static AffineMap getFromOpaquePointer(const void *pointer) { return AffineMap(reinterpret_cast(const_cast(pointer))); } private: ImplType *map{nullptr}; static AffineMap getImpl(unsigned dimCount, unsigned symbolCount, ArrayRef results, MLIRContext *context); }; // Make AffineExpr hashable. inline ::llvm::hash_code hash_value(AffineMap arg) { return ::llvm::hash_value(arg.map); } /// A mutable affine map. Its affine expressions are however unique. struct MutableAffineMap { public: MutableAffineMap() = default; MutableAffineMap(AffineMap map); ArrayRef getResults() const { return results; } AffineExpr getResult(unsigned idx) const { return results[idx]; } void setResult(unsigned idx, AffineExpr result) { results[idx] = result; } unsigned getNumResults() const { return results.size(); } unsigned getNumDims() const { return numDims; } void setNumDims(unsigned d) { numDims = d; } unsigned getNumSymbols() const { return numSymbols; } void setNumSymbols(unsigned d) { numSymbols = d; } MLIRContext *getContext() const { return context; } /// Returns true if the idx'th result expression is a multiple of factor. bool isMultipleOf(unsigned idx, int64_t factor) const; /// Resets this MutableAffineMap with 'map'. void reset(AffineMap map); /// Simplify the (result) expressions in this map using analysis (used by //-simplify-affine-expr pass). void simplify(); /// Get the AffineMap corresponding to this MutableAffineMap. Note that an /// AffineMap will be uniqued and stored in context, while a mutable one /// isn't. AffineMap getAffineMap() const; private: // Same meaning as AffineMap's fields. SmallVector results; unsigned numDims = 0; unsigned numSymbols = 0; /// A pointer to the IR's context to store all newly created /// AffineExprStorage's. MLIRContext *context = nullptr; }; /// Simplifies an affine map by simplifying its underlying AffineExpr results. AffineMap simplifyAffineMap(AffineMap map); /// Drop the dims that are listed in `unusedDims`. AffineMap compressDims(AffineMap map, const llvm::SmallBitVector &unusedDims); /// Drop the dims that are not used. AffineMap compressUnusedDims(AffineMap map); /// Drop the dims that are not used by any of the individual maps in `maps`. /// Asserts that all maps in `maps` are normalized to the same number of /// dims and symbols. SmallVector compressUnusedDims(ArrayRef maps); /// Drop the symbols that are listed in `unusedSymbols`. AffineMap compressSymbols(AffineMap map, const llvm::SmallBitVector &unusedSymbols); /// Drop the symbols that are not used. AffineMap compressUnusedSymbols(AffineMap map); /// Drop the symbols that are not used by any of the individual maps in `maps`. /// Asserts that all maps in `maps` are normalized to the same number of /// dims and symbols. SmallVector compressUnusedSymbols(ArrayRef maps); /// Fold all attributes among the given operands into the affine map. Return the /// folded affine map. Return all remaining values via `remainingValues`. AffineMap foldAttributesIntoMap(Builder &b, AffineMap map, ArrayRef operands, SmallVector &remainingValues); /// Returns a map with the same dimension and symbol count as `map`, but whose /// results are the unique affine expressions of `map`. AffineMap removeDuplicateExprs(AffineMap map); /// Returns a map of codomain to domain dimensions such that the first codomain /// dimension for a particular domain dimension is selected. /// Returns an empty map if the input map is empty. /// Returns null map (not empty map) if `map` is not invertible (i.e. `map` does /// not contain a subset that is a permutation of full domain rank). /// /// Prerequisites: /// 1. `map` has no symbols. /// /// Example 1: /// /// ```mlir /// (d0, d1, d2) -> (d1, d1, d0, d2, d1, d2, d1, d0) /// 0 2 3 /// ``` /// /// returns: /// /// ```mlir /// (d0, d1, d2, d3, d4, d5, d6, d7) -> (d2, d0, d3) /// ``` /// /// Example 2: /// /// ```mlir /// (d0, d1, d2) -> (d1, d0 + d1, d0, d2, d1, d2, d1, d0) /// 0 2 3 /// ``` /// /// returns: /// /// ```mlir /// (d0, d1, d2, d3, d4, d5, d6, d7) -> (d2, d0, d3) /// ``` AffineMap inversePermutation(AffineMap map); /// Return the reverse map of a projected permutation where the projected /// dimensions are transformed into 0s. /// /// Prerequisites: `map` must be a projected permutation. /// /// Example 1: /// /// ```mlir /// affine_map<(d0, d1, d2, d3) -> (d2, d0)> /// ``` /// /// returns: /// /// ```mlir /// affine_map<(d0, d1) -> (d1, 0, d0, 0)> /// ``` /// /// Example 2: /// /// ```mlir /// affine_map<(d0, d1, d2, d3) -> (d0, d3)> /// ``` /// /// returns: /// /// ```mlir /// affine_map<(d0, d1) -> (d0, 0, 0, d1)> /// ``` /// /// Example 3: /// /// ```mlir /// affine_map<(d0, d1, d2, d3) -> (d2)> /// ``` /// /// returns: /// /// ```mlir /// affine_map<(d0) -> (0, 0, d0, 0)> /// ``` /// Example 4: /// /// ```mlir /// affine_map<(d0, d1, d2) -> (d0, 0)> /// ``` /// /// returns: /// /// ```mlir /// affine_map<(d0, d1) -> (d0, 0, 0)> /// ``` AffineMap inverseAndBroadcastProjectedPermutation(AffineMap map); /// Concatenates a list of `maps` into a single AffineMap, stepping over /// potentially empty maps. Assumes each of the underlying map has 0 symbols. /// The resulting map has a number of dims equal to the max of `maps`' dims and /// the concatenated results as its results. /// Returns an empty map if all input `maps` are empty. /// /// Example: /// When applied to the following list of 3 affine maps, /// /// ```mlir /// { /// (i, j, k) -> (i, k), /// (i, j, k) -> (k, j), /// (i, j, k) -> (i, j) /// } /// ``` /// /// Returns the map: /// /// ```mlir /// (i, j, k) -> (i, k, k, j, i, j) /// ``` AffineMap concatAffineMaps(ArrayRef maps, MLIRContext *context); /// Returns the map that results from projecting out the dimensions specified in /// `projectedDimensions`. The projected dimensions are set to 0. /// /// Example: /// 1) map : affine_map<(d0, d1, d2) -> (d0, d1)> /// projected_dimensions : {2} /// result : affine_map<(d0, d1) -> (d0, d1)> /// /// 2) map : affine_map<(d0, d1) -> (d0 + d1)> /// projected_dimensions : {1} /// result : affine_map<(d0) -> (d0)> /// /// 3) map : affine_map<(d0, d1, d2) -> (d0, d1)> /// projected_dimensions : {1} /// result : affine_map<(d0, d1) -> (d0, 0)> /// /// This function also compresses the dims when the boolean flag is true. AffineMap projectDims(AffineMap map, const llvm::SmallBitVector &projectedDimensions, bool compressDimsFlag = false); /// Symbol counterpart of `projectDims`. /// This function also compresses the symbols when the boolean flag is true. AffineMap projectSymbols(AffineMap map, const llvm::SmallBitVector &projectedSymbols, bool compressSymbolsFlag = false); /// Calls `projectDims(map, projectedDimensions, compressDimsFlag)`. /// If `compressSymbolsFlag` is true, additionally call `compressUnusedSymbols`. AffineMap getProjectedMap(AffineMap map, const llvm::SmallBitVector &projectedDimensions, bool compressDimsFlag = true, bool compressSymbolsFlag = true); // Return a bitvector where each bit set indicates a dimension that is not used // by any of the maps in the input array `maps`. llvm::SmallBitVector getUnusedDimsBitVector(ArrayRef maps); // Return a bitvector where each bit set indicates a symbol that is not used // by any of the maps in the input array `maps`. llvm::SmallBitVector getUnusedSymbolsBitVector(ArrayRef maps); /// Expand `map` to operate on `rank` dims while projecting out the dims in /// `projectedDimensions`. This amounts to composing `map` with /// `id(rank).dropResults(projectedDimensions)`. AffineMap expandDimsToRank(AffineMap map, int64_t rank, const llvm::SmallBitVector &projectedDimensions); inline raw_ostream &operator<<(raw_ostream &os, AffineMap map) { map.print(os); return os; } //===----------------------------------------------------------------------===// // Templated helper functions. //===----------------------------------------------------------------------===// /// Apply a permutation from `map` to `source` and return the result. template SmallVector applyPermutationMap(AffineMap map, llvm::ArrayRef source) { assert(map.isProjectedPermutation()); assert(map.getNumInputs() == source.size()); SmallVector result; result.reserve(map.getNumResults()); for (AffineExpr expr : map.getResults()) { if (auto dimExpr = dyn_cast(expr)) { result.push_back(source[dimExpr.getPosition()]); } else if (auto constExpr = dyn_cast(expr)) { assert(constExpr.getValue() == 0 && "Unexpected constant in projected permutation map"); result.push_back(0); } else { llvm_unreachable("Unexpected result in projected permutation map"); } } return result; } /// Calculates maximum dimension and symbol positions from the expressions /// in `exprsLists` and stores them in `maxDim` and `maxSym` respectively. template static void getMaxDimAndSymbol(ArrayRef exprsList, int64_t &maxDim, int64_t &maxSym) { for (const auto &exprs : exprsList) { for (auto expr : exprs) { expr.walk([&maxDim, &maxSym](AffineExpr e) { if (auto d = dyn_cast(e)) maxDim = std::max(maxDim, static_cast(d.getPosition())); if (auto s = dyn_cast(e)) maxSym = std::max(maxSym, static_cast(s.getPosition())); }); } } } } // namespace mlir namespace llvm { // AffineExpr hash just like pointers template <> struct DenseMapInfo { static unsigned getHashValue(mlir::AffineMap val) { return mlir::hash_value(val); } static bool isEqual(mlir::AffineMap LHS, mlir::AffineMap RHS) { return LHS == RHS; } }; } // namespace llvm #endif // MLIR_IR_AFFINEMAP_H