MLIR 24.0.0git
ConversionUtils.h
Go to the documentation of this file.
1//===- ConversionUtils.h - Helper functions for tosa conversion -*- C++ -*-===//
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// Utility functions for TOSA lowering
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef DIALECT_TOSA_UTILS_COVERSION_UTILS_H_
14#define DIALECT_TOSA_UTILS_COVERSION_UTILS_H_
15
22#include <optional>
23
24namespace mlir {
25namespace tosa {
26
27// Creates a SmallVector of Stringrefs for N parallel loops
28SmallVector<utils::IteratorType>
29getNParallelLoopsAttrs(unsigned nParallelLoops);
30
31// Takes a vector of values and condenses them to a vector with no gaps.
32SmallVector<Value> condenseValues(const SmallVector<Value> &values);
33
34// Takes the parameters for a clamp and turns it into a series of ops for float
35// inputs.
36Value clampFloatHelper(Location loc, Value arg, Value min, Value max,
37 OpBuilder &rewriter);
38
39// Takes the parameters for a clamp and turns it into a series of ops for
40// integer inputs.
41Value clampIntHelper(Location loc, Value arg, Value min, Value max,
42 OpBuilder &rewriter, bool isUnsigned);
43
44// Determines whether the integer value falls witin the range of integer type.
45bool validIntegerRange(IntegerType ty, int64_t value);
46
47// Checks for a dynamic batch dim in any of the passed parameters of an op.
48// The batch dimention must be #0 and the rest of the dimensions must be static.
49template <typename Op>
50std::optional<SmallVector<Value>>
52 ArrayRef<Value> params) {
54 SmallVector<Value> dynamicDims;
55 for (const Value &param : params) {
56 auto paramTy = cast<ShapedType>(param.getType());
57 if (!paramTy.hasStaticShape())
58 dynTypes.push_back(paramTy);
59 }
60
61 if (dynTypes.empty())
62 return dynamicDims;
63
64 for (const ShapedType &dynTy : dynTypes) {
65 if (llvm::any_of(dynTy.getShape().drop_front(), ShapedType::isDynamic)) {
66 (void)rewriter.notifyMatchFailure(
67 op, "input can only be dynamic for batch size");
68 return std::nullopt;
69 }
70 }
71
72 dynamicDims.push_back(
73 tensor::DimOp::create(rewriter, op->getLoc(), params[0], 0));
74 return dynamicDims;
75}
76
77/// Common code to create the reshape op where necessary to make the rank of two
78/// values equal. input1 and input2 will be updated when the rank has
79/// changed. The caller is expected to use these to rewrite the original
80/// operator with the RESHAPE now in the graph.
81LogicalResult EqualizeRanks(PatternRewriter &rewriter, Location loc,
82 Value &input1, Value &input2);
83
84LogicalResult EqualizeRanks(ImplicitLocOpBuilder &builder, Value &input1,
85 Value &input2);
86
87// Creates a TOSA operation and performs shape inference on the individual
88// op. This allows shape inference when lowering down to TOSA.
89template <typename TosaOp, typename... Args>
91 Args &&...args) {
92 auto op = TosaOp::create(builder, resultTy, args...);
93
94 InferShapedTypeOpInterface shapeInterface =
95 dyn_cast<InferShapedTypeOpInterface>(op.getOperation());
96 if (!shapeInterface)
97 return op;
98
100 if (shapeInterface
101 .inferReturnTypeComponents(
102 op.getContext(), builder.getLoc(), op->getOperands(),
103 op->getDiscardableAttrDictionary(), op->getPropertiesStorage(),
104 op->getRegions(), returnedShapes)
105 .failed())
106 return op;
107
108 // We need to use the element type of the existing result type to generate
109 // the new result shaped type. This is because rescale can include a cast to
110 // different bit-width types and does not have a TypeAttr to define the
111 // target type.
112 auto result = op->getResult(0);
113 const auto &predictedShape = returnedShapes[0];
114 auto currentKnowledge = ValueKnowledge::getKnowledgeFromType(resultTy);
115
116 // Compute the knowledge based on the inferred type.
117 auto inferredKnowledge = ValueKnowledge::getPessimisticValueState();
118 inferredKnowledge.dtype = mlir::cast<ShapedType>(resultTy).getElementType();
119 inferredKnowledge.hasRank = predictedShape.hasRank();
120 if (predictedShape.hasRank()) {
121 for (auto dim : predictedShape.getDims()) {
122 inferredKnowledge.sizes.push_back(dim);
123 }
124 }
125
126 // Compute the new type based on the joined version.
127 auto newKnowledge = ValueKnowledge::join(currentKnowledge, inferredKnowledge);
128 Type newTy =
129 newKnowledge.hasRank
130 ? Type{mlir::RankedTensorType::get(llvm::ArrayRef(newKnowledge.sizes),
131 newKnowledge.dtype)}
132 : Type{mlir::UnrankedTensorType::get(newKnowledge.dtype)};
133 result.setType(newTy);
134 return op;
135}
136
137// Creates a TOSA operation by:
138// - first equalize ranks for ops with SameOperandsAndResultRank trait
139// - create operator
140// - performs shape inference on this operator
141template <typename TosaOp, typename... Args>
143 Args &&...args) {
144 if (TosaOp::template hasTrait<::mlir::OpTrait::SameOperandsAndResultRank>()) {
145 // op requires same ranks for tensor operands
146 if constexpr (sizeof...(Args) == 2) {
147 auto argX = std::get<0>(std::tie(args...));
148 auto argY = std::get<1>(std::tie(args...));
149 using ArgX = decltype(argX);
150 using ArgY = decltype(argY);
151 if constexpr (std::is_same_v<ArgX, Value> &&
152 std::is_same_v<ArgY, Value>) {
153 Value x = std::get<0>(std::tie(args...));
154 Value y = std::get<1>(std::tie(args...));
155 if (EqualizeRanks(builder, x, y).failed()) {
156 // incompatible broadcast shapes, no reshape is inserted
157 // ResultsBroadcastableShape verify will handle this
158 }
159 return createOpAndInferShape<TosaOp>(builder, resultTy, x, y);
160 }
161 }
162 if constexpr (sizeof...(Args) == 3) {
163 auto argX = std::get<0>(std::tie(args...));
164 auto argY = std::get<1>(std::tie(args...));
165 auto argZ = std::get<2>(std::tie(args...));
166 using ArgX = decltype(argX);
167 using ArgY = decltype(argY);
168 using ArgZ = decltype(argZ);
169 if constexpr (std::is_same_v<ArgX, Value> &&
170 std::is_same_v<ArgY, Value> && std::is_same_v<ArgZ, bool>) {
171 // special case for ArithmeticRightShiftOp
172 Value x = std::get<0>(std::tie(args...));
173 Value y = std::get<1>(std::tie(args...));
174 bool round = std::get<2>(std::tie(args...));
175 if (EqualizeRanks(builder, x, y).failed()) {
176 // incompatible broadcast shapes, no reshape is inserted
177 // ResultsBroadcastableShape verify will handle this
178 }
179 return createOpAndInferShape<TosaOp>(builder, resultTy, x, y, round);
180 }
181 if constexpr (std::is_same_v<ArgX, Value> &&
182 std::is_same_v<ArgY, Value> &&
183 std::is_same_v<ArgZ, Value>) {
184 // special case for Select
185 Value x = std::get<0>(std::tie(args...));
186 Value y = std::get<1>(std::tie(args...));
187 Value z = std::get<2>(std::tie(args...));
188
189 if (EqualizeRanks(builder, x, y).failed() ||
190 EqualizeRanks(builder, x, z).failed() ||
191 EqualizeRanks(builder, y, z).failed()) {
192 // incompatible broadcast shapes, no reshape is inserted
193 // ResultsBroadcastableShape verify will handle this
194 }
195
196 return createOpAndInferShape<TosaOp>(builder, resultTy, x, y, z);
197 }
198 }
199 }
200
201 return createOpAndInferShape<TosaOp>(builder, resultTy, args...);
202}
203
204// Creates a TOSA operation by:
205// - first equalize ranks for ops with SameOperandsAndResultRank trait
206// - create operator
207// - performs shape inference on this operator
208template <typename TosaOp, typename... Args>
210 Type resultTy, Args &&...args) {
211 ImplicitLocOpBuilder builder(loc, rewriter);
212 return CreateOpAndInferShape<TosaOp>(builder, resultTy, args...);
213}
214
215// Apply an int32_t permutation to some input, that should be of the same
216// size as perms. Perms should contain some permutation of 0 - perms.size() - 1.
217template <typename T>
219 ArrayRef<int32_t> perms) {
220 SmallVector<T> permuted;
221 size_t N = input.size();
222 permuted.resize_for_overwrite(N);
223 for (size_t i = 0; i < N; i++)
224 permuted[i] = input[perms[i]];
225 return permuted;
226}
227
228// Computes shape value using tosa const_shape op.
233
235
237 llvm::SmallVector<int64_t> &result_shape);
238
239// returns a small vector of int64_t values that attr contains
241 const int rank);
242
243// returns true iff constant indices for scatter op contains unique indices
244// per batch
245bool hasUniqueConstantScatterIndices(ShapedType indicesType,
246 DenseIntElementsAttr indicesAttr);
247
248// Try to get the values of a DenseResourceElementsAttr construct
249template <typename T>
250std::optional<ArrayRef<T>> tryGetDenseResourceValues(ElementsAttr attr) {
251 if (auto denseResource = dyn_cast<DenseResourceElementsAttr>(attr)) {
252 // Check that the resource memory blob exists
253 AsmResourceBlob *blob = denseResource.getRawHandle().getBlob();
254 if (!blob)
255 return std::nullopt;
256
257 // Check that the data are in a valid form
258 if (!DenseElementsAttr::isValidRawBuffer(attr.getShapedType(),
259 blob->getData())) {
260 return std::nullopt;
261 }
262
263 return blob->template getDataAs<T>();
264 }
265
266 return std::nullopt;
267}
268
269// returns the value of a constant scalar int tensor, or failure if
270// the value cannot be extracted
271template <typename T>
272FailureOr<T> getConstantScalarIntValue(Value val);
273
274} // namespace tosa
275} // namespace mlir
276
277#endif // DIALECT_TOSA_UTILS_COVERSION_UTILS_H_
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
This class represents a processed binary blob of data.
Definition AsmState.h:91
ArrayRef< char > getData() const
Return the raw underlying data of this blob.
Definition AsmState.h:145
An attribute that represents a reference to a dense vector or tensor object.
static bool isValidRawBuffer(ShapedType type, ArrayRef< char > rawBuffer)
Returns true if the given buffer is a valid raw buffer for the given type.
An attribute that represents a reference to a dense integer vector or tensor object.
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
Definition Builders.h:632
Location getLoc() const
Accessors for the implied location.
Definition Builders.h:665
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
Location getLoc()
The source location the operation was defined or derived from.
This provides public APIs that all operations should have.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
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
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
TosaOp createOpAndInferShape(ImplicitLocOpBuilder &builder, Type resultTy, Args &&...args)
Value clampFloatHelper(Location loc, Value arg, Value min, Value max, OpBuilder &rewriter)
SmallVector< T > applyTOSAPermutation(ArrayRef< T > input, ArrayRef< int32_t > perms)
SmallVector< utils::IteratorType > getNParallelLoopsAttrs(unsigned nParallelLoops)
bool hasUniqueConstantScatterIndices(ShapedType indicesType, DenseIntElementsAttr indicesAttr)
SmallVector< Value > condenseValues(const SmallVector< Value > &values)
LogicalResult EqualizeRanks(PatternRewriter &rewriter, Location loc, Value &input1, Value &input2)
Common code to create the reshape op where necessary to make the rank of two values equal.
std::optional< SmallVector< Value > > checkHasDynamicBatchDims(PatternRewriter &rewriter, Op op, ArrayRef< Value > params)
TosaOp CreateOpAndInferShape(ImplicitLocOpBuilder &builder, Type resultTy, Args &&...args)
SmallVector< int64_t > convertFromIntAttr(const DenseElementsAttr &attr, const int rank)
std::optional< ArrayRef< T > > tryGetDenseResourceValues(ElementsAttr attr)
FailureOr< T > getConstantScalarIntValue(Value val)
bool validIntegerRange(IntegerType ty, int64_t value)
Value getTosaConstShape(ImplicitLocOpBuilder &builder, llvm::ArrayRef< int64_t > shape)
SmallVector< int64_t > convertFromMlirShape(ArrayRef< int64_t > shape)
Value clampIntHelper(Location loc, Value arg, Value min, Value max, OpBuilder &rewriter, bool isUnsigned)
bool getConstShapeValues(Operation *op, llvm::SmallVector< int64_t > &result_shape)
Include the generated interface declarations.
static ValueKnowledge join(const ValueKnowledge &lhs, const ValueKnowledge &rhs)
Definition ShapeUtils.h:81
static ValueKnowledge getPessimisticValueState()
Definition ShapeUtils.h:61
static ValueKnowledge getKnowledgeFromType(Type type)
Definition ShapeUtils.h:45