MLIR 24.0.0git
TosaConvertIntegerTypeToSignless.cpp
Go to the documentation of this file.
1//===- TosaConvertIntegerTypeToSignless.cpp
2//-------------------------------------------===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===-------------------------------------------------------------------------------===//
9
10// -----------
11// Motivation:
12// -----------
13
14// The TOSA specification uses a signless type system, which means that
15// information about signedness must be encapsulated by the operations
16// themselves. For example, tosa.rescale provides the attributes
17// `input_unsigned` and `output_unsigned` to indicate whether the input/output
18// should be interpreted as unsigned or signed.
19
20// The TOSA dialect, on the other hand, allows the use of signed or unsigned
21// types in addition to signless. As such, when converting from TOSA dialect to
22// other formats, we need to ensure that we conform to the TOSA specification.
23
24// ---------
25// Overview:
26// ---------
27
28// This pass converts signed or unsigned integer types to signless. It currently
29// does this greedily for all operators and can also change the signature of the
30// function. Should the signature of the entrypoint function change, it will be
31// the responsibility of the user to carry signedness information of the inputs
32// and outputs independently.
33
38
39namespace mlir {
40namespace tosa {
41
42#define GEN_PASS_DEF_TOSACONVERTINTEGERTYPETOSIGNLESS
43#include "mlir/Dialect/Tosa/Transforms/Passes.h.inc"
44
45namespace {
46class ToSignlessTensorTypeConverter : public TypeConverter {
47 static Type convertType(Type type) {
48 const auto tensorType = dyn_cast<TensorType>(type);
49 if (!tensorType)
50 return type;
51
52 const auto intType = dyn_cast<IntegerType>(tensorType.getElementType());
53 if (!intType ||
54 intType.getSignedness() == IntegerType::SignednessSemantics::Signless)
55 return type;
56
57 const auto signlessType = IntegerType::get(
58 intType.getContext(), intType.getWidth(), IntegerType::Signless);
59 return tensorType.cloneWith(std::nullopt, signlessType);
60 }
61
62public:
63 explicit ToSignlessTensorTypeConverter() { addConversion(convertType); }
64};
65
66class ConvertGenericOpWithIntegerTensorType : public ConversionPattern {
67public:
68 ConvertGenericOpWithIntegerTensorType(TypeConverter &typeConverter,
69 MLIRContext *context)
70 : ConversionPattern(typeConverter, MatchAnyOpTypeTag{}, 0, context) {}
71
72 LogicalResult
73 matchAndRewrite(Operation *op, ArrayRef<Value> operands,
74 ConversionPatternRewriter &rewriter) const final {
75 // Typically TOSA operators have a single result, but some have an
76 // arbitrary number. 4 seems like a good balance as an optimization
77 // hint for storing result types.
78 constexpr unsigned int numResults = 4;
79
80 // Convert integer types to signless
81 SmallVector<Type, numResults> resultTypes;
82 if (failed(typeConverter->convertTypes(op->getResultTypes(), resultTypes)))
83 return failure();
84
85 // Create new op with replaced operands and results
86 auto *newOp = Operation::create(
87 op->getLoc(), op->getName(), resultTypes, operands,
88 op->getDiscardableAttrDictionary().getValue(),
89 op->getPropertiesStorage(), op->getSuccessors(), op->getNumRegions());
90
91 // Handle regions in e.g. tosa.cond_if and tosa.while_loop
92 for (auto regions : llvm::zip(op->getRegions(), newOp->getRegions())) {
93 Region &before = std::get<0>(regions);
94 Region &parent = std::get<1>(regions);
95 rewriter.inlineRegionBefore(before, parent, parent.end());
96 if (failed(rewriter.convertRegionTypes(&parent, *typeConverter)))
97 return failure();
98 }
99
100 // Replace with rewritten op
101 rewriter.insert(newOp);
102 rewriter.replaceOp(op, newOp->getResults());
103 return success();
104 }
105};
106
107class ConvertTosaConstWithIntegerTensorType
108 : public OpConversionPattern<tosa::ConstOp> {
109 using OpConversionPattern::OpConversionPattern;
110
111 LogicalResult
112 matchAndRewrite(tosa::ConstOp op, OpAdaptor adaptor,
113 ConversionPatternRewriter &rewriter) const final {
114 const ElementsAttr oldAttr = op.getValues();
115 const auto oldTy = llvm::cast<ShapedType>(oldAttr.getType());
116 const auto newTy =
117 llvm::cast<ShapedType>(typeConverter->convertType(oldTy));
118 if (oldTy == newTy)
119 return success();
120
121 ElementsAttr newAttr = oldAttr;
122 if (auto denseAttr = llvm::dyn_cast<DenseElementsAttr>(oldAttr)) {
123 newAttr =
124 DenseElementsAttr::getFromRawBuffer(newTy, denseAttr.getRawData());
125 } else {
126 return rewriter.notifyMatchFailure(op, "unknown elements attribute type");
127 }
128
129 rewriter.replaceOpWithNewOp<tosa::ConstOp>(op, newTy, newAttr);
130 return success();
131 }
132};
133
134class TosaConvertIntegerTypeToSignless
135 : public impl::TosaConvertIntegerTypeToSignlessBase<
136 TosaConvertIntegerTypeToSignless> {
137public:
138 void runOnOperation() override {
139 MLIRContext *context = &getContext();
140 ConversionTarget target(*context);
141 ToSignlessTensorTypeConverter typeConverter;
142
143 target.addDynamicallyLegalOp<func::FuncOp>([&](func::FuncOp op) {
144 return typeConverter.isSignatureLegal(op.getFunctionType()) &&
145 typeConverter.isLegal(&op.getBody());
146 });
147 target.addDynamicallyLegalOp<tosa::ConstOp>([&](tosa::ConstOp op) {
148 return typeConverter.isLegal(op.getType()) &&
149 typeConverter.isLegal(op.getValues().getType());
150 });
151 target.markUnknownOpDynamicallyLegal([&](Operation *op) {
152 return typeConverter.isLegal(op->getOperandTypes()) &&
153 typeConverter.isLegal(op->getResultTypes());
154 });
155
156 RewritePatternSet patterns(context);
157 populateFunctionOpInterfaceTypeConversionPattern<func::FuncOp>(
158 patterns, typeConverter);
159 patterns.add<ConvertGenericOpWithIntegerTensorType>(typeConverter, context);
160 patterns.add<ConvertTosaConstWithIntegerTensorType>(typeConverter, context);
161
162 if (failed(
163 applyFullConversion(getOperation(), target, std::move(patterns))))
164 signalPassFailure();
165 }
166};
167
168} // namespace
169
170} // namespace tosa
171} // namespace mlir
return success()
b getContext())
static DenseElementsAttr getFromRawBuffer(ShapedType type, ArrayRef< char > rawBuffer)
Construct a dense elements attribute from a raw buffer representing the data for this attribute.
static Operation * create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, PropertyRef properties, BlockRange successors, unsigned numRegions)
Create a new Operation with the specific fields.
Definition Operation.cpp:65
Include the generated interface declarations.