blob: 15efbed95b351cf840fc57a23df9e7e1fbf77537 [file]
// Copyright 2021 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-dialects/Dialect/IREE/IREEDialect.h"
#include "iree-dialects/Dialect/IREE/IREEOps.h"
#include "iree/compiler/Dialect/Flow/IR/FlowDialect.h"
#include "iree/compiler/Dialect/Flow/IR/FlowOps.h"
#include "iree/compiler/Dialect/Flow/IR/FlowTypes.h"
#include "iree/compiler/Dialect/HAL/IR/HALDialect.h"
#include "iree/compiler/Dialect/HAL/IR/HALOps.h"
#include "iree/compiler/Dialect/HAL/IR/HALTypes.h"
#include "iree/compiler/Dialect/Util/IR/UtilDialect.h"
#include "iree/compiler/Dialect/Util/IR/UtilOps.h"
#include "iree/compiler/Dialect/Util/IR/UtilTypes.h"
#include "iree/compiler/InputConversion/Common/PassDetail.h"
#include "iree/compiler/InputConversion/Common/Passes.h"
#include "mlir/Conversion/SCFToStandard/SCFToStandard.h"
#include "mlir/Dialect/SCF/SCF.h"
#include "mlir/Dialect/StandardOps/IR/Ops.h"
#include "mlir/IR/BuiltinDialect.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Transforms/DialectConversion.h"
namespace IREEPublic = mlir::iree;
namespace mlir {
namespace iree_compiler {
namespace {
// Allowlist of function attributes to retain when importing funcs.
constexpr const char *kRetainedAttributes[] = {
"iree.reflection",
"sym_visibility",
"noinline",
};
struct IREEImportPublicPass
: public IREEImportPublicBase<IREEImportPublicPass> {
void getDependentDialects(DialectRegistry &registry) const override {
registry.insert<mlir::iree::IREEDialect, IREE::Flow::FlowDialect,
IREE::HAL::HALDialect, IREE::Util::UtilDialect,
mlir::StandardOpsDialect>();
}
void runOnOperation() override;
};
class IREETypeConverter : public TypeConverter {
public:
IREETypeConverter();
};
// Generic 1:1 conversion pattern which effectively just renames an op.
// It does not support regions or ops with successors.
class OneToOneConverionPattern : public ConversionPattern {
public:
OneToOneConverionPattern(TypeConverter &converter, StringRef srcName,
StringRef targetName, MLIRContext *context,
PatternBenefit benefit)
: ConversionPattern(converter, srcName, benefit, context),
targetName(targetName) {}
LogicalResult matchAndRewrite(
Operation *srcOp, ArrayRef<Value> operands,
ConversionPatternRewriter &rewriter) const override {
SmallVector<Type> resultTypes;
if (failed(typeConverter->convertTypes(srcOp->getResultTypes(),
resultTypes))) {
return srcOp->emitError()
<< "could not convert result types to IREE internal types";
}
OperationState state(srcOp->getLoc(), targetName, operands, resultTypes,
srcOp->getAttrs());
Operation *targetOp = rewriter.createOperation(state);
rewriter.replaceOp(srcOp, targetOp->getResults());
return success();
}
private:
StringRef targetName;
};
class BufferViewToTensorPattern
: public OpConversionPattern<IREEPublic::BufferViewToTensorOp> {
using OpConversionPattern<
IREEPublic::BufferViewToTensorOp>::OpConversionPattern;
LogicalResult matchAndRewrite(
IREEPublic::BufferViewToTensorOp srcOp, ArrayRef<Value> operands,
ConversionPatternRewriter &rewriter) const override {
IREEPublic::BufferViewToTensorOpAdaptor adaptor(operands);
Type resultType = typeConverter->convertType(srcOp.target().getType());
if (!resultType) return failure();
rewriter.replaceOpWithNewOp<IREE::HAL::TensorCastOp>(
srcOp, resultType, adaptor.source(), adaptor.target_dims());
return success();
}
};
class TensorToBufferViewPattern
: public OpConversionPattern<IREEPublic::TensorToBufferViewOp> {
using OpConversionPattern<
IREEPublic::TensorToBufferViewOp>::OpConversionPattern;
LogicalResult matchAndRewrite(
IREEPublic::TensorToBufferViewOp srcOp, ArrayRef<Value> operands,
ConversionPatternRewriter &rewriter) const override {
IREEPublic::TensorToBufferViewOpAdaptor adaptor(operands);
Type resultType = typeConverter->convertType(srcOp.target().getType());
if (!resultType) return failure();
rewriter.replaceOpWithNewOp<IREE::HAL::TensorCastOp>(
srcOp, resultType, adaptor.source(), adaptor.source_dims());
return success();
}
};
class BuiltinFuncOpPattern : public OpConversionPattern<FuncOp> {
using OpConversionPattern<FuncOp>::OpConversionPattern;
LogicalResult matchAndRewrite(
FuncOp srcOp, ArrayRef<Value> operands,
ConversionPatternRewriter &rewriter) const override {
FunctionType srcFuncType = srcOp.getType();
TypeConverter::SignatureConversion signatureConversion(
srcOp.getNumArguments());
// Convert function arguments.
for (unsigned i = 0, e = srcFuncType.getNumInputs(); i < e; ++i) {
if (failed(getTypeConverter()->convertSignatureArg(
i, srcFuncType.getInput(i), signatureConversion))) {
return rewriter.notifyMatchFailure(srcOp, "argument failed to convert");
}
}
// Convert function results.
SmallVector<Type, 1> convertedResultTypes;
if (failed(getTypeConverter()->convertTypes(srcFuncType.getResults(),
convertedResultTypes))) {
return rewriter.notifyMatchFailure(srcOp, "results failed to convert");
}
// Create new function with converted argument and result types.
// Note that attributes are dropped. Consider preserving some if needed.
auto newFuncType = mlir::FunctionType::get(
srcOp.getContext(), signatureConversion.getConvertedTypes(),
convertedResultTypes);
auto newFuncOp =
rewriter.create<FuncOp>(srcOp.getLoc(), srcOp.getName(), newFuncType);
rewriter.inlineRegionBefore(srcOp.getBody(), newFuncOp.getBody(),
newFuncOp.end());
// Retain function attributes in the allowlist.
auto retainedAttributes = ArrayRef<const char *>(
kRetainedAttributes,
sizeof(kRetainedAttributes) / sizeof(kRetainedAttributes[0]));
for (auto retainAttrName : retainedAttributes) {
StringRef attrName(retainAttrName);
Attribute attr = srcOp->getAttr(attrName);
if (attr) {
newFuncOp->setAttr(attrName, attr);
}
}
// Tell the rewriter to convert the region signature.
TypeConverter &typeConverter = *getTypeConverter();
if (failed(rewriter.convertRegionTypes(&newFuncOp.getBody(), typeConverter,
&signatureConversion))) {
return failure();
}
rewriter.replaceOp(srcOp, llvm::None);
return success();
}
};
class GlobalOpPattern : public OpConversionPattern<IREEPublic::GlobalOp> {
using OpConversionPattern::OpConversionPattern;
LogicalResult matchAndRewrite(
IREEPublic::GlobalOp srcOp, ArrayRef<Value> operands,
ConversionPatternRewriter &rewriter) const override {
Type newType = typeConverter->convertType(srcOp.type());
if (!newType) return failure();
auto globalOp = rewriter.replaceOpWithNewOp<IREE::Util::GlobalOp>(
srcOp, srcOp.getName(), srcOp.is_mutable(), newType,
srcOp.initial_value());
globalOp.setVisibility(srcOp.getVisibility());
if (srcOp.initializer().hasValue()) {
auto initializerOp =
rewriter.create<IREE::Util::InitializerOp>(srcOp.getLoc());
auto ip = rewriter.saveInsertionPoint();
rewriter.setInsertionPointToStart(initializerOp.addEntryBlock());
auto callOp = rewriter.create<mlir::CallOp>(
srcOp.getLoc(), srcOp.initializerAttr(), TypeRange{newType});
rewriter.create<IREE::Util::GlobalStoreOp>(
srcOp.getLoc(), callOp.getResult(0), srcOp.getName());
rewriter.create<IREE::Util::InitializerReturnOp>(srcOp.getLoc());
rewriter.restoreInsertionPoint(ip);
}
return success();
}
};
// Matches any op and generically converts types. Matches with benefit 0.
class GenericTypeConvert : public ConversionPattern {
public:
GenericTypeConvert(TypeConverter &converter, MLIRContext *context,
PatternBenefit benefit)
: ConversionPattern(converter, MatchAnyOpTypeTag(), benefit, context) {}
LogicalResult matchAndRewrite(
Operation *op, ArrayRef<Value> operands,
ConversionPatternRewriter &rewriter) const override {
llvm::SmallVector<NamedAttribute, 4> newAttr;
llvm::append_range(newAttr, op->getAttrs());
llvm::SmallVector<Type, 4> newResults;
(void)getTypeConverter()->convertTypes(op->getResultTypes(), newResults);
OperationState state(op->getLoc(), op->getName().getStringRef(), operands,
newResults, newAttr, op->getSuccessors());
for (Region &r : op->getRegions()) {
Region *newRegion = state.addRegion();
rewriter.inlineRegionBefore(r, *newRegion, newRegion->begin());
TypeConverter::SignatureConversion result(newRegion->getNumArguments());
(void)getTypeConverter()->convertSignatureArgs(
newRegion->getArgumentTypes(), result);
rewriter.applySignatureConversion(newRegion, result);
}
Operation *newOp = rewriter.createOperation(state);
rewriter.replaceOp(op, newOp->getResults());
return success();
}
};
} // namespace
IREETypeConverter::IREETypeConverter() {
addConversion([](Type t) { return t; });
addConversion([=](IREEPublic::BufferViewType t) {
return IREE::HAL::BufferViewType::get(t.getContext());
});
addConversion([=](IREEPublic::ListType t) -> IREE::Util::ListType {
auto subType = convertType(t.getElementType());
if (!subType) return nullptr;
return IREE::Util::ListType::get(subType);
});
addConversion([=](IREEPublic::PtrType t) -> IREE::Util::PtrType {
auto subType = convertType(t.getTargetType());
if (!subType) return nullptr;
return IREE::Util::PtrType::get(subType);
});
addConversion([](IREEPublic::VariantType t) {
return IREE::Util::VariantType::get(t.getContext());
});
}
void IREEImportPublicPass::runOnOperation() {
auto &context = getContext();
RewritePatternSet patterns(&getContext());
ConversionTarget target(getContext());
target.addLegalDialect<IREE::Flow::FlowDialect>();
target.addLegalDialect<IREE::HAL::HALDialect>();
target.addLegalDialect<IREE::Util::UtilDialect>();
target.addIllegalDialect<IREEPublic::IREEDialect>();
auto ireeDialect = context.getOrLoadDialect<IREEPublic::IREEDialect>();
auto isIllegalType = [&](Type t) {
return t.getDialect().getTypeID() == ireeDialect->getTypeID();
};
target.addDynamicallyLegalOp<FuncOp>([&](FuncOp funcOp) {
for (Type type : funcOp.getType().getInputs()) {
if (isIllegalType(type)) return false;
}
for (Type type : funcOp.getType().getResults()) {
if (isIllegalType(type)) return false;
}
return true;
});
target.markUnknownOpDynamicallyLegal([&](Operation *op) {
for (Type type : op->getResultTypes()) {
if (isIllegalType(type)) return false;
}
for (Type type : op->getOperandTypes()) {
if (isIllegalType(type)) return false;
}
return true;
});
IREETypeConverter typeConverter;
PatternBenefit specific_benefit = 100;
patterns.insert<GenericTypeConvert>(typeConverter, &getContext(), 0);
patterns.insert<BuiltinFuncOpPattern>(typeConverter, &getContext(),
specific_benefit);
patterns.insert<BufferViewToTensorPattern>(typeConverter, &getContext(),
specific_benefit);
patterns.insert<TensorToBufferViewPattern>(typeConverter, &getContext(),
specific_benefit);
patterns.insert<GlobalOpPattern>(typeConverter, &getContext(), 0);
#define ONETOONE(SrcOpTy, TargetOpTy) \
patterns.insert<OneToOneConverionPattern>( \
typeConverter, SrcOpTy::getOperationName(), \
TargetOpTy::getOperationName(), &getContext(), specific_benefit)
ONETOONE(IREEPublic::BufferViewRankOp, IREE::HAL::BufferViewRankOp);
ONETOONE(IREEPublic::BufferViewDimOp, IREE::HAL::BufferViewDimOp);
ONETOONE(IREEPublic::ListCreateOp, IREE::Util::ListCreateOp);
ONETOONE(IREEPublic::ListSizeOp, IREE::Util::ListSizeOp);
ONETOONE(IREEPublic::ListResizeOp, IREE::Util::ListResizeOp);
ONETOONE(IREEPublic::ListGetOp, IREE::Util::ListGetOp);
ONETOONE(IREEPublic::ListSetOp, IREE::Util::ListSetOp);
ONETOONE(IREEPublic::NullOp, IREE::Util::NullOp);
ONETOONE(IREEPublic::TensorCloneOp, IREE::Flow::TensorCloneOp);
ONETOONE(IREEPublic::TensorLoadOp, IREE::Flow::TensorLoadOp);
ONETOONE(IREEPublic::TensorReshapeOp, IREE::Flow::TensorReshapeOp);
ONETOONE(IREEPublic::TensorSliceOp, IREE::Flow::TensorSliceOp);
ONETOONE(IREEPublic::TensorSplatOp, IREE::Flow::TensorSplatOp);
ONETOONE(IREEPublic::TensorStoreOp, IREE::Flow::TensorStoreOp);
ONETOONE(IREEPublic::TensorUpdateOp, IREE::Flow::TensorUpdateOp);
ONETOONE(IREEPublic::TensorTraceOp, IREE::Flow::TensorTraceOp);
ONETOONE(IREEPublic::GlobalAddressOp, IREE::Util::GlobalAddressOp);
ONETOONE(IREEPublic::GlobalLoadOp, IREE::Util::GlobalLoadOp);
ONETOONE(IREEPublic::GlobalLoadIndirectOp, IREE::Util::GlobalLoadIndirectOp);
ONETOONE(IREEPublic::GlobalStoreOp, IREE::Util::GlobalStoreOp);
ONETOONE(IREEPublic::GlobalStoreIndirectOp,
IREE::Util::GlobalStoreIndirectOp);
if (failed(applyFullConversion(getOperation(), target, std::move(patterns))))
signalPassFailure();
}
std::unique_ptr<OperationPass<ModuleOp>> createIREEImportPublicPass() {
return std::make_unique<IREEImportPublicPass>();
}
} // namespace iree_compiler
} // namespace mlir