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 "llvm/ADT/APFloat.h"
23#include <optional>
24
25namespace mlir {
26namespace tosa {
27
28// Creates a SmallVector of Stringrefs for N parallel loops
29SmallVector<utils::IteratorType>
30getNParallelLoopsAttrs(unsigned nParallelLoops);
31
32// Takes a vector of values and condenses them to a vector with no gaps.
33SmallVector<Value> condenseValues(const SmallVector<Value> &values);
34
35// Takes the parameters for a clamp and turns it into a series of ops for float
36// inputs.
37Value clampFloatHelper(Location loc, Value arg, Value min, Value max,
38 OpBuilder &rewriter);
39
40// Takes the parameters for a clamp and turns it into a series of ops for
41// integer inputs.
42Value clampIntHelper(Location loc, Value arg, Value min, Value max,
43 OpBuilder &rewriter, bool isUnsigned);
44
45// Determines whether the integer value falls witin the range of integer type.
46bool validIntegerRange(IntegerType ty, int64_t value);
47
48// Returns the identity value to seed a float min/max computation with, such as
49// a reduction accumulator or a pooling window. TOSA seeds REDUCE_MIN with
50// maximum_s<in_out_t>() and REDUCE_MAX/ARGMAX/MAX_POOL2D with
51// minimum_s<in_out_t>(), and for floating-point types those bounds are
52// +/-infinity rather than the largest finite value. Only use them when the
53// caller opted in *and* the format can represent them: APFloat::getInf() is
54// unreachable for FiniteOnly semantics and silently returns a NaN for NanOnly
55// semantics such as f8E4M3FN, which would poison the whole computation through
56// NaN-propagating arith.minimumf/arith.maximumf.
57APFloat getFloatMinMaxIdentity(const llvm::fltSemantics &semantics,
58 bool negative, bool allowNonFinites);
59
60// Checks for a dynamic batch dim in any of the passed parameters of an op.
61// The batch dimention must be #0 and the rest of the dimensions must be static.
62template <typename Op>
63std::optional<SmallVector<Value>>
65 ArrayRef<Value> params) {
67 SmallVector<Value> dynamicDims;
68 for (const Value &param : params) {
69 auto paramTy = cast<ShapedType>(param.getType());
70 if (!paramTy.hasStaticShape())
71 dynTypes.push_back(paramTy);
72 }
73
74 if (dynTypes.empty())
75 return dynamicDims;
76
77 for (const ShapedType &dynTy : dynTypes) {
78 if (llvm::any_of(dynTy.getShape().drop_front(), ShapedType::isDynamic)) {
79 (void)rewriter.notifyMatchFailure(
80 op, "input can only be dynamic for batch size");
81 return std::nullopt;
82 }
83 }
84
85 dynamicDims.push_back(
86 tensor::DimOp::create(rewriter, op->getLoc(), params[0], 0));
87 return dynamicDims;
88}
89
90/// Common code to create the reshape op where necessary to make the rank of two
91/// values equal. input1 and input2 will be updated when the rank has
92/// changed. The caller is expected to use these to rewrite the original
93/// operator with the RESHAPE now in the graph.
94LogicalResult EqualizeRanks(PatternRewriter &rewriter, Location loc,
95 Value &input1, Value &input2);
96
97LogicalResult EqualizeRanks(ImplicitLocOpBuilder &builder, Value &input1,
98 Value &input2);
99
100// Creates a TOSA operation and performs shape inference on the individual
101// op. This allows shape inference when lowering down to TOSA.
102template <typename TosaOp, typename... Args>
104 Args &&...args) {
105 auto op = TosaOp::create(builder, resultTy, args...);
106
107 InferShapedTypeOpInterface shapeInterface =
108 dyn_cast<InferShapedTypeOpInterface>(op.getOperation());
109 if (!shapeInterface)
110 return op;
111
113 if (shapeInterface
114 .inferReturnTypeComponents(
115 op.getContext(), builder.getLoc(), op->getOperands(),
116 op->getDiscardableAttrDictionary(), op->getPropertiesStorage(),
117 op->getRegions(), returnedShapes)
118 .failed())
119 return op;
120
121 // We need to use the element type of the existing result type to generate
122 // the new result shaped type. This is because rescale can include a cast to
123 // different bit-width types and does not have a TypeAttr to define the
124 // target type.
125 auto result = op->getResult(0);
126 const auto &predictedShape = returnedShapes[0];
127 auto currentKnowledge = ValueKnowledge::getKnowledgeFromType(resultTy);
128
129 // Compute the knowledge based on the inferred type.
130 auto inferredKnowledge = ValueKnowledge::getPessimisticValueState();
131 inferredKnowledge.dtype = mlir::cast<ShapedType>(resultTy).getElementType();
132 inferredKnowledge.hasRank = predictedShape.hasRank();
133 if (predictedShape.hasRank()) {
134 for (auto dim : predictedShape.getDims()) {
135 inferredKnowledge.sizes.push_back(dim);
136 }
137 }
138
139 // Compute the new type based on the joined version.
140 auto newKnowledge = ValueKnowledge::join(currentKnowledge, inferredKnowledge);
141 Type newTy =
142 newKnowledge.hasRank
143 ? Type{mlir::RankedTensorType::get(llvm::ArrayRef(newKnowledge.sizes),
144 newKnowledge.dtype)}
145 : Type{mlir::UnrankedTensorType::get(newKnowledge.dtype)};
146 result.setType(newTy);
147 return op;
148}
149
150// Creates a TOSA operation by:
151// - first equalize ranks for ops with SameOperandsAndResultRank trait
152// - create operator
153// - performs shape inference on this operator
154template <typename TosaOp, typename... Args>
156 Args &&...args) {
157 if (TosaOp::template hasTrait<::mlir::OpTrait::SameOperandsAndResultRank>()) {
158 // op requires same ranks for tensor operands
159 if constexpr (sizeof...(Args) == 2) {
160 auto argX = std::get<0>(std::tie(args...));
161 auto argY = std::get<1>(std::tie(args...));
162 using ArgX = decltype(argX);
163 using ArgY = decltype(argY);
164 if constexpr (std::is_same_v<ArgX, Value> &&
165 std::is_same_v<ArgY, Value>) {
166 Value x = std::get<0>(std::tie(args...));
167 Value y = std::get<1>(std::tie(args...));
168 if (EqualizeRanks(builder, x, y).failed()) {
169 // incompatible broadcast shapes, no reshape is inserted
170 // ResultsBroadcastableShape verify will handle this
171 }
172 return createOpAndInferShape<TosaOp>(builder, resultTy, x, y);
173 }
174 }
175 if constexpr (sizeof...(Args) == 3) {
176 auto argX = std::get<0>(std::tie(args...));
177 auto argY = std::get<1>(std::tie(args...));
178 auto argZ = std::get<2>(std::tie(args...));
179 using ArgX = decltype(argX);
180 using ArgY = decltype(argY);
181 using ArgZ = decltype(argZ);
182 if constexpr (std::is_same_v<ArgX, Value> &&
183 std::is_same_v<ArgY, Value> && std::is_same_v<ArgZ, bool>) {
184 // special case for ArithmeticRightShiftOp
185 Value x = std::get<0>(std::tie(args...));
186 Value y = std::get<1>(std::tie(args...));
187 bool round = std::get<2>(std::tie(args...));
188 if (EqualizeRanks(builder, x, y).failed()) {
189 // incompatible broadcast shapes, no reshape is inserted
190 // ResultsBroadcastableShape verify will handle this
191 }
192 return createOpAndInferShape<TosaOp>(builder, resultTy, x, y, round);
193 }
194 if constexpr (std::is_same_v<ArgX, Value> &&
195 std::is_same_v<ArgY, Value> &&
196 std::is_same_v<ArgZ, Value>) {
197 // special case for Select
198 Value x = std::get<0>(std::tie(args...));
199 Value y = std::get<1>(std::tie(args...));
200 Value z = std::get<2>(std::tie(args...));
201
202 if (EqualizeRanks(builder, x, y).failed() ||
203 EqualizeRanks(builder, x, z).failed() ||
204 EqualizeRanks(builder, y, z).failed()) {
205 // incompatible broadcast shapes, no reshape is inserted
206 // ResultsBroadcastableShape verify will handle this
207 }
208
209 return createOpAndInferShape<TosaOp>(builder, resultTy, x, y, z);
210 }
211 }
212 }
213
214 return createOpAndInferShape<TosaOp>(builder, resultTy, args...);
215}
216
217// Creates a TOSA operation by:
218// - first equalize ranks for ops with SameOperandsAndResultRank trait
219// - create operator
220// - performs shape inference on this operator
221template <typename TosaOp, typename... Args>
223 Type resultTy, Args &&...args) {
224 ImplicitLocOpBuilder builder(loc, rewriter);
225 return CreateOpAndInferShape<TosaOp>(builder, resultTy, args...);
226}
227
228// Apply an int32_t permutation to some input, that should be of the same
229// size as perms. Perms should contain some permutation of 0 - perms.size() - 1.
230template <typename T>
232 ArrayRef<int32_t> perms) {
233 SmallVector<T> permuted;
234 size_t N = input.size();
235 permuted.resize_for_overwrite(N);
236 for (size_t i = 0; i < N; i++)
237 permuted[i] = input[perms[i]];
238 return permuted;
239}
240
241// Computes shape value using tosa const_shape op.
246
248
250 llvm::SmallVector<int64_t> &result_shape);
251
252// returns a small vector of int64_t values that attr contains
254 const int rank);
255
256// returns true iff constant indices for scatter op contains unique indices
257// per batch
258bool hasUniqueConstantScatterIndices(ShapedType indicesType,
259 DenseIntElementsAttr indicesAttr);
260
261// Try to get the values of a DenseResourceElementsAttr construct
262template <typename T>
263std::optional<ArrayRef<T>> tryGetDenseResourceValues(ElementsAttr attr) {
264 if (auto denseResource = dyn_cast<DenseResourceElementsAttr>(attr)) {
265 // Check that the resource memory blob exists
266 AsmResourceBlob *blob = denseResource.getRawHandle().getBlob();
267 if (!blob)
268 return std::nullopt;
269
270 // Check that the data are in a valid form
271 if (!DenseElementsAttr::isValidRawBuffer(attr.getShapedType(),
272 blob->getData())) {
273 return std::nullopt;
274 }
275
276 return blob->template getDataAs<T>();
277 }
278
279 return std::nullopt;
280}
281
282// returns the value of a constant scalar int tensor, or failure if
283// the value cannot be extracted
284template <typename T>
285FailureOr<T> getConstantScalarIntValue(Value val);
286
287} // namespace tosa
288} // namespace mlir
289
290#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)
APFloat getFloatMinMaxIdentity(const llvm::fltSemantics &semantics, bool negative, bool allowNonFinites)
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