blob: 2a7d4007f40647634a2d7ebdfec964fe5f9aba2b [file] [edit]
// 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/Dialect/LinalgExt/Utils/Utils.h"
#include "iree/compiler/Dialect/Util/IR/UtilOps.h"
#include "iree/compiler/GlobalOptimization/Passes.h"
#include "llvm/Support/Debug.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/Linalg/IR/Linalg.h"
#include "mlir/Dialect/Linalg/Utils/Utils.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
namespace mlir::iree_compiler::GlobalOptimization {
#define GEN_PASS_DEF_DEMOTECONTRACTIONINPUTSPASS
#include "iree/compiler/GlobalOptimization/Passes.h.inc"
namespace {
// Template pattern for demoting contraction inputs to a narrower floating-point
// type.
// SrcType: The source floating-point type (e.g., Float32Type)
// DestType: The destination floating-point type (e.g., BFloat16Type,
// Float16Type)
template <typename SrcType, typename DestType>
struct DemoteContractionInputsPattern
: OpInterfaceRewritePattern<linalg::LinalgOp> {
using OpInterfaceRewritePattern<linalg::LinalgOp>::OpInterfaceRewritePattern;
explicit DemoteContractionInputsPattern(MLIRContext *ctx,
const DemoteOperation &operation)
: OpInterfaceRewritePattern<linalg::LinalgOp>(ctx),
demoteOperation(operation) {}
LogicalResult matchAndRewrite(linalg::LinalgOp linalgOp,
PatternRewriter &rewriter) const override {
if (demoteOperation == DemoteOperation::None) {
return failure();
}
if (!isa<linalg::ContractionOpInterface, linalg::ConvolutionOpInterface>(
linalgOp.getOperation())) {
return failure();
}
Type srcType = SrcType::get(rewriter.getContext());
if (!llvm::all_of(linalgOp->getOperands(), [&](auto operand) {
auto operandType = dyn_cast<RankedTensorType>(operand.getType());
return operandType && operandType.getElementType() == srcType;
})) {
return failure();
}
auto replaceOpInputs = [&](auto *typePtr) {
Location loc = linalgOp.getLoc();
Type destType = DestType::get(rewriter.getContext());
SmallVector<Value> demotedInputs;
for (auto inputOperand : linalgOp.getDpsInputOperands()) {
auto input = inputOperand->get();
auto inputType = cast<RankedTensorType>(input.getType());
auto demotedInputType = RankedTensorType::get(
inputType.getShape(), destType, inputType.getEncoding());
SmallVector<AffineMap> maps(
2, rewriter.getMultiDimIdentityMap(inputType.getRank()));
SmallVector<utils::IteratorType> iteratorTypes(
inputType.getRank(), utils::IteratorType::parallel);
SmallVector<OpFoldResult> mixedSizes =
tensor::getMixedSizes(rewriter, loc, input);
Value empty =
tensor::EmptyOp::create(rewriter, loc, mixedSizes, destType);
demotedInputs.push_back(
linalg::GenericOp::create(
rewriter, loc, TypeRange{demotedInputType}, ValueRange{input},
ValueRange{empty}, maps, iteratorTypes,
[&](OpBuilder &b, Location loc, ValueRange args) {
Value result =
arith::TruncFOp::create(b, loc, destType, args[0]);
linalg::YieldOp::create(b, loc, result);
})
->getResult(0));
}
auto namedOp = cast<std::remove_pointer_t<decltype(typePtr)>>(
linalgOp.getOperation());
rewriter.replaceOpWithNewOp<std::remove_pointer_t<decltype(typePtr)>>(
linalgOp, demotedInputs, linalgOp.getDpsInits(),
linalg::getPrunedAttributeList(namedOp));
};
bool demoteMatmul = (demoteOperation == DemoteOperation::All) ||
(demoteOperation == DemoteOperation::Matmul);
bool demoteConv = (demoteOperation == DemoteOperation::All) ||
(demoteOperation == DemoteOperation::Conv);
Operation *op = linalgOp.getOperation();
if (demoteMatmul && IREE::LinalgExt::isPureMatmul(op)) {
replaceOpInputs(static_cast<linalg::MatmulOp *>(nullptr));
} else if (demoteMatmul && isa<linalg::MatvecOp>(op)) {
replaceOpInputs(static_cast<linalg::MatvecOp *>(nullptr));
} else if (demoteMatmul && isa<linalg::VecmatOp>(op)) {
replaceOpInputs(static_cast<linalg::VecmatOp *>(nullptr));
} else if (demoteMatmul && IREE::LinalgExt::isPureBatchMatmul(op)) {
replaceOpInputs(static_cast<linalg::BatchMatmulOp *>(nullptr));
} else if (demoteMatmul && isa<linalg::BatchMatvecOp>(op)) {
replaceOpInputs(static_cast<linalg::BatchMatvecOp *>(nullptr));
} else if (demoteMatmul && isa<linalg::BatchVecmatOp>(op)) {
replaceOpInputs(static_cast<linalg::BatchVecmatOp *>(nullptr));
} else if (demoteMatmul && isa<linalg::MatmulTransposeAOp>(op)) {
replaceOpInputs(static_cast<linalg::MatmulTransposeAOp *>(nullptr));
} else if (demoteMatmul && isa<linalg::MatmulTransposeBOp>(op)) {
replaceOpInputs(static_cast<linalg::MatmulTransposeBOp *>(nullptr));
} else if (demoteMatmul && isa<linalg::BatchMatmulTransposeAOp>(op)) {
replaceOpInputs(static_cast<linalg::BatchMatmulTransposeAOp *>(nullptr));
} else if (demoteMatmul && isa<linalg::BatchMatmulTransposeBOp>(op)) {
replaceOpInputs(static_cast<linalg::BatchMatmulTransposeBOp *>(nullptr));
} else if (demoteConv && isa<linalg::Conv2DOp>(op)) {
replaceOpInputs(static_cast<linalg::Conv2DOp *>(nullptr));
} else if (demoteConv && isa<linalg::Conv2DNchwFchwOp>(op)) {
replaceOpInputs(static_cast<linalg::Conv2DNchwFchwOp *>(nullptr));
} else if (demoteConv && isa<linalg::Conv2DNhwcHwcfOp>(op)) {
replaceOpInputs(static_cast<linalg::Conv2DNhwcHwcfOp *>(nullptr));
} else if (demoteConv && isa<linalg::Conv2DNhwcFhwcOp>(op)) {
replaceOpInputs(static_cast<linalg::Conv2DNhwcFhwcOp *>(nullptr));
} else if (demoteConv && isa<linalg::Conv2DNgchwFgchwOp>(op)) {
replaceOpInputs(static_cast<linalg::Conv2DNgchwFgchwOp *>(nullptr));
} else if (demoteConv && isa<linalg::Conv2DNgchwGfchwOp>(op)) {
replaceOpInputs(static_cast<linalg::Conv2DNgchwGfchwOp *>(nullptr));
} else {
return failure();
}
return success();
}
private:
DemoteOperation demoteOperation;
};
class DemoteContractionInputsPass
: public impl::DemoteContractionInputsPassBase<
DemoteContractionInputsPass> {
public:
using Base::Base;
void runOnOperation() override {
MLIRContext *context = &getContext();
RewritePatternSet patterns(context);
DemoteOperation ops = demoteOperation.getValue();
switch (demoteType.getValue()) {
case DemoteType::F16:
patterns.insert<DemoteContractionInputsPattern<Float32Type, Float16Type>>(
context, ops);
break;
case DemoteType::BF16:
patterns
.insert<DemoteContractionInputsPattern<Float32Type, BFloat16Type>>(
context, ops);
break;
default:
llvm_unreachable("Unsupported demotion type");
}
if (failed(applyPatternsGreedily(getOperation(), std::move(patterns)))) {
return signalPassFailure();
}
}
};
} // namespace
std::unique_ptr<Pass>
createDemoteContractionInputsPass(DemoteType type, DemoteOperation operation) {
return std::make_unique<DemoteContractionInputsPass>(
DemoteContractionInputsPassOptions{type, operation});
}
} // namespace mlir::iree_compiler::GlobalOptimization