23#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/TypeSwitch.h"
29#include "mlir/Dialect/NVGPU/IR/NVGPUDialect.cpp.inc"
31void NVGPUDialect::initialize() {
33#define GET_TYPEDEF_LIST
34#include "mlir/Dialect/NVGPU/IR/NVGPUTypeDefs.cpp.inc"
37#define GET_ATTRDEF_LIST
38#include "mlir/Dialect/NVGPU/IR/NVGPUAttrDefs.cpp.inc"
42#include "mlir/Dialect/NVGPU/IR/NVGPUOps.cpp.inc"
44 declarePromisedInterfaces<memref::IndexedAccessOpInterface, LdMatrixOp>();
45 declarePromisedInterfaces<memref::IndexedMemCopyOpInterface,
49bool NVGPUDialect::isSharedMemoryAddressSpace(
Attribute memorySpace) {
52 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(memorySpace))
53 return intAttr.getValue().getZExtValue() ==
54 NVGPUDialect::kSharedMemoryAddressSpace;
55 if (
auto gpuAttr = llvm::dyn_cast<gpu::AddressSpaceAttr>(memorySpace))
56 return gpuAttr.getValue() == gpu::AddressSpace::Workgroup;
60bool NVGPUDialect::hasSharedMemoryAddressSpace(MemRefType type) {
61 Attribute memorySpace = type.getMemorySpace();
62 return isSharedMemoryAddressSpace(memorySpace);
69LogicalResult DeviceAsyncCopyOp::verify() {
70 auto srcMemref = llvm::cast<MemRefType>(getSrc().
getType());
71 auto dstMemref = llvm::cast<MemRefType>(getDst().
getType());
73 if (!srcMemref.isLastDimUnitStride())
74 return emitError(
"source memref most minor dim must have unit stride");
75 if (!dstMemref.isLastDimUnitStride())
76 return emitError(
"destination memref most minor dim must have unit stride");
77 if (!NVGPUDialect::hasSharedMemoryAddressSpace(dstMemref))
79 <<
"destination memref must have a memory space attribute of "
81 << NVGPUDialect::kSharedMemoryAddressSpace
82 <<
") or gpu::AddressSpaceAttr(Workgroup)";
83 if (dstMemref.getElementType() != srcMemref.getElementType())
84 return emitError(
"source and destination must have the same element type");
85 if (
size_t(srcMemref.getRank()) != getSrcIndices().size())
86 return emitOpError() <<
"expected " << srcMemref.getRank()
87 <<
" source indices, got " << getSrcIndices().size();
88 if (
size_t(dstMemref.getRank()) != getDstIndices().size())
89 return emitOpError() <<
"expected " << dstMemref.getRank()
90 <<
" destination indices, got "
91 << getDstIndices().size();
92 int64_t dstElements = getDstElements().getZExtValue();
93 int64_t sizeInBytes = (dstMemref.getElementTypeBitWidth() * dstElements) / 8;
94 if (sizeInBytes != 4 && sizeInBytes != 8 && sizeInBytes != 16) {
95 unsigned dstWidth = dstMemref.getElementTypeBitWidth();
97 diag <<
"Requested copy elements is " << dstElements <<
" with width "
98 << dstMemref.getElementTypeBitWidth()
99 <<
". But copy elements could be one of ";
100 if ((32 / dstWidth) > 0)
101 diag << (32 / dstWidth) <<
", ";
102 if ((64 / dstWidth) > 0)
103 diag << (64 / dstWidth) <<
", ";
104 if ((128 / dstWidth) > 0)
105 diag << (128 / dstWidth) <<
".";
108 if (getBypassL1().has_value()) {
109 int64_t req = 16 * 8 / dstMemref.getElementTypeBitWidth();
110 if (getBypassL1().value() && sizeInBytes != 16) {
111 return emitOpError() <<
"bypassL1 does not satify alignment for "
112 << dstMemref <<
" with destination element "
114 <<
". Unset bypassL1, or set "
115 "destination element to "
128 build(odsBuilder, odsState, matrixC.
getType(), matrixA, matrixB, matrixC,
129 mmaShape, UnitAttr());
136 build(odsBuilder, odsState, matrixC.
getType(), matrixA, matrixB, matrixC,
138 tf32Enabled ? odsBuilder.
getUnitAttr() : UnitAttr());
146 const std::array<int64_t, 3> &mmaShape,
147 bool tf32Enabled,
bool sparse =
false) {
165 auto aVector = matrixA.getType();
166 auto bVector = matrixB.getType();
167 auto cVector = matrixC.getType();
175 Type aType = aVector.getElementType();
178 if (sparse && aType.
isF64())
179 return op->
emitError() <<
"f64 is not supported for sparse mode";
191 shapeK = 128 / operandBitwidth;
193 numElementA = 32 / operandBitwidth;
194 numElementB = 32 / operandBitwidth;
197 <<
"expected input data type (i4,i8,f16,bf16,tf32,f64,"
198 "f8E4M3FN,f8E5M2) supported by "
206 if (aShape.size() != 2) {
207 return op->
emitError() <<
"matrixA must be 2 dimensional vector";
210 if (bShape.size() != 2) {
211 return op->
emitError() <<
"matrixB must be 2 dimensional vector";
214 if (cShape.size() != 2) {
215 return op->
emitError() <<
"matrixC must be 2 dimensional vector";
218 auto [m, n, k] = mmaShape;
221 int64_t sparseFactor = sparse ? 2 : 1;
222 if (aShape[0] * aShape[1] *
kWarpSize != m * k / sparseFactor)
224 <<
"expected " << m * k <<
" warp-wide matrix A elements";
227 if (bShape[0] * bShape[1] *
kWarpSize != k * n)
229 <<
"expected " << k * n <<
" warp-wide matrix B elements";
232 if (cShape[0] * cShape[1] *
kWarpSize != m * n)
234 <<
"expected " << m * n <<
" warp-wide matrix C elements";
237 if (tf32Enabled && !(aType.
isF32()))
239 <<
"expected tf32 tensor cores only for F32 operands";
251 if ((aShape[0] != mTile * kTile / (sparse ? 2 : 1)) ||
252 (aShape[1] != numElementA))
253 return op->
emitOpError() <<
"expected matrix A to be shaped ("
254 << mTile * kTile <<
" x " << numElementA <<
")";
257 if ((bShape[0] != kTile * nTile) || (bShape[1] != numElementB))
258 return op->
emitOpError() <<
"expected matrix B to be shaped ("
259 << kTile * nTile <<
" x " << numElementB <<
")";
262 if ((cShape[0] != mTile * nTile) || (cShape[1] != numElementC))
263 return op->
emitOpError() <<
"expected matrix C to be shaped ("
264 << mTile * nTile <<
" x " << numElementC <<
")";
269LogicalResult MmaSyncOp::verify() {
270 if (getMmaShape().size() != 3)
271 return emitOpError() <<
"mmaShape must have exactly 3 elements";
273 return verifyMmaSyncOp(this->getOperation(), getMatrixA(), getMatrixB(),
274 getMatrixC(), getMmaShapeAsArray(),
275 getTf32Enabled().value_or(
false));
285 build(odsBuilder, odsState, matrixC.
getType(), matrixA, matrixB, matrixC,
289LogicalResult MmaSparseSyncOp::verify() {
290 unsigned sparsitySelector = getSparsitySelector();
291 if (sparsitySelector > 1)
292 return emitOpError() <<
"sparsity selector should be 0 or 1";
294 if (getMmaShape().size() != 3)
295 return emitOpError() <<
"mmaShape must have exactly 3 elements";
297 return verifyMmaSyncOp(this->getOperation(), getMatrixA(), getMatrixB(),
298 getMatrixC(), getMmaShapeAsArray(),
299 getTf32Enabled().value_or(
false),
true);
305LogicalResult LdMatrixOp::verify() {
307 auto srcMemref = llvm::cast<MemRefType>(getSrcMemref().
getType());
310 auto resVector = llvm::cast<VectorType>(getRes().
getType());
314 Type resType = resVector.getElementType();
318 int64_t numElementsPer32b = 32 / elementBitWidth;
321 int64_t numTiles = getNumTiles();
324 bool isTranspose = getTranspose();
330 if (!NVGPUDialect::hasSharedMemoryAddressSpace(srcMemref))
332 <<
"expected nvgpu.ldmatrix srcMemref must have a memory space "
333 "attribute of IntegerAttr("
334 << NVGPUDialect::kSharedMemoryAddressSpace
335 <<
") or gpu::AddressSpaceAttr(Workgroup)";
336 if (elementBitWidth > 32)
337 return emitError() <<
"nvgpu.ldmatrix works for 32b or lower";
338 if (isTranspose && !(elementBitWidth == 16))
340 <<
"nvgpu.ldmatrix transpose works only at 16b granularity";
341 if (resShape.size() != 2) {
342 return emitError() <<
"results must be 2 dimensional vector";
344 if (!(resShape[1] == numElementsPer32b))
345 return emitError() <<
"expected vector register shape[1] = "
346 << numElementsPer32b;
347 if (!(resShape[0] == numTiles))
349 <<
"expected vector register shape[0] and numTiles to match";
360 case TensorMapSwizzleKind::SWIZZLE_32B:
362 case TensorMapSwizzleKind::SWIZZLE_64B:
364 case TensorMapSwizzleKind::SWIZZLE_128B:
372 Operation *op, TensorMapDescriptorType descType,
373 std::optional<MemRefType> memrefType = std::nullopt) {
374 MemRefType descMemref = descType.getTensor();
376 if (descType.getInterleave() != TensorMapInterleaveKind::INTERLEAVE_NONE)
377 return op->
emitError() <<
"Interleave options are not supported yet.";
380 if (!NVGPUDialect::hasSharedMemoryAddressSpace(descMemref)) {
381 return op->
emitError() <<
"the tensor map descriptor has incorrect address "
382 "space, it must be shared memory address space.";
385 if (!descMemref.hasStaticShape())
386 return op->
emitError() <<
"the tensor map descriptor must be static shaped";
388 for (
auto dim : descMemref.getShape()) {
390 return op->
emitError() <<
"the tensor map descriptor must have "
391 "dimensions between 1 and "
395 if (descMemref.getRank() > 1 &&
396 descType.getSwizzle() != TensorMapSwizzleKind::SWIZZLE_NONE) {
397 unsigned lastDimensionByte =
398 descMemref.getElementTypeBitWidth() * descMemref.getShape().back() / 8;
400 if (lastDimensionByte != expectByte)
401 return op->
emitError() <<
"the tensormap descriptor must have last "
403 << expectByte <<
" bytes but it is "
404 << lastDimensionByte <<
" bytes";
408 if (!memrefType.has_value())
411 MemRefType dstMemref = memrefType.value();
414 if (descMemref.getElementType() != dstMemref.getElementType()) {
415 return op->
emitError() <<
"the element type of tensor map descriptor and "
416 "memref must be same";
419 if (!NVGPUDialect::hasSharedMemoryAddressSpace(dstMemref)) {
420 return op->
emitError() <<
"the destination memref has incorrect address "
421 "space, it must be shared memory address space.";
423 if (!dstMemref.hasStaticShape())
424 return op->
emitError() <<
"the destination memref must be static shaped";
426 if (dstMemref.getRank() != descMemref.getRank()) {
427 return op->
emitError() <<
"the shape of tensor map descriptor and "
428 "memref must have same rank";
430 if (!descMemref.getShape().equals(dstMemref.getShape())) {
431 return op->
emitError() <<
"memref and tensor map shapes mismatch "
432 << descMemref <<
" != " << dstMemref;
436 descMemref.getShape().back() * descMemref.getElementTypeBitWidth() / 8;
438 return op->
emitError() <<
"the bytes in the last dimension of the tensor "
439 "map must be a multiple of 16";
444LogicalResult TmaAsyncLoadOp::verify() {
447 if (error.has_value())
448 return error.value();
452 <<
" coordinates are supported.";
455 size_t(getTensorMapDescriptor().
getType().getTensor().getRank())) {
456 return emitError() <<
"number of coordinates do not match with the rank of "
457 "tensor descriptor map.";
467LogicalResult TmaAsyncStoreOp::verify() {
470 if (error.has_value())
471 return error.value();
475 <<
" coordinates are supported.";
478 size_t(getTensorMapDescriptor().
getType().getTensor().getRank())) {
479 return emitError() <<
"number of coordinates do not match with the rank of "
480 "tensor descriptor map.";
486LogicalResult TmaCreateDescriptorOp::verify() {
489 <<
" coordinates are supported.";
492 std::optional<InFlightDiagnostic> error =
494 if (error.has_value())
495 return error.value();
504LogicalResult WarpgroupGenerateDescriptorOp::verify() {
505 std::optional<InFlightDiagnostic> error =
507 if (error.has_value())
508 return error.value();
510 if (getTensorMap().
getType().getSwizzle() !=
511 TensorMapSwizzleKind::SWIZZLE_128B) {
513 << stringifyTensorMapSwizzleKind(
514 TensorMapSwizzleKind::SWIZZLE_128B)
515 <<
" is supported for the time being";
518 if (getTensorMap().
getType().getInterleave() !=
519 TensorMapInterleaveKind::INTERLEAVE_NONE) {
521 << stringifyTensorMapInterleaveKind(
522 TensorMapInterleaveKind::INTERLEAVE_NONE)
523 <<
" is supported for the time being";
553 if (isa<Float8E5M2Type, Float8E4M3FNType>(typeA) &&
554 isa<Float8E5M2Type, Float8E4M3FNType>(typeB) &&
569 72, 80, 88, 96, 104, 112, 120, 128,
570 136, 144, 152, 160, 168, 176, 184, 192,
571 200, 208, 216, 224, 232, 240, 248, 256};
573 80, 96, 112, 128, 144, 160,
574 176, 192, 208, 224, 240, 256};
576 isa<Float8E5M2Type, Float8E4M3FNType>(typeA))
577 if (llvm::is_contained(allowedN, sizeN))
581 if (llvm::is_contained(allowedNshort, sizeN))
586LogicalResult WarpgroupMmaOp::verify() {
587 if (getTransposeA() && !getTransposeB())
589 <<
"supports non-transpose A (Row Major) "
590 "and transpose B (Column Major) for the time being ";
591 MemRefType matrixA = getDescriptorA().
getType().getTensor();
592 MemRefType matrixB = getDescriptorB().
getType().getTensor();
593 VectorType matrixC = getMatrixC().
getType().getFragmented();
594 VectorType matrixD = getMatrixD().getType().getFragmented();
596 if (matrixC != matrixD)
597 return emitOpError() <<
"type of matrix C and matrix D must be the same";
599 if (matrixA.getRank() != 2 || matrixB.getRank() != 2 ||
600 matrixC.getRank() != 2 || matrixD.getRank() != 2) {
602 <<
"has matrices A, B, C and D, they must be 2 dimensional";
605 if (matrixA.getShape()[1] != matrixB.getShape()[0])
606 return emitOpError() <<
"2nd dim matrix-A (" << matrixA.getShape()[1]
607 <<
")!= 1st dim matrix-B (" << matrixB.getShape()[0]
609 if (matrixA.getShape()[0] != matrixC.getShape()[0])
610 return emitOpError() <<
"1st dim matrix-A ( " << matrixA.getShape()[0]
611 <<
" )!= 1st dim matrix-C ( " << matrixC.getShape()[0]
613 if (matrixB.getShape()[1] != matrixC.getShape()[1])
614 return emitOpError() <<
"2nd dim matrix-B ( " << matrixB.getShape()[1]
615 <<
" ) != 2nd dim matrix-C ( " << matrixC.getShape()[1]
619 matrixA.getElementType(),
620 matrixB.getElementType())))
621 return emitOpError() << matrixC.getElementType()
622 <<
" += " << matrixA.getElementType() <<
" * "
623 << matrixB.getElementType()
624 <<
", it is not supported.";
627 return emitOpError() <<
"has input type " << matrixB <<
" n is set to "
628 << matrixB.getDimSize(1) <<
", it is not supported";
632 if (!matrixC.getElementType().isF32() && !matrixA.getElementType().isF16() &&
633 !matrixA.getElementType().isBF16()) {
634 return emitOpError() <<
"hit a limitation: " << matrixC.getElementType()
635 <<
" += " << matrixA.getElementType() <<
" * "
636 << matrixB.getElementType()
637 <<
", it is not supported yet";
643LogicalResult WarpgroupMmaStoreOp::verify() {
644 MemRefType dstMemrefType = getDstMemref().getType();
645 VectorType vtype = getMatrixD().getType().getFragmented();
648 if (!vtype.getElementType().isF32()) {
650 <<
"hit a limitation: only f32 results for the time being";
652 if (vtype.getDimSize(0) != dstMemrefType.getDimSize(0) ||
653 vtype.getDimSize(1) != dstMemrefType.getDimSize(1)) {
654 return emitOpError() <<
"results [" << vtype <<
"][" << vtype.getDimSize(1)
655 <<
"] values. However, destination memref["
656 << dstMemrefType.getDimSize(0) <<
"]["
657 << dstMemrefType.getDimSize(1)
658 <<
"] does not have same size as results";
667LogicalResult WarpgroupMmaInitAccumulatorOp::verify() {
668 WarpgroupAccumulatorType accType = getMatrixC().getType();
669 int64_t sizeM = accType.getFragmented().getDimSize(0);
670 int64_t sizeN = accType.getFragmented().getDimSize(1);
671 Type elemType = accType.getFragmented().getElementType();
675 return emitOpError() <<
"has type " << accType.getFragmented()
676 <<
". It does not fit into warp-group "
677 "level (wgmma) matrix multiplication instruction "
678 "(or not supported yet)";
687LogicalResult RcpOp::verify() {
689 bool approx = getApprox();
690 mlir::NVVM::FPRoundingModeAttr rnd = getRoundingAttr();
692 if (!approx || !ftz) {
694 <<
"has a limitation. non-approx or non-ftz is not supported yet.";
696 if (rnd.getValue() != mlir::NVVM::FPRoundingMode::NONE) {
697 return emitOpError() <<
"has a limitation. " << rnd
698 <<
" is not supported yet.";
709 bool srcIsVector = llvm::isa<VectorType>(inType);
710 bool dstIsVector = llvm::isa<VectorType>(outType);
711 if (srcIsVector != dstIsVector)
712 return op->
emitOpError(
"input and output must both be scalars or both be "
714 << inType <<
" and " << outType;
716 auto srcVector = llvm::cast<VectorType>(inType);
717 auto dstVector = llvm::cast<VectorType>(outType);
718 if (srcVector.getShape() != dstVector.getShape())
719 return op->
emitOpError(
"input and output shapes must match, got ")
720 << inType <<
" and " << outType;
725LogicalResult TruncfOp::verify() {
726 Type inType = getIn().getType();
738 if (srcBitWidth <= dstBitWidth)
739 return emitOpError(
"result type ")
740 << dstType <<
" must be narrower than operand type " << srcType;
742 if (!(srcBitWidth == 64 || srcBitWidth == 32 || srcBitWidth == 16))
743 return emitOpError(
"input type must be 64/32/16 bitwidth, but got ")
746 if (llvm::isa<Float8E8M0FNUType>(dstType)) {
747 if (rnd != mlir::NVVM::FPRoundingMode::RZ &&
748 rnd != mlir::NVVM::FPRoundingMode::RP)
749 return emitOpError(
"expects RZ or RP rounding mode when result type is "
752 }
else if (rnd == mlir::NVVM::FPRoundingMode::RS) {
755 if (!(srcBitWidth == 32 && dstBitWidth == 16))
756 return emitOpError(
"RS (stochastic) rounding is only supported for "
757 "f32->f16/bf16, got ")
758 << srcType <<
" -> " << dstType;
759 if (!getRandomBits())
760 return emitOpError(
"random_bits operand is required with RS rounding");
761 }
else if (srcType.isF64() && dstBitWidth >= 16) {
762 if (rnd != mlir::NVVM::FPRoundingMode::RN)
763 return emitOpError(
"expects RN rounding mode for f64 input, but got ")
765 }
else if (srcBitWidth == 32 && dstBitWidth == 16) {
766 if (rnd != mlir::NVVM::FPRoundingMode::RN &&
767 rnd != mlir::NVVM::FPRoundingMode::RZ)
768 return emitOpError(
"expects RN or RZ rounding mode for f32 to f16/bf16, "
771 }
else if (rnd != mlir::NVVM::FPRoundingMode::RN) {
772 return emitOpError(
"expects RN rounding mode, but got ") << getRndAttr();
775 if (getRandomBits() && rnd != mlir::NVVM::FPRoundingMode::RS)
776 return emitOpError(
"random_bits can only be used with RS rounding mode");
785LogicalResult ExtfOp::verify() {
786 Type inType = getIn().getType();
797 if (srcBitWidth >= dstBitWidth)
798 return emitOpError(
"result type ")
799 << dstType <<
" must be wider than operand type " << srcType;
801 if (dstBitWidth != 16 && dstBitWidth != 32 && dstBitWidth != 64)
802 return emitOpError(
"result type must be 16, 32, or 64 bitwidth, but got ")
805 if (llvm::isa<Float8E8M0FNUType>(srcType) &&
806 !llvm::isa<BFloat16Type>(dstType) && !dstType.
isF32())
807 return emitOpError(
"expects bf16 or f32 output type when input type is "
810 if (rnd != mlir::NVVM::FPRoundingMode::RN)
811 return emitOpError(
"expects RN rounding mode, but got ") << getRndAttr();
813 if (getRelu() && llvm::isa<BFloat16Type>(dstType))
814 return emitOpError(
"relu is not supported for bf16 destination");
823#define GET_ATTRDEF_CLASSES
824#include "mlir/Dialect/NVGPU/IR/NVGPUAttrDefs.cpp.inc"
826#include "mlir/Dialect/NVGPU/IR/NVGPUEnums.cpp.inc"
828#define GET_OP_CLASSES
829#include "mlir/Dialect/NVGPU/IR/NVGPUOps.cpp.inc"
831#define GET_TYPEDEF_CLASSES
832#include "mlir/Dialect/NVGPU/IR/NVGPUTypeDefs.cpp.inc"
static std::string diag(const llvm::Value &value)
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.