blob: b226c9769921aa5da47d6eb52a1a6d26d06177cf [file] [log] [blame]
//===- AffineMap.h - MLIR Affine Map Class ----------------------*- C++ -*-===//
//
// Part of the MLIR 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_AFFINE_MAP_H
#define MLIR_IR_AFFINE_MAP_H
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMapInfo.h"
namespace mlir {
namespace detail {
struct AffineMapStorage;
} // end namespace detail
class AffineExpr;
class Attribute;
struct LogicalResult;
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() : map(nullptr) {}
explicit AffineMap(ImplType *map) : map(map) {}
/// Returns a zero result affine map with no dimensions or symbols: () -> ().
static AffineMap get(MLIRContext *context);
static AffineMap get(unsigned dimCount, unsigned symbolCount,
ArrayRef<AffineExpr> results);
/// 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 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<unsigned> permutation,
MLIRContext *context);
MLIRContext *getContext() const;
explicit operator bool() { 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 empty map, i.e., () -> ().
bool isEmpty() const;
/// Returns true if this affine map is a single result constant function.
bool isSingleConstant() const;
/// Returns the constant result of this map. This methods asserts that the map
/// has a single constant result.
int64_t getSingleConstantResult() 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<AffineExpr> getResults() const;
AffineExpr getResult(unsigned idx) const;
/// Walk all of the AffineExpr's in this mapping. Each node in an expression
/// tree is visited in postorder.
void walkExprs(std::function<void(AffineExpr)> 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<AffineExpr> dimReplacements,
ArrayRef<AffineExpr> symReplacements,
unsigned numResultDims,
unsigned numResultSyms);
/// Folds the results of the application of an affine map on the provided
/// operands to a constant if possible.
LogicalResult constantFold(ArrayRef<Attribute> operandConstants,
SmallVectorImpl<Attribute> &results) 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);
/// Returns true if the AffineMap represents a subset (i.e. a projection) of a
/// symbol-less permutation map.
bool isProjectedPermutation();
/// Returns true if the AffineMap represents a symbol-less permutation map.
bool isPermutation();
/// Returns the map consisting of the `resultPos` subset.
AffineMap getSubMap(ArrayRef<unsigned> resultPos);
friend ::llvm::hash_code hash_value(AffineMap arg);
private:
ImplType *map;
static AffineMap getImpl(unsigned dimCount, unsigned symbolCount,
ArrayRef<AffineExpr> results, MLIRContext *context);
};
// Make AffineExpr hashable.
inline ::llvm::hash_code hash_value(AffineMap arg) {
return ::llvm::hash_value(arg.map);
}
/// Simplify an affine map by simplifying its underlying AffineExpr results.
AffineMap simplifyAffineMap(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 or 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);
/// 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<AffineMap> maps);
inline raw_ostream &operator<<(raw_ostream &os, AffineMap map) {
map.print(os);
return os;
}
} // end namespace mlir
namespace llvm {
// AffineExpr hash just like pointers
template <> struct DenseMapInfo<mlir::AffineMap> {
static mlir::AffineMap getEmptyKey() {
auto pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
return mlir::AffineMap(static_cast<mlir::AffineMap::ImplType *>(pointer));
}
static mlir::AffineMap getTombstoneKey() {
auto pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
return mlir::AffineMap(static_cast<mlir::AffineMap::ImplType *>(pointer));
}
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_AFFINE_MAP_H