MLIR 23.0.0git
NVGPUDialect.cpp
Go to the documentation of this file.
1//===- NVGPUDialect.cpp - MLIR NVGPU ops implementation -------------------===//
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 the NVGPU dialect and its operations.
10//
11//===----------------------------------------------------------------------===//
12
16#include "mlir/IR/Builders.h"
19#include "mlir/IR/Diagnostics.h"
22#include "mlir/IR/Verifier.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/TypeSwitch.h"
25
26using namespace mlir;
27using namespace mlir::nvgpu;
28
29#include "mlir/Dialect/NVGPU/IR/NVGPUDialect.cpp.inc"
30
31void NVGPUDialect::initialize() {
32 addTypes<
33#define GET_TYPEDEF_LIST
34#include "mlir/Dialect/NVGPU/IR/NVGPUTypeDefs.cpp.inc"
35 >();
36 addAttributes<
37#define GET_ATTRDEF_LIST
38#include "mlir/Dialect/NVGPU/IR/NVGPUAttrDefs.cpp.inc"
39 >();
40 addOperations<
41#define GET_OP_LIST
42#include "mlir/Dialect/NVGPU/IR/NVGPUOps.cpp.inc"
43 >();
44 declarePromisedInterfaces<memref::IndexedAccessOpInterface, LdMatrixOp>();
45 declarePromisedInterfaces<memref::IndexedMemCopyOpInterface,
46 DeviceAsyncCopyOp>();
47}
48
49bool NVGPUDialect::isSharedMemoryAddressSpace(Attribute memorySpace) {
50 if (!memorySpace)
51 return false;
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;
57 return false;
58}
59
60bool NVGPUDialect::hasSharedMemoryAddressSpace(MemRefType type) {
61 Attribute memorySpace = type.getMemorySpace();
62 return isSharedMemoryAddressSpace(memorySpace);
63}
64
65//===----------------------------------------------------------------------===//
66// NVGPU_DeviceAsyncCopyOp
67//===----------------------------------------------------------------------===//
68
69LogicalResult DeviceAsyncCopyOp::verify() {
70 auto srcMemref = llvm::cast<MemRefType>(getSrc().getType());
71 auto dstMemref = llvm::cast<MemRefType>(getDst().getType());
72
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))
78 return emitError()
79 << "destination memref must have a memory space attribute of "
80 "IntegerAttr("
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) << ".";
106 return diag;
107 }
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 "
113 << dstElements
114 << ". Unset bypassL1, or set "
115 "destination element to "
116 << req;
117 }
118 }
119 return success();
120}
121
122//===----------------------------------------------------------------------===//
123// NVGPU_MmaSyncOp
124//===----------------------------------------------------------------------===//
125void MmaSyncOp::build(::mlir::OpBuilder &odsBuilder,
126 ::mlir::OperationState &odsState, Value matrixA,
127 Value matrixB, Value matrixC, ArrayAttr mmaShape) {
128 build(odsBuilder, odsState, matrixC.getType(), matrixA, matrixB, matrixC,
129 mmaShape, UnitAttr());
130}
131
132void MmaSyncOp::build(::mlir::OpBuilder &odsBuilder,
133 ::mlir::OperationState &odsState, Value matrixA,
134 Value matrixB, Value matrixC, ArrayRef<int64_t> mmaShape,
135 bool tf32Enabled) {
136 build(odsBuilder, odsState, matrixC.getType(), matrixA, matrixB, matrixC,
137 odsBuilder.getI64ArrayAttr(mmaShape),
138 tf32Enabled ? odsBuilder.getUnitAttr() : UnitAttr());
139}
140
141/// Performs verification for MmaSyncOp and MmaSparseSyncOp.
142static LogicalResult verifyMmaSyncOp(Operation *op,
146 const std::array<int64_t, 3> &mmaShape,
147 bool tf32Enabled, bool sparse = false) {
148 // The verification for mma.sync covering various shapes and data types is
149 // based on the fundamental tensor core shape.
150
151 // "Fundamental" tensor core shapes:
152 // - For F32 (TF32), F16, S8, and S4 data
153 // types the fundamental tensor core operation is of shape 8-by-8-by-128b.
154 // - F64 is an exception and is of shape 8-by-8-by-256b.
155 int64_t shapeM = 8;
156 int64_t shapeN = 8;
157 int64_t shapeK; // set based on data type (128b for all data types except F64)
158
159 // Number of elements A, B, and C per thread per fundamental tensor core tile
160 int64_t numElementA; // set based on data type (32b except F64)
161 int64_t numElementB; // set based on data type (32b except F64)
162 int64_t numElementC{2}; // two accumulator elements per fundamental tile
163
164 // nvgpu.mma.sync vector operands (per thread)
165 auto aVector = matrixA.getType();
166 auto bVector = matrixB.getType();
167 auto cVector = matrixC.getType();
168
169 // vector shapes
170 ArrayRef<int64_t> aShape = aVector.getShape();
171 ArrayRef<int64_t> bShape = bVector.getShape();
172 ArrayRef<int64_t> cShape = cVector.getShape();
173
174 // vector element type
175 Type aType = aVector.getElementType();
176
177 // Certain data types are not allowed in sparse mode.
178 if (sparse && aType.isF64())
179 return op->emitError() << "f64 is not supported for sparse mode";
180
181 if (aType.isF64()) {
182 // exception to 8-by-8-128b fundamental tensor core tile size
183 shapeK = 4;
184 numElementA = 1;
185 numElementB = 1;
186 } else if (aType.isF32() || aType.isBF16() || aType.isF16() ||
187 aType.isInteger(8) || aType.isInteger(4)) {
188 // 8-by-8-128b fundamental tensor core tile size
189 int operandBitwidth = aType.getIntOrFloatBitWidth();
190 shapeK = 128 / operandBitwidth; // 128b wide shapeK
191
192 numElementA = 32 / operandBitwidth; // 32b wide operand A
193 numElementB = 32 / operandBitwidth; // 32b wide operand B
194 } else {
195 return op->emitError()
196 << "expected input data type (i4,i8,f16,bf16,tf32,f64) "
197 "supported by "
198 << op->getName();
199 }
200
201 //
202 // Basic verification
203 //
204
205 if (aShape.size() != 2) {
206 return op->emitError() << "matrixA must be 2 dimensional vector";
207 }
208
209 if (bShape.size() != 2) {
210 return op->emitError() << "matrixB must be 2 dimensional vector";
211 }
212
213 if (cShape.size() != 2) {
214 return op->emitError() << "matrixC must be 2 dimensional vector";
215 }
216
217 auto [m, n, k] = mmaShape;
218
219 // verify warp-wide size for vector a
220 int64_t sparseFactor = sparse ? 2 : 1;
221 if (aShape[0] * aShape[1] * kWarpSize != m * k / sparseFactor)
222 return op->emitOpError()
223 << "expected " << m * k << " warp-wide matrix A elements";
224
225 // verify warp-wide size for vector b
226 if (bShape[0] * bShape[1] * kWarpSize != k * n)
227 return op->emitOpError()
228 << "expected " << k * n << " warp-wide matrix B elements";
229
230 // verify warp-wide size for vector c
231 if (cShape[0] * cShape[1] * kWarpSize != m * n)
232 return op->emitOpError()
233 << "expected " << m * n << " warp-wide matrix C elements";
234
235 // verify tf32 tensor cores are enabled for only F32 datatype
236 if (tf32Enabled && !(aType.isF32()))
237 return op->emitOpError()
238 << "expected tf32 tensor cores only for F32 operands";
239
240 //
241 // Extended verification
242 //
243
244 // tiles of fundamental tensor core operations
245 int64_t mTile = m / shapeM;
246 int64_t nTile = n / shapeN;
247 int64_t kTile = k / shapeK;
248
249 // verify shape of aVector
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 << ")";
254
255 // verify shape of bVector
256 if ((bShape[0] != kTile * nTile) || (bShape[1] != numElementB))
257 return op->emitOpError() << "expected matrix B to be shaped ("
258 << kTile * nTile << " x " << numElementB << ")";
259
260 // verify shape of cVector
261 if ((cShape[0] != mTile * nTile) || (cShape[1] != numElementC))
262 return op->emitOpError() << "expected matrix C to be shaped ("
263 << mTile * nTile << " x " << numElementC << ")";
264
265 return success();
266}
267
268LogicalResult MmaSyncOp::verify() {
269 if (getMmaShape().size() != 3)
270 return emitOpError() << "mmaShape must have exactly 3 elements";
271
272 return verifyMmaSyncOp(this->getOperation(), getMatrixA(), getMatrixB(),
273 getMatrixC(), getMmaShapeAsArray(),
274 getOperation()->hasAttr(getTf32EnabledAttrName()));
275}
276
277//===----------------------------------------------------------------------===//
278// NVGPU_MmaSparseSyncOp
279//===----------------------------------------------------------------------===//
280void MmaSparseSyncOp::build(::mlir::OpBuilder &odsBuilder,
281 ::mlir::OperationState &odsState, Value matrixA,
282 Value matrixB, Value matrixC, Value sparseMetadata,
283 ArrayRef<int64_t> mmaShape) {
284 build(odsBuilder, odsState, matrixC.getType(), matrixA, matrixB, matrixC,
285 sparseMetadata, odsBuilder.getI64ArrayAttr(mmaShape), 0, UnitAttr());
286}
287
288LogicalResult MmaSparseSyncOp::verify() {
289 unsigned sparsitySelector = getSparsitySelector();
290 if (sparsitySelector > 1)
291 return emitOpError() << "sparsity selector should be 0 or 1";
292
293 if (getMmaShape().size() != 3)
294 return emitOpError() << "mmaShape must have exactly 3 elements";
295
296 return verifyMmaSyncOp(this->getOperation(), getMatrixA(), getMatrixB(),
297 getMatrixC(), getMmaShapeAsArray(),
298 getOperation()->hasAttr(getTf32EnabledAttrName()),
299 true);
300}
301
302//===----------------------------------------------------------------------===//
303// NVGPU_LdMatrixOp
304//===----------------------------------------------------------------------===//
305LogicalResult LdMatrixOp::verify() {
306 // ldmatrix reads data from source in shared memory
307 auto srcMemref = llvm::cast<MemRefType>(getSrcMemref().getType());
308
309 // ldmatrix writes data to result/destination in vector registers
310 auto resVector = llvm::cast<VectorType>(getRes().getType());
311
312 // vector register shape, element type, and bitwidth
313 ArrayRef<int64_t> resShape = resVector.getShape();
314 Type resType = resVector.getElementType();
315 int64_t elementBitWidth = resType.getIntOrFloatBitWidth();
316
317 // ldmatrix loads 32 bits into vector registers per 8-by-8 tile per thread
318 int64_t numElementsPer32b = 32 / elementBitWidth;
319
320 // number of 8-by-8 tiles
321 int64_t numTiles = getNumTiles();
322
323 // transpose elements in vector registers at 16b granularity when true
324 bool isTranspose = getTranspose();
325
326 //
327 // verification
328 //
329
330 if (!NVGPUDialect::hasSharedMemoryAddressSpace(srcMemref))
331 return emitError()
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))
339 return emitError()
340 << "nvgpu.ldmatrix transpose works only at 16b granularity";
341 if (resShape.size() != 2) {
342 return emitError() << "results must be 2 dimensional vector";
343 }
344 if (!(resShape[1] == numElementsPer32b))
345 return emitError() << "expected vector register shape[1] = "
346 << numElementsPer32b;
347 if (!(resShape[0] == numTiles))
348 return emitError()
349 << "expected vector register shape[0] and numTiles to match";
350
351 return success();
352}
353
354//===----------------------------------------------------------------------===//
355// NVGPU_TmaAsyncLoadOp
356//===----------------------------------------------------------------------===//
357
358static unsigned getSwizzleBytes(TensorMapSwizzleKind kind) {
359 switch (kind) {
360 case TensorMapSwizzleKind::SWIZZLE_32B:
361 return 32;
362 case TensorMapSwizzleKind::SWIZZLE_64B:
363 return 64;
364 case TensorMapSwizzleKind::SWIZZLE_128B:
365 return 128;
366 default:
367 return 0;
368 }
369}
370
371std::optional<InFlightDiagnostic> verifyTmaDescriptorWithMemref(
372 Operation *op, TensorMapDescriptorType descType,
373 std::optional<MemRefType> memrefType = std::nullopt) {
374 MemRefType descMemref = descType.getTensor();
375 // Limitation
376 if (descType.getInterleave() != TensorMapInterleaveKind::INTERLEAVE_NONE)
377 return op->emitError() << "Interleave options are not supported yet.";
378
379 // Address space check for shared memory check
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.";
383 }
384 // Support only static shape for the time being
385 if (!descMemref.hasStaticShape())
386 return op->emitError() << "the tensor map descriptor must be static shaped";
387
388 for (auto dim : descMemref.getShape()) {
389 if (dim <= 0 || dim > kMaxTMADimension) {
390 return op->emitError() << "the tensor map descriptor must have "
391 "dimensions between 1 and "
392 << kMaxTMADimension << " but it is " << dim;
393 }
394 }
395 if (descMemref.getRank() > 1 &&
396 descType.getSwizzle() != TensorMapSwizzleKind::SWIZZLE_NONE) {
397 unsigned lastDimensionByte =
398 descMemref.getElementTypeBitWidth() * descMemref.getShape().back() / 8;
399 unsigned expectByte = getSwizzleBytes(descType.getSwizzle());
400 if (lastDimensionByte != expectByte)
401 return op->emitError() << "the tensormap descriptor must have last "
402 "dimension of "
403 << expectByte << " bytes but it is "
404 << lastDimensionByte << " bytes";
405 }
406
407 // No verification if memref type is not provided
408 if (!memrefType.has_value())
409 return std::nullopt;
410
411 MemRefType dstMemref = memrefType.value();
412
413 // Check element type
414 if (descMemref.getElementType() != dstMemref.getElementType()) {
415 return op->emitError() << "the element type of tensor map descriptor and "
416 "memref must be same";
417 }
418
419 if (!NVGPUDialect::hasSharedMemoryAddressSpace(dstMemref)) {
420 return op->emitError() << "the destination memref has incorrect address "
421 "space, it must be shared memory address space.";
422 }
423 if (!dstMemref.hasStaticShape())
424 return op->emitError() << "the destination memref must be static shaped";
425
426 if (dstMemref.getRank() != descMemref.getRank()) {
427 return op->emitError() << "the shape of tensor map descriptor and "
428 "memref must have same rank";
429 }
430 if (!descMemref.getShape().equals(dstMemref.getShape())) {
431 return op->emitError() << "memref and tensor map shapes mismatch "
432 << descMemref << " != " << dstMemref;
433 }
434
435 int lastDimBytes =
436 descMemref.getShape().back() * descMemref.getElementTypeBitWidth() / 8;
437 if (lastDimBytes % kTMALastdimByte != 0) {
438 return op->emitError() << "the bytes in the last dimension of the tensor "
439 "map must be a multiple of 16";
440 }
441 return std::nullopt;
442}
443
444LogicalResult TmaAsyncLoadOp::verify() {
445 std::optional<InFlightDiagnostic> error = verifyTmaDescriptorWithMemref(
446 *this, getTensorMapDescriptor().getType(), getDst().getType());
447 if (error.has_value())
448 return error.value();
449
450 if (getCoordinates().size() > kMaxTMATensorDimension) {
451 return emitError() << "Maximum " << kMaxTMATensorDimension
452 << " coordinates are supported.";
453 }
454 if (getCoordinates().size() !=
455 size_t(getTensorMapDescriptor().getType().getTensor().getRank())) {
456 return emitError() << "number of coordinates do not match with the rank of "
457 "tensor descriptor map.";
458 }
459
460 return success();
461}
462
463//===----------------------------------------------------------------------===//
464// NVGPU_TmaAsyncStoreOp
465//===----------------------------------------------------------------------===//
466
467LogicalResult TmaAsyncStoreOp::verify() {
468 std::optional<InFlightDiagnostic> error = verifyTmaDescriptorWithMemref(
469 *this, getTensorMapDescriptor().getType(), getSrc().getType());
470 if (error.has_value())
471 return error.value();
472
473 if (getCoordinates().size() > kMaxTMATensorDimension) {
474 return emitError() << "Maximum " << kMaxTMATensorDimension
475 << " coordinates are supported.";
476 }
477 if (getCoordinates().size() !=
478 size_t(getTensorMapDescriptor().getType().getTensor().getRank())) {
479 return emitError() << "number of coordinates do not match with the rank of "
480 "tensor descriptor map.";
481 }
482
483 return success();
484}
485
486LogicalResult TmaCreateDescriptorOp::verify() {
487 if (getBoxDimensions().size() > kMaxTMATensorDimension) {
488 return emitError() << "Maximum " << kMaxTMATensorDimension
489 << " coordinates are supported.";
490 }
491
492 std::optional<InFlightDiagnostic> error =
493 verifyTmaDescriptorWithMemref(*this, getTensorMap().getType());
494 if (error.has_value())
495 return error.value();
496
497 return success();
498}
499
500//===----------------------------------------------------------------------===//
501// NVGPU_WarpgroupGenerateDescriptorOp
502//===----------------------------------------------------------------------===//
503
504LogicalResult WarpgroupGenerateDescriptorOp::verify() {
505 std::optional<InFlightDiagnostic> error =
506 verifyTmaDescriptorWithMemref(*this, getTensorMap().getType());
507 if (error.has_value())
508 return error.value();
509
510 if (getTensorMap().getType().getSwizzle() !=
511 TensorMapSwizzleKind::SWIZZLE_128B) {
512 return emitError() << "supports only "
513 << stringifyTensorMapSwizzleKind(
514 TensorMapSwizzleKind::SWIZZLE_128B)
515 << " is supported for the time being";
516 }
517
518 if (getTensorMap().getType().getInterleave() !=
519 TensorMapInterleaveKind::INTERLEAVE_NONE) {
520 return emitError() << "supports only "
521 << stringifyTensorMapInterleaveKind(
522 TensorMapInterleaveKind::INTERLEAVE_NONE)
523 << " is supported for the time being";
524 }
525
526 return success();
527}
528
529//===----------------------------------------------------------------------===//
530// WarpgroupMmaOp
531//===----------------------------------------------------------------------===//
532
533LogicalResult isAllowedWGMMADataType(Type typeD, Type typeA, Type typeB) {
534 // F32 += F16 + F16
535 // F16 += F16 + F16
536 if (typeA.isF16() && typeB.isF16() && (typeD.isF32() || typeD.isF16()))
537 return success();
538 // F32 += TF32 + TF32
539 if (typeA.isTF32() && typeD.isF32() && typeB.isTF32())
540 return success();
541 // s32 += i8 + i8
542 if (typeA.isInteger(16) && typeB.isInteger(16) && typeD.isInteger(32))
543 return success();
544 // s32 += i1 + i1
545 if (typeA.isInteger(1) && typeB.isInteger(1) && typeD.isInteger(32))
546 return success();
547 // F32 += BF16 + BF16
548 // F16 += BF16 + BF16
549 if (typeA.isBF16() && typeB.isBF16() && (typeD.isF32() || typeD.isF16()))
550 return success();
551 // F16 += f8 + f8
552 // F32 += f8 + f8
553 if (isa<Float8E5M2Type, Float8E4M3FNType>(typeA) &&
554 isa<Float8E5M2Type, Float8E4M3FNType>(typeB) &&
555 (typeD.isF32() || typeD.isF16()))
556 return success();
557
558 return failure();
559}
560
561LogicalResult isAllowedSizeM(int sizeM) {
562 if (sizeM % kWgmmaSizeM)
563 return failure();
564 return success();
565}
566
567LogicalResult isAllowedSizeN(int sizeN, Type typeA) {
568 SmallVector<int> allowedN = {8, 16, 24, 32, 40, 48, 56, 64,
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};
572 SmallVector<int> allowedNshort = {8, 16, 24, 32, 48, 64,
573 80, 96, 112, 128, 144, 160,
574 176, 192, 208, 224, 240, 256};
575 if (typeA.isBF16() || typeA.isF16() || typeA.isF32() || typeA.isTF32() ||
576 isa<Float8E5M2Type, Float8E4M3FNType>(typeA))
577 if (llvm::is_contained(allowedN, sizeN))
578 return success();
579
580 if (typeA.isInteger(8) || typeA.isInteger(1))
581 if (llvm::is_contained(allowedNshort, sizeN))
582 return success();
583 return failure();
584}
585
586LogicalResult WarpgroupMmaOp::verify() {
587 if (getTransposeA() && !getTransposeB())
588 return emitOpError()
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();
595
596 if (matrixC != matrixD)
597 return emitOpError() << "type of matrix C and matrix D must be the same";
598
599 if (matrixA.getRank() != 2 || matrixB.getRank() != 2 ||
600 matrixC.getRank() != 2 || matrixD.getRank() != 2) {
601 return emitOpError()
602 << "has matrices A, B, C and D, they must be 2 dimensional";
603 }
604
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]
608 << " )";
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]
612 << " )";
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]
616 << " )";
617
618 if (failed(isAllowedWGMMADataType(matrixC.getElementType(),
619 matrixA.getElementType(),
620 matrixB.getElementType())))
621 return emitOpError() << matrixC.getElementType()
622 << " += " << matrixA.getElementType() << " * "
623 << matrixB.getElementType()
624 << ", it is not supported.";
625 // Check N
626 if (failed(isAllowedSizeN(matrixB.getDimSize(1), matrixA.getElementType()))) {
627 return emitOpError() << "has input type " << matrixB << " n is set to "
628 << matrixB.getDimSize(1) << ", it is not supported";
629 }
630
631 // Currently, f16/bf16 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";
638 }
639
640 return success();
641}
642
643LogicalResult WarpgroupMmaStoreOp::verify() {
644 MemRefType dstMemrefType = getDstMemref().getType();
645 VectorType vtype = getMatrixD().getType().getFragmented();
646
647 // Limitation
648 if (!vtype.getElementType().isF32()) {
649 return emitOpError()
650 << "hit a limitation: only f32 results for the time being";
651 }
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";
659 }
660 return success();
661}
662
663//===----------------------------------------------------------------------===//
664// WarpgroupMmaInitAccumulatorOp
665//===----------------------------------------------------------------------===//
666
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();
672
673 if (failed(isAllowedSizeM(sizeM)) ||
674 failed(isAllowedSizeN(sizeN, elemType))) {
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)";
679 }
680 return success();
681}
682
683//===----------------------------------------------------------------------===//
684// RcpOp
685//===----------------------------------------------------------------------===//
686
687LogicalResult RcpOp::verify() {
688 bool ftz = getFtz();
689 bool approx = getApprox();
690 mlir::NVVM::FPRoundingModeAttr rnd = getRoundingAttr();
691 // Currently, only `rcp_approx` and `ftz` is supported.
692 if (!approx || !ftz) {
693 return emitOpError()
694 << "has a limitation. non-approx or non-ftz is not supported yet.";
695 }
696 if (rnd.getValue() != mlir::NVVM::FPRoundingMode::NONE) {
697 return emitOpError() << "has a limitation. " << rnd
698 << " is not supported yet.";
699 }
700 return success();
701}
702
703//===----------------------------------------------------------------------===//
704// TableGen'd dialect, type, and op definitions
705//===----------------------------------------------------------------------===//
706
707#define GET_ATTRDEF_CLASSES
708#include "mlir/Dialect/NVGPU/IR/NVGPUAttrDefs.cpp.inc"
709
710#include "mlir/Dialect/NVGPU/IR/NVGPUEnums.cpp.inc"
711
712#define GET_OP_CLASSES
713#include "mlir/Dialect/NVGPU/IR/NVGPUOps.cpp.inc"
714
715#define GET_TYPEDEF_CLASSES
716#include "mlir/Dialect/NVGPU/IR/NVGPUTypeDefs.cpp.inc"
return success()
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.
ArrayAttr()
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 int kWarpSize
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.
Definition Attributes.h:25
UnitAttr getUnitAttr()
Definition Builders.cpp:102
ArrayAttr getI64ArrayAttr(ArrayRef< int64_t > values)
Definition Builders.cpp:286
This class represents a diagnostic that is inflight and set to be reported.
This class helps build Operations.
Definition Builders.h:209
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
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.
Definition Operation.h:115
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
bool isF64() const
Definition Types.cpp:41
bool isTF32() const
Definition Types.cpp:39
bool isF32() const
Definition Types.cpp:40
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
bool isF16() const
Definition Types.cpp:38
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
bool isBF16() const
Definition Types.cpp:37
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
SmallVector< int64_t, 4 > getCoordinates(ArrayRef< int64_t > basis, unsigned linearIndex)
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
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.
Definition Value.h:494
This represents an operation in an abstracted form, suitable for use with the builder APIs.