| // 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 "mlir/Dialect/Func/IR/FuncOps.h" | |
| #include "mlir/Dialect/Tosa/IR/TosaOps.h" | |
| #include "mlir/Dialect/Tosa/Transforms/Passes.h" | |
| #include "mlir/Interfaces/FunctionInterfaces.h" | |
| #include "mlir/Transforms/DialectConversion.h" | |
| using namespace mlir; | |
| namespace mlir::iree_compiler { | |
| #define GEN_PASS_DEF_CONVERTI48TOI64PASS | |
| #include "compiler/plugins/input/TOSA/InputConversion/Passes.h.inc" | |
| namespace { | |
| class Converti48Toi64Pass final | |
| : public impl::Converti48Toi64PassBase<Converti48Toi64Pass> { | |
| public: | |
| explicit Converti48Toi64Pass() = default; | |
| void runOnOperation() override; | |
| }; | |
| struct i48Toi64Converter : TypeConverter { | |
| public: | |
| static Type convertType(Type type) { | |
| if (type.isInteger(48)) { | |
| return IntegerType::get(type.getContext(), /*width=*/64); | |
| } | |
| return type; | |
| } | |
| static Type convertTensor(RankedTensorType type) { | |
| auto newType = RankedTensorType::get(type.getShape(), | |
| convertType(type.getElementType())); | |
| return newType; | |
| } | |
| explicit i48Toi64Converter() { | |
| addConversion([](Type type) { return convertType(type); }); | |
| addConversion(convertTensor); | |
| } | |
| }; | |
| static Attribute | |
| convertIntegerAttributeIfNeeded(Attribute attr, | |
| const TypeConverter &converter) { | |
| auto typedAttr = dyn_cast<TypedAttr>(attr); | |
| if (!typedAttr) { | |
| return attr; | |
| } | |
| Type newType = converter.convertType(typedAttr.getType()); | |
| if (!newType || newType == typedAttr.getType()) { | |
| return attr; | |
| } | |
| if (auto intAttr = dyn_cast<IntegerAttr>(attr)) { | |
| if (auto intType = dyn_cast<IntegerType>(newType)) { | |
| return IntegerAttr::get(intType, intAttr.getValue().getZExtValue()); | |
| } | |
| } | |
| if (auto shapedType = dyn_cast<ShapedType>(newType)) { | |
| if (auto denseAttr = dyn_cast<DenseIntElementsAttr>(attr)) { | |
| auto elementType = dyn_cast<IntegerType>(shapedType.getElementType()); | |
| if (elementType) { | |
| return denseAttr.mapValues( | |
| elementType, [&elementType](const APInt &value) { | |
| return APInt(elementType.getWidth(), value.getZExtValue()); | |
| }); | |
| } | |
| } | |
| } | |
| return attr; | |
| } | |
| // Handles the type conversion component of the TypeConversion. This updates | |
| // conversion patterns that used the original i48 tensor types to be | |
| // updated to the i64 variants. | |
| class GenericTypeConvert : public ConversionPattern { | |
| public: | |
| GenericTypeConvert(MLIRContext *context, TypeConverter &converter) | |
| : ConversionPattern(converter, MatchAnyOpTypeTag(), 0, context) {} | |
| LogicalResult | |
| matchAndRewrite(Operation *op, ArrayRef<Value> operands, | |
| ConversionPatternRewriter &rewriter) const override { | |
| llvm::SmallVector<Type, 4> newResults; | |
| if (isa<mlir::FunctionOpInterface>(op)) { | |
| return rewriter.notifyMatchFailure(op, "is a func op"); | |
| } | |
| if (failed(getTypeConverter()->convertTypes(op->getResultTypes(), | |
| newResults))) { | |
| return rewriter.notifyMatchFailure(op, "result type conversion failed"); | |
| } | |
| Operation *newOp = op->clone( | |
| Operation::CloneOptions().withResultTypes(llvm::to_vector(newResults))); | |
| bool conversionFailed = false; | |
| newOp->getName().walkInherentAttrs(newOp, [&](StringRef, Attribute &attr) { | |
| if (Attribute converted = | |
| convertIntegerAttributeIfNeeded(attr, *getTypeConverter())) { | |
| attr = converted; | |
| } else { | |
| conversionFailed = true; | |
| } | |
| }); | |
| SmallVector<NamedAttribute> attrs; | |
| for (NamedAttribute attr : op->getDiscardableAttrs()) { | |
| if (Attribute converted = convertIntegerAttributeIfNeeded( | |
| attr.getValue(), *getTypeConverter())) { | |
| attrs.emplace_back(attr.getName(), converted); | |
| } else { | |
| conversionFailed = true; | |
| } | |
| } | |
| if (conversionFailed) { | |
| newOp->destroy(); | |
| return rewriter.notifyMatchFailure(op, "unsupported attribute type"); | |
| } | |
| newOp->setDiscardableAttrs(attrs); | |
| newOp->setOperands(operands); | |
| rewriter.insert(newOp); | |
| for (auto [r, newRegion] : | |
| llvm::zip_equal(op->getRegions(), newOp->getRegions())) { | |
| rewriter.inlineRegionBefore(r, newRegion, newRegion.begin()); | |
| TypeConverter::SignatureConversion result(newRegion.getNumArguments()); | |
| (void)getTypeConverter()->convertSignatureArgs( | |
| newRegion.getArgumentTypes(), result); | |
| rewriter.applySignatureConversion(&newRegion.front(), result); | |
| } | |
| rewriter.replaceOp(op, newOp->getResults()); | |
| return success(); | |
| } | |
| }; | |
| static bool isIllegalType(Type type) { | |
| if (auto shapedType = dyn_cast<ShapedType>(type)) { | |
| return isIllegalType(shapedType.getElementType()); | |
| } | |
| return type.isInteger(48); | |
| } | |
| void Converti48Toi64Pass::runOnOperation() { | |
| i48Toi64Converter converter; | |
| ConversionTarget target(getContext()); | |
| // Operations are legal if they don't contain any illegal type. | |
| target.markUnknownOpDynamicallyLegal([](Operation *op) { | |
| if (auto funcOp = dyn_cast<mlir::FunctionOpInterface>(op)) { | |
| for (Type type : funcOp.getArgumentTypes()) { | |
| if (isIllegalType(type)) { | |
| return false; | |
| } | |
| } | |
| for (Type type : funcOp.getResultTypes()) { | |
| if (isIllegalType(type)) { | |
| return false; | |
| } | |
| } | |
| } | |
| for (Type type : op->getResultTypes()) { | |
| if (type && isIllegalType(type)) { | |
| return false; | |
| } | |
| } | |
| for (Type type : op->getOperandTypes()) { | |
| if (type && isIllegalType(type)) { | |
| return false; | |
| } | |
| } | |
| bool legal = true; | |
| auto check = [&](Attribute attr) { | |
| if (auto typedAttr = dyn_cast<TypedAttr>(attr)) { | |
| legal &= !isIllegalType(typedAttr.getType()); | |
| } | |
| }; | |
| op->getName().walkInherentAttrs( | |
| op, [&](StringRef, Attribute &attr) { check(attr); }); | |
| for (NamedAttribute attr : op->getDiscardableAttrs()) { | |
| check(attr.getValue()); | |
| } | |
| return legal; | |
| }); | |
| auto *ctx = &getContext(); | |
| mlir::FunctionOpInterface funcOp = getOperation(); | |
| RewritePatternSet patterns(&getContext()); | |
| patterns.add<GenericTypeConvert>(ctx, converter); | |
| populateFunctionOpInterfaceTypeConversionPattern<func::FuncOp>(patterns, | |
| converter); | |
| if (failed(applyFullConversion(funcOp, target, std::move(patterns)))) { | |
| signalPassFailure(); | |
| } | |
| } | |
| } // namespace | |
| } // namespace mlir::iree_compiler |