blob: abc7d8ea293a5072af8d17ffd224f885356829db [file]
// Copyright 2023 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
#include "iree/compiler/Codegen/LLVMCPU/LLVMCPUPasses.h"
#include "iree/compiler/Codegen/PassDetail.h"
#include "mlir/Dialect/Linalg/Transforms/Transforms.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/Dialect/Vector/Transforms/LoweringPatterns.h"
#include "mlir/Dialect/Vector/Transforms/Passes.h"
#include "mlir/Dialect/Vector/Transforms/VectorTransforms.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#define DEBUG_TYPE "iree-llvmcpu-vector-lowering"
namespace mlir {
namespace iree_compiler {
static bool has16x16Transpose(func::FuncOp funcOp) {
bool res = false;
funcOp.walk([&](vector::TransposeOp op) {
auto srcGtOneDims = isTranspose2DSlice(op);
if (failed(srcGtOneDims)) return WalkResult::advance();
VectorType srcType = op.getSourceVectorType();
int64_t m = srcType.getDimSize(std::get<0>(srcGtOneDims.value()));
int64_t n = srcType.getDimSize(std::get<1>(srcGtOneDims.value()));
if (m == 16 && n == 16) {
res = true;
return WalkResult::interrupt();
}
return WalkResult::advance();
});
return res;
}
namespace {
/// Pass to lower Vector ops before conversion to LLVM.
class LLVMCPUVectorLoweringPass
: public LLVMCPUVectorLoweringBase<LLVMCPUVectorLoweringPass> {
public:
using LLVMCPUVectorLoweringBase::LLVMCPUVectorLoweringBase;
LLVMCPUVectorLoweringPass(const LLVMCPUVectorLoweringPassOptions &options) {
this->splitVectorTransfersTo = options.splitVectorTransfersTo;
this->lowerVectorTransposeToAVX2 = options.lowerVectorTransposeToAVX2;
}
void getDependentDialects(DialectRegistry &registry) const override {
registry.insert<tensor::TensorDialect, linalg::LinalgDialect,
vector::VectorDialect>();
}
void runOnOperation() override;
};
void LLVMCPUVectorLoweringPass::runOnOperation() {
MLIRContext *ctx = &getContext();
auto funcOp = getOperation();
// Per-function lowering pipeline.
auto vectorTransposeLowering = vector::VectorTransposeLowering::Shuffle1D;
auto vectorMultiReductionLowering =
vector::VectorMultiReductionLowering::InnerReduction;
auto vectorContractLowering = vector::VectorContractLowering::OuterProduct;
auto vectorTransferSplit =
llvm::StringSwitch<vector::VectorTransferSplit>(
splitVectorTransfersTo.getValue())
.Case("none", vector::VectorTransferSplit::None)
.Case("linalg-copy", vector::VectorTransferSplit::LinalgCopy)
.Case("vector-transfers", vector::VectorTransferSplit::VectorTransfer)
.Default(vector::VectorTransferSplit::None);
auto vectorTransformOptions =
vector::VectorTransformsOptions()
.setVectorTransposeLowering(vectorTransposeLowering)
.setVectorTransformsOptions(vectorContractLowering)
.setVectorMultiReductionLowering(vectorMultiReductionLowering)
.setVectorTransferSplit(vectorTransferSplit);
// Lower high level vector operations like contract or multidim reduce ops
// to lower level vector ops.
{
RewritePatternSet patterns(ctx);
vector::populateVectorToVectorCanonicalizationPatterns(patterns);
vector::populateVectorContractLoweringPatterns(
patterns, vectorTransformOptions,
/*benefit=*/1,
/*disableOuterProductLowering=*/true);
vector::populateVectorTransferPermutationMapLoweringPatterns(patterns);
vector::populateVectorMultiReductionLoweringPatterns(
patterns, vectorMultiReductionLowering);
populateVectorTransferFullPartialPatterns(patterns, vectorTransformOptions);
(void)applyPatternsAndFoldGreedily(funcOp, std::move(patterns));
}
LLVM_DEBUG({
llvm::dbgs() << "\n--- After lowering high level vector ops to lower level "
"vector ops ---\n";
funcOp.print(llvm::dbgs(), OpPrintingFlags().useLocalScope());
llvm::dbgs() << "\n\n";
});
// Make sure we remove redundant vector ops (e.g., vector tranposes) before we
// lower them and can't be optimized away anymore.
{
RewritePatternSet patterns(ctx);
SmallVector<Dialect *> dialects;
dialects.push_back(ctx->getLoadedDialect<vector::VectorDialect>());
dialects.push_back(ctx->getLoadedDialect<memref::MemRefDialect>());
dialects.push_back(ctx->getLoadedDialect<linalg::LinalgDialect>());
for (auto &dialect : dialects)
dialect->getCanonicalizationPatterns(patterns);
(void)applyPatternsAndFoldGreedily(funcOp, std::move(patterns));
}
{
RewritePatternSet patterns(ctx);
vector::populateVectorTransferLoweringPatterns(patterns,
/*maxTransferRank=*/1);
auto vectorTransferToSCFOptions =
VectorTransferToSCFOptions().enableFullUnroll();
populateVectorToSCFConversionPatterns(patterns, vectorTransferToSCFOptions);
(void)applyPatternsAndFoldGreedily(funcOp, std::move(patterns));
}
LLVM_DEBUG({
llvm::dbgs() << "\n--- After lowering vector transfers to SCF ---\n";
funcOp.print(llvm::dbgs(), OpPrintingFlags().useLocalScope());
llvm::dbgs() << "\n\n";
});
// Lowering for vector.transpose ops.
{
if (has16x16Transpose(funcOp)) {
vectorTransformOptions.setVectorTransposeLowering(
vector::VectorTransposeLowering::Shuffle16x16);
}
RewritePatternSet patterns(ctx);
vector::populateVectorToVectorCanonicalizationPatterns(patterns);
vector::populateVectorTransposeLoweringPatterns(patterns,
vectorTransformOptions);
if (lowerVectorTransposeToAVX2) {
auto avx2LoweringOptions =
x86vector::avx2::LoweringOptions().setTransposeOptions(
x86vector::avx2::TransposeLoweringOptions()
.lower4x8xf32()
.lower8x8xf32());
x86vector::avx2::populateSpecializedTransposeLoweringPatterns(
patterns, avx2LoweringOptions, /*benefit=*/10);
}
(void)applyPatternsAndFoldGreedily(funcOp, std::move(patterns));
}
LLVM_DEBUG({
llvm::dbgs() << "\n--- After lowering vector transpose ops ---\n";
funcOp.print(llvm::dbgs(), OpPrintingFlags().useLocalScope());
llvm::dbgs() << "\n\n";
});
// 'vector.shape_cast' are very expensive operations that are even generated
// by some of the lowerings above (e.g., transpose lowering). There are
// chances to cancel them out if they are not lowered too early so we lower
// them at the very end of the pass.
{
RewritePatternSet patterns(ctx);
vector::populateVectorShapeCastLoweringPatterns(patterns);
(void)applyPatternsAndFoldGreedily(funcOp, std::move(patterns));
}
}
} // namespace
std::unique_ptr<OperationPass<func::FuncOp>> createLLVMCPUVectorLoweringPass() {
return std::make_unique<LLVMCPUVectorLoweringPass>();
}
std::unique_ptr<OperationPass<func::FuncOp>> createLLVMCPUVectorLoweringPass(
const LLVMCPUVectorLoweringPassOptions &options) {
return std::make_unique<LLVMCPUVectorLoweringPass>(options);
}
} // namespace iree_compiler
} // namespace mlir