| // 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 ®istry) 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 |