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";
190 shapeK = 128 / operandBitwidth;
192 numElementA = 32 / operandBitwidth;
193 numElementB = 32 / operandBitwidth;
196 <<
"expected input data type (i4,i8,f16,bf16,tf32,f64) "
205 if (aShape.size() != 2) {
206 return op->
emitError() <<
"matrixA must be 2 dimensional vector";
209 if (bShape.size() != 2) {
210 return op->
emitError() <<
"matrixB must be 2 dimensional vector";
213 if (cShape.size() != 2) {
214 return op->
emitError() <<
"matrixC must be 2 dimensional vector";
217 auto [m, n, k] = mmaShape;
220 int64_t sparseFactor = sparse ? 2 : 1;
221 if (aShape[0] * aShape[1] *
kWarpSize != m * k / sparseFactor)
223 <<
"expected " << m * k <<
" warp-wide matrix A elements";
226 if (bShape[0] * bShape[1] *
kWarpSize != k * n)
228 <<
"expected " << k * n <<
" warp-wide matrix B elements";
231 if (cShape[0] * cShape[1] *
kWarpSize != m * n)
233 <<
"expected " << m * n <<
" warp-wide matrix C elements";
236 if (tf32Enabled && !(aType.
isF32()))
238 <<
"expected tf32 tensor cores only for F32 operands";
250 if ((aShape[0] != mTile * kTile / (sparse ? 2 : 1)) ||
251 (aShape[1] != numElementA))
252 return op->
emitOpError() <<
"expected matrix A to be shaped ("
253 << mTile * kTile <<
" x " << numElementA <<
")";
256 if ((bShape[0] != kTile * nTile) || (bShape[1] != numElementB))
257 return op->
emitOpError() <<
"expected matrix B to be shaped ("
258 << kTile * nTile <<
" x " << numElementB <<
")";
261 if ((cShape[0] != mTile * nTile) || (cShape[1] != numElementC))
262 return op->
emitOpError() <<
"expected matrix C to be shaped ("
263 << mTile * nTile <<
" x " << numElementC <<
")";
268LogicalResult MmaSyncOp::verify() {
269 if (getMmaShape().size() != 3)
270 return emitOpError() <<
"mmaShape must have exactly 3 elements";
272 return verifyMmaSyncOp(this->getOperation(), getMatrixA(), getMatrixB(),
273 getMatrixC(), getMmaShapeAsArray(),
274 getOperation()->hasAttr(getTf32EnabledAttrName()));
284 build(odsBuilder, odsState, matrixC.
getType(), matrixA, matrixB, matrixC,
288LogicalResult MmaSparseSyncOp::verify() {
289 unsigned sparsitySelector = getSparsitySelector();
290 if (sparsitySelector > 1)
291 return emitOpError() <<
"sparsity selector should be 0 or 1";
293 if (getMmaShape().size() != 3)
294 return emitOpError() <<
"mmaShape must have exactly 3 elements";
296 return verifyMmaSyncOp(this->getOperation(), getMatrixA(), getMatrixB(),
297 getMatrixC(), getMmaShapeAsArray(),
298 getOperation()->hasAttr(getTf32EnabledAttrName()),
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())))
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.";
707#define GET_ATTRDEF_CLASSES
708#include "mlir/Dialect/NVGPU/IR/NVGPUAttrDefs.cpp.inc"
710#include "mlir/Dialect/NVGPU/IR/NVGPUEnums.cpp.inc"
712#define GET_OP_CLASSES
713#include "mlir/Dialect/NVGPU/IR/NVGPUOps.cpp.inc"
715#define GET_TYPEDEF_CLASSES
716#include "mlir/Dialect/NVGPU/IR/NVGPUTypeDefs.cpp.inc"
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
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 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.
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.