blob: c0609eb971e23e6baa57138fb62619864b083c79 [file]
// Copyright 2022 The IREE Authors
//
// Licensed 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
#ifndef IREE_COMPILER_DIALECT_LINALGEXT_UTILS_UTILS_H_
#define IREE_COMPILER_DIALECT_LINALGEXT_UTILS_UTILS_H_
#include "mlir/Dialect/Linalg/IR/LinalgInterfaces.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Dialect.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/PatternMatch.h"
namespace mlir {
struct Range;
}; // namespace mlir
namespace mlir::iree_compiler::IREE::LinalgExt {
// Helper method to add 2 OpFoldResult inputs with affine.apply.
OpFoldResult addOfrs(OpBuilder &builder, Location loc, OpFoldResult a,
OpFoldResult b);
// Helper method to multiply 2 OpFoldResult inputs with affine.apply.
OpFoldResult mulOfrs(OpBuilder &builder, Location loc, OpFoldResult a,
OpFoldResult b);
/// Returns a `memref.dim` or `tensor.dim` operation to get the shape of `v` at
/// `dim`.
Value getDimValue(OpBuilder &builder, Location loc, Value v, int64_t dim);
/// Returns a `memref.dim` or `tensor.dim` operation to get the shape of `v` at
/// `dim`. If the shape is constant, returns the shape as an `IntegerAttr`.
OpFoldResult getDim(OpBuilder &builder, Location loc, Value v, int64_t dim);
SmallVector<OpFoldResult> getDims(OpBuilder &builder, Location loc, Value v);
/// Returns a `memref.subview` or a `tensor.extract_slice` based on the type of
/// `src`.
Operation *getSlice(OpBuilder &b, Location loc, Value src,
ArrayRef<Range> slice);
Operation *getSlice(OpBuilder &b, Location loc, Value src,
ArrayRef<OpFoldResult> offsets,
ArrayRef<OpFoldResult> sizes,
ArrayRef<OpFoldResult> strides);
/// Returns a `memref.cast` or `tensor.cast` based on the type of `src`.
Value castValue(OpBuilder &builder, Location loc, Value src, ShapedType type);
/// Returns a vector that interchanges `elements` starting at offset `offset`
/// based on the indexes in `interchangeVector`.
template <typename T>
SmallVector<T> interchange(ArrayRef<T> elements,
ArrayRef<int64_t> interchangeVector,
int offset = 0) {
SmallVector<T> vec = llvm::to_vector(elements);
for (auto [idx, val] : llvm::enumerate(interchangeVector)) {
vec[idx + offset] = elements[val + offset];
}
return vec;
}
template <typename T>
SmallVector<T> undoInterchange(ArrayRef<T> elements,
ArrayRef<int64_t> interchangeVector,
int offset = 0) {
SmallVector<T> vec = llvm::to_vector(elements);
for (auto [idx, val] : llvm::enumerate(interchangeVector)) {
vec[val + offset] = elements[idx + offset];
}
return vec;
}
/// Returns the `interchangeVector` based on `dimsPos`.
SmallVector<int64_t> computeInterchangeFromDimPos(ArrayRef<int64_t> dimsPos,
int64_t rank);
/// Converts a 2D float array to a constant value. The 2D array is stored as
/// a 1D row-major array in `val` and has shape `rows` x `cols`.
Value createValueFrom2DConstant(const float *val, int64_t rows, int64_t cols,
Location loc, RewriterBase &rewriter);
// Converts OpFoldResults to int64_t shape entries, unconditionally mapping all
// Value's to kDynamic, even if they are arith.constant values.
SmallVector<int64_t> asShapeWithAnyValueAsDynamic(ArrayRef<OpFoldResult> ofrs);
enum class Permutation {
NCHW_TO_NHWC,
NHWC_TO_NCHW,
FCHW_TO_HWCF,
TTNHWC_TO_TTNCHW,
TTNCHW_TO_TTNHWC,
TTFC_TO_TTCF,
};
// Permutes the elements of a SmallVector depending on the permutation specified
template <Permutation P, typename T>
static void permute(SmallVectorImpl<T> &vector) {
switch (P) {
case Permutation::NCHW_TO_NHWC:
std::rotate(vector.begin() + 1, vector.begin() + 2, vector.end());
break;
case Permutation::NHWC_TO_NCHW:
std::rotate(vector.rbegin(), vector.rbegin() + 1, vector.rend() - 1);
break;
case Permutation::FCHW_TO_HWCF:
std::rotate(vector.begin(), vector.begin() + 2, vector.end()); // to HWFC
std::rotate(vector.rbegin(), vector.rbegin() + 1, vector.rend() - 2);
break;
case Permutation::TTNCHW_TO_TTNHWC:
std::rotate(vector.begin() + 3, vector.begin() + 4, vector.end());
break;
case Permutation::TTNHWC_TO_TTNCHW:
std::rotate(vector.rbegin(), vector.rbegin() + 1, vector.rend() - 3);
break;
case Permutation::TTFC_TO_TTCF:
std::rotate(vector.rbegin(), vector.rbegin() + 1, vector.rend() - 2);
break;
default:
break;
}
}
/// Return dim expresssions that can be used as replacements in map that
/// contains `numSymbols` symbols. The new dim expressions have positions
/// `numDims, numDims + 1, numDims + 2, ...., numDims + numSymbols - 1`.
SmallVector<AffineExpr> getDimExprsForSymbols(MLIRContext *context,
unsigned numDims,
unsigned numSymbols);
/// Convert all symbols in the map to dim expressions, such that the new dim
/// expressions have positions `numDims, numDims + 1, numDims + 2, ...., numDims
/// + numSymbols - 1`.
AffineMap convertDimsToSymbols(AffineMap map, unsigned numDims,
unsigned numSymbols,
SmallVector<AffineExpr> &symbolReplacements);
SmallVector<AffineMap>
convertDimsToSymbols(ArrayRef<AffineMap> maps, unsigned numDims,
unsigned numSymbols,
SmallVector<AffineExpr> &symbolReplacements);
SmallVector<AffineMap> convertDimsToSymbols(MLIRContext *context,
ArrayRef<AffineMap> map,
unsigned numDims,
unsigned numSymbols);
/// Returns the indexing maps array for a convolution operation with IGEMM
/// indexing. The resulting indexing maps should represent the indexing of some
/// contraction that computes the equivalent IGEMM matmul of the convolution.
FailureOr<SmallVector<AffineMap>>
getIGEMMContractionIndexingMaps(linalg::LinalgOp linalgOp);
/// Returns the loop bounds of a convolution op with IGEMM indexing. This
/// function assumes the same ordering of dimensions as
/// getIGEMMContractionIndexingMaps;
FailureOr<SmallVector<int64_t>> getIGEMMLoopBounds(linalg::LinalgOp linalgOp);
/// Returns the operand list for a convolution with IGEMM indexing. This is
/// used to determine which inputs are the lhs and rhs, since depending on the
/// layout, the order can be different (e.g., NCHW has the lhs and rhs swapped).
FailureOr<SmallVector<Value>> getIGEMMOperands(linalg::LinalgOp linalgOp);
/// Returns true if the operation increases bitwidths of tensors.
/// This function checks that the genericOp:
/// 1. Has only one output.
/// 2. Has all parallel loops.
/// 3. Compared to the element type of the input with highest rank,
/// the output element type has a higher bitwidth.
bool isBitExtendOp(Operation *op);
/// Returns true if the operation decreases bitwidths of tensors.
/// This function checks that the genericOp:
/// 1. Has only one output.
/// 2. Has all parallel loops.
/// 3. Compared to the element type of the input with highest rank,
/// the output element type has a lower bitwidth.
bool isBitTruncateOp(Operation *op);
/// Returns true if the operation is a BroadcastOp or a GenericOp performing
/// a broadcast.
/// This function checks that the genericOp:
/// 1. Has a single input and output.
/// 2. Has all parallel loops.
/// 3. Has an identity output map.
/// 4. Has a projected permutation input map.
/// 5. The input map has fewer results than the output map.
/// 6. Has a body with only a linalg.yield op.
bool isBroadcastingOp(linalg::LinalgOp op);
/// Returns true if the operation is a `linalg.generic` that is similar in
/// effect to a gather.
/// This function checks that the genericOp:
/// 1. Has a single input and output.
/// 2. Has all parallel loops.
/// 2. `linalg.yield` consumes the result of a `tensor.extract_slice`
bool isGatherlikeOp(Operation *op);
} // namespace mlir::iree_compiler::IREE::LinalgExt
#endif // IREE_COMPILER_DIALECT_LINALGEXT_UTILS_UTILS_H_