MLIR 24.0.0git
Utils.cpp
Go to the documentation of this file.
1//===- Utils.cpp - Utilities to support the Linalg dialect ----------------===//
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// This file implements utilities for the Linalg dialect.
10//
11//===----------------------------------------------------------------------===//
12
18#include "mlir/IR/Diagnostics.h"
19#include "llvm/ADT/SmallBitVector.h"
20#include "llvm/ADT/SmallVectorExtras.h"
21#include <numeric>
22
23using namespace mlir;
24
25std::optional<SmallVector<OpFoldResult>>
27 ShapedType expandedType,
29 ArrayRef<OpFoldResult> inputShape) {
30
31 SmallVector<Value> outputShapeValues;
32 SmallVector<int64_t> outputShapeInts;
33 // For zero-rank inputs, all dims in result shape are unit extent.
34 if (inputShape.empty()) {
35 outputShapeInts.resize(expandedType.getRank(), 1);
36 return getMixedValues(outputShapeInts, outputShapeValues, b);
37 }
38
39 // Check for all static shapes.
40 if (expandedType.hasStaticShape()) {
41 ArrayRef<int64_t> staticShape = expandedType.getShape();
42 outputShapeInts.assign(staticShape.begin(), staticShape.end());
43 return getMixedValues(outputShapeInts, outputShapeValues, b);
44 }
45
46 outputShapeInts.resize(expandedType.getRank(), ShapedType::kDynamic);
47 for (const auto &it : llvm::enumerate(reassociation)) {
48 ReassociationIndices indexGroup = it.value();
49
50 int64_t indexGroupStaticSizesProductInt = 1;
51 bool foundDynamicShape = false;
52 for (int64_t index : indexGroup) {
53 int64_t outputDimSize = expandedType.getDimSize(index);
54 // Cannot infer expanded shape with multiple dynamic dims in the
55 // same reassociation group!
56 if (ShapedType::isDynamic(outputDimSize)) {
57 if (foundDynamicShape)
58 return std::nullopt;
59 foundDynamicShape = true;
60 } else {
61 outputShapeInts[index] = outputDimSize;
62 indexGroupStaticSizesProductInt *= outputDimSize;
63 }
64 }
65 if (!foundDynamicShape)
66 continue;
67
68 int64_t inputIndex = it.index();
69 // Call get<Value>() under the assumption that we're not casting
70 // dynamism.
71 Value indexGroupSize = cast<Value>(inputShape[inputIndex]);
72 Value indexGroupStaticSizesProduct =
73 arith::ConstantIndexOp::create(b, loc, indexGroupStaticSizesProductInt);
74 Value dynamicDimSize = b.createOrFold<arith::DivSIOp>(
75 loc, indexGroupSize, indexGroupStaticSizesProduct);
76 outputShapeValues.push_back(dynamicDimSize);
77 }
78
79 if ((int64_t)outputShapeValues.size() !=
80 llvm::count(outputShapeInts, ShapedType::kDynamic))
81 return std::nullopt;
82
83 return getMixedValues(outputShapeInts, outputShapeValues, b);
84}
85
86/// Matches a ConstantIndexOp.
87/// TODO: This should probably just be a general matcher that uses matchConstant
88/// and checks the operation for an index type.
92
93llvm::SmallBitVector mlir::getPositionsOfShapeOne(unsigned rank,
95 llvm::SmallBitVector dimsToProject(shape.size());
96 for (unsigned pos = 0, e = shape.size(); pos < e && rank > 0; ++pos) {
97 if (shape[pos] == 1) {
98 dimsToProject.set(pos);
99 --rank;
100 }
101 }
102 return dimsToProject;
103}
104
106 OpFoldResult ofr) {
107 if (auto value = dyn_cast_if_present<Value>(ofr))
108 return value;
109 auto attr = cast<IntegerAttr>(cast<Attribute>(ofr));
110 return arith::ConstantOp::create(
111 b, loc, b.getIntegerAttr(attr.getType(), attr.getValue().getSExtValue()));
112}
113
115 OpFoldResult ofr) {
116 if (auto value = dyn_cast_if_present<Value>(ofr))
117 return value;
118 auto attr = cast<IntegerAttr>(cast<Attribute>(ofr));
119 return arith::ConstantIndexOp::create(b, loc, attr.getValue().getSExtValue());
120}
121
123 Type targetType, Value value) {
124 if (targetType == value.getType())
125 return value;
126
127 bool targetIsIndex = targetType.isIndex();
128 bool valueIsIndex = value.getType().isIndex();
129 if (targetIsIndex ^ valueIsIndex)
130 return arith::IndexCastOp::create(b, loc, targetType, value);
131
132 auto targetIntegerType = dyn_cast<IntegerType>(targetType);
133 auto valueIntegerType = dyn_cast<IntegerType>(value.getType());
134 assert(targetIntegerType && valueIntegerType &&
135 "unexpected cast between types other than integers and index");
136 assert(targetIntegerType.getSignedness() == valueIntegerType.getSignedness());
137
138 if (targetIntegerType.getWidth() > valueIntegerType.getWidth())
139 return arith::ExtSIOp::create(b, loc, targetIntegerType, value);
140 return arith::TruncIOp::create(b, loc, targetIntegerType, value);
141}
142
144 IntegerType toType, bool isUnsigned) {
145 // If operand is floating point, cast directly to the int type.
146 if (isa<FloatType>(operand.getType())) {
147 if (isUnsigned)
148 return arith::FPToUIOp::create(b, toType, operand);
149 return arith::FPToSIOp::create(b, toType, operand);
150 }
151 // Cast index operands directly to the int type.
152 if (operand.getType().isIndex())
153 return arith::IndexCastOp::create(b, toType, operand);
154 if (auto fromIntType = dyn_cast<IntegerType>(operand.getType())) {
155 // Either extend or truncate.
156 if (toType.getWidth() > fromIntType.getWidth()) {
157 if (isUnsigned)
158 return arith::ExtUIOp::create(b, toType, operand);
159 return arith::ExtSIOp::create(b, toType, operand);
160 }
161 if (toType.getWidth() < fromIntType.getWidth())
162 return arith::TruncIOp::create(b, toType, operand);
163 return operand;
164 }
165
166 return {};
167}
168
170 FloatType toType, bool isUnsigned) {
171 // If operand is integer, cast directly to the float type.
172 // Note that it is unclear how to cast from BF16<->FP16.
173 if (isa<IntegerType>(operand.getType())) {
174 if (isUnsigned)
175 return arith::UIToFPOp::create(b, toType, operand);
176 return arith::SIToFPOp::create(b, toType, operand);
177 }
178 if (auto fromFpTy = dyn_cast<FloatType>(operand.getType())) {
179 if (toType.getWidth() > fromFpTy.getWidth())
180 return arith::ExtFOp::create(b, toType, operand,
181 arith::FastMathFlagsAttr{});
182 if (toType.getWidth() < fromFpTy.getWidth())
183 return arith::TruncFOp::create(b, toType, operand);
184 return operand;
185 }
186
187 return {};
188}
189
191 ComplexType targetType,
192 bool isUnsigned) {
193 if (auto fromComplexType = dyn_cast<ComplexType>(operand.getType())) {
194 if (isa<FloatType>(targetType.getElementType()) &&
195 isa<FloatType>(fromComplexType.getElementType())) {
196 Value real = complex::ReOp::create(b, operand);
197 Value imag = complex::ImOp::create(b, operand);
198 Type targetETy = targetType.getElementType();
199 if (targetType.getElementType().getIntOrFloatBitWidth() <
200 fromComplexType.getElementType().getIntOrFloatBitWidth()) {
201 real = arith::TruncFOp::create(b, targetETy, real);
202 imag = arith::TruncFOp::create(b, targetETy, imag);
203 } else {
204 real = arith::ExtFOp::create(b, targetETy, real,
205 arith::FastMathFlagsAttr{});
206 imag = arith::ExtFOp::create(b, targetETy, imag,
207 arith::FastMathFlagsAttr{});
208 }
209 return complex::CreateOp::create(b, targetType, real, imag);
210 }
211 }
212
213 if (isa<FloatType>(operand.getType())) {
214 FloatType toFpTy = cast<FloatType>(targetType.getElementType());
215 auto toBitwidth = toFpTy.getIntOrFloatBitWidth();
216 Value from = operand;
217 if (from.getType().getIntOrFloatBitWidth() < toBitwidth) {
218 from = arith::ExtFOp::create(b, toFpTy, from, arith::FastMathFlagsAttr{});
219 }
220 if (from.getType().getIntOrFloatBitWidth() > toBitwidth) {
221 from = arith::TruncFOp::create(b, toFpTy, from);
222 }
224 b, toFpTy, mlir::APFloat(toFpTy.getFloatSemantics(), 0));
225 return complex::CreateOp::create(b, targetType, from, zero);
226 }
227
228 if (isa<IntegerType>(operand.getType())) {
229 FloatType toFpTy = cast<FloatType>(targetType.getElementType());
230 Value from = operand;
231 if (isUnsigned) {
232 from = arith::UIToFPOp::create(b, toFpTy, from);
233 } else {
234 from = arith::SIToFPOp::create(b, toFpTy, from);
235 }
237 b, toFpTy, mlir::APFloat(toFpTy.getFloatSemantics(), 0));
238 return complex::CreateOp::create(b, targetType, from, zero);
239 }
240
241 return {};
242}
243
245 Type toType, bool isUnsignedCast) {
246 if (operand.getType() == toType)
247 return operand;
248 ImplicitLocOpBuilder ib(loc, b);
250 if (auto intTy = dyn_cast<IntegerType>(toType)) {
251 result = convertScalarToIntDtype(ib, operand, intTy, isUnsignedCast);
252 } else if (auto floatTy = dyn_cast<FloatType>(toType)) {
253 result = convertScalarToFpDtype(ib, operand, floatTy, isUnsignedCast);
254 } else if (auto complexTy = dyn_cast<ComplexType>(toType)) {
255 result =
256 convertScalarToComplexDtype(ib, operand, complexTy, isUnsignedCast);
257 }
258
259 if (result)
260 return result;
261
262 emitWarning(loc) << "could not cast operand of type " << operand.getType()
263 << " to " << toType;
264 return operand;
265}
266
269 ArrayRef<OpFoldResult> valueOrAttrVec) {
270 return llvm::map_to_vector<4>(
271 valueOrAttrVec, [&](OpFoldResult value) -> Value {
272 return getValueOrCreateConstantIndexOp(b, loc, value);
273 });
274}
275
277 Type type, const APInt &value) {
278 TypedAttr attr;
279 if (isa<IntegerType>(type)) {
280 attr = builder.getIntegerAttr(type, value);
281 } else {
282 auto vecTy = cast<ShapedType>(type);
283 attr = SplatElementsAttr::get(vecTy, value);
284 }
285
286 return arith::ConstantOp::create(builder, loc, attr);
287}
288
290 Type type, int64_t value) {
291 unsigned elementBitWidth = 0;
292 if (auto intTy = dyn_cast<IntegerType>(type))
293 elementBitWidth = intTy.getWidth();
294 else
295 elementBitWidth = cast<ShapedType>(type).getElementTypeBitWidth();
296
297 return createScalarOrSplatConstant(builder, loc, type,
298 APInt(elementBitWidth, value));
299}
300
302 Type type, const APFloat &value) {
303 if (isa<FloatType>(type))
304 return builder.createOrFold<arith::ConstantOp>(
305 loc, type, builder.getFloatAttr(type, value));
306 TypedAttr splat = SplatElementsAttr::get(cast<ShapedType>(type), value);
307 return builder.createOrFold<arith::ConstantOp>(loc, type, splat);
308}
309
311 if (auto value = dyn_cast_if_present<Value>(ofr))
312 return value.getType();
313 auto attr = cast<IntegerAttr>(cast<Attribute>(ofr));
314 return attr.getType();
315}
316
318 return arith::AndIOp::create(b, loc, lhs, rhs);
319}
321 if (isa<FloatType>(lhs.getType()))
322 return arith::AddFOp::create(b, loc, lhs, rhs);
323 return arith::AddIOp::create(b, loc, lhs, rhs, ovf);
324}
326 if (isa<FloatType>(lhs.getType()))
327 return arith::SubFOp::create(b, loc, lhs, rhs);
328 return arith::SubIOp::create(b, loc, lhs, rhs, ovf);
329}
331 if (isa<FloatType>(lhs.getType()))
332 return arith::MulFOp::create(b, loc, lhs, rhs);
333 return arith::MulIOp::create(b, loc, lhs, rhs, ovf);
334}
336 if (isa<FloatType>(lhs.getType()))
337 return arith::CmpFOp::create(b, loc, arith::CmpFPredicate::OGT, lhs, rhs);
338 return arith::CmpIOp::create(b, loc, arith::CmpIPredicate::sgt, lhs, rhs);
339}
341 if (isa<FloatType>(lhs.getType()))
342 return arith::CmpFOp::create(b, loc, arith::CmpFPredicate::OLT, lhs, rhs);
343 return arith::CmpIOp::create(b, loc, arith::CmpIPredicate::slt, lhs, rhs);
344}
346 return arith::SelectOp::create(b, loc, cmp, lhs, rhs);
347}
348
349namespace mlir::arith {
350
352 return createProduct(builder, loc, values, values.front().getType());
353}
354
356 Type resultType) {
357 Value one = ConstantOp::create(builder, loc, resultType,
358 builder.getOneAttr(resultType));
359 ArithBuilder arithBuilder(builder, loc);
360 return llvm::accumulate(values, one, [&arithBuilder](Value acc, Value v) {
361 return arithBuilder.mul(acc, v);
362 });
363}
364
365FloatType parseFloatType(MLIRContext *ctx, StringRef name) {
366 // Parsing non-builtin types is unsafe because the respective dialect may not
367 // have been loaded.
368 if (!name.empty() && name.front() == '!')
369 return FloatType();
370
371 // Suppress diagnostics: callers handle invalid type strings themselves.
372 ScopedDiagnosticHandler handler(ctx, [](Diagnostic &) {});
373 return dyn_cast_or_null<FloatType>(mlir::parseType(name, ctx));
374}
375
376} // namespace mlir::arith
static Value convertScalarToComplexDtype(ImplicitLocOpBuilder &b, Value operand, ComplexType targetType, bool isUnsigned)
Definition Utils.cpp:190
static Value convertScalarToIntDtype(ImplicitLocOpBuilder &b, Value operand, IntegerType toType, bool isUnsigned)
Definition Utils.cpp:143
static Value convertScalarToFpDtype(ImplicitLocOpBuilder &b, Value operand, FloatType toType, bool isUnsigned)
Definition Utils.cpp:169
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:237
FloatAttr getFloatAttr(Type type, double value)
Definition Builders.cpp:263
TypedAttr getOneAttr(Type type)
Definition Builders.cpp:351
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
Definition Builders.h:632
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class helps build Operations.
Definition Builders.h:210
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
Definition Builders.h:528
This class represents a single result from folding an operation.
This diagnostic handler is a simple RAII class that registers and erases a diagnostic handler on a gi...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIndex() const
Definition Types.cpp:56
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
static ConstantFloatOp create(OpBuilder &builder, Location location, FloatType type, const APFloat &value)
Definition ArithOps.cpp:369
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:398
FloatType parseFloatType(MLIRContext *ctx, StringRef name)
Definition Utils.cpp:365
Value createProduct(OpBuilder &builder, Location loc, ArrayRef< Value > values)
Definition Utils.cpp:351
Include the generated interface declarations.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
InFlightDiagnostic emitWarning(Location loc)
Utility method to emit a warning message using this location.
Value convertScalarToDtype(OpBuilder &b, Location loc, Value operand, Type toType, bool isUnsignedCast)
Converts a scalar value operand to type toType.
Definition Utils.cpp:244
Value createScalarOrSplatConstant(OpBuilder &builder, Location loc, Type type, const APInt &value)
Create a constant of type type at location loc whose value is value (an APInt or APFloat whose type m...
Definition Utils.cpp:276
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:310
constexpr T real(const NonFloatComplex< T > &x)
Definition Complex.h:255
Value getValueOrCreateConstantIntOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition Utils.cpp:105
Value getValueOrCreateCastToIndexLike(OpBuilder &b, Location loc, Type targetType, Value value)
Create a cast from an index-like value (index or integer) to another index-like value.
Definition Utils.cpp:122
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition Utils.cpp:114
std::optional< SmallVector< OpFoldResult > > inferExpandShapeOutputShape(OpBuilder &b, Location loc, ShapedType expandedType, ArrayRef< ReassociationIndices > reassociation, ArrayRef< OpFoldResult > inputShape)
Infer the output shape for a {memref|tensor}.expand_shape when it is possible to do so.
Definition Utils.cpp:26
constexpr T imag(const NonFloatComplex< T > &x)
Definition Complex.h:260
Type parseType(llvm::StringRef typeStr, MLIRContext *context, size_t *numRead=nullptr, bool isKnownNullTerminated=false)
This parses a single MLIR type to an MLIR context if it was valid.
llvm::SmallBitVector getPositionsOfShapeOne(unsigned rank, ArrayRef< int64_t > shape)
Definition Utils.cpp:93
detail::op_matcher< arith::ConstantIndexOp > matchConstantIndex()
Matches a ConstantIndexOp.
Definition Utils.cpp:89
SmallVector< int64_t, 2 > ReassociationIndices
Definition Utils.h:27
Helper struct to build simple arithmetic quantities with minimal type inference support.
Definition Utils.h:103
Value mul(Value lhs, Value rhs)
Definition Utils.cpp:330
Value _and(Value lhs, Value rhs)
Definition Utils.cpp:317
Value slt(Value lhs, Value rhs)
Definition Utils.cpp:340
Value select(Value cmp, Value lhs, Value rhs)
Definition Utils.cpp:345
Value add(Value lhs, Value rhs)
Definition Utils.cpp:320
Value sgt(Value lhs, Value rhs)
Definition Utils.cpp:335
Value sub(Value lhs, Value rhs)
Definition Utils.cpp:325
The matcher that matches a certain kind of op.
Definition Matchers.h:283