22#include "llvm/ADT/STLExtras.h"
27#include "mlir/Dialect/NVGPU/IR/NVGPUDialect.cpp.inc"
29void NVGPUDialect::initialize() {
31#define GET_TYPEDEF_LIST
32#include "mlir/Dialect/NVGPU/IR/NVGPUTypeDefs.cpp.inc"
35#define GET_ATTRDEF_LIST
36#include "mlir/Dialect/NVGPU/IR/NVGPUAttrDefs.cpp.inc"
40#include "mlir/Dialect/NVGPU/IR/NVGPUOps.cpp.inc"
42 declarePromisedInterfaces<memref::IndexedAccessOpInterface, LdMatrixOp>();
43 declarePromisedInterfaces<memref::IndexedMemCopyOpInterface,
47bool NVGPUDialect::isSharedMemoryAddressSpace(
Attribute memorySpace) {
50 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(memorySpace))
51 return intAttr.getValue().getZExtValue() ==
52 NVGPUDialect::kSharedMemoryAddressSpace;
53 if (
auto gpuAttr = llvm::dyn_cast<gpu::AddressSpaceAttr>(memorySpace))
54 return gpuAttr.getValue() == gpu::AddressSpace::Workgroup;
58bool NVGPUDialect::hasSharedMemoryAddressSpace(MemRefType type) {
59 Attribute memorySpace = type.getMemorySpace();
60 return isSharedMemoryAddressSpace(memorySpace);
67LogicalResult DeviceAsyncCopyOp::verify() {
68 auto srcMemref = llvm::cast<MemRefType>(getSrc().
getType());
69 auto dstMemref = llvm::cast<MemRefType>(getDst().
getType());
71 if (!srcMemref.isLastDimUnitStride())
72 return emitError(
"source memref most minor dim must have unit stride");
73 if (!dstMemref.isLastDimUnitStride())
74 return emitError(
"destination memref most minor dim must have unit stride");
75 if (!NVGPUDialect::hasSharedMemoryAddressSpace(dstMemref))
77 <<
"destination memref must have a memory space attribute of "
79 << NVGPUDialect::kSharedMemoryAddressSpace
80 <<
") or gpu::AddressSpaceAttr(Workgroup)";
81 if (dstMemref.getElementType() != srcMemref.getElementType())
82 return emitError(
"source and destination must have the same element type");
83 if (
size_t(srcMemref.getRank()) != getSrcIndices().size())
84 return emitOpError() <<
"expected " << srcMemref.getRank()
85 <<
" source indices, got " << getSrcIndices().size();
86 if (
size_t(dstMemref.getRank()) != getDstIndices().size())
87 return emitOpError() <<
"expected " << dstMemref.getRank()
88 <<
" destination indices, got "
89 << getDstIndices().size();
90 int64_t dstElements = getDstElements().getZExtValue();
91 int64_t sizeInBytes = (dstMemref.getElementTypeBitWidth() * dstElements) / 8;
92 if (sizeInBytes != 4 && sizeInBytes != 8 && sizeInBytes != 16) {
93 unsigned dstWidth = dstMemref.getElementTypeBitWidth();
95 diag <<
"Requested copy elements is " << dstElements <<
" with width "
96 << dstMemref.getElementTypeBitWidth()
97 <<
". But copy elements could be one of ";
98 if ((32 / dstWidth) > 0)
99 diag << (32 / dstWidth) <<
", ";
100 if ((64 / dstWidth) > 0)
101 diag << (64 / dstWidth) <<
", ";
102 if ((128 / dstWidth) > 0)
103 diag << (128 / dstWidth) <<
".";
106 if (getBypassL1().has_value()) {
107 int64_t req = 16 * 8 / dstMemref.getElementTypeBitWidth();
108 if (getBypassL1().value() && sizeInBytes != 16) {
109 return emitOpError() <<
"bypassL1 does not satify alignment for "
110 << dstMemref <<
" with destination element "
112 <<
". Unset bypassL1, or set "
113 "destination element to "
126 build(odsBuilder, odsState, matrixC.
getType(), matrixA, matrixB, matrixC,
127 mmaShape, UnitAttr());
134 build(odsBuilder, odsState, matrixC.
getType(), matrixA, matrixB, matrixC,
136 tf32Enabled ? odsBuilder.
getUnitAttr() : UnitAttr());
144 const std::array<int64_t, 3> &mmaShape,
145 bool tf32Enabled,
bool sparse =
false) {
163 auto aVector = matrixA.getType();
164 auto bVector = matrixB.getType();
165 auto cVector = matrixC.getType();
173 Type aType = aVector.getElementType();
176 if (sparse && aType.
isF64())
177 return op->
emitError() <<
"f64 is not supported for sparse mode";
189 shapeK = 128 / operandBitwidth;
191 numElementA = 32 / operandBitwidth;
192 numElementB = 32 / operandBitwidth;
195 <<
"expected input data type (i4,i8,f16,bf16,tf32,f64,"
196 "f8E4M3FN,f8E5M2) supported by "
204 if (aShape.size() != 2) {
205 return op->
emitError() <<
"matrixA must be 2 dimensional vector";
208 if (bShape.size() != 2) {
209 return op->
emitError() <<
"matrixB must be 2 dimensional vector";
212 if (cShape.size() != 2) {
213 return op->
emitError() <<
"matrixC must be 2 dimensional vector";
216 auto [m, n, k] = mmaShape;
219 int64_t sparseFactor = sparse ? 2 : 1;
220 if (aShape[0] * aShape[1] *
kWarpSize != m * k / sparseFactor)
222 <<
"expected " << m * k <<
" warp-wide matrix A elements";
225 if (bShape[0] * bShape[1] *
kWarpSize != k * n)
227 <<
"expected " << k * n <<
" warp-wide matrix B elements";
230 if (cShape[0] * cShape[1] *
kWarpSize != m * n)
232 <<
"expected " << m * n <<
" warp-wide matrix C elements";
235 if (tf32Enabled && !(aType.
isF32()))
237 <<
"expected tf32 tensor cores only for F32 operands";
249 if ((aShape[0] != mTile * kTile / (sparse ? 2 : 1)) ||
250 (aShape[1] != numElementA))
251 return op->
emitOpError() <<
"expected matrix A to be shaped ("
252 << mTile * kTile <<
" x " << numElementA <<
")";
255 if ((bShape[0] != kTile * nTile) || (bShape[1] != numElementB))
256 return op->
emitOpError() <<
"expected matrix B to be shaped ("
257 << kTile * nTile <<
" x " << numElementB <<
")";
260 if ((cShape[0] != mTile * nTile) || (cShape[1] != numElementC))
261 return op->
emitOpError() <<
"expected matrix C to be shaped ("
262 << mTile * nTile <<
" x " << numElementC <<
")";
267LogicalResult MmaSyncOp::verify() {
268 if (getMmaShape().size() != 3)
269 return emitOpError() <<
"mmaShape must have exactly 3 elements";
271 return verifyMmaSyncOp(this->getOperation(), getMatrixA(), getMatrixB(),
272 getMatrixC(), getMmaShapeAsArray(),
273 getTf32Enabled().value_or(
false));
283 build(odsBuilder, odsState, matrixC.
getType(), matrixA, matrixB, matrixC,
287LogicalResult MmaSparseSyncOp::verify() {
288 unsigned sparsitySelector = getSparsitySelector();
289 if (sparsitySelector > 1)
290 return emitOpError() <<
"sparsity selector should be 0 or 1";
292 if (getMmaShape().size() != 3)
293 return emitOpError() <<
"mmaShape must have exactly 3 elements";
295 return verifyMmaSyncOp(this->getOperation(), getMatrixA(), getMatrixB(),
296 getMatrixC(), getMmaShapeAsArray(),
297 getTf32Enabled().value_or(
false),
true);
303LogicalResult LdMatrixOp::verify() {
305 auto srcMemref = llvm::cast<MemRefType>(getSrcMemref().
getType());
308 auto resVector = llvm::cast<VectorType>(getRes().
getType());
312 Type resType = resVector.getElementType();
316 int64_t numElementsPer32b = 32 / elementBitWidth;
319 int64_t numTiles = getNumTiles();
322 bool isTranspose = getTranspose();
328 if (!NVGPUDialect::hasSharedMemoryAddressSpace(srcMemref))
330 <<
"expected nvgpu.ldmatrix srcMemref must have a memory space "
331 "attribute of IntegerAttr("
332 << NVGPUDialect::kSharedMemoryAddressSpace
333 <<
") or gpu::AddressSpaceAttr(Workgroup)";
334 if (elementBitWidth > 32)
335 return emitError() <<
"nvgpu.ldmatrix works for 32b or lower";
336 if (isTranspose && !(elementBitWidth == 16))
338 <<
"nvgpu.ldmatrix transpose works only at 16b granularity";
339 if (resShape.size() != 2) {
340 return emitError() <<
"results must be 2 dimensional vector";
342 if (!(resShape[1] == numElementsPer32b))
343 return emitError() <<
"expected vector register shape[1] = "
344 << numElementsPer32b;
345 if (!(resShape[0] == numTiles))
347 <<
"expected vector register shape[0] and numTiles to match";
358 case TensorMapSwizzleKind::SWIZZLE_32B:
360 case TensorMapSwizzleKind::SWIZZLE_64B:
362 case TensorMapSwizzleKind::SWIZZLE_128B:
370 Operation *op, TensorMapDescriptorType descType,
371 std::optional<MemRefType> memrefType = std::nullopt) {
372 MemRefType descMemref = descType.getTensor();
374 if (descType.getInterleave() != TensorMapInterleaveKind::INTERLEAVE_NONE)
375 return op->
emitError() <<
"Interleave options are not supported yet.";
378 if (!NVGPUDialect::hasSharedMemoryAddressSpace(descMemref)) {
379 return op->
emitError() <<
"the tensor map descriptor has incorrect address "
380 "space, it must be shared memory address space.";
383 if (!descMemref.hasStaticShape())
384 return op->
emitError() <<
"the tensor map descriptor must be static shaped";
386 for (
auto dim : descMemref.getShape()) {
388 return op->
emitError() <<
"the tensor map descriptor must have "
389 "dimensions between 1 and "
393 if (descMemref.getRank() > 1 &&
394 descType.getSwizzle() != TensorMapSwizzleKind::SWIZZLE_NONE) {
395 unsigned lastDimensionByte =
396 descMemref.getElementTypeBitWidth() * descMemref.getShape().back() / 8;
398 if (lastDimensionByte != expectByte)
399 return op->
emitError() <<
"the tensormap descriptor must have last "
401 << expectByte <<
" bytes but it is "
402 << lastDimensionByte <<
" bytes";
406 if (!memrefType.has_value())
409 MemRefType dstMemref = memrefType.value();
412 if (descMemref.getElementType() != dstMemref.getElementType()) {
413 return op->
emitError() <<
"the element type of tensor map descriptor and "
414 "memref must be same";
417 if (!NVGPUDialect::hasSharedMemoryAddressSpace(dstMemref)) {
418 return op->
emitError() <<
"the destination memref has incorrect address "
419 "space, it must be shared memory address space.";
421 if (!dstMemref.hasStaticShape())
422 return op->
emitError() <<
"the destination memref must be static shaped";
424 if (dstMemref.getRank() != descMemref.getRank()) {
425 return op->
emitError() <<
"the shape of tensor map descriptor and "
426 "memref must have same rank";
428 if (!descMemref.getShape().equals(dstMemref.getShape())) {
429 return op->
emitError() <<
"memref and tensor map shapes mismatch "
430 << descMemref <<
" != " << dstMemref;
434 descMemref.getShape().back() * descMemref.getElementTypeBitWidth() / 8;
436 return op->
emitError() <<
"the bytes in the last dimension of the tensor "
437 "map must be a multiple of 16";
442LogicalResult TmaAsyncLoadOp::verify() {
445 if (error.has_value())
446 return error.value();
450 <<
" coordinates are supported.";
453 size_t(getTensorMapDescriptor().
getType().getTensor().getRank())) {
454 return emitError() <<
"number of coordinates do not match with the rank of "
455 "tensor descriptor map.";
465LogicalResult TmaAsyncStoreOp::verify() {
468 if (error.has_value())
469 return error.value();
473 <<
" coordinates are supported.";
476 size_t(getTensorMapDescriptor().
getType().getTensor().getRank())) {
477 return emitError() <<
"number of coordinates do not match with the rank of "
478 "tensor descriptor map.";
484LogicalResult TmaCreateDescriptorOp::verify() {
487 <<
" coordinates are supported.";
490 std::optional<InFlightDiagnostic> error =
492 if (error.has_value())
493 return error.value();
502LogicalResult WarpgroupGenerateDescriptorOp::verify() {
503 std::optional<InFlightDiagnostic> error =
505 if (error.has_value())
506 return error.value();
508 if (getTensorMap().
getType().getSwizzle() !=
509 TensorMapSwizzleKind::SWIZZLE_128B) {
511 << stringifyTensorMapSwizzleKind(
512 TensorMapSwizzleKind::SWIZZLE_128B)
513 <<
" is supported for the time being";
516 if (getTensorMap().
getType().getInterleave() !=
517 TensorMapInterleaveKind::INTERLEAVE_NONE) {
519 << stringifyTensorMapInterleaveKind(
520 TensorMapInterleaveKind::INTERLEAVE_NONE)
521 <<
" is supported for the time being";
551 if (isa<Float8E5M2Type, Float8E4M3FNType>(typeA) &&
552 isa<Float8E5M2Type, Float8E4M3FNType>(typeB) &&
567 72, 80, 88, 96, 104, 112, 120, 128,
568 136, 144, 152, 160, 168, 176, 184, 192,
569 200, 208, 216, 224, 232, 240, 248, 256};
571 80, 96, 112, 128, 144, 160,
572 176, 192, 208, 224, 240, 256};
574 isa<Float8E5M2Type, Float8E4M3FNType>(typeA))
575 if (llvm::is_contained(allowedN, sizeN))
579 if (llvm::is_contained(allowedNshort, sizeN))
584LogicalResult WarpgroupMmaOp::verify() {
585 if (getTransposeA() && !getTransposeB())
587 <<
"supports non-transpose A (Row Major) "
588 "and transpose B (Column Major) for the time being ";
589 MemRefType matrixA = getDescriptorA().
getType().getTensor();
590 MemRefType matrixB = getDescriptorB().
getType().getTensor();
591 VectorType matrixC = getMatrixC().
getType().getFragmented();
592 VectorType matrixD = getMatrixD().getType().getFragmented();
594 if (matrixC != matrixD)
595 return emitOpError() <<
"type of matrix C and matrix D must be the same";
597 if (matrixA.getRank() != 2 || matrixB.getRank() != 2 ||
598 matrixC.getRank() != 2 || matrixD.getRank() != 2) {
600 <<
"has matrices A, B, C and D, they must be 2 dimensional";
603 if (matrixA.getShape()[1] != matrixB.getShape()[0])
604 return emitOpError() <<
"2nd dim matrix-A (" << matrixA.getShape()[1]
605 <<
")!= 1st dim matrix-B (" << matrixB.getShape()[0]
607 if (matrixA.getShape()[0] != matrixC.getShape()[0])
608 return emitOpError() <<
"1st dim matrix-A ( " << matrixA.getShape()[0]
609 <<
" )!= 1st dim matrix-C ( " << matrixC.getShape()[0]
611 if (matrixB.getShape()[1] != matrixC.getShape()[1])
612 return emitOpError() <<
"2nd dim matrix-B ( " << matrixB.getShape()[1]
613 <<
" ) != 2nd dim matrix-C ( " << matrixC.getShape()[1]
617 matrixA.getElementType(),
618 matrixB.getElementType())))
619 return emitOpError() << matrixC.getElementType()
620 <<
" += " << matrixA.getElementType() <<
" * "
621 << matrixB.getElementType()
622 <<
", it is not supported.";
625 return emitOpError() <<
"has input type " << matrixB <<
" n is set to "
626 << matrixB.getDimSize(1) <<
", it is not supported";
630 if (!matrixC.getElementType().isF32() && !matrixA.getElementType().isF16() &&
631 !matrixA.getElementType().isBF16()) {
632 return emitOpError() <<
"hit a limitation: " << matrixC.getElementType()
633 <<
" += " << matrixA.getElementType() <<
" * "
634 << matrixB.getElementType()
635 <<
", it is not supported yet";
641LogicalResult WarpgroupMmaStoreOp::verify() {
642 MemRefType dstMemrefType = getDstMemref().getType();
643 VectorType vtype = getMatrixD().getType().getFragmented();
646 if (!vtype.getElementType().isF32()) {
648 <<
"hit a limitation: only f32 results for the time being";
650 if (vtype.getDimSize(0) != dstMemrefType.getDimSize(0) ||
651 vtype.getDimSize(1) != dstMemrefType.getDimSize(1)) {
652 return emitOpError() <<
"results [" << vtype <<
"][" << vtype.getDimSize(1)
653 <<
"] values. However, destination memref["
654 << dstMemrefType.getDimSize(0) <<
"]["
655 << dstMemrefType.getDimSize(1)
656 <<
"] does not have same size as results";
665LogicalResult WarpgroupMmaInitAccumulatorOp::verify() {
666 WarpgroupAccumulatorType accType = getMatrixC().getType();
667 int64_t sizeM = accType.getFragmented().getDimSize(0);
668 int64_t sizeN = accType.getFragmented().getDimSize(1);
669 Type elemType = accType.getFragmented().getElementType();
673 return emitOpError() <<
"has type " << accType.getFragmented()
674 <<
". It does not fit into warp-group "
675 "level (wgmma) matrix multiplication instruction "
676 "(or not supported yet)";
685LogicalResult RcpOp::verify() {
687 bool approx = getApprox();
688 mlir::NVVM::FPRoundingModeAttr rnd = getRoundingAttr();
690 if (!approx || !ftz) {
692 <<
"has a limitation. non-approx or non-ftz is not supported yet.";
694 if (rnd.getValue() != mlir::NVVM::FPRoundingMode::NONE) {
695 return emitOpError() <<
"has a limitation. " << rnd
696 <<
" is not supported yet.";
707 bool srcIsVector = llvm::isa<VectorType>(inType);
708 bool dstIsVector = llvm::isa<VectorType>(outType);
709 if (srcIsVector != dstIsVector)
710 return op->
emitOpError(
"input and output must both be scalars or both be "
712 << inType <<
" and " << outType;
714 auto srcVector = llvm::cast<VectorType>(inType);
715 auto dstVector = llvm::cast<VectorType>(outType);
716 if (srcVector.getShape() != dstVector.getShape())
717 return op->
emitOpError(
"input and output shapes must match, got ")
718 << inType <<
" and " << outType;
723LogicalResult TruncfOp::verify() {
724 Type inType = getIn().getType();
736 if (srcBitWidth <= dstBitWidth)
737 return emitOpError(
"result type ")
738 << dstType <<
" must be narrower than operand type " << srcType;
740 if (!(srcBitWidth == 64 || srcBitWidth == 32 || srcBitWidth == 16))
741 return emitOpError(
"input type must be 64/32/16 bitwidth, but got ")
744 if (llvm::isa<Float8E8M0FNUType>(dstType)) {
745 if (rnd != mlir::NVVM::FPRoundingMode::RZ &&
746 rnd != mlir::NVVM::FPRoundingMode::RP)
747 return emitOpError(
"expects RZ or RP rounding mode when result type is "
750 }
else if (rnd == mlir::NVVM::FPRoundingMode::RS) {
753 if (!(srcBitWidth == 32 && dstBitWidth == 16))
754 return emitOpError(
"RS (stochastic) rounding is only supported for "
755 "f32->f16/bf16, got ")
756 << srcType <<
" -> " << dstType;
757 if (!getRandomBits())
758 return emitOpError(
"random_bits operand is required with RS rounding");
759 }
else if (srcType.isF64() && dstBitWidth >= 16) {
760 if (rnd != mlir::NVVM::FPRoundingMode::RN)
761 return emitOpError(
"expects RN rounding mode for f64 input, but got ")
763 }
else if (srcBitWidth == 32 && dstBitWidth == 16) {
764 if (rnd != mlir::NVVM::FPRoundingMode::RN &&
765 rnd != mlir::NVVM::FPRoundingMode::RZ)
766 return emitOpError(
"expects RN or RZ rounding mode for f32 to f16/bf16, "
769 }
else if (rnd != mlir::NVVM::FPRoundingMode::RN) {
770 return emitOpError(
"expects RN rounding mode, but got ") << getRndAttr();
773 if (getRandomBits() && rnd != mlir::NVVM::FPRoundingMode::RS)
774 return emitOpError(
"random_bits can only be used with RS rounding mode");
783LogicalResult ExtfOp::verify() {
784 Type inType = getIn().getType();
795 if (srcBitWidth >= dstBitWidth)
796 return emitOpError(
"result type ")
797 << dstType <<
" must be wider than operand type " << srcType;
799 if (dstBitWidth != 16 && dstBitWidth != 32 && dstBitWidth != 64)
800 return emitOpError(
"result type must be 16, 32, or 64 bitwidth, but got ")
803 if (llvm::isa<Float8E8M0FNUType>(srcType) &&
804 !llvm::isa<BFloat16Type>(dstType) && !dstType.
isF32())
805 return emitOpError(
"expects bf16 or f32 output type when input type is "
808 if (rnd != mlir::NVVM::FPRoundingMode::RN)
809 return emitOpError(
"expects RN rounding mode, but got ") << getRndAttr();
811 if (getRelu() && llvm::isa<BFloat16Type>(dstType))
812 return emitOpError(
"relu is not supported for bf16 destination");
821#define GET_ATTRDEF_CLASSES
822#include "mlir/Dialect/NVGPU/IR/NVGPUAttrDefs.cpp.inc"
824#include "mlir/Dialect/NVGPU/IR/NVGPUEnums.cpp.inc"
826#define GET_OP_CLASSES
827#include "mlir/Dialect/NVGPU/IR/NVGPUOps.cpp.inc"
829#define GET_TYPEDEF_CLASSES
830#include "mlir/Dialect/NVGPU/IR/NVGPUTypeDefs.cpp.inc"
LogicalResult isAllowedSizeM(int sizeM)
static LogicalResult verifyMmaSyncOp(Operation *op, TypedValue< VectorType > matrixA, TypedValue< VectorType > matrixB, TypedValue< VectorType > matrixC, const std::array< int64_t, 3 > &mmaShape, bool tf32Enabled, bool sparse=false)
Performs verification for MmaSyncOp and MmaSparseSyncOp.
std::optional< InFlightDiagnostic > verifyTmaDescriptorWithMemref(Operation *op, TensorMapDescriptorType descType, std::optional< MemRefType > memrefType=std::nullopt)
LogicalResult isAllowedSizeN(int sizeN, Type typeA)
LogicalResult isAllowedWGMMADataType(Type typeD, Type typeA, Type typeB)
static LogicalResult verifyConversionShapes(Operation *op, Type inType, Type outType)
static unsigned getSwizzleBytes(TensorMapSwizzleKind kind)
constexpr unsigned kTMALastdimByte
The bytes in the last dimension of the tensor map must be a multiple of 16.
constexpr int kWgmmaSizeM
M size of wgmma.mma_async instruction.
constexpr unsigned kMaxTMATensorDimension
Maximum TMA tile dimension (tensorRank) must be non-zero and less than or equal to the maximum suppor...
constexpr unsigned kMaxTMADimension
Maximum TMA tile size (boxDim), which specifies number of elements to be traversed along each of the ...
Attributes are known-constant values of operations.
ArrayAttr getI64ArrayAttr(ArrayRef< int64_t > values)
This class represents a diagnostic that is inflight and set to be reported.
This class helps build Operations.
Operation is the basic unit of execution within MLIR.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
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...
bool isInteger() const
Return true if this is an integer type (with the specified width).
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
SmallVector< int64_t, 4 > getCoordinates(ArrayRef< int64_t > basis, unsigned linearIndex)
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
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.
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
This represents an operation in an abstracted form, suitable for use with the builder APIs.