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 = tosa::CastOp::create(rewriter, op.getLoc(), intermediateType,
63 input, /*input_unsigned*/ false);
64 auto outer =
65 tosa::CastOp::create(rewriter, op.getLoc(), outputType,
66 inner.getOutput(), /*input_unsigned*/ false);
67 rewriter.replaceOp(op, outer.getOutput());
68 return success();
69 }
70};
71
72class BoolGatherRewrite : public OpRewritePattern<tosa::GatherOp> {
73public:
75
76 LogicalResult matchAndRewrite(tosa::GatherOp op,
77 PatternRewriter &rewriter) const override {
78 const Value values = op.getValues();
79 const Value indices = op.getIndices();
80
81 const Type valuesType = values.getType();
82 const Type resultType = op.getType();
83
84 const Type i1Type = rewriter.getI1Type();
85 const Type i32Type = rewriter.getI32Type();
86 if (getElementTypeOrSelf(valuesType) != i1Type ||
87 getElementTypeOrSelf(indices.getType()) != i32Type)
88 return rewriter.notifyMatchFailure(
89 op, "expected values of bool type and indices of i32 type");
90
91 const Type i8Type = rewriter.getI8Type();
92 const Type valuesI8Type = cast<TensorType>(valuesType).clone(i8Type);
93 const Type resultI8Type = cast<TensorType>(resultType).clone(i8Type);
94
95 auto valuesToI8 = tosa::CastOp::create(rewriter, op.getLoc(), valuesI8Type,
96 values, /*input_unsigned*/ false);
97 auto gatherI8 = tosa::GatherOp::create(rewriter, op.getLoc(), resultI8Type,
98 valuesToI8.getOutput(), indices);
99 auto i8ToBool =
100 tosa::CastOp::create(rewriter, op.getLoc(), resultType,
101 gatherI8.getOutput(), /*input_unsigned*/ false);
102 rewriter.replaceOp(op, i8ToBool.getOutput());
103 return success();
104 }
105};
106
107class BoolScatterRewrite : public OpRewritePattern<tosa::ScatterOp> {
108public:
110
111 LogicalResult matchAndRewrite(tosa::ScatterOp op,
112 PatternRewriter &rewriter) const override {
113 const Value valuesIn = op.getValuesIn();
114 const Value indices = op.getIndices();
115
116 const Type valuesInType = valuesIn.getType();
117 const Type i1Type = rewriter.getI1Type();
118 const Type i32Type = rewriter.getI32Type();
119 if (getElementTypeOrSelf(valuesInType) != i1Type ||
120 getElementTypeOrSelf(indices.getType()) != i32Type)
121 return rewriter.notifyMatchFailure(
122 op, "expected values of bool type and indices of i32 type");
123
124 const Value input = op.getInput();
125 const Type inputType = input.getType();
126 const Type resultType = op.getType();
127
128 const Type i8Type = rewriter.getI8Type();
129 const Type valuesInI8Type = cast<TensorType>(valuesInType).clone(i8Type);
130 const Type inputI8Type = cast<TensorType>(inputType).clone(i8Type);
131 const Type resultI8Type = cast<TensorType>(resultType).clone(i8Type);
132
133 auto valuesInToI8 =
134 tosa::CastOp::create(rewriter, op.getLoc(), valuesInI8Type, valuesIn,
135 /*input_unsigned*/ false);
136 auto inputToI8 = tosa::CastOp::create(rewriter, op.getLoc(), inputI8Type,
137 input, /*input_unsigned*/ false);
138 auto scatterI8 = tosa::ScatterOp::create(
139 rewriter, op.getLoc(), resultI8Type, valuesInToI8.getOutput(), indices,
140 inputToI8.getOutput());
141 auto i8ToBool = tosa::CastOp::create(rewriter, op.getLoc(), resultType,
142 scatterI8.getValuesOut(),
143 /*input_unsigned*/ false);
144 rewriter.replaceOp(op, i8ToBool.getOutput());
145 return success();
146 }
147};
148
149static LogicalResult isMatMulTTypeCompatibleForDowngrade(tosa::MatMulTOp op) {
150 const Type aElementType = getStorageElementTypeOrSelf(op.getA().getType());
151 const Type bElementType = getStorageElementTypeOrSelf(op.getB().getType());
152 const Type outputElementType =
153 getStorageElementTypeOrSelf(op.getOutput().getType());
154
155 if (aElementType != bElementType)
156 return failure();
157
158 if (isa<BlockScaledType>(aElementType) || isa<BlockScaledType>(bElementType))
159 return failure();
160
161 if ((aElementType.isF16() && outputElementType.isF16()) ||
162 (aElementType.isF16() && outputElementType.isF32()) ||
163 (aElementType.isF32() && outputElementType.isF32()) ||
164 (aElementType.isBF16() && outputElementType.isF32()) ||
165 (aElementType.isInteger(8) && outputElementType.isInteger(32)) ||
166 (aElementType.isInteger(16) && outputElementType.isInteger(48)) ||
167 (isa<Float8E5M2Type>(aElementType) && outputElementType.isF16()) ||
168 (isa<Float8E4M3FNType>(aElementType) && outputElementType.isF16()))
169 return success();
170
171 return failure();
172}
173
174class MatMulTRewrite : public OpRewritePattern<tosa::MatMulTOp> {
175public:
177
178 LogicalResult matchAndRewrite(tosa::MatMulTOp op,
179 PatternRewriter &rewriter) const override {
180 if (failed(isMatMulTTypeCompatibleForDowngrade(op)))
181 return rewriter.notifyMatchFailure(
182 op, "expected 1.0-compatible matmul_t element types");
183
184 const Type aType = op.getA().getType();
185 const Type bType = op.getB().getType();
186 const ShapeAdaptor aShape(aType);
187 const ShapeAdaptor bShape(bType);
188 if (!aShape.hasRank() || !bShape.hasRank())
189 return rewriter.notifyMatchFailure(op, "expected ranked A and B tensors");
190
191 const int64_t dSize = bShape.getDimSize(0);
192 const int64_t nSize = aShape.getDimSize(0);
193
194 // To convert broadcasting behaviour to TOSA 1.0, we're required to tile the
195 // input. TOSA 1.0 does not support shape expressions, so the batch size
196 // must be known at compile time.
197 if (ShapedType::isDynamic(dSize) ||
198 (dSize == 1 && ShapedType::isDynamic(nSize)))
199 return rewriter.notifyMatchFailure(
200 op, "expected known batch size for broadcast");
201
202 const int64_t wSize = bShape.getDimSize(1);
203 const int64_t cSize = bShape.getDimSize(2);
204 const Location loc = op.getLoc();
205 const RankedTensorType transposedBType =
206 cast<RankedTensorType>(bType).clone({dSize, cSize, wSize});
207 auto transpose =
208 tosa::TransposeOp::create(rewriter, loc, transposedBType, op.getB(),
209 rewriter.getDenseI32ArrayAttr({0, 2, 1}));
210 Value matMulB = transpose.getOutput();
211
212 // Matmul does not support broadcasting, so tile b if required
213 if (dSize == 1 && nSize != 1) {
214 const RankedTensorType tiledBType =
215 cast<RankedTensorType>(bType).clone({nSize, cSize, wSize});
216 const Value multiples = getTosaConstShape(rewriter, loc, {nSize, 1, 1});
217 auto tile =
218 tosa::TileOp::create(rewriter, loc, tiledBType, matMulB, multiples);
219 matMulB = tile.getOutput();
220 }
221
222 auto matmul = tosa::MatMulOp::create(rewriter, loc, op.getType(), op.getA(),
223 matMulB, op.getAZp(), op.getBZp());
224 rewriter.replaceOp(op, matmul.getOutput());
225 return success();
226 }
227};
228
229struct TosaDowngrade1p1To1p0Pass
230 : public tosa::impl::TosaDowngrade1p1To1p0PassBase<
231 TosaDowngrade1p1To1p0Pass> {
232 using Base::Base;
233
234 void runOnOperation() override {
235 MLIRContext &context = getContext();
236 func::FuncOp func = getOperation();
237
238 RewritePatternSet patterns(&context);
239 patterns.add<BoolFp32CastRewrite, BoolGatherRewrite, BoolScatterRewrite,
240 MatMulTRewrite>(&context);
241 FrozenRewritePatternSet frozenPatterns(std::move(patterns));
242
243 if (failed(applyPatternsGreedily(func, frozenPatterns)))
244 return signalPassFailure();
245 }
246};
247
248} // namespace
return success()
b getContext())
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:171
FloatType getF32Type()
Definition Builders.cpp:51
IntegerType getI32Type()
Definition Builders.cpp:71
IntegerType getI1Type()
Definition Builders.cpp:61
IntegerType getI8Type()
Definition Builders.cpp:67
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:583
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:1351
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...