MLIR 24.0.0git
TosaDowngrade1p1To1p0.cpp
Go to the documentation of this file.
1//===- TosaDowngrade1_1To1_0.cpp -----------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Rewrites constructs which are only compatible in TOSA specification 1.1 and
10// above to their TOSA 1.0 counterparts where possible. Downgrading is
11// best-effort and validation should be performed afterwards to ensure
12// compatibility with the TOSA 1.0 specification.
13//
14//===----------------------------------------------------------------------===//
15
17
23
24namespace mlir {
25namespace tosa {
26#define GEN_PASS_DEF_TOSADOWNGRADE1P1TO1P0PASS
27#include "mlir/Dialect/Tosa/Transforms/Passes.h.inc"
28} // namespace tosa
29} // namespace mlir
30
31using namespace mlir;
32using namespace mlir::tosa;
33
34namespace {
35
36class BoolFp32CastRewrite : public OpRewritePattern<tosa::CastOp> {
37public:
39
40 LogicalResult matchAndRewrite(tosa::CastOp op,
41 PatternRewriter &rewriter) const override {
42 const Value input = op.getInput();
43
44 const Type i1Type = rewriter.getI1Type();
45 const Type f32Type = rewriter.getF32Type();
46
47 const Type inputElemType = getElementTypeOrSelf(input.getType());
48 const Type outputElemType = getElementTypeOrSelf(op.getType());
49 const bool isFp32ToBool =
50 inputElemType == f32Type && outputElemType == i1Type;
51 const bool isBoolToFp32 =
52 inputElemType == i1Type && outputElemType == f32Type;
53
54 if (!isFp32ToBool && !isBoolToFp32)
55 return rewriter.notifyMatchFailure(op,
56 "expected cast between bool and f32");
57
58 const Type outputType = op.getType();
59 const Type i8Type = rewriter.getI8Type();
60 const Type intermediateType = cast<TensorType>(outputType).clone(i8Type);
61
62 auto inner =
63 tosa::CastOp::create(rewriter, op.getLoc(), intermediateType, input);
64 auto outer = tosa::CastOp::create(rewriter, op.getLoc(), outputType,
65 inner.getOutput());
66 rewriter.replaceOp(op, outer.getOutput());
67 return success();
68 }
69};
70
71class BoolGatherRewrite : public OpRewritePattern<tosa::GatherOp> {
72public:
74
75 LogicalResult matchAndRewrite(tosa::GatherOp op,
76 PatternRewriter &rewriter) const override {
77 const Value values = op.getValues();
78 const Value indices = op.getIndices();
79
80 const Type valuesType = values.getType();
81 const Type resultType = op.getType();
82
83 const Type i1Type = rewriter.getI1Type();
84 const Type i32Type = rewriter.getI32Type();
85 if (getElementTypeOrSelf(valuesType) != i1Type ||
86 getElementTypeOrSelf(indices.getType()) != i32Type)
87 return rewriter.notifyMatchFailure(
88 op, "expected values of bool type and indices of i32 type");
89
90 const Type i8Type = rewriter.getI8Type();
91 const Type valuesI8Type = cast<TensorType>(valuesType).clone(i8Type);
92 const Type resultI8Type = cast<TensorType>(resultType).clone(i8Type);
93
94 auto valuesToI8 =
95 tosa::CastOp::create(rewriter, op.getLoc(), valuesI8Type, values);
96 auto gatherI8 = tosa::GatherOp::create(rewriter, op.getLoc(), resultI8Type,
97 valuesToI8.getOutput(), indices);
98 auto i8ToBool = tosa::CastOp::create(rewriter, op.getLoc(), resultType,
99 gatherI8.getOutput());
100 rewriter.replaceOp(op, i8ToBool.getOutput());
101 return success();
102 }
103};
104
105class BoolScatterRewrite : public OpRewritePattern<tosa::ScatterOp> {
106public:
108
109 LogicalResult matchAndRewrite(tosa::ScatterOp op,
110 PatternRewriter &rewriter) const override {
111 const Value valuesIn = op.getValuesIn();
112 const Value indices = op.getIndices();
113
114 const Type valuesInType = valuesIn.getType();
115 const Type i1Type = rewriter.getI1Type();
116 const Type i32Type = rewriter.getI32Type();
117 if (getElementTypeOrSelf(valuesInType) != i1Type ||
118 getElementTypeOrSelf(indices.getType()) != i32Type)
119 return rewriter.notifyMatchFailure(
120 op, "expected values of bool type and indices of i32 type");
121
122 const Value input = op.getInput();
123 const Type inputType = input.getType();
124 const Type resultType = op.getType();
125
126 const Type i8Type = rewriter.getI8Type();
127 const Type valuesInI8Type = cast<TensorType>(valuesInType).clone(i8Type);
128 const Type inputI8Type = cast<TensorType>(inputType).clone(i8Type);
129 const Type resultI8Type = cast<TensorType>(resultType).clone(i8Type);
130
131 auto valuesInToI8 =
132 tosa::CastOp::create(rewriter, op.getLoc(), valuesInI8Type, valuesIn);
133 auto inputToI8 =
134 tosa::CastOp::create(rewriter, op.getLoc(), inputI8Type, input);
135 auto scatterI8 = tosa::ScatterOp::create(
136 rewriter, op.getLoc(), resultI8Type, valuesInToI8.getOutput(), indices,
137 inputToI8.getOutput());
138 auto i8ToBool = tosa::CastOp::create(rewriter, op.getLoc(), resultType,
139 scatterI8.getValuesOut());
140 rewriter.replaceOp(op, i8ToBool.getOutput());
141 return success();
142 }
143};
144
145static LogicalResult isMatMulTTypeCompatibleForDowngrade(tosa::MatMulTOp op) {
146 const Type aElementType = getStorageElementTypeOrSelf(op.getA().getType());
147 const Type bElementType = getStorageElementTypeOrSelf(op.getB().getType());
148 const Type outputElementType =
149 getStorageElementTypeOrSelf(op.getOutput().getType());
150
151 if (aElementType != bElementType)
152 return failure();
153
154 if (isa<BlockScaledType>(aElementType) || isa<BlockScaledType>(bElementType))
155 return failure();
156
157 if ((aElementType.isF16() && outputElementType.isF16()) ||
158 (aElementType.isF16() && outputElementType.isF32()) ||
159 (aElementType.isF32() && outputElementType.isF32()) ||
160 (aElementType.isBF16() && outputElementType.isF32()) ||
161 (aElementType.isInteger(8) && outputElementType.isInteger(32)) ||
162 (aElementType.isInteger(16) && outputElementType.isInteger(48)) ||
163 (isa<Float8E5M2Type>(aElementType) && outputElementType.isF16()) ||
164 (isa<Float8E4M3FNType>(aElementType) && outputElementType.isF16()))
165 return success();
166
167 return failure();
168}
169
170class MatMulTRewrite : public OpRewritePattern<tosa::MatMulTOp> {
171public:
173
174 LogicalResult matchAndRewrite(tosa::MatMulTOp op,
175 PatternRewriter &rewriter) const override {
176 if (failed(isMatMulTTypeCompatibleForDowngrade(op)))
177 return rewriter.notifyMatchFailure(
178 op, "expected 1.0-compatible matmul_t element types");
179
180 const Type aType = op.getA().getType();
181 const Type bType = op.getB().getType();
182 const ShapeAdaptor aShape(aType);
183 const ShapeAdaptor bShape(bType);
184 if (!aShape.hasRank() || !bShape.hasRank())
185 return rewriter.notifyMatchFailure(op, "expected ranked A and B tensors");
186
187 const int64_t dSize = bShape.getDimSize(0);
188 const int64_t nSize = aShape.getDimSize(0);
189
190 // To convert broadcasting behaviour to TOSA 1.0, we're required to tile the
191 // input. TOSA 1.0 does not support shape expressions, so the batch size
192 // must be known at compile time.
193 if (ShapedType::isDynamic(dSize) ||
194 (dSize == 1 && ShapedType::isDynamic(nSize)))
195 return rewriter.notifyMatchFailure(
196 op, "expected known batch size for broadcast");
197
198 const int64_t wSize = bShape.getDimSize(1);
199 const int64_t cSize = bShape.getDimSize(2);
200 const Location loc = op.getLoc();
201 const RankedTensorType transposedBType =
202 cast<RankedTensorType>(bType).clone({dSize, cSize, wSize});
203 auto transpose =
204 tosa::TransposeOp::create(rewriter, loc, transposedBType, op.getB(),
205 rewriter.getDenseI32ArrayAttr({0, 2, 1}));
206 Value matMulB = transpose.getOutput();
207
208 // Matmul does not support broadcasting, so tile b if required
209 if (dSize == 1 && nSize != 1) {
210 const RankedTensorType tiledBType =
211 cast<RankedTensorType>(bType).clone({nSize, cSize, wSize});
212 const Value multiples = getTosaConstShape(rewriter, loc, {nSize, 1, 1});
213 auto tile =
214 tosa::TileOp::create(rewriter, loc, tiledBType, matMulB, multiples);
215 matMulB = tile.getOutput();
216 }
217
218 auto matmul = tosa::MatMulOp::create(rewriter, loc, op.getType(), op.getA(),
219 matMulB, op.getAZp(), op.getBZp());
220 rewriter.replaceOp(op, matmul.getOutput());
221 return success();
222 }
223};
224
225struct TosaDowngrade1p1To1p0Pass
226 : public tosa::impl::TosaDowngrade1p1To1p0PassBase<
227 TosaDowngrade1p1To1p0Pass> {
228 using Base::Base;
229
230 void runOnOperation() override {
231 MLIRContext &context = getContext();
232 func::FuncOp func = getOperation();
233
234 RewritePatternSet patterns(&context);
235 patterns.add<BoolFp32CastRewrite, BoolGatherRewrite, BoolScatterRewrite,
236 MatMulTRewrite>(&context);
237 FrozenRewritePatternSet frozenPatterns(std::move(patterns));
238
239 if (failed(applyPatternsGreedily(func, frozenPatterns)))
240 return signalPassFailure();
241 }
242};
243
244} // namespace
return success()
b getContext())
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:167
FloatType getF32Type()
Definition Builders.cpp:47
IntegerType getI32Type()
Definition Builders.cpp:67
IntegerType getI1Type()
Definition Builders.cpp:57
IntegerType getI8Type()
Definition Builders.cpp:63
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isF32() const
Definition Types.cpp:40
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
bool isF16() const
Definition Types.cpp:38
bool isBF16() const
Definition Types.cpp:37
Type getType() const
Return the type of this value.
Definition Value.h:105
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Type getStorageElementTypeOrSelf(Type type)
Definition TosaOps.cpp:585
Value getTosaConstShape(ImplicitLocOpBuilder &builder, llvm::ArrayRef< int64_t > shape)
Include the generated interface declarations.
LogicalResult applyPatternsGreedily(Region &region, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
SmallVector< Loops, 8 > tile(ArrayRef< scf::ForOp > forOps, ArrayRef< Value > sizes, ArrayRef< scf::ForOp > targets)
Performs tiling fo imperfectly nested loops (with interchange) by strip-mining the forOps by sizes an...
Definition Utils.cpp:1330
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
Patterns must specify the root operation name they match against, and can also specify the benefit of...