MLIR 24.0.0git
XeVMDialect.cpp
Go to the documentation of this file.
1//===-- XeVMDialect.cpp - XeVM dialect registration -------------*- C++ -*-===//
2//
3// This file is licensed 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//===----------------------------------------------------------------------===//
12#include "llvm/ADT/APFloat.h"
13#include "llvm/ADT/SmallSet.h"
14#include "llvm/Support/FileSystem.h"
15#include "llvm/Support/MathExtras.h"
16
17using namespace mlir;
18using namespace mlir::xevm;
19
20#include "mlir/Dialect/LLVMIR/XeVMOpsDialect.cpp.inc"
21#include "mlir/Dialect/LLVMIR/XeVMOpsEnums.cpp.inc"
22
23namespace {
24static constexpr uint32_t subgroupSize = 16;
25
26template <typename Op>
27LogicalResult verifyMatrixInput(Op op) {
28 static_assert(llvm::is_one_of<Op, BlockLoad2dOp, BlockStore2dOp,
29 BlockPrefetch2dOp>::value,
30 "Unexpected template parameter");
31
32 std::optional<int64_t> width = getConstantIntValue(op.getBaseWidth());
33 std::optional<int64_t> pitch = getConstantIntValue(op.getBasePitch());
34 if (pitch && width && *pitch < *width)
35 return op->emitOpError(
36 "4th operand (base pitch) should be >= 2nd operand (base width)");
37
38 uint32_t elemSize = op.getElemSizeInBits();
39 if (elemSize < 8 || !llvm::isPowerOf2_32(elemSize) || elemSize > 32)
40 return op->emitOpError("expecting 'elem_size_in_bits' to be 8, 16, or 32");
41
42 uint32_t tileHeight = op.getTileHeight();
43 if (tileHeight > 32 || !llvm::isPowerOf2_32(tileHeight))
44 return op->emitOpError("expecting tile_height to be 1, 2, 4, 8, 16, or 32");
45
46 uint32_t vBlocks = op.getVBlocks();
47 if (vBlocks > 8 || !llvm::isPowerOf2_32(vBlocks))
48 return op->emitOpError("expecting v_blocks to be 1, 2, 4, or 8");
49
50 return success();
51}
52
53LogicalResult verify2DBlockLoadRestriction(BlockLoad2dOp op) {
54 VectorType resTy = op.getRes().getType();
55 if (!resTy.getElementType().isIntOrFloat())
56 return op.emitOpError()
57 << "expecting result element type to be int or float";
58 unsigned resElemTySize = resTy.getElementType().getIntOrFloatBitWidth();
59 unsigned resSize = resTy.getNumElements() * resElemTySize;
60 unsigned expectedSize = op.getElemSizeInBits() * op.getTileHeight() *
61 op.getTileWidth() * op.getVBlocks() / subgroupSize;
62 if (resSize != expectedSize)
63 return op.emitOpError() << "result size of " << resSize
64 << " bits does not match the expected size of "
65 << expectedSize << " bits";
66
67 if (op.getTranspose() && op.getPackRegister())
68 return op.emitOpError("transpose and pack_register are mutually exclusive");
69
70 if (!op.getTranspose() && !op.getPackRegister()) {
71 uint32_t tileHeight = op.getTileHeight();
72 if (tileHeight < 1 || tileHeight > 32)
73 return op.emitOpError("expecting tile_height to be between 1 and 32");
74
75 uint32_t tileWidth = op.getTileWidth();
76 uint32_t vBlocks = op.getVBlocks();
77 switch (op.getElemSizeInBits()) {
78 case 8:
79 if (tileWidth < 4 || tileWidth > 64)
80 return op.emitOpError("expecting tile_width to be between 4 and 64");
81 if (vBlocks != 1 && vBlocks != 2 && vBlocks != 4)
82 return op.emitOpError("expecting v_blocks to be 1, 2, or 4");
83 if (tileWidth * vBlocks > 64)
84 return op.emitOpError(
85 "tile_width * v_blocks should be less than or equal "
86 "to 64 for 8 bit elements");
87 break;
88 case 16:
89 if (tileWidth < 2 || tileWidth > 32)
90 return op.emitOpError("expecting tile_width to be between 2 and 32");
91 if (vBlocks != 1 && vBlocks != 2 && vBlocks != 4)
92 return op.emitOpError("expecting v_blocks to be 1, 2, or 4");
93 if (tileWidth * vBlocks > 32)
94 return op.emitOpError(
95 "tile_width * v_blocks should be less than or equal "
96 "to 32 for 16 bit elements");
97 break;
98 case 32:
99 if (tileWidth < 1 || tileWidth > 16)
100 return op.emitOpError("expecting tile_width to be between 1 and 16");
101 if (vBlocks != 1 && vBlocks != 2)
102 return op.emitOpError("expecting v_blocks to be 1 or 2");
103 if (tileWidth * vBlocks > 16)
104 return op.emitOpError(
105 "tile_width * v_blocks should be less than or equal "
106 "to 16 for 32 bit elements");
107 break;
108 case 64:
109 if (tileWidth < 1 || tileWidth > 8)
110 return op.emitOpError("expecting tile_width to be between 1 and 8");
111 if (vBlocks != 1)
112 return op.emitOpError("expecting v_blocks to be 1");
113 break;
114 default:
115 return op.emitOpError(
116 "expecting elem_size_in_bits to be 8, 16, 32, or 64");
117 }
118
119 return success();
120 }
121
122 if (op.getTranspose()) {
123 assert(!op.getPackRegister() && "Expecting pack_register should be false");
124
125 uint32_t vBlocks = op.getVBlocks();
126 if (vBlocks != 1)
127 return op.emitOpError("expecting v_blocks to be 1");
128
129 uint32_t tileHeight = op.getTileHeight();
130 uint32_t tileWidth = op.getTileWidth();
131 switch (op.getElemSizeInBits()) {
132 case 32:
133 if (tileHeight < 1 || tileHeight > 32)
134 return op.emitOpError("expecting tile_height to be between 1 and 32");
135 if (tileWidth < 1 || tileWidth > 8)
136 return op.emitOpError("expecting tile_width to be between 1 and 8");
137 break;
138 case 64:
139 if (tileHeight != 8)
140 return op.emitOpError(
141 "expecting tile_height to be 8 for 64 bit elements");
142 if (tileWidth != 1 && tileWidth != 2 && tileWidth != 4)
143 return op.emitOpError("expecting tile_width to be 1, 2, or 4");
144 break;
145 default:
146 return op.emitOpError("transpose is only supported for 32 and 64 bit "
147 "elements");
148 }
149
150 return success();
151 }
152
153 assert(op.getPackRegister() && !op.getTranspose() &&
154 "Expecting pack_register should be true and transpose should be "
155 "false");
156
157 uint32_t vBlocks = op.getVBlocks();
158 if (vBlocks != 1 && vBlocks != 2 && vBlocks != 4)
159 return op.emitOpError("expecting v_blocks to be 1, 2, or 4");
160
161 uint32_t tileHeight = op.getTileHeight();
162 uint32_t tileWidth = op.getTileWidth();
163 switch (op.getElemSizeInBits()) {
164 case 8:
165 if (tileHeight < 4 || tileHeight > 32)
166 return op.emitOpError("expecting tile_height to be between 4 and 32");
167 if (tileWidth < 4 || tileWidth > 16)
168 return op.emitOpError("expecting tile_width to be between 4 and 16");
169 break;
170 case 16:
171 if (tileHeight < 2 || tileHeight > 32)
172 return op.emitOpError("expecting tile_height to be between 2 and 32");
173 if (tileWidth < 2 || tileWidth > 16)
174 return op.emitOpError("expecting tile_width to be between 2 and 16");
175 if (tileWidth * vBlocks > 32)
176 return op.emitOpError(
177 "tile_width * v_blocks should be less than or equal "
178 "to 32 for 16 bit elements");
179 break;
180 default:
181 return op.emitOpError("pack_register is only supported for 8 and 16 bit "
182 "elements");
183 }
184
185 return success();
186}
187
188static LogicalResult verify2DBlockStoreRestriction(BlockStore2dOp op) {
189 uint32_t tileHeight = op.getTileHeight();
190 if (tileHeight < 1 || tileHeight > 8)
191 return op.emitOpError("expecting tile_height to be between 1 and 8");
192
193 uint32_t tileWidth = op.getTileWidth();
194 switch (op.getElemSizeInBits()) {
195 case 8:
196 if (tileWidth < 4 || tileWidth > 64)
197 return op.emitOpError("expecting tile_width to be between 4 and 64");
198 break;
199 case 16:
200 if (tileWidth < 2 || tileWidth > 32)
201 return op.emitOpError("expecting tile_width to be between 2 and 32");
202 break;
203 case 32:
204 if (tileWidth < 1 || tileWidth > 16)
205 return op.emitOpError("expecting tile_width to be between 1 and 16");
206 break;
207 case 64:
208 if (tileWidth < 1 || tileWidth > 8)
209 return op.emitOpError("expecting tile_width to be between 1 and 8");
210 break;
211 default:
212 return op.emitOpError("expecting elem_size_in_bits to be 8, 16, 32, or 64");
213 }
214
215 uint32_t vBlocks = op.getVBlocks();
216 if (vBlocks != 1)
217 return op.emitOpError("expecting v_blocks to be 1");
218 return success();
219}
220
221} // namespace
222
223LogicalResult BlockLoad2dOp::verify() {
224 if (verify2DBlockLoadRestriction(*this).failed())
225 return failure();
226
227 if (verifyMatrixInput(*this).failed())
228 return failure();
229
230 VectorType resTy = getRes().getType();
231 if (!resTy.getElementType().isIntOrFloat())
232 return emitOpError() << "expecting result element type to be int of float";
233 unsigned resElemTySize = resTy.getElementType().getIntOrFloatBitWidth();
234 if (getElemSizeInBits() == 32 || getPackRegister()) {
235 if (resElemTySize != 32)
236 return emitOpError() << "expecting result element type to be 32 bits";
237 }
238
239 uint32_t tileWidth = getTileWidth();
240 if (getPackRegister()) {
241 if (tileWidth != 16)
242 return emitOpError(
243 "tile_width when pack_register is true should be equal "
244 "to subgroup size (16 elements)");
245 return success();
246 }
247
248 return success();
249}
250
251LogicalResult BlockStore2dOp::verify() {
252 if (verify2DBlockStoreRestriction(*this).failed())
253 return failure();
254
255 if (verifyMatrixInput(*this).failed())
256 return failure();
257
258 uint32_t tileWidth = getTileWidth();
259 switch (getElemSizeInBits()) {
260 case 8:
261 if (tileWidth != 16 && tileWidth != 32)
262 return emitOpError("tile_width for 8 bit elements should be equal to "
263 "16 or 32");
264 break;
265 case 16:
266 if (tileWidth != 16)
267 return emitOpError("tile_width for 16 bit elements should be equal "
268 "to 16");
269 break;
270 case 32:
271 if (tileWidth != 16)
272 return emitOpError("tile_width for 32 bit elements should be equal "
273 "to 16");
274 break;
275 default:
276 llvm_unreachable("unexpected element size");
277 }
278
279 return success();
280}
281
282LogicalResult BlockPrefetch2dOp::verify() {
283 if (verifyMatrixInput(*this).failed())
284 return failure();
285
286 uint32_t tileWidth = getTileWidth();
287 switch (getElemSizeInBits()) {
288 case 8:
289 if (tileWidth != 16 && tileWidth != 32)
290 return emitOpError("tile_width for 8 bit elements should be equal to "
291 "16 or 32");
292 break;
293 case 16:
294 if (tileWidth != 16)
295 return emitOpError("tile_width for 16 bit elements should be equal "
296 "to 16");
297 break;
298 case 32:
299 if (tileWidth != 8 && tileWidth != 16)
300 return emitOpError(
301 "tile_width for 32 bit elements should be equal to 8 or 16");
302 break;
303 default:
304 llvm_unreachable("unexpected element size");
305 }
306
307 return success();
308}
309
310template <typename OpType, typename = std::enable_if_t<llvm::is_one_of<
311 OpType, BlockLoadOp, BlockStoreOp>::value>>
312LogicalResult verify1DBlockArg(OpType op) {
313 Type srcOrDstTy;
314 if constexpr (std::is_same_v<OpType, BlockLoadOp>)
315 srcOrDstTy = op.getResult().getType();
316 else
317 srcOrDstTy = op.getVal().getType();
318 VectorType vTy = dyn_cast<VectorType>(srcOrDstTy);
319 // scalar case is always valid
320 if (!vTy)
321 return success();
322 int elemTySize = vTy.getElementType().getIntOrFloatBitWidth() / 8;
323 if (elemTySize == 1) {
324 llvm::SmallSet<int, 4> validSizes{2, 4, 8, 16};
325 if (validSizes.contains(vTy.getNumElements()))
326 return success();
327 else
328 return op.emitOpError(
329 "vector size must be 2, 4, 8 or 16 for 8-bit element type");
330 } else {
331 llvm::SmallSet<int, 3> validSizes{2, 4, 8};
332 if (validSizes.contains(vTy.getNumElements()))
333 return success();
334 else
335 return op.emitOpError(
336 "vector size must be 2, 4 or 8 for element type > 8 bits");
337 }
338}
339
340LogicalResult BlockLoadOp::verify() { return verify1DBlockArg(*this); }
341
342LogicalResult BlockStoreOp::verify() { return verify1DBlockArg(*this); }
343
344LogicalResult MMAOp::verify() {
345 if (getC()) {
346 if (getResult().getType() != getC().getType())
347 return emitOpError("type of C operand must match result type");
348 }
349 return success();
350}
351
352LogicalResult MMAMxOp::verify() {
353 if (getC()) {
354 if (getResult().getType() != getC().getType())
355 return emitOpError("type of C operand must match result type");
356 }
357 return success();
358}
359
360/// Number of bits one narrow float value occupies. The narrow values of a
361/// `xevm.truncf` destination, or a `xevm.extf` source, are packed into whole
362/// bytes, so a sub-byte format fits several values per byte.
363static int64_t getNarrowFloatBitWidth(TruncfDstElemTypes etype) {
364 return etype == TruncfDstElemTypes::E2M1 ? 4 : 8;
365}
366static int64_t getNarrowFloatBitWidth(ExtfSrcElemTypes etype) {
367 return etype == ExtfSrcElemTypes::E2M1 ? 4 : 8;
368}
369
370/// Number of values `ty` holds: its length if it is a vector, and one
371/// otherwise. SPIR-V has no vector of length one and uses a scalar instead, so
372/// a conversion of two fp4 values, which pack into a single byte, has a scalar
373/// on its packed side.
375 if (auto vecTy = dyn_cast<VectorType>(ty))
376 return vecTy.getNumElements();
377 return 1;
378}
379
380/// Total bit width of `ty`, which is a scalar or a vector of a scalar.
382 if (auto vecTy = dyn_cast<VectorType>(ty))
383 return vecTy.getNumElements() * vecTy.getElementTypeBitWidth();
384 return ty.getIntOrFloatBitWidth();
385}
386
387/// Verifies that `packedTy` is exactly wide enough to hold `numValues` values
388/// of `narrowBits` bits each, rounded up to whole bytes.
389static LogicalResult verifyPackedWidth(Operation *op, StringRef packedName,
390 Type packedTy, int64_t numValues,
391 int64_t narrowBits) {
392 int64_t expected = llvm::alignTo(numValues * narrowBits, 8);
393 int64_t actual = getPackedBitWidth(packedTy);
394 if (actual != expected)
395 return op->emitOpError()
396 << packedName << " should be " << expected << " bits wide to hold "
397 << numValues << " value(s) of " << narrowBits << " bits, but it is "
398 << actual;
399 return success();
400}
401
402LogicalResult TruncfOp::verify() {
403 Type srcTy = getSrc().getType();
404 Type dstTy = getDst().getType();
405 if (getElementTypeOrSelf(srcTy).getIntOrFloatBitWidth() <=
406 getElementTypeOrSelf(dstTy).getIntOrFloatBitWidth())
407 return emitError(
408 "dst element bitwidth should be less than src element bitwidth");
409 return verifyPackedWidth(*this, "dst", dstTy, getNumValues(srcTy),
410 getNarrowFloatBitWidth(getDstEtype().getEtype()));
411}
412
413LogicalResult ExtfOp::verify() {
414 Type srcTy = getSrc().getType();
415 Type dstTy = getDst().getType();
416 if (getElementTypeOrSelf(srcTy).getIntOrFloatBitWidth() >=
417 getElementTypeOrSelf(dstTy).getIntOrFloatBitWidth())
418 return emitError(
419 "dst element bitwidth should be greater than src element bitwidth");
420 return verifyPackedWidth(*this, "src", srcTy, getNumValues(dstTy),
421 getNarrowFloatBitWidth(getSrcEtype().getEtype()));
422}
423
424/// Float semantics the element type attributes of `xevm.truncf` and `xevm.extf`
425/// stand for. The narrow formats are the OCP FP8 and FP4 ones: `bf8` is
426/// E5M2, `f8` is E4M3 and `e2m1` is FP4.
427static const llvm::fltSemantics *getFloatSemantics(TruncfSrcElemTypes etype) {
428 switch (etype) {
429 case TruncfSrcElemTypes::F16:
430 return &llvm::APFloat::IEEEhalf();
431 case TruncfSrcElemTypes::BF16:
432 return &llvm::APFloat::BFloat();
433 }
434 return nullptr;
435}
436
437static const llvm::fltSemantics *getFloatSemantics(ExtfDstElemTypes etype) {
438 switch (etype) {
439 case ExtfDstElemTypes::F16:
440 return &llvm::APFloat::IEEEhalf();
441 case ExtfDstElemTypes::BF16:
442 return &llvm::APFloat::BFloat();
443 }
444 return nullptr;
445}
446
447static const llvm::fltSemantics *getFloatSemantics(TruncfDstElemTypes etype) {
448 switch (etype) {
449 case TruncfDstElemTypes::BF8:
450 return &llvm::APFloat::Float8E5M2();
451 case TruncfDstElemTypes::F8:
452 return &llvm::APFloat::Float8E4M3FN();
453 case TruncfDstElemTypes::E2M1:
454 return &llvm::APFloat::Float4E2M1FN();
455 }
456 return nullptr;
457}
458
459static const llvm::fltSemantics *getFloatSemantics(ExtfSrcElemTypes etype) {
460 switch (etype) {
461 case ExtfSrcElemTypes::BF8:
462 return &llvm::APFloat::Float8E5M2();
463 case ExtfSrcElemTypes::F8:
464 return &llvm::APFloat::Float8E4M3FN();
465 case ExtfSrcElemTypes::E2M1:
466 return &llvm::APFloat::Float4E2M1FN();
467 }
468 return nullptr;
469}
470
471/// truncf(extf(a)) -> a, when the two ops convert through the same pair of
472/// formats and every narrow value survives the round trip. `bf8` does not
473/// qualify: it is IEEE-like, and extending a signaling NaN quiets it, so the
474/// original value cannot be recovered. This mirrors `arith.truncf`, which gates
475/// the same fold on `APFloatBase::isLosslesslyConvertibleTo`.
476OpFoldResult TruncfOp::fold(FoldAdaptor) {
477 auto extfOp = getSrc().getDefiningOp<ExtfOp>();
478 if (!extfOp)
479 return {};
480
481 const llvm::fltSemantics *narrowSem =
482 getFloatSemantics(getDstEtype().getEtype());
483 const llvm::fltSemantics *wideSem =
484 getFloatSemantics(getSrcEtype().getEtype());
485 if (narrowSem != getFloatSemantics(extfOp.getSrcEtype().getEtype()) ||
486 wideSem != getFloatSemantics(extfOp.getDstEtype().getEtype()))
487 return {};
488
489 Value narrowSrc = extfOp.getSrc();
490 if (narrowSrc.getType() != getDst().getType())
491 return {};
492
493 if (!llvm::APFloatBase::isLosslesslyConvertibleTo(*narrowSem, *wideSem))
494 return {};
495
496 return narrowSrc;
497}
498
499LogicalResult BitcastShuffleOp::verify() {
500 Type srcTy = getSrc().getType();
501 Type resTy = getRes().getType();
502 auto srcVecTy = dyn_cast<VectorType>(srcTy);
503 auto resVecTy = dyn_cast<VectorType>(resTy);
504 // Only a pack (vector -> scalar) and an unpack (scalar -> vector) are
505 // supported, so exactly one side is a vector.
506 if (static_cast<bool>(srcVecTy) == static_cast<bool>(resVecTy))
507 return emitOpError("expected exactly one of src and res to be a vector: a "
508 "pack takes a vector and returns a scalar, an unpack "
509 "takes a scalar and returns a vector");
510
511 auto getTotalBitWidth = [](Type ty) -> unsigned {
512 if (auto vecTy = dyn_cast<VectorType>(ty))
513 return vecTy.getNumElements() * vecTy.getElementTypeBitWidth();
514 return ty.getIntOrFloatBitWidth();
515 };
516 if (getTotalBitWidth(srcTy) != getTotalBitWidth(resTy))
517 return emitOpError("src and res types must have the same total bit width");
518 return success();
519}
520
521LogicalResult
522XeVMTargetAttr::verify(function_ref<InFlightDiagnostic()> emitError, int O,
523 StringRef triple, StringRef chip, DictionaryAttr flags,
524 ArrayAttr linkFiles) {
525 if (O < 0 || O > 3) {
526 return emitError()
527 << "The optimization level must be a number between 0 and 3.";
528 }
529 if (triple.empty()) {
530 return emitError() << "The target triple cannot be empty.";
531 }
532 if (chip.empty()) {
533 return emitError() << "The target chip cannot be empty.";
534 }
535 if (linkFiles) {
536 for (Attribute fileAttr : linkFiles) {
537 if (auto fileStrAttr = llvm::dyn_cast<StringAttr>(fileAttr)) {
538 StringRef filePath = fileStrAttr.getValue();
539 if (filePath.empty()) {
540 return emitError() << "File paths in linkFiles cannot be empty.";
541 }
542 if (!llvm::sys::fs::exists(filePath)) {
543 return emitError() << "File '" << filePath << "' does not exist.";
544 }
545 }
546 }
547 }
548 return success();
549}
550
551void XeVMDialect::initialize() {
552 addOperations<
553#define GET_OP_LIST
554#include "mlir/Dialect/LLVMIR/XeVMOps.cpp.inc"
555 >();
556
557 addAttributes<
558#define GET_ATTRDEF_LIST
559#include "mlir/Dialect/LLVMIR/XeVMOpsAttributes.cpp.inc"
560 >();
561 declarePromisedInterface<mlir::gpu::TargetAttrInterface,
562 mlir::xevm::XeVMTargetAttr>();
563}
564
565#define GET_OP_CLASSES
566#include "mlir/Dialect/LLVMIR/XeVMOps.cpp.inc"
567
568#define GET_ATTRDEF_CLASSES
569#include "mlir/Dialect/LLVMIR/XeVMOpsAttributes.cpp.inc"
return success()
ArrayAttr()
static LogicalResult verifyPackedWidth(Operation *op, StringRef packedName, Type packedTy, int64_t numValues, int64_t narrowBits)
Verifies that packedTy is exactly wide enough to hold numValues values of narrowBits bits each,...
static const llvm::fltSemantics * getFloatSemantics(TruncfSrcElemTypes etype)
Float semantics the element type attributes of xevm.truncf and xevm.extf stand for.
LogicalResult verify1DBlockArg(OpType op)
static int64_t getNarrowFloatBitWidth(TruncfDstElemTypes etype)
Number of bits one narrow float value occupies.
static int64_t getPackedBitWidth(Type ty)
Total bit width of ty, which is a scalar or a vector of a scalar.
static int64_t getNumValues(Type ty)
Number of values ty holds: its length if it is a vector, and one otherwise.
This class represents a diagnostic that is inflight and set to be reported.
This class represents a single result from folding an operation.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
This provides public APIs that all operations should have.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
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
Include the generated interface declarations.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:311
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
llvm::function_ref< Fn > function_ref
Definition LLVM.h:155