MLIR 24.0.0git
NVVMDialect.cpp
Go to the documentation of this file.
1//===- NVVMDialect.cpp - NVVM IR Ops and Dialect registration -------------===//
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 defines the types and operation details for the NVVM IR dialect in
10// MLIR, and the LLVM IR dialect. It also registers the dialect.
11//
12// The NVVM dialect only contains GPU specific additions on top of the general
13// LLVM dialect.
14//
15//===----------------------------------------------------------------------===//
16
18
22#include "mlir/IR/Builders.h"
25#include "mlir/IR/Diagnostics.h"
27#include "mlir/IR/MLIRContext.h"
28#include "mlir/IR/Operation.h"
30#include "mlir/IR/Types.h"
32#include "llvm/ADT/STLExtras.h"
33#include "llvm/ADT/TypeSwitch.h"
34#include "llvm/IR/IRBuilder.h"
35#include "llvm/IR/NVVMIntrinsicUtils.h"
36#include "llvm/Support/Casting.h"
37#include "llvm/Support/FormatVariadic.h"
38#include "llvm/Support/NVPTXAddrSpace.h"
39#include "llvm/Support/raw_ostream.h"
40#include <cassert>
41#include <cmath>
42#include <optional>
43#include <string>
44
45using namespace mlir;
46using namespace NVVM;
47
48#include "mlir/Dialect/LLVMIR/NVVMOpsDialect.cpp.inc"
49#include "mlir/Dialect/LLVMIR/NVVMOpsEnums.cpp.inc"
50
51static constexpr unsigned notIntrinsic = llvm::Intrinsic::not_intrinsic;
52
53//===----------------------------------------------------------------------===//
54// Helper/Utility methods
55//===----------------------------------------------------------------------===//
56
57static bool isPtrInAddrSpace(mlir::Value ptr, NVVMMemorySpace targetAS) {
58 auto ptrTy = llvm::cast<LLVM::LLVMPointerType>(ptr.getType());
59 return ptrTy.getAddressSpace() == static_cast<unsigned>(targetAS);
60}
61
63 return isPtrInAddrSpace(ptr, NVVMMemorySpace::Generic);
64}
65
67 return isPtrInAddrSpace(ptr, NVVMMemorySpace::Shared);
68}
69
71 return isPtrInAddrSpace(ptr, NVVMMemorySpace::SharedCluster);
72}
73
74static llvm::Value *castPtrToAddrSpace(llvm::IRBuilderBase &builder,
75 llvm::Value *ptr,
76 NVVMMemorySpace targetAS) {
77 unsigned AS = static_cast<unsigned>(targetAS);
78 return builder.CreateAddrSpaceCast(
79 ptr, llvm::PointerType::get(builder.getContext(), AS));
80}
81
82// Helper method to convert CtaGroupKind in NVVM Dialect to CtaGroupKind in LLVM
83static llvm::nvvm::CTAGroupKind
84getNVVMCtaGroupKind(NVVM::CTAGroupKind ctaGroup) {
85 switch (ctaGroup) {
86 case NVVM::CTAGroupKind::CTA_1:
87 return llvm::nvvm::CTAGroupKind::CG_1;
88 case NVVM::CTAGroupKind::CTA_2:
89 return llvm::nvvm::CTAGroupKind::CG_2;
90 }
91 llvm_unreachable("unsupported cta_group value");
92}
93
94//===----------------------------------------------------------------------===//
95// Verifier methods
96//===----------------------------------------------------------------------===//
97
98// This verifier is shared among the following Ops:
99// CpAsyncBulkTensorSharedCTAToGlobalOp (TMA Store)
100// CpAsyncBulkTensorReduceOp (TMA Store-Reduce)
101static LogicalResult cpAsyncBulkTensorCommonVerifier(size_t tensorDims,
102 bool isIm2Col,
103 size_t numIm2ColOffsets,
104 Location loc) {
105 if (tensorDims < 1 || tensorDims > 5)
106 return emitError(loc, "expects coordinates between 1 to 5 dimension");
107
108 // For Im2Col mode, there are two constraints:
109 if (isIm2Col) {
110 // 1. Tensor must always be at least 3-d.
111 if (tensorDims < 3)
112 return emitError(
113 loc,
114 "to use im2col mode, the tensor has to be at least 3-dimensional");
115 // 2. When there are Im2ColOffsets, they must be (Dims - 2) in number.
116 if (numIm2ColOffsets && (tensorDims != (numIm2ColOffsets + 2)))
117 return emitError(
118 loc, "im2col offsets must be 2 less than number of coordinates");
119 }
120 return success();
121}
122
123LogicalResult CpAsyncBulkTensorSharedCTAToGlobalOp::verify() {
124 TMAStoreMode mode = getMode();
125 // We lower through inline-ptx when getPredicate() is true.
126 // a) Only TILE mode is supported
127 // b) Cache-hint is not supported
128 if (getPredicate()) {
129 if (mode != TMAStoreMode::TILE)
130 return emitError("Inline-ptx lowering supported only for Tile mode.");
131 if (getL2CacheHint())
132 return emitError("Inline-ptx lowering unsupported with L2 cache-hint.");
133 }
134
135 size_t dims = getCoordinates().size();
136 switch (mode) {
137 case TMAStoreMode::TILE:
138 return cpAsyncBulkTensorCommonVerifier(dims, false, 0, getLoc());
139 case TMAStoreMode::IM2COL:
140 case TMAStoreMode::IM2COL_W:
141 return cpAsyncBulkTensorCommonVerifier(dims, true, 0, getLoc());
142 case TMAStoreMode::TILE_SCATTER4:
143 if (dims != 5)
144 return emitError("Scatter4 mode expects 5 coordinates");
145 }
146 return success();
147}
148
149LogicalResult CpAsyncOp::verify() {
150 if (getModifier() != LoadCacheModifierKind::CG &&
151 getModifier() != LoadCacheModifierKind::CA)
152 return emitError("Only CG and CA cache modifiers are supported.");
153 if (getSize() != 4 && getSize() != 8 && getSize() != 16)
154 return emitError("expected byte size to be either 4, 8 or 16.");
155 if (getModifier() == LoadCacheModifierKind::CG && getSize() != 16)
156 return emitError("CG cache modifier is only support for 16 bytes copy.");
157 return success();
158}
159
160// This verify params can be shared across TMA Load and Prefetch Ops.
161static LogicalResult verifyTMALoadParams(size_t tensorDims, size_t numIm2colOff,
162 TMALoadMode mode, Location loc) {
163 if (tensorDims < 1 || tensorDims > 5)
164 return emitError(loc, "expects coordinates between 1 to 5 dimension");
165
166 auto checkTMALoadParams = [&](TMALoadMode mode, bool isIm2col,
167 size_t expectedIm2colOff) -> LogicalResult {
168 if (isIm2col && (tensorDims < 3))
169 return emitError(loc)
170 << "to use " << mode
171 << " mode, the tensor has to be at least 3-dimensional";
172
173 if (numIm2colOff != expectedIm2colOff)
174 return emitError(loc) << " im2col offsets expected " << expectedIm2colOff
175 << " (provided " << numIm2colOff << ")";
176
177 return success();
178 };
179
180 switch (mode) {
181 case TMALoadMode::TILE:
182 return checkTMALoadParams(mode, false, 0);
183 case TMALoadMode::IM2COL:
184 return checkTMALoadParams(mode, true, tensorDims - 2);
185 case TMALoadMode::IM2COL_W:
186 case TMALoadMode::IM2COL_W_128:
187 return checkTMALoadParams(mode, true, 2);
188 case TMALoadMode::TILE_GATHER4:
189 return (tensorDims == 5)
190 ? checkTMALoadParams(mode, false, 0)
191 : emitError(loc, "Gather4 mode expects 5 coordinates");
192 }
193 return success();
194}
195
196LogicalResult CpAsyncBulkTensorPrefetchOp::verify() {
197 return verifyTMALoadParams(getCoordinates().size(), getIm2colOffsets().size(),
198 getMode(), getLoc());
199}
200
201LogicalResult CpAsyncBulkTensorGlobalToSharedClusterOp::verify() {
202 TMALoadMode mode = getMode();
203 bool isCTAOnly = getIsCTAOnly();
204 if (getPredicate()) { // Inline-asm based lowering
205 if (isCTAOnly)
206 return emitError("Predicate is supported only for shared::cluster mode.");
207 if (mode != TMALoadMode::TILE && mode != TMALoadMode::IM2COL)
208 return emitError(
209 "Predicate is supported only for Tile and Im2col modes.");
210 } else { // Intrinsics-based lowering
211 NVVMMemorySpace expectedAS =
212 isCTAOnly ? NVVMMemorySpace::Shared : NVVMMemorySpace::SharedCluster;
213 unsigned AS = llvm::cast<LLVM::LLVMPointerType>(getDstMem().getType())
214 .getAddressSpace();
215 if (AS != expectedAS)
216 return emitError()
217 << (isCTAOnly
218 ? "Shared::cta destination requires address-space 3."
219 : "Shared::cluster destination requires address-space 7.");
220 // Checks specific to shared::cta mode
221 if (isCTAOnly) {
222 if (getMulticastMask())
223 return emitError("Multicast is not supported with shared::cta mode.");
224 if (getGroup())
225 return emitError("CTAGroup is not supported with shared::cta mode.");
226 }
227 }
228
229 return verifyTMALoadParams(getCoordinates().size(), getIm2colOffsets().size(),
230 getMode(), getLoc());
231}
232
233LogicalResult CpAsyncBulkTensorReduceOp::verify() {
234 TMAStoreMode mode = getMode();
235 size_t dims = getCoordinates().size();
236 switch (mode) {
237 case TMAStoreMode::TILE:
238 return cpAsyncBulkTensorCommonVerifier(dims, false, 0, getLoc());
239 case TMAStoreMode::IM2COL:
240 case TMAStoreMode::IM2COL_W:
241 return cpAsyncBulkTensorCommonVerifier(dims, true, 0, getLoc());
242 case TMAStoreMode::TILE_SCATTER4:
243 return emitError("Scatter mode unsupported for CpAsyncBulkTensorReduceOp");
244 }
245 return success();
246}
247
248LogicalResult CpAsyncBulkGlobalToSharedClusterOp::verify() {
249 bool isSharedCTA = isPtrInSharedCTASpace(getDstMem());
250 if (isSharedCTA && getMulticastMask())
251 return emitError("Multicast is not supported with shared::cta mode.");
252
253 return success();
254}
255
256static LogicalResult verifyMBarrierArriveLikeOp(Operation *op, Value addr,
257 NVVM::MemScopeKind scope,
258 Value retVal = nullptr) {
259 if (scope != NVVM::MemScopeKind::CTA && scope != NVVM::MemScopeKind::CLUSTER)
260 return op->emitError("mbarrier scope must be either CTA or Cluster");
261
262 bool isSharedCluster = isPtrInSharedClusterSpace(addr);
263 bool hasRetValue = static_cast<bool>(retVal);
264 if (isSharedCluster && hasRetValue)
265 return op->emitError(
266 "mbarrier in shared_cluster space cannot return any value");
267
268 return success();
269}
270
271LogicalResult MBarrierArriveOp::verify() {
272 return verifyMBarrierArriveLikeOp(getOperation(), getAddr(), getScope(),
273 getRes());
274}
275
276LogicalResult MBarrierArriveDropOp::verify() {
277 return verifyMBarrierArriveLikeOp(getOperation(), getAddr(), getScope(),
278 getRes());
279}
280
281LogicalResult MBarrierArriveExpectTxOp::verify() {
282 // The inline-ptx version of this Op does not support all features.
283 // With predicate, this Op lowers to inline-ptx. So, verify and
284 // error-out if there are unsupported features.
285 if (getPredicate()) {
286 if (getScope() != NVVM::MemScopeKind::CTA)
287 return emitError("mbarrier scope must be CTA when using predicate");
288
289 if (isPtrInSharedClusterSpace(getAddr()))
290 return emitError("mbarrier in shared_cluster space is not supported when "
291 "using predicate");
292
293 if (getRes())
294 return emitError("return-value is not supported when using predicate");
295
296 if (getRelaxed() == true)
297 return emitError("mbarrier with relaxed semantics is not supported when "
298 "using predicate");
299 }
300 return verifyMBarrierArriveLikeOp(getOperation(), getAddr(), getScope(),
301 getRes());
302}
303
304LogicalResult MBarrierArriveDropExpectTxOp::verify() {
305 return verifyMBarrierArriveLikeOp(getOperation(), getAddr(), getScope(),
306 getRes());
307}
308
309//===----------------------------------------------------------------------===//
310// inferReturnTypes for mbarrier arrive-like ops
311//===----------------------------------------------------------------------===//
312
313/// Only shared_cluster (ptr<7>) produces zero results; all other address
314/// spaces (including generic) return i64.
315static LogicalResult
317 SmallVectorImpl<Type> &inferredReturnTypes) {
318 if (!isPtrInSharedClusterSpace(addr))
319 inferredReturnTypes.push_back(IntegerType::get(context, 64));
320 return success();
321}
322
323LogicalResult
324MBarrierArriveOp::inferReturnTypes(MLIRContext *context,
325 std::optional<Location> location,
326 MBarrierArriveOp::Adaptor adaptor,
327 SmallVectorImpl<Type> &inferredReturnTypes) {
328 return inferMBarrierArriveResultTypes(context, adaptor.getAddr(),
329 inferredReturnTypes);
330}
331
332LogicalResult MBarrierArriveDropOp::inferReturnTypes(
333 MLIRContext *context, std::optional<Location> location,
334 MBarrierArriveDropOp::Adaptor adaptor,
335 SmallVectorImpl<Type> &inferredReturnTypes) {
336 return inferMBarrierArriveResultTypes(context, adaptor.getAddr(),
337 inferredReturnTypes);
338}
339
340LogicalResult MBarrierArriveExpectTxOp::inferReturnTypes(
341 MLIRContext *context, std::optional<Location> location,
342 MBarrierArriveExpectTxOp::Adaptor adaptor,
343 SmallVectorImpl<Type> &inferredReturnTypes) {
344 // Predicate forces no return value (inline PTX path).
345 // Note: predicate + shared_cluster is rejected by the verifier separately.
346 if (adaptor.getPredicate())
347 return success();
348 return inferMBarrierArriveResultTypes(context, adaptor.getAddr(),
349 inferredReturnTypes);
350}
351
352LogicalResult MBarrierArriveDropExpectTxOp::inferReturnTypes(
353 MLIRContext *context, std::optional<Location> location,
354 MBarrierArriveDropExpectTxOp::Adaptor adaptor,
355 SmallVectorImpl<Type> &inferredReturnTypes) {
356 return inferMBarrierArriveResultTypes(context, adaptor.getAddr(),
357 inferredReturnTypes);
358}
359
360/// For ops with optional results, allow the user to omit the result even when
361/// inference would produce one. This preserves backward compatibility: the
362/// result can be silently discarded (e.g., for fire-and-forget arrive ops).
364 TypeRange actual) {
365 if (actual.empty())
366 return true;
367 return inferred == actual;
368}
369
370bool MBarrierArriveOp::isCompatibleReturnTypes(TypeRange l, TypeRange r) {
372}
373bool MBarrierArriveDropOp::isCompatibleReturnTypes(TypeRange l, TypeRange r) {
375}
376bool MBarrierArriveExpectTxOp::isCompatibleReturnTypes(TypeRange l,
377 TypeRange r) {
379}
380bool MBarrierArriveDropExpectTxOp::isCompatibleReturnTypes(TypeRange l,
381 TypeRange r) {
383}
384
385LogicalResult MBarrierExpectTxOp::verify() {
386 return verifyMBarrierArriveLikeOp(getOperation(), getAddr(), getScope());
387}
388
389LogicalResult MBarrierCompleteTxOp::verify() {
390 return verifyMBarrierArriveLikeOp(getOperation(), getAddr(), getScope());
391}
392
393LogicalResult MBarrierTestWaitOp::verify() {
394 return verifyMBarrierArriveLikeOp(getOperation(), getAddr(), getScope());
395}
396
397LogicalResult MBarrierTryWaitOp::verify() {
398 return verifyMBarrierArriveLikeOp(getOperation(), getAddr(), getScope());
399}
400
401LogicalResult ConvertFloatToTF32Op::verify() {
402 using RndMode = NVVM::FPRoundingMode;
403 switch (getRnd()) {
404 case RndMode::RNA:
405 if (getRelu())
406 return emitError("Relu not supported with rna rounding mode.");
407 break;
408 case RndMode::RN:
409 case RndMode::RZ:
410 break;
411 default:
412 return emitError(
413 "Only {rn,rz,rna} rounding modes supported for ConvertFloatToTF32Op.");
414 }
415 return success();
416}
417
418LogicalResult ConvertF32x2ToF6x2Op::verify() {
420
421 if (!llvm::isa<mlir::Float6E2M3FNType, mlir::Float6E3M2FNType>(getDstTy())) {
422 return emitOpError("Only ")
423 << mlir::Float6E2M3FNType::get(ctx) << " and "
424 << mlir::Float6E3M2FNType::get(ctx)
425 << " types are supported for conversions from f32x2 to f6x2.";
426 }
427 return success();
428}
429
430LogicalResult ConvertF32x2ToF8x2Op::verify() {
431 using RndMode = NVVM::FPRoundingMode;
432 using SatMode = NVVM::SaturationMode;
433
434 bool isRoundingModeRN = getRnd() == RndMode::RN;
435 bool isRoundingModeRZ = getRnd() == RndMode::RZ;
436 bool isRoundingModeRP = getRnd() == RndMode::RP;
437 bool isSatFinite = getSat() == SatMode::SATFINITE;
438
439 bool hasRelu = getRelu();
440
442
444 .Case<mlir::Float8E4M3FNType, mlir::Float8E5M2Type>(
445 [&](mlir::Type) -> LogicalResult {
446 if (!isRoundingModeRN) {
447 return emitOpError("Only RN rounding mode is supported for "
448 "conversions from f32x2 to ")
449 << mlir::Float8E4M3FNType::get(ctx) << " and "
450 << mlir::Float8E5M2Type::get(ctx) << " types";
451 }
452 if (!isSatFinite) {
453 return emitOpError("Only SATFINITE saturation mode is supported "
454 "for conversions "
455 "from f32x2 to ")
456 << mlir::Float8E4M3FNType::get(ctx) << " and "
457 << mlir::Float8E5M2Type::get(ctx) << " types";
458 }
459 return success();
460 })
461 .Case<mlir::Float8E8M0FNUType>([&](mlir::Type) -> LogicalResult {
462 if (!(isRoundingModeRZ || isRoundingModeRP)) {
463 return emitOpError("Only RZ and RP rounding modes are supported for "
464 "conversions from f32x2 to ")
465 << mlir::Float8E8M0FNUType::get(ctx) << " type";
466 }
467 if (hasRelu) {
468 return emitOpError("relu not supported for conversions to ")
469 << mlir::Float8E8M0FNUType::get(ctx) << " type";
470 }
471 return success();
472 })
473 .Default([&](mlir::Type) {
474 return emitOpError("Only ")
475 << mlir::Float8E4M3FNType::get(ctx) << ", "
476 << mlir::Float8E5M2Type::get(ctx) << ", and "
477 << mlir::Float8E8M0FNUType::get(ctx)
478 << " types are "
479 "supported for conversions from f32x2 to f8x2";
480 });
481}
482
483LogicalResult ConvertF16x2ToF8x2Op::verify() {
485
486 if (!llvm::isa<mlir::Float8E4M3FNType, mlir::Float8E5M2Type>(getDstTy())) {
487 return emitOpError("Only ")
488 << mlir::Float8E4M3FNType::get(ctx) << " and "
489 << mlir::Float8E5M2Type::get(ctx)
490 << " types are supported for conversions from f16x2 to f8x2.";
491 }
492 return success();
493}
494
495LogicalResult ConvertBF16x2ToF8x2Op::verify() {
496 using RndMode = NVVM::FPRoundingMode;
497 using SatMode = NVVM::SaturationMode;
498
499 bool isRoundingModeRN = getRnd() == RndMode::RN;
500 bool isRoundingModeRZ = getRnd() == RndMode::RZ;
501 bool isRoundingModeRP = getRnd() == RndMode::RP;
502 bool isSatFinite = getSat() == SatMode::SATFINITE;
503 bool hasRelu = getRelu();
504
506
508 .Case<mlir::Float8E4M3FNType, mlir::Float8E5M2Type>(
509 [&](mlir::Type) -> LogicalResult {
510 if (!isRoundingModeRN)
511 return emitOpError("Only RN rounding mode is supported for "
512 "conversions from bf16x2 to ")
513 << mlir::Float8E4M3FNType::get(ctx) << " and "
514 << mlir::Float8E5M2Type::get(ctx) << " types";
515 if (!isSatFinite)
516 return emitOpError("Only SATFINITE saturation mode is supported "
517 "for conversions from bf16x2 to ")
518 << mlir::Float8E4M3FNType::get(ctx) << " and "
519 << mlir::Float8E5M2Type::get(ctx) << " types";
520 return success();
521 })
522 .Case<mlir::Float8E8M0FNUType>([&](mlir::Type) -> LogicalResult {
523 if (!(isRoundingModeRZ || isRoundingModeRP))
524 return emitOpError("Only RZ and RP rounding modes are supported for "
525 "conversions from bf16x2 to ")
526 << mlir::Float8E8M0FNUType::get(ctx) << " type";
527 if (hasRelu)
528 return emitOpError("relu not supported for conversions to ")
529 << mlir::Float8E8M0FNUType::get(ctx) << " type";
530 return success();
531 })
532 .Default([&](mlir::Type) -> LogicalResult {
533 llvm_unreachable("Invalid conversion in ConvertBF16x2ToF8x2Op");
534 return failure();
535 });
536}
537
538LogicalResult ConvertF32x2ToF4x2Op::verify() {
540
541 if (!llvm::isa<mlir::Float4E2M1FNType>(getDstTy()))
542 return emitOpError("Only ")
543 << mlir::Float4E2M1FNType::get(ctx)
544 << " type is supported for conversions from f32x2 to f4x2.";
545
546 return success();
547}
548
549LogicalResult ConvertF8x2ToBF16x2Op::verify() {
551 if (llvm::isa<Float8E8M0FNUType>(getSrcType())) {
552 if (getSat() != SaturationMode::NONE)
553 return emitOpError(
554 "Only NONE saturation mode is supported for conversions from ")
555 << Float8E8M0FNUType::get(ctx) << " type";
556 if (getScaleFactor())
557 return emitOpError("scaleFactor not supported for conversions from ")
558 << Float8E8M0FNUType::get(ctx) << " type";
559 if (getRelu())
560 return emitOpError("relu not supported for conversions from ")
561 << Float8E8M0FNUType::get(ctx) << " type";
562 }
563
564 return success();
565}
566
567LogicalResult PermuteOp::verify() {
568 using Mode = NVVM::PermuteMode;
569 bool hasHi = static_cast<bool>(getHi());
570
571 switch (getMode()) {
572 case Mode::DEFAULT:
573 case Mode::F4E:
574 case Mode::B4E:
575 if (!hasHi)
576 return emitError("mode '") << getMode() << "' requires 'hi' operand.";
577 break;
578 case Mode::RC8:
579 case Mode::ECL:
580 case Mode::ECR:
581 case Mode::RC16:
582 if (hasHi)
583 return emitError("mode '")
584 << getMode() << "' does not accept 'hi' operand.";
585 break;
586 }
587
588 return success();
589}
590
591//===----------------------------------------------------------------------===//
592// Stochastic Rounding Conversion Ops
593//===----------------------------------------------------------------------===//
594
595static LogicalResult verifyConvertF32x2ToFP16x2Op(Twine dstType,
596 FPRoundingMode rnd,
597 bool hasRandomBits,
598 Operation *op) {
599 static constexpr FPRoundingMode validRndModes[] = {
600 FPRoundingMode::RN, FPRoundingMode::RZ, FPRoundingMode::RS};
601
602 if (!llvm::is_contained(validRndModes, rnd)) {
603 return op->emitOpError(
604 "Only RN, RZ, and RS rounding modes are supported for "
605 "conversions from f32x2 to ")
606 << dstType << ".";
607 }
608
609 if (rnd == FPRoundingMode::RS) {
610 if (!hasRandomBits) {
611 return op->emitOpError("random_bits is required for RS rounding mode.");
612 }
613 } else {
614 if (hasRandomBits) {
615 return op->emitOpError(
616 "random_bits not supported for RN and RZ rounding modes.");
617 }
618 }
619
620 return success();
621}
622
623LogicalResult ConvertF32x2ToF16x2Op::verify() {
624 return verifyConvertF32x2ToFP16x2Op("f16x2", getRnd(),
625 getRandomBits() ? true : false, *this);
626}
627
628LogicalResult ConvertF32x2ToBF16x2Op::verify() {
629 return verifyConvertF32x2ToFP16x2Op("bf16x2", getRnd(),
630 getRandomBits() ? true : false, *this);
631}
632
633LogicalResult ConvertF32x4ToF8x4Op::verify() {
635
636 if (!llvm::isa<mlir::Float8E4M3FNType, mlir::Float8E5M2Type>(getDstTy()))
637 return emitOpError("Only ")
638 << mlir::Float8E4M3FNType::get(ctx) << " and "
639 << mlir::Float8E5M2Type::get(ctx)
640 << " types are supported for conversions from f32x4 to f8x4.";
641
642 return success();
643}
644
645LogicalResult ConvertF32x4ToF6x4Op::verify() {
647
648 if (!llvm::isa<mlir::Float6E2M3FNType, mlir::Float6E3M2FNType>(getDstTy()))
649 return emitOpError("Only ")
650 << mlir::Float6E2M3FNType::get(ctx) << " and "
651 << mlir::Float6E3M2FNType::get(ctx)
652 << " types are supported for conversions from f32x4 to f6x4.";
653
654 return success();
655}
656
657LogicalResult ConvertF32x4ToF4x4Op::verify() {
659
660 if (!llvm::isa<mlir::Float4E2M1FNType>(getDstTy()))
661 return emitOpError("Only ") << mlir::Float4E2M1FNType::get(ctx)
662 << " type is supported for conversions from "
663 "f32x4 to f4x4.";
664
665 return success();
666}
667
668LogicalResult BulkStoreOp::verify() {
669 if (getInitVal() != 0)
670 return emitOpError("only 0 is supported for initVal, got ") << getInitVal();
671 return success();
672}
673
674LogicalResult PMEventOp::verify() {
675 auto eventId = getEventId();
676 auto maskedEventId = getMaskedEventId();
677 if (!maskedEventId && !eventId) {
678 return emitOpError() << "either `id` or `mask` must be set";
679 }
680
681 if (maskedEventId && eventId) {
682 return emitOpError() << "`id` and `mask` cannot be set at the same time";
683 }
684
685 if (eventId) {
686 if (eventId < 0 || eventId > 15) {
687 return emitOpError() << "`id` must be between 0 and 15";
688 }
689 }
690
691 return llvm::success();
692}
693
694// Given the element type of an operand and whether or not it is an accumulator,
695// this function returns the PTX type (`NVVM::MMATypes`) that corresponds to the
696// operand's element type.
697std::optional<mlir::NVVM::MMATypes>
698MmaOp::inferOperandMMAType(Type operandElType, bool isAccumulator) {
699 auto half2Type =
700 VectorType::get(2, Float16Type::get(operandElType.getContext()));
701 if (operandElType.isF64())
702 return NVVM::MMATypes::f64;
703 if (operandElType.isF16() || operandElType == half2Type)
704 return NVVM::MMATypes::f16;
705 if (operandElType.isF32() && isAccumulator)
706 return NVVM::MMATypes::f32;
707 if (operandElType.isF32() && !isAccumulator)
708 return NVVM::MMATypes::tf32;
709 if (llvm::isa<IntegerType>(operandElType)) {
710 if (isAccumulator)
711 return NVVM::MMATypes::s32;
712 return std::nullopt;
713 }
714
715 if (auto structType = llvm::dyn_cast<LLVM::LLVMStructType>(operandElType)) {
716 if (structType.getBody().empty())
717 return std::nullopt;
718 return inferOperandMMAType(structType.getBody()[0], isAccumulator);
719 }
720
721 return std::nullopt;
722}
723
724static bool isInt4PtxType(MMATypes type) {
725 return (type == MMATypes::u4 || type == MMATypes::s4);
726}
727
728static bool isInt8PtxType(MMATypes type) {
729 return (type == MMATypes::u8 || type == MMATypes::s8);
730}
731
732static bool isIntegerPtxType(MMATypes type) {
733 return isInt4PtxType(type) || isInt8PtxType(type) || type == MMATypes::b1 ||
734 type == MMATypes::s32;
735}
736
737MMATypes MmaOp::accumPtxType() {
738 std::optional<mlir::NVVM::MMATypes> val = inferOperandMMAType(
739 getODSOperands(2).getTypes().front(), /*isAccumulator=*/true);
740 assert(val.has_value() && "accumulator PTX type should always be inferrable");
741 return val.value();
742}
743
744MMATypes MmaOp::resultPtxType() {
745 std::optional<mlir::NVVM::MMATypes> val =
746 inferOperandMMAType(getResult().getType(), /*isAccumulator=*/true);
747 assert(val.has_value() && "result PTX type should always be inferrable");
748 return val.value();
749}
750
751void MmaOp::print(OpAsmPrinter &p) {
752 SmallVector<Type, 4> regTypes;
753 struct MMAOperandFragment {
754 StringRef operandName;
755 StringRef ptxTypeAttr;
756 SmallVector<Value, 4> regs;
757 explicit MMAOperandFragment(StringRef name, StringRef ptxTypeName)
758 : operandName(name), ptxTypeAttr(ptxTypeName) {}
759 };
760
761 std::array<MMAOperandFragment, 3> frags{
762 MMAOperandFragment("A", getMultiplicandAPtxTypeAttrName()),
763 MMAOperandFragment("B", getMultiplicandBPtxTypeAttrName()),
764 MMAOperandFragment("C", "")};
765 SmallVector<StringRef, 4> ignoreAttrNames{
766 mlir::NVVM::MmaOp::getOperandSegmentSizeAttr()};
767
768 for (unsigned fragIdx = 0; fragIdx < frags.size(); fragIdx++) {
769 auto &frag = frags[fragIdx];
770 auto varOperandSpec = getODSOperandIndexAndLength(fragIdx);
771 for (auto operandIdx = varOperandSpec.first;
772 operandIdx < varOperandSpec.first + varOperandSpec.second;
773 operandIdx++) {
774 frag.regs.push_back(this->getOperand(operandIdx));
775 if (operandIdx == 0) {
776 regTypes.push_back(this->getOperand(operandIdx).getType());
777 }
778 }
779 std::optional<MMATypes> inferredType = MmaOp::inferOperandMMAType(
780 regTypes.back(), /*isAccumulator=*/fragIdx >= 2);
781 if (inferredType)
782 ignoreAttrNames.push_back(frag.ptxTypeAttr);
783 }
784
785 auto printMmaOperand = [&](const MMAOperandFragment &frag) -> void {
786 p << " " << frag.operandName;
787 p << "[";
788 p.printOperands(frag.regs);
789 p << "] ";
790 };
791
792 for (const auto &frag : frags) {
793 printMmaOperand(frag);
794 }
795
796 p.printOptionalAttrDict(this->getOperation()->getAttrs(), ignoreAttrNames);
797
798 // Print the types of the operands and result.
799 p << " : " << "(";
800 llvm::interleaveComma(SmallVector<Type, 3>{frags[0].regs[0].getType(),
801 frags[1].regs[0].getType(),
802 frags[2].regs[0].getType()},
803 p);
804 p << ")";
805 p.printArrowTypeList(TypeRange{this->getRes().getType()});
806}
807
808void MmaOp::build(OpBuilder &builder, OperationState &result, Type resultType,
809 ValueRange operandA, ValueRange operandB, ValueRange operandC,
810 ArrayRef<int64_t> shape, std::optional<MMAB1Op> b1Op,
811 std::optional<MMAIntOverflow> intOverflow,
812 std::optional<std::array<MMATypes, 2>> multiplicandPtxTypes,
813 std::optional<std::array<MMALayout, 2>> multiplicandLayouts) {
814
815 assert(shape.size() == 3 && "expected shape to have size 3 (m, n, k)");
816 MLIRContext *ctx = builder.getContext();
817 result.addAttribute(
818 "shape", builder.getAttr<MMAShapeAttr>(shape[0], shape[1], shape[2]));
819
820 result.addOperands(operandA);
821 result.addOperands(operandB);
822 result.addOperands(operandC);
823
824 if (multiplicandPtxTypes) {
825 result.addAttribute("multiplicandAPtxType",
826 MMATypesAttr::get(ctx, (*multiplicandPtxTypes)[0]));
827 result.addAttribute("multiplicandBPtxType",
828 MMATypesAttr::get(ctx, (*multiplicandPtxTypes)[1]));
829 } else {
830 if (auto res = inferOperandMMAType(operandA[0].getType(), false))
831 result.addAttribute("multiplicandAPtxType", MMATypesAttr::get(ctx, *res));
832 if (auto res = inferOperandMMAType(operandB[0].getType(), false))
833 result.addAttribute("multiplicandBPtxType", MMATypesAttr::get(ctx, *res));
834 }
835
836 if (multiplicandLayouts) {
837 result.addAttribute("layoutA",
838 MMALayoutAttr::get(ctx, (*multiplicandLayouts)[0]));
839 result.addAttribute("layoutB",
840 MMALayoutAttr::get(ctx, (*multiplicandLayouts)[1]));
841 } else {
842 result.addAttribute("layoutA", MMALayoutAttr::get(ctx, MMALayout::row));
843 result.addAttribute("layoutB", MMALayoutAttr::get(ctx, MMALayout::col));
844 }
845
846 if (intOverflow.has_value())
847 result.addAttribute("intOverflowBehavior",
848 MMAIntOverflowAttr::get(ctx, *intOverflow));
849 if (b1Op.has_value())
850 result.addAttribute("b1Op", MMAB1OpAttr::get(ctx, *b1Op));
851
852 result.addTypes(resultType);
853 result.addAttribute(
854 MmaOp::getOperandSegmentSizeAttr(),
855 builder.getDenseI32ArrayAttr({static_cast<int32_t>(operandA.size()),
856 static_cast<int32_t>(operandB.size()),
857 static_cast<int32_t>(operandC.size())}));
858}
859
860// <operation> :=
861// A `[` $operandA `]` B `[` $operandB `]` C `[` $operandC `]`
862// attr-dict : (type($operandA[0]), type($operandB[0]), type($operandC[0]))
863// `->` type($res)
864ParseResult MmaOp::parse(OpAsmParser &parser, OperationState &result) {
865 struct MMAOperandFragment {
866 std::optional<MMATypes> elemtype;
867 SmallVector<OpAsmParser::UnresolvedOperand, 4> regs;
868 SmallVector<Type> regTypes;
869 };
870
871 Builder &builder = parser.getBuilder();
872 std::array<MMAOperandFragment, 4> frags;
873
874 NamedAttrList namedAttributes;
875
876 // A helper to parse the operand segments.
877 auto parseMmaOperand = [&](StringRef operandName,
878 MMAOperandFragment &frag) -> LogicalResult {
879 if (parser.parseKeyword(operandName).failed())
880 return failure();
881 if (parser
882 .parseOperandList(frag.regs, OpAsmParser::Delimiter::OptionalSquare)
883 .failed())
884 return failure();
885 return success();
886 };
887
888 // Parse the operand segments.
889 if (parseMmaOperand("A", frags[0]).failed())
890 return failure();
891 if (parseMmaOperand("B", frags[1]).failed())
892 return failure();
893 if (parseMmaOperand("C", frags[2]).failed())
894 return failure();
895
896 if (parser.parseOptionalAttrDict(namedAttributes).failed())
897 return failure();
898
899 // Parse the type specification and resolve operands.
900 SmallVector<Type, 3> operandTypes;
901 if (failed(parser.parseColon()))
902 return failure();
903 if (failed(parser.parseLParen()))
904 return failure();
905 if (failed(parser.parseTypeList(operandTypes)))
906 return failure();
907 if (failed(parser.parseRParen()))
908 if (operandTypes.size() != 3)
909 return parser.emitError(
910 parser.getNameLoc(),
911 "expected one type for each operand segment but got " +
912 Twine(operandTypes.size()) + " types");
913 for (const auto &iter : llvm::enumerate(operandTypes)) {
914 auto &frag = frags[iter.index()];
915 frag.regTypes.resize(frag.regs.size(), iter.value());
916 if (failed(parser.resolveOperands(frag.regs, frag.regTypes,
917 parser.getNameLoc(), result.operands)))
918 return failure();
919 frag.elemtype = inferOperandMMAType(frag.regTypes[0],
920 /*isAccumulator*/ iter.index() < 2);
921 }
922
923 Type resultType;
924 if (parser.parseArrow() || parser.parseType(resultType))
925 return failure();
926 frags[3].elemtype = inferOperandMMAType(resultType, /*isAccumulator*/ true);
927
928 std::array<StringRef, 2> names{"multiplicandAPtxType",
929 "multiplicandBPtxType"};
930 for (unsigned idx = 0; idx < names.size(); idx++) {
931 const auto &frag = frags[idx];
932 std::optional<NamedAttribute> attr = namedAttributes.getNamed(names[idx]);
933 if (!frag.elemtype.has_value() && !attr.has_value()) {
934 return parser.emitError(
935 parser.getNameLoc(),
936 "attribute " + names[idx] +
937 " is not provided explicitly and cannot be inferred");
938 }
939 if (!attr.has_value())
940 result.addAttribute(
941 names[idx], MMATypesAttr::get(parser.getContext(), *frag.elemtype));
942 }
943
944 result.addTypes(resultType);
945 if (!namedAttributes.empty())
946 result.addAttributes(namedAttributes);
947 result.addAttribute(MmaOp::getOperandSegmentSizeAttr(),
948 builder.getDenseI32ArrayAttr({
949 static_cast<int32_t>(frags[0].regs.size()),
950 static_cast<int32_t>(frags[1].regs.size()),
951 static_cast<int32_t>(frags[2].regs.size()),
952 }));
953 return success();
954}
955
956LogicalResult MmaOp::verify() {
957 MLIRContext *context = getContext();
958 auto f16Ty = Float16Type::get(context);
959 auto i32Ty = IntegerType::get(context, 32);
960 auto f16x2Ty = VectorType::get(2, f16Ty);
961 auto f32Ty = Float32Type::get(context);
962 auto f16x2x4StructTy = LLVM::LLVMStructType::getLiteral(
963 context, {f16x2Ty, f16x2Ty, f16x2Ty, f16x2Ty});
964
965 auto s32x4StructTy =
966 LLVM::LLVMStructType::getLiteral(context, {i32Ty, i32Ty, i32Ty, i32Ty});
967 auto f32x8StructTy =
968 LLVM::LLVMStructType::getLiteral(context, SmallVector<Type>(8, f32Ty));
969 auto f16x2x2StructTy =
970 LLVM::LLVMStructType::getLiteral(context, {f16x2Ty, f16x2Ty});
971 auto f32x4StructTy =
972 LLVM::LLVMStructType::getLiteral(context, {f32Ty, f32Ty, f32Ty, f32Ty});
973 auto s32x2StructTy =
974 LLVM::LLVMStructType::getLiteral(context, {i32Ty, i32Ty});
975
976 std::array<int64_t, 3> mmaShape{getShapeAttr().getM(), getShapeAttr().getN(),
977 getShapeAttr().getK()};
978
979 // These variables define the set of allowed data types for matrices A, B, C,
980 // and result.
981 using AllowedShapes = SmallVector<std::array<int64_t, 3>, 2>;
982 using AllowedTypes = SmallVector<SmallVector<Type, 4>, 2>;
983 AllowedShapes allowedShapes;
984 AllowedTypes expectedA;
985 AllowedTypes expectedB;
986 AllowedTypes expectedC;
987 SmallVector<Type> expectedResult;
988
989 // When M = 16, we just need to calculate the number of 8xk tiles, where
990 // k is a factor that depends on the data type.
991 if (mmaShape[0] == 16) {
992 int64_t kFactor;
993 Type multiplicandFragType;
994 switch (*getMultiplicandAPtxType()) {
995 case MMATypes::tf32:
996 kFactor = 4;
997 multiplicandFragType = i32Ty;
998 expectedResult.push_back(LLVM::LLVMStructType::getLiteral(
999 context, {f32Ty, f32Ty, f32Ty, f32Ty}));
1000 break;
1001 case MMATypes::bf16:
1002 kFactor = 8;
1003 multiplicandFragType = i32Ty;
1004 expectedResult.push_back(LLVM::LLVMStructType::getLiteral(
1005 context, {f32Ty, f32Ty, f32Ty, f32Ty}));
1006 break;
1007 case MMATypes::f16:
1008 kFactor = 8;
1009 multiplicandFragType = f16x2Ty;
1010 expectedResult.push_back(f16x2x2StructTy);
1011 expectedResult.push_back(f32x4StructTy);
1012 break;
1013 case MMATypes::e4m3:
1014 case MMATypes::e5m2:
1015 // FP8 (m16n8k16 / m16n8k32) packs 4 values per 32-bit register, same
1016 // as s8/u8, but the accumulator is f16 or f32 (not integer).
1017 kFactor = 16;
1018 multiplicandFragType = i32Ty;
1019 expectedResult.push_back(f16x2x2StructTy);
1020 expectedResult.push_back(f32x4StructTy);
1021 break;
1022 case MMATypes::s4:
1023 case MMATypes::u4:
1024 kFactor = 32;
1025 break;
1026 case MMATypes::b1:
1027 kFactor = 128;
1028 break;
1029 case MMATypes::s8:
1030 case MMATypes::u8:
1031 kFactor = 16;
1032 break;
1033 default:
1034 return emitError("invalid shape or multiplicand type: ")
1035 << getMultiplicandAPtxType().value();
1036 }
1037
1038 if (isIntegerPtxType(getMultiplicandAPtxType().value())) {
1039 expectedResult.push_back(s32x4StructTy);
1040 expectedC.emplace_back(4, i32Ty);
1041 multiplicandFragType = i32Ty;
1042 } else {
1043 expectedC.emplace_back(2, f16x2Ty);
1044 expectedC.emplace_back(4, f32Ty);
1045 }
1046
1047 int64_t unitA = (mmaShape[0] / 8) * (mmaShape[2] / kFactor);
1048 int64_t unitB = (mmaShape[1] / 8) * (mmaShape[2] / kFactor);
1049 expectedA.emplace_back(unitA, multiplicandFragType);
1050 expectedB.emplace_back(unitB, multiplicandFragType);
1051 allowedShapes.push_back({16, 8, kFactor});
1052 allowedShapes.push_back({16, 8, kFactor * 2});
1053
1054 if (resultPtxType() != accumPtxType())
1055 return emitOpError("ctype does not match dtype");
1056 }
1057
1058 // In the M=8 case, there is only 1 possible case per data type.
1059 if (mmaShape[0] == 8) {
1060 if (*getMultiplicandAPtxType() == MMATypes::f16) {
1061 expectedA.emplace_back(2, f16x2Ty);
1062 expectedB.emplace_back(2, f16x2Ty);
1063 expectedResult.push_back(f16x2x4StructTy);
1064 expectedResult.push_back(f32x8StructTy);
1065 expectedC.emplace_back(4, f16x2Ty);
1066 expectedC.emplace_back(8, f32Ty);
1067 allowedShapes.push_back({8, 8, 4});
1068 }
1069 if (*getMultiplicandAPtxType() == MMATypes::f64) {
1070 Type f64Ty = Float64Type::get(context);
1071 expectedA.emplace_back(1, f64Ty);
1072 expectedB.emplace_back(1, f64Ty);
1073 expectedC.emplace_back(2, f64Ty);
1074 expectedResult.emplace_back(LLVM::LLVMStructType::getLiteral(
1075 context, SmallVector<Type>(2, f64Ty)));
1076 allowedShapes.push_back({8, 8, 4});
1077 }
1078 if (isIntegerPtxType(getMultiplicandAPtxType().value())) {
1079 expectedA.push_back({i32Ty});
1080 expectedB.push_back({i32Ty});
1081 expectedC.push_back({i32Ty, i32Ty});
1082 expectedResult.push_back(s32x2StructTy);
1083 if (isInt4PtxType(getMultiplicandAPtxType().value()))
1084 allowedShapes.push_back({8, 8, 32});
1085 if (isInt8PtxType(getMultiplicandAPtxType().value()))
1086 allowedShapes.push_back({8, 8, 16});
1087 if (getMultiplicandAPtxType().value() == MMATypes::b1)
1088 allowedShapes.push_back({8, 8, 128});
1089 }
1090 }
1091
1092 std::string errorMessage;
1093 llvm::raw_string_ostream errorStream(errorMessage);
1094
1095 // Check that we matched an existing shape/dtype combination.
1096 if (expectedA.empty() || expectedB.empty() || expectedC.empty() ||
1097 !llvm::is_contained(allowedShapes, mmaShape)) {
1098 errorStream << "unimplemented variant for MMA shape <";
1099 llvm::interleaveComma(mmaShape, errorStream);
1100 errorStream << ">";
1101 return emitOpError(errorMessage);
1102 }
1103
1104 // Verify the operand types for segments of A, B, and C operands.
1105 std::array<StringRef, 3> operandNames{"A", "B", "C"};
1106 for (const auto &iter : llvm::enumerate(
1107 SmallVector<AllowedTypes, 3>{expectedA, expectedB, expectedC})) {
1108 auto spec = this->getODSOperandIndexAndLength(iter.index());
1109 SmallVector<Type, 4> operandTySeg(operand_type_begin() + spec.first,
1110 operand_type_begin() + spec.first +
1111 spec.second);
1112 bool match = llvm::is_contained(iter.value(), operandTySeg);
1113
1114 if (!match) {
1115 errorStream << "Could not match types for the "
1116 << operandNames[iter.index()]
1117 << " operands; expected one of ";
1118 for (const auto &x : iter.value()) {
1119 errorStream << x.size() << "x" << x[0] << " ";
1120 }
1121 errorStream << "but got ";
1122 llvm::interleaveComma(operandTySeg, errorStream);
1123 return emitOpError(errorMessage);
1124 }
1125 }
1126
1127 // Check the result type
1128 if (!llvm::any_of(expectedResult, [&](Type expectedResultType) {
1129 return expectedResultType == getResult().getType();
1130 })) {
1131 errorStream
1132 << "Could not match allowed types for the result; expected one of ";
1133 llvm::interleaveComma(expectedResult, errorStream);
1134 errorStream << " but got " << getResult().getType();
1135 return emitOpError(errorMessage);
1136 }
1137
1138 // Ensure that binary MMA variants have a b1 MMA operation defined.
1139 if (getMultiplicandAPtxType() == MMATypes::b1 && !getB1Op()) {
1140 return emitOpError("op requires " + getB1OpAttrName().strref() +
1141 " attribute");
1142 }
1143
1144 // Ensure int4/int8 MMA variants specify the accum overflow behavior
1145 // attribute.
1146 if (isInt4PtxType(*getMultiplicandAPtxType()) ||
1147 isInt8PtxType(*getMultiplicandAPtxType())) {
1148 if (!getIntOverflowBehavior())
1149 return emitOpError("op requires " +
1150 getIntOverflowBehaviorAttrName().strref() +
1151 " attribute");
1152 }
1153
1154 // Validate layout combinations. According to the operation description, most
1155 // MMA operations require layoutA=row and layoutB=col. Only m8n8k4 with f16
1156 // can use other layout combinations.
1157 bool isM8N8K4_F16 =
1158 (mmaShape[0] == 8 && mmaShape[1] == 8 && mmaShape[2] == 4 &&
1159 getMultiplicandAPtxType() == MMATypes::f16);
1160
1161 if (!isM8N8K4_F16) {
1162 // For all other shapes/types, layoutA must be row and layoutB must be col
1163 if (getLayoutA() != MMALayout::row || getLayoutB() != MMALayout::col) {
1164 return emitOpError("requires layoutA = #nvvm.mma_layout<row> and "
1165 "layoutB = #nvvm.mma_layout<col> for shape <")
1166 << mmaShape[0] << ", " << mmaShape[1] << ", " << mmaShape[2]
1167 << "> with element types " << *getMultiplicandAPtxType() << " and "
1168 << *getMultiplicandBPtxType()
1169 << ". Only m8n8k4 with f16 supports other layouts.";
1170 }
1171 }
1172
1173 return success();
1174}
1175
1176MMATypes MmaSpOp::accumPtxType() {
1177 std::optional<mlir::NVVM::MMATypes> val = MmaOp::inferOperandMMAType(
1178 getODSOperands(2).getTypes().front(), /*isAccumulator=*/true);
1179 assert(val.has_value() && "accumulator PTX type should always be inferrable");
1180 return val.value();
1181}
1182
1183MMATypes MmaSpOp::resultPtxType() {
1184 std::optional<mlir::NVVM::MMATypes> val =
1185 MmaOp::inferOperandMMAType(getResult().getType(), /*isAccumulator=*/true);
1186 assert(val.has_value() && "result PTX type should always be inferrable");
1187 return val.value();
1188}
1189
1191MmaSpOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
1192 llvm::IRBuilderBase &builder) {
1193 auto thisOp = cast<NVVM::MmaSpOp>(op);
1194
1195 // Get operands
1197 for (mlir::Value v : thisOp.getOperands())
1198 args.push_back(mt.lookupValue(v));
1199
1200 // Get intrinsic ID using the existing getIntrinsicID method
1201 auto intId = MmaSpOp::getIntrinsicID(
1202 thisOp.getShape().getM(), thisOp.getShape().getN(),
1203 thisOp.getShape().getK(), thisOp.getIntOverflowBehavior(),
1204 thisOp.getOrderedMetadata(), thisOp.getKind(),
1205 *thisOp.getMultiplicandAPtxType(), *thisOp.getMultiplicandBPtxType(),
1206 thisOp.accumPtxType(), thisOp.resultPtxType());
1207
1208 return {intId, args};
1209}
1210
1211void MmaSpOp::print(OpAsmPrinter &p) {
1212 SmallVector<Type, 4> regTypes;
1213 struct MMAOperandFragment {
1214 StringRef operandName;
1215 StringRef ptxTypeAttr;
1216 SmallVector<Value, 4> regs;
1217 explicit MMAOperandFragment(StringRef name, StringRef ptxTypeName)
1218 : operandName(name), ptxTypeAttr(ptxTypeName) {}
1219 };
1220
1221 std::array<MMAOperandFragment, 5> frags{
1222 MMAOperandFragment("A", getMultiplicandAPtxTypeAttrName()),
1223 MMAOperandFragment("B", getMultiplicandBPtxTypeAttrName()),
1224 MMAOperandFragment("C", ""), MMAOperandFragment("sparseMetadata", ""),
1225 MMAOperandFragment("selector", "")};
1226 SmallVector<StringRef, 4> ignoreAttrNames{
1227 mlir::NVVM::MmaSpOp::getOperandSegmentSizeAttr()};
1228
1229 // Handle variadic operands A, B, C
1230 for (unsigned fragIdx = 0; fragIdx < 3; fragIdx++) {
1231 auto &frag = frags[fragIdx];
1232 auto varOperandSpec = getODSOperandIndexAndLength(fragIdx);
1233 for (auto operandIdx = varOperandSpec.first;
1234 operandIdx < varOperandSpec.first + varOperandSpec.second;
1235 operandIdx++) {
1236 frag.regs.push_back(this->getOperand(operandIdx));
1237 if (operandIdx == varOperandSpec.first) {
1238 regTypes.push_back(this->getOperand(operandIdx).getType());
1239 }
1240 }
1241 std::optional<MMATypes> inferredType = MmaOp::inferOperandMMAType(
1242 regTypes.back(), /*isAccumulator=*/fragIdx >= 2);
1243 if (inferredType)
1244 ignoreAttrNames.push_back(frag.ptxTypeAttr);
1245 }
1246
1247 // Handle sparse metadata and selector (single operands)
1248 frags[3].regs.push_back(getSparseMetadata());
1249 frags[4].regs.push_back(getSparsitySelector());
1250
1251 auto printMmaSpOperand = [&](const MMAOperandFragment &frag) -> void {
1252 p << " " << frag.operandName;
1253 p << "[";
1254 p.printOperands(frag.regs);
1255 p << "]";
1256 };
1257
1258 for (const auto &frag : frags)
1259 printMmaSpOperand(frag);
1260
1261 p.printOptionalAttrDict((*this)->getAttrs(), ignoreAttrNames);
1262 p << " : ";
1263 p << "(";
1264 for (int i = 0; i < 3; ++i) {
1265 p << regTypes[i];
1266 if (i < 2)
1267 p << ", ";
1268 }
1269 p << ") -> " << getResult().getType();
1270}
1271
1272void MmaSpOp::build(
1273 OpBuilder &builder, OperationState &result, Type resultType,
1274 ValueRange operandA, ValueRange operandB, ValueRange operandC,
1275 Value sparseMetadata, Value sparsitySelector, ArrayRef<int64_t> shape,
1276 std::optional<MMAIntOverflow> intOverflow,
1277 std::optional<std::array<MMATypes, 2>> multiplicandPtxTypes) {
1278
1279 assert(shape.size() == 3 && "expected shape to have size 3 (m, n, k)");
1280 MLIRContext *ctx = builder.getContext();
1281 result.addAttribute(
1282 "shape", builder.getAttr<MMAShapeAttr>(shape[0], shape[1], shape[2]));
1283
1284 result.addOperands(operandA);
1285 result.addOperands(operandB);
1286 result.addOperands(operandC);
1287 result.addOperands(sparseMetadata);
1288 result.addOperands(sparsitySelector);
1289
1290 if (multiplicandPtxTypes) {
1291 result.addAttribute("multiplicandAPtxType",
1292 MMATypesAttr::get(ctx, (*multiplicandPtxTypes)[0]));
1293 result.addAttribute("multiplicandBPtxType",
1294 MMATypesAttr::get(ctx, (*multiplicandPtxTypes)[1]));
1295 } else {
1296 if (auto res = MmaOp::inferOperandMMAType(operandA[0].getType(), false))
1297 result.addAttribute("multiplicandAPtxType", MMATypesAttr::get(ctx, *res));
1298 if (auto res = MmaOp::inferOperandMMAType(operandB[0].getType(), false))
1299 result.addAttribute("multiplicandBPtxType", MMATypesAttr::get(ctx, *res));
1300 }
1301
1302 if (intOverflow.has_value())
1303 result.addAttribute("intOverflowBehavior",
1304 MMAIntOverflowAttr::get(ctx, *intOverflow));
1305
1306 result.addTypes(resultType);
1307 result.addAttribute(
1308 MmaSpOp::getOperandSegmentSizeAttr(),
1309 builder.getDenseI32ArrayAttr({static_cast<int32_t>(operandA.size()),
1310 static_cast<int32_t>(operandB.size()),
1311 static_cast<int32_t>(operandC.size()), 1,
1312 1})); // sparseMetadata and sparsitySelector
1313}
1314
1315ParseResult MmaSpOp::parse(OpAsmParser &parser, OperationState &result) {
1316 struct MMAOperandFragment {
1317 std::optional<MMATypes> elemtype;
1318 SmallVector<OpAsmParser::UnresolvedOperand, 4> regs;
1319 SmallVector<Type> regTypes;
1320 };
1321
1322 Builder &builder = parser.getBuilder();
1323 std::array<MMAOperandFragment, 6> frags; // A, B, C, sparseMetadata, selector
1324
1325 NamedAttrList namedAttributes;
1326
1327 // A helper to parse the operand segments.
1328 auto parseMmaSpOperand = [&](StringRef operandName,
1329 MMAOperandFragment &frag) -> LogicalResult {
1330 if (parser.parseKeyword(operandName).failed())
1331 return failure();
1332 if (parser
1333 .parseOperandList(frag.regs, OpAsmParser::Delimiter::OptionalSquare)
1334 .failed())
1335 return failure();
1336 return success();
1337 };
1338
1339 // Parse the operand segments.
1340 if (parseMmaSpOperand("A", frags[0]).failed())
1341 return failure();
1342 if (parseMmaSpOperand("B", frags[1]).failed())
1343 return failure();
1344 if (parseMmaSpOperand("C", frags[2]).failed())
1345 return failure();
1346 if (parseMmaSpOperand("sparseMetadata", frags[3]).failed())
1347 return failure();
1348 if (parseMmaSpOperand("selector", frags[4]).failed())
1349 return failure();
1350
1351 if (parser.parseOptionalAttrDict(namedAttributes).failed())
1352 return failure();
1353
1354 // Parse the type specification and resolve operands.
1355 SmallVector<Type, 3> operandTypes;
1356 if (failed(parser.parseColon()))
1357 return failure();
1358 if (failed(parser.parseLParen()))
1359 return failure();
1360 if (failed(parser.parseTypeList(operandTypes)))
1361 return failure();
1362 if (failed(parser.parseRParen()))
1363 return failure();
1364 if (operandTypes.size() != 3)
1365 return parser.emitError(
1366 parser.getNameLoc(),
1367 "expected one type for each operand segment but got " +
1368 Twine(operandTypes.size()) + " types");
1369 for (const auto &iter : llvm::enumerate(operandTypes)) {
1370 auto &frag = frags[iter.index()];
1371 frag.regTypes.resize(frag.regs.size(), iter.value());
1372 if (failed(parser.resolveOperands(frag.regs, frag.regTypes,
1373 parser.getNameLoc(), result.operands)))
1374 return failure();
1375 frag.elemtype =
1376 MmaOp::inferOperandMMAType(frag.regTypes[0],
1377 /*isAccumulator*/ iter.index() >= 2);
1378 }
1379
1380 Type resultType;
1381 if (parser.parseArrow() || parser.parseType(resultType))
1382 return failure();
1383 frags[5].elemtype =
1384 MmaOp::inferOperandMMAType(resultType, /*isAccumulator*/ true);
1385
1386 // Resolve sparse metadata and selector (assume i32 type)
1387 Type i32Type = builder.getIntegerType(32);
1388 if (parser
1389 .resolveOperands(frags[3].regs, i32Type, parser.getCurrentLocation(),
1390 result.operands)
1391 .failed())
1392 return failure();
1393 if (parser
1394 .resolveOperands(frags[4].regs, i32Type, parser.getCurrentLocation(),
1395 result.operands)
1396 .failed())
1397 return failure();
1398
1399 std::array<StringRef, 2> names{"multiplicandAPtxType",
1400 "multiplicandBPtxType"};
1401 for (unsigned idx = 0; idx < names.size(); idx++) {
1402 const auto &frag = frags[idx];
1403 std::optional<NamedAttribute> attr = namedAttributes.getNamed(names[idx]);
1404 if (!frag.elemtype.has_value() && !attr.has_value()) {
1405 return parser.emitError(
1406 parser.getNameLoc(),
1407 "attribute " + names[idx] +
1408 " is not provided explicitly and cannot be inferred");
1409 }
1410 if (!attr.has_value())
1411 result.addAttribute(
1412 names[idx], MMATypesAttr::get(parser.getContext(), *frag.elemtype));
1413 }
1414
1415 result.addTypes(resultType);
1416 if (!namedAttributes.empty())
1417 result.addAttributes(namedAttributes);
1418 result.addAttribute(MmaSpOp::getOperandSegmentSizeAttr(),
1419 builder.getDenseI32ArrayAttr({
1420 static_cast<int32_t>(frags[0].regs.size()),
1421 static_cast<int32_t>(frags[1].regs.size()),
1422 static_cast<int32_t>(frags[2].regs.size()),
1423 1, // sparseMetadata
1424 1 // sparsitySelector
1425 }));
1426 return success();
1427}
1428
1429LogicalResult MmaSpOp::verify() {
1430 MLIRContext *context = getContext();
1431 auto f16Ty = Float16Type::get(context);
1432 auto i32Ty = IntegerType::get(context, 32);
1433 auto f16x2Ty = VectorType::get(2, f16Ty);
1434 auto f32Ty = Float32Type::get(context);
1435 auto f16x2x4StructTy = LLVM::LLVMStructType::getLiteral(
1436 context, {f16x2Ty, f16x2Ty, f16x2Ty, f16x2Ty});
1437
1438 auto s32x4StructTy =
1439 LLVM::LLVMStructType::getLiteral(context, {i32Ty, i32Ty, i32Ty, i32Ty});
1440 auto f32x8StructTy =
1441 LLVM::LLVMStructType::getLiteral(context, SmallVector<Type>(8, f32Ty));
1442 auto f16x2x2StructTy =
1443 LLVM::LLVMStructType::getLiteral(context, {f16x2Ty, f16x2Ty});
1444 auto f32x4StructTy =
1445 LLVM::LLVMStructType::getLiteral(context, {f32Ty, f32Ty, f32Ty, f32Ty});
1446 auto s32x2StructTy =
1447 LLVM::LLVMStructType::getLiteral(context, {i32Ty, i32Ty});
1448
1449 std::array<int64_t, 3> mmaShape{getShapeAttr().getM(), getShapeAttr().getN(),
1450 getShapeAttr().getK()};
1451
1452 // These variables define the set of allowed data types for matrices A, B, C,
1453 // and result.
1454 using AllowedShapes = SmallVector<std::array<int64_t, 3>, 2>;
1455 using AllowedTypes = SmallVector<SmallVector<Type, 4>, 2>;
1456 AllowedShapes allowedShapes;
1457 AllowedTypes expectedA;
1458 AllowedTypes expectedB;
1459 AllowedTypes expectedC;
1460 SmallVector<Type> expectedResult;
1461
1462 // When M = 16, we just need to calculate the number of 8xk tiles, where
1463 // k is a factor that depends on the data type.
1464 if (mmaShape[0] == 16) {
1465 int64_t kFactor;
1466 Type multiplicandFragType;
1467 switch (*getMultiplicandAPtxType()) {
1468 case MMATypes::tf32:
1469 kFactor = 4;
1470 multiplicandFragType = i32Ty;
1471 expectedResult.push_back(LLVM::LLVMStructType::getLiteral(
1472 context, {f32Ty, f32Ty, f32Ty, f32Ty}));
1473 // Sparse MMA supports m16n8k8 and m16n8k16 for tf32
1474 allowedShapes.push_back({16, 8, 8});
1475 allowedShapes.push_back({16, 8, 16});
1476 break;
1477 case MMATypes::bf16:
1478 kFactor = 8;
1479 multiplicandFragType = i32Ty;
1480 expectedResult.push_back(LLVM::LLVMStructType::getLiteral(
1481 context, {f32Ty, f32Ty, f32Ty, f32Ty}));
1482 // Sparse MMA supports m16n8k16 and m16n8k32 for bf16
1483 allowedShapes.push_back({16, 8, 16});
1484 allowedShapes.push_back({16, 8, 32});
1485 break;
1486 case MMATypes::f16:
1487 kFactor = 8;
1488 multiplicandFragType = f16x2Ty;
1489 expectedResult.push_back(f16x2x2StructTy);
1490 expectedResult.push_back(f32x4StructTy);
1491 // Sparse MMA supports m16n8k16 and m16n8k32 for f16
1492 allowedShapes.push_back({16, 8, 16});
1493 allowedShapes.push_back({16, 8, 32});
1494 break;
1495 case MMATypes::s4:
1496 case MMATypes::u4:
1497 kFactor = 32;
1498 // Sparse MMA supports m16n8k64 and m16n8k128 for s4/u4
1499 allowedShapes.push_back({16, 8, 64});
1500 allowedShapes.push_back({16, 8, 128});
1501 break;
1502 case MMATypes::s8:
1503 case MMATypes::u8:
1504 kFactor = 16;
1505 // Sparse MMA supports m16n8k32 and m16n8k64 for s8/u8
1506 allowedShapes.push_back({16, 8, 32});
1507 allowedShapes.push_back({16, 8, 64});
1508 break;
1509 case MMATypes::e4m3:
1510 case MMATypes::e5m2:
1511 case MMATypes::e3m2:
1512 case MMATypes::e2m3:
1513 case MMATypes::e2m1:
1514 kFactor = 16;
1515 multiplicandFragType = i32Ty;
1516 expectedResult.push_back(f16x2x2StructTy);
1517 expectedResult.push_back(f32x4StructTy);
1518 // Sparse MMA supports m16n8k64 for FP8 types
1519 allowedShapes.push_back({16, 8, 64});
1520 break;
1521 default:
1522 return emitError("invalid shape or multiplicand type: ")
1523 << getMultiplicandAPtxType().value();
1524 }
1525
1526 if (isIntegerPtxType(getMultiplicandAPtxType().value())) {
1527 expectedResult.push_back(s32x4StructTy);
1528 expectedC.emplace_back(4, i32Ty);
1529 multiplicandFragType = i32Ty;
1530 } else if (*getMultiplicandAPtxType() >= MMATypes::e4m3 &&
1531 *getMultiplicandAPtxType() <= MMATypes::e2m1) {
1532 // FP8 types
1533 expectedC.emplace_back(2, f16x2Ty);
1534 expectedC.emplace_back(4, f32Ty);
1535 } else {
1536 expectedC.emplace_back(2, f16x2Ty);
1537 expectedC.emplace_back(4, f32Ty);
1538 }
1539
1540 // For sparse MMA, A operand is compressed (2:4 sparsity means half the
1541 // elements)
1542 int64_t unitA = (mmaShape[0] / 8) * (mmaShape[2] / kFactor) / 2;
1543 int64_t unitB = (mmaShape[1] / 8) * (mmaShape[2] / kFactor);
1544 expectedA.emplace_back(unitA, multiplicandFragType);
1545 expectedB.emplace_back(unitB, multiplicandFragType);
1546
1547 if (resultPtxType() != accumPtxType())
1548 return emitOpError("ctype does not match dtype");
1549 }
1550
1551 // In the M=8 case, there is only 1 possible case per data type.
1552 if (mmaShape[0] == 8) {
1553 if (*getMultiplicandAPtxType() == MMATypes::f16) {
1554 expectedA.emplace_back(2, f16x2Ty);
1555 expectedB.emplace_back(2, f16x2Ty);
1556 expectedResult.push_back(f16x2x4StructTy);
1557 expectedResult.push_back(f32x8StructTy);
1558 expectedC.emplace_back(4, f16x2Ty);
1559 expectedC.emplace_back(8, f32Ty);
1560 allowedShapes.push_back({8, 8, 4});
1561 }
1562 if (*getMultiplicandAPtxType() == MMATypes::f64) {
1563 Type f64Ty = Float64Type::get(context);
1564 expectedA.emplace_back(1, f64Ty);
1565 expectedB.emplace_back(1, f64Ty);
1566 expectedC.emplace_back(2, f64Ty);
1567 expectedResult.emplace_back(LLVM::LLVMStructType::getLiteral(
1568 context, SmallVector<Type>(2, f64Ty)));
1569 allowedShapes.push_back({8, 8, 4});
1570 }
1571 if (isIntegerPtxType(getMultiplicandAPtxType().value())) {
1572 expectedA.push_back({i32Ty});
1573 expectedB.push_back({i32Ty});
1574 expectedC.push_back({i32Ty, i32Ty});
1575 expectedResult.push_back(s32x2StructTy);
1576 if (isInt4PtxType(getMultiplicandAPtxType().value()))
1577 allowedShapes.push_back({8, 8, 32});
1578 if (isInt8PtxType(getMultiplicandAPtxType().value()))
1579 allowedShapes.push_back({8, 8, 16});
1580 }
1581 }
1582
1583 std::string errorMessage;
1584 llvm::raw_string_ostream errorStream(errorMessage);
1585
1586 // Check that we matched an existing shape/dtype combination.
1587 if (expectedA.empty() || expectedB.empty() || expectedC.empty() ||
1588 !llvm::is_contained(allowedShapes, mmaShape)) {
1589 errorStream << "unimplemented variant for MMA shape <";
1590 llvm::interleaveComma(mmaShape, errorStream);
1591 errorStream << ">";
1592 return emitOpError(errorMessage);
1593 }
1594
1595 // Verify the operand types for segments of A, B, and C operands.
1596 std::array<StringRef, 3> operandNames{"A", "B", "C"};
1597 for (const auto &iter : llvm::enumerate(
1598 SmallVector<AllowedTypes, 3>{expectedA, expectedB, expectedC})) {
1599 auto spec = this->getODSOperandIndexAndLength(iter.index());
1600 SmallVector<Type, 4> operandTySeg(operand_type_begin() + spec.first,
1601 operand_type_begin() + spec.first +
1602 spec.second);
1603 bool match = llvm::is_contained(iter.value(), operandTySeg);
1604
1605 if (!match) {
1606 errorStream << "Could not match types for the "
1607 << operandNames[iter.index()]
1608 << " operands; expected one of ";
1609 for (const auto &x : iter.value()) {
1610 errorStream << x.size() << "x" << x[0] << " ";
1611 }
1612 errorStream << "but got ";
1613 llvm::interleaveComma(operandTySeg, errorStream);
1614 return emitOpError(errorMessage);
1615 }
1616 }
1617
1618 // Check the result type
1619 if (!llvm::any_of(expectedResult, [&](Type expectedResultType) {
1620 return expectedResultType == getResult().getType();
1621 })) {
1622 errorStream
1623 << "Could not match allowed types for the result; expected one of ";
1624 llvm::interleaveComma(expectedResult, errorStream);
1625 errorStream << " but got " << getResult().getType();
1626 return emitOpError(errorMessage);
1627 }
1628
1629 // Ensure int4/int8 MMA variants specify the accum overflow behavior
1630 // attribute.
1631 if (isInt4PtxType(*getMultiplicandAPtxType()) ||
1632 isInt8PtxType(*getMultiplicandAPtxType())) {
1633 if (!getIntOverflowBehavior())
1634 return emitOpError("op requires " +
1635 getIntOverflowBehaviorAttrName().strref() +
1636 " attribute");
1637 }
1638
1639 // Validate sparse metadata type (should be i32)
1640 if (!getSparseMetadata().getType().isInteger(32)) {
1641 return emitOpError() << "sparse metadata must be i32 type";
1642 }
1643
1644 // Validate sparsity selector type (should be i32)
1645 if (!getSparsitySelector().getType().isInteger(32)) {
1646 return emitOpError() << "sparsity selector must be i32 type";
1647 }
1648
1649 return success();
1650}
1651
1652//===----------------------------------------------------------------------===//
1653// MMA Block Scale Operations - Shared Helpers
1654//===----------------------------------------------------------------------===//
1655
1656namespace {
1657// Shared structure for MMA operand fragments (A, B, C)
1658struct MMAOperandFragment {
1659 StringRef operandName;
1660 StringRef ptxTypeAttr;
1661 SmallVector<Value, 4> regs;
1662 explicit MMAOperandFragment(StringRef name, StringRef ptxTypeName)
1663 : operandName(name), ptxTypeAttr(ptxTypeName) {}
1664};
1665} // namespace
1666
1667// Helper to print operand list in the format: name[operands]
1668static void printOperandList(OpAsmPrinter &p, StringRef name,
1669 ArrayRef<Value> operands) {
1670 p << " " << name << "[";
1671 p.printOperands(operands);
1672 p << "]";
1673}
1674
1675// Helper to parse operand list in the format: name[operands]
1676static LogicalResult
1677parseMmaOperand(OpAsmParser &parser, StringRef operandName,
1679 if (parser.parseKeyword(operandName).failed())
1680 return failure();
1682 .failed())
1683 return failure();
1684 return success();
1685}
1686
1687// Helper to process operand fragments and determine which attributes can be
1688// inferred
1689template <typename Op>
1690static void
1691processOperandFragments(Op &op, std::array<MMAOperandFragment, 3> &frags,
1692 SmallVectorImpl<Type> &regTypes,
1693 SmallVectorImpl<StringRef> &ignoreAttrNames) {
1694 for (unsigned fragIdx = 0; fragIdx < frags.size(); fragIdx++) {
1695 auto &frag = frags[fragIdx];
1696 auto varOperandSpec = op.getODSOperandIndexAndLength(fragIdx);
1697 for (auto operandIdx = varOperandSpec.first;
1698 operandIdx < varOperandSpec.first + varOperandSpec.second;
1699 operandIdx++) {
1700 frag.regs.push_back(op.getOperand(operandIdx));
1701 if (fragIdx == 0 && operandIdx == varOperandSpec.first) {
1702 regTypes.push_back(op.getOperand(operandIdx).getType());
1703 }
1704 }
1705 if (fragIdx < 2) {
1706 regTypes.push_back(frag.regs[0].getType());
1707 }
1708 std::optional<MMATypes> inferredType =
1709 MmaOp::inferOperandMMAType(regTypes.back(),
1710 /*isAccumulator=*/fragIdx >= 2);
1711 if (inferredType)
1712 ignoreAttrNames.push_back(frag.ptxTypeAttr);
1713 }
1714}
1715
1716// Helper to parse type signature: (A_type, B_type, C_type)
1717static LogicalResult
1719 SmallVectorImpl<Type> &operandTypes) {
1720 if (parser.parseColon().failed() || parser.parseLParen().failed())
1721 return failure();
1722
1723 auto typeParser = [&]() {
1724 Type ty;
1725 if (parser.parseType(ty).failed())
1726 return failure();
1727 operandTypes.push_back(ty);
1728 return success();
1729 };
1730 if (parser.parseCommaSeparatedList(typeParser))
1731 return failure();
1732
1733 if (operandTypes.size() != 3)
1734 return parser.emitError(parser.getCurrentLocation(),
1735 "expected exactly 3 types");
1736
1737 return parser.parseRParen();
1738}
1739
1740// Helper to infer and set multiplicand PTX type attributes
1741static void
1743 const SmallVectorImpl<Type> &operandTypes) {
1744 if (!attrs.get("multiplicandAPtxType")) {
1745 if (auto inferredType =
1746 MmaOp::inferOperandMMAType(operandTypes[0], false)) {
1747 attrs.set("multiplicandAPtxType", MMATypesAttr::get(ctx, *inferredType));
1748 }
1749 }
1750 if (!attrs.get("multiplicandBPtxType")) {
1751 if (auto inferredType =
1752 MmaOp::inferOperandMMAType(operandTypes[1], false)) {
1753 attrs.set("multiplicandBPtxType", MMATypesAttr::get(ctx, *inferredType));
1754 }
1755 }
1756}
1757
1758// Helper to add common block scale properties
1759template <typename OpType>
1762 ScaleVecSize scaleVecSize,
1763 BlockScaleFormat blockScaleFormat,
1764 MMABlockScaleKind kind) {
1765 MLIRContext *ctx = builder.getContext();
1766 auto &properties = result.getOrAddProperties<typename OpType::Properties>();
1767 properties.setShape(
1768 builder.getAttr<MMAShapeAttr>(shape[0], shape[1], shape[2]));
1769 properties.setScaleVecSize(ScaleVecSizeAttr::get(ctx, scaleVecSize));
1770 properties.setBlockScaleFormat(
1771 BlockScaleFormatAttr::get(ctx, blockScaleFormat));
1772 properties.setKind(MMABlockScaleKindAttr::get(ctx, kind));
1773}
1774
1775// Helper to infer and add multiplicand PTX types to builder
1778 ValueRange operandB,
1779 std::optional<std::array<MMATypes, 2>> multiplicandPtxTypes) {
1780 if (multiplicandPtxTypes) {
1781 result.addAttribute("multiplicandAPtxType",
1782 MMATypesAttr::get(ctx, (*multiplicandPtxTypes)[0]));
1783 result.addAttribute("multiplicandBPtxType",
1784 MMATypesAttr::get(ctx, (*multiplicandPtxTypes)[1]));
1785 } else {
1786 if (auto res = MmaOp::inferOperandMMAType(operandA[0].getType(), false))
1787 result.addAttribute("multiplicandAPtxType", MMATypesAttr::get(ctx, *res));
1788 if (auto res = MmaOp::inferOperandMMAType(operandB[0].getType(), false))
1789 result.addAttribute("multiplicandBPtxType", MMATypesAttr::get(ctx, *res));
1790 }
1791}
1792
1793// Template helper for common accumPtxType/resultPtxType implementation
1794template <typename OpTy>
1795static MMATypes inferPtxTypeFromResult(OpTy op) {
1796 return *MmaOp::inferOperandMMAType(
1797 cast<LLVM::LLVMStructType>(op.getRes().getType()).getBody()[0],
1798 /*isAccumulator=*/true);
1799}
1800
1801//===----------------------------------------------------------------------===//
1802// MmaBlockScaleOp
1803//===----------------------------------------------------------------------===//
1804
1805void MmaBlockScaleOp::print(OpAsmPrinter &p) {
1806 SmallVector<Type, 4> regTypes;
1807 std::array<MMAOperandFragment, 3> frags{
1808 MMAOperandFragment("A", getMultiplicandAPtxTypeAttrName()),
1809 MMAOperandFragment("B", getMultiplicandBPtxTypeAttrName()),
1810 MMAOperandFragment("C", "")};
1811 SmallVector<StringRef, 4> ignoreAttrNames{
1812 mlir::NVVM::MmaBlockScaleOp::getOperandSegmentSizeAttr()};
1813
1814 processOperandFragments(*this, frags, regTypes, ignoreAttrNames);
1815
1816 // Print A, B, C operands
1817 for (const auto &frag : frags)
1818 printOperandList(p, frag.operandName, frag.regs);
1819
1820 // Print scale operands
1821 printOperandList(p, "scaleA",
1822 {getScaleAData(), getByteIdA(), getThreadIdA()});
1823 printOperandList(p, "scaleB",
1824 {getScaleBData(), getByteIdB(), getThreadIdB()});
1825
1826 p.printOptionalAttrDict(this->getOperation()->getAttrs(), ignoreAttrNames);
1827
1828 // Print type signature
1829 p << " : (";
1830 llvm::interleaveComma(SmallVector<Type, 3>{frags[0].regs[0].getType(),
1831 frags[1].regs[0].getType(),
1832 frags[2].regs[0].getType()},
1833 p);
1834 p << ")";
1835 p.printArrowTypeList(TypeRange{this->getRes().getType()});
1836}
1837
1838ParseResult MmaBlockScaleOp::parse(OpAsmParser &parser,
1840 struct LocalOperandFragment {
1841 std::optional<MMATypes> elemtype;
1842 SmallVector<OpAsmParser::UnresolvedOperand, 4> regs;
1843 };
1844
1845 Builder &builder = parser.getBuilder();
1846 std::array<LocalOperandFragment, 3> frags;
1847 NamedAttrList namedAttributes;
1848
1849 // Parse A[...] B[...] C[...]
1850 if (parseMmaOperand(parser, "A", frags[0].regs).failed() ||
1851 parseMmaOperand(parser, "B", frags[1].regs).failed() ||
1852 parseMmaOperand(parser, "C", frags[2].regs).failed())
1853 return failure();
1854
1855 // Parse scale operands: scaleA[...] scaleB[...]
1856 SmallVector<OpAsmParser::UnresolvedOperand, 3> scaleAOperands, scaleBOperands;
1857 if (parseMmaOperand(parser, "scaleA", scaleAOperands).failed() ||
1858 parseMmaOperand(parser, "scaleB", scaleBOperands).failed())
1859 return failure();
1860
1861 if (parser.parseOptionalAttrDict(namedAttributes).failed())
1862 return failure();
1863
1864 // Parse type signature
1865 SmallVector<Type, 3> operandTypes;
1866 if (parseMmaTypeSignature(parser, operandTypes).failed())
1867 return failure();
1868
1869 // Parse result type
1870 SmallVector<Type, 1> resultTypes;
1871 if (parser.parseArrowTypeList(resultTypes).failed())
1872 return failure();
1873
1874 // Infer element types and resolve operands
1875 for (const auto &[idx, frag] : llvm::enumerate(frags)) {
1876 frag.elemtype = MmaOp::inferOperandMMAType(operandTypes[idx],
1877 /*isAccumulator=*/idx >= 2);
1878 if (parser
1879 .resolveOperands(frag.regs, operandTypes[idx], parser.getNameLoc(),
1880 result.operands)
1881 .failed())
1882 return failure();
1883 }
1884
1885 // Resolve scale operands
1886 SmallVector<Type, 3> scaleTypes = {builder.getI32Type(), builder.getI16Type(),
1887 builder.getI16Type()};
1888 if (parser
1889 .resolveOperands(scaleAOperands, scaleTypes, parser.getNameLoc(),
1890 result.operands)
1891 .failed() ||
1892 parser
1893 .resolveOperands(scaleBOperands, scaleTypes, parser.getNameLoc(),
1894 result.operands)
1895 .failed())
1896 return failure();
1897
1898 // Add attributes
1899 result.addAttributes(namedAttributes);
1900 inferAndSetMultiplicandTypes(parser.getContext(), result.attributes,
1901 operandTypes);
1902
1903 result.addTypes(resultTypes);
1904 result.addAttribute(MmaBlockScaleOp::getOperandSegmentSizeAttr(),
1905 builder.getDenseI32ArrayAttr({
1906 static_cast<int32_t>(frags[0].regs.size()),
1907 static_cast<int32_t>(frags[1].regs.size()),
1908 static_cast<int32_t>(frags[2].regs.size()),
1909 1, // scaleAData
1910 1, // byteIdA
1911 1, // threadIdA
1912 1, // scaleBData
1913 1, // byteIdB
1914 1 // threadIdB
1915 }));
1916 return success();
1917}
1918
1919void MmaBlockScaleOp::build(
1920 OpBuilder &builder, OperationState &result, Type resultType,
1921 ValueRange operandA, ValueRange operandB, ValueRange operandC,
1922 Value scaleAData, Value byteIdA, Value threadIdA, Value scaleBData,
1923 Value byteIdB, Value threadIdB, ArrayRef<int64_t> shape,
1924 std::optional<std::array<MMATypes, 2>> multiplicandPtxTypes,
1925 ScaleVecSize scaleVecSize, BlockScaleFormat blockScaleFormat,
1926 MMABlockScaleKind kind) {
1927 assert(shape.size() == 3 && "expected shape to have size 3 (m, n, k)");
1928
1930 blockScaleFormat, kind);
1931
1932 result.addOperands(operandA);
1933 result.addOperands(operandB);
1934 result.addOperands(operandC);
1935 result.addOperands(
1936 {scaleAData, byteIdA, threadIdA, scaleBData, byteIdB, threadIdB});
1937
1938 addInferredMultiplicandTypes(builder.getContext(), result, operandA, operandB,
1939 multiplicandPtxTypes);
1940
1941 result.addTypes(resultType);
1942 result.addAttribute(MmaBlockScaleOp::getOperandSegmentSizeAttr(),
1943 builder.getDenseI32ArrayAttr({
1944 static_cast<int32_t>(operandA.size()),
1945 static_cast<int32_t>(operandB.size()),
1946 static_cast<int32_t>(operandC.size()),
1947 1, // scaleAData
1948 1, // byteIdA
1949 1, // threadIdA
1950 1, // scaleBData
1951 1, // byteIdB
1952 1 // threadIdB
1953 }));
1954}
1955
1956NVVM::IDArgPair MmaBlockScaleOp::getIntrinsicIDAndArgs(
1957 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
1958 auto curOp = cast<NVVM::MmaBlockScaleOp>(op);
1959
1961 // Add A, B, C operands
1962 for (Value operand : curOp.getOperandA())
1963 args.push_back(mt.lookupValue(operand));
1964 for (Value operand : curOp.getOperandB())
1965 args.push_back(mt.lookupValue(operand));
1966 for (Value operand : curOp.getOperandC())
1967 args.push_back(mt.lookupValue(operand));
1968
1969 // Add scale operands
1970 args.push_back(mt.lookupValue(curOp.getScaleAData()));
1971 args.push_back(mt.lookupValue(curOp.getByteIdA()));
1972 args.push_back(mt.lookupValue(curOp.getThreadIdA()));
1973 args.push_back(mt.lookupValue(curOp.getScaleBData()));
1974 args.push_back(mt.lookupValue(curOp.getByteIdB()));
1975 args.push_back(mt.lookupValue(curOp.getThreadIdB()));
1976
1977 unsigned intId = MmaBlockScaleOp::getIntrinsicID(
1978 curOp.getShape().getM(), curOp.getShape().getN(), curOp.getShape().getK(),
1979 *curOp.getMultiplicandAPtxType(), *curOp.getMultiplicandBPtxType(),
1980 inferPtxTypeFromResult(curOp), curOp.getScaleVecSize(),
1981 curOp.getBlockScaleFormat(), curOp.getKind());
1982
1983 return {intId, args};
1984}
1985
1986LogicalResult MmaBlockScaleOp::verify() {
1987 LogicalResult result = success();
1988 int m = getShape().getM();
1989 int n = getShape().getN();
1990 int k = getShape().getK();
1991
1992 if (m == 16 && n == 8 && k == 64) {
1993 if (getMultiplicandAPtxType() != NVVM::MMATypes::e2m1 ||
1994 getMultiplicandBPtxType() != NVVM::MMATypes::e2m1)
1996 "unsupported MMATypes attribute for mma.m16n8k64.(mxf4nvf4|mxf4)");
1997 if (getKind() == NVVM::MMABlockScaleKind::MXF4) {
1998 if (getScaleVecSize() != NVVM::ScaleVecSize::X2)
2000 "unsupported ScaleVecSize attribute for mma.m16n8k64.mxf4");
2001 if (getBlockScaleFormat() != NVVM::BlockScaleFormat::UE8M0)
2003 "unsupported BlockScaleFormat attribute for mma.m16n8k64.mxf4");
2004 } else if (getKind() == NVVM::MMABlockScaleKind::MXF4NVF4) {
2005 if (!((getScaleVecSize() == NVVM::ScaleVecSize::X2 &&
2006 getBlockScaleFormat() == NVVM::BlockScaleFormat::UE8M0) ||
2007 (getScaleVecSize() == NVVM::ScaleVecSize::X4 &&
2008 (getBlockScaleFormat() == NVVM::BlockScaleFormat::UE4M3 ||
2009 getBlockScaleFormat() == NVVM::BlockScaleFormat::UE8M0))))
2010 result = emitOpError("unsupported ScaleVecSize and BlockScaleFormat "
2011 "attributes for mma.m16n8k64.mxf4nvf4");
2012 } else {
2013 result = emitOpError("unsupported Kind attribute for mma.m16n8k64");
2014 }
2015 } else if (m == 16 && n == 8 && k == 32) {
2016 if (!(getKind() == NVVM::MMABlockScaleKind::MXF8F6F4 &&
2017 getScaleVecSize() == NVVM::ScaleVecSize::X1 &&
2018 getBlockScaleFormat() == NVVM::BlockScaleFormat::UE8M0))
2019 result =
2020 emitOpError("unsupported Kind, ScaleVecSize and BlockScaleFormat "
2021 "attributes for mma.m16n8k32");
2022 } else {
2023 result = emitOpError("unsupported Geom for mma with block scaling");
2024 }
2025 return result;
2026}
2027
2028//===----------------------------------------------------------------------===//
2029// MmaSpBlockScaleOp
2030//===----------------------------------------------------------------------===//
2031
2032void MmaSpBlockScaleOp::print(OpAsmPrinter &p) {
2033 SmallVector<Type, 4> regTypes;
2034 std::array<MMAOperandFragment, 3> frags{
2035 MMAOperandFragment("A", getMultiplicandAPtxTypeAttrName()),
2036 MMAOperandFragment("B", getMultiplicandBPtxTypeAttrName()),
2037 MMAOperandFragment("C", "")};
2038 SmallVector<StringRef, 4> ignoreAttrNames{
2039 mlir::NVVM::MmaSpBlockScaleOp::getOperandSegmentSizeAttr()};
2040
2041 processOperandFragments(*this, frags, regTypes, ignoreAttrNames);
2042
2043 // Print A, B, C operands
2044 for (const auto &frag : frags)
2045 printOperandList(p, frag.operandName, frag.regs);
2046
2047 // Print sparse-specific operands
2048 printOperandList(p, "sparseMetadata", {getSparseMetadata()});
2049 printOperandList(p, "selector", {getSparsitySelector()});
2050
2051 // Print scale operands
2052 printOperandList(p, "scaleA",
2053 {getScaleAData(), getByteIdA(), getThreadIdA()});
2054 printOperandList(p, "scaleB",
2055 {getScaleBData(), getByteIdB(), getThreadIdB()});
2056
2057 p.printOptionalAttrDict(this->getOperation()->getAttrs(), ignoreAttrNames);
2058
2059 // Print type signature
2060 p << " : (";
2061 llvm::interleaveComma(SmallVector<Type, 3>{frags[0].regs[0].getType(),
2062 frags[1].regs[0].getType(),
2063 frags[2].regs[0].getType()},
2064 p);
2065 p << ")";
2066 p.printArrowTypeList(TypeRange{this->getRes().getType()});
2067}
2068
2069ParseResult MmaSpBlockScaleOp::parse(OpAsmParser &parser,
2071 struct LocalOperandFragment {
2072 std::optional<MMATypes> elemtype;
2073 SmallVector<OpAsmParser::UnresolvedOperand, 4> regs;
2074 };
2075
2076 Builder &builder = parser.getBuilder();
2077 std::array<LocalOperandFragment, 3> frags;
2078 NamedAttrList namedAttributes;
2079
2080 // Parse A[...] B[...] C[...]
2081 if (parseMmaOperand(parser, "A", frags[0].regs).failed() ||
2082 parseMmaOperand(parser, "B", frags[1].regs).failed() ||
2083 parseMmaOperand(parser, "C", frags[2].regs).failed())
2084 return failure();
2085
2086 // Parse sparse-specific operands
2088 selectorOperands;
2089 if (parseMmaOperand(parser, "sparseMetadata", metadataOperands).failed() ||
2090 parseMmaOperand(parser, "selector", selectorOperands).failed())
2091 return failure();
2092
2093 // Parse scale operands
2094 SmallVector<OpAsmParser::UnresolvedOperand, 3> scaleAOperands, scaleBOperands;
2095 if (parseMmaOperand(parser, "scaleA", scaleAOperands).failed() ||
2096 parseMmaOperand(parser, "scaleB", scaleBOperands).failed())
2097 return failure();
2098
2099 if (parser.parseOptionalAttrDict(namedAttributes).failed())
2100 return failure();
2101
2102 // Parse type signature
2103 SmallVector<Type, 3> operandTypes;
2104 if (parseMmaTypeSignature(parser, operandTypes).failed())
2105 return failure();
2106
2107 // Parse result type
2108 SmallVector<Type, 1> resultTypes;
2109 if (parser.parseArrowTypeList(resultTypes).failed())
2110 return failure();
2111
2112 // Infer element types and resolve operands
2113 for (const auto &[idx, frag] : llvm::enumerate(frags)) {
2114 frag.elemtype = MmaOp::inferOperandMMAType(operandTypes[idx],
2115 /*isAccumulator=*/idx >= 2);
2116 if (parser
2117 .resolveOperands(frag.regs, operandTypes[idx], parser.getNameLoc(),
2118 result.operands)
2119 .failed())
2120 return failure();
2121 }
2122
2123 // Resolve sparse metadata and selector
2124 Type i32Type = builder.getI32Type();
2125 if (parser
2126 .resolveOperands(metadataOperands, i32Type, parser.getNameLoc(),
2127 result.operands)
2128 .failed() ||
2129 parser
2130 .resolveOperands(selectorOperands, i32Type, parser.getNameLoc(),
2131 result.operands)
2132 .failed())
2133 return failure();
2134
2135 // Resolve scale operands
2136 SmallVector<Type, 3> scaleTypes = {i32Type, builder.getI16Type(),
2137 builder.getI16Type()};
2138 if (parser
2139 .resolveOperands(scaleAOperands, scaleTypes, parser.getNameLoc(),
2140 result.operands)
2141 .failed() ||
2142 parser
2143 .resolveOperands(scaleBOperands, scaleTypes, parser.getNameLoc(),
2144 result.operands)
2145 .failed())
2146 return failure();
2147
2148 // Add attributes
2149 result.addAttributes(namedAttributes);
2150 inferAndSetMultiplicandTypes(parser.getContext(), result.attributes,
2151 operandTypes);
2152
2153 // orderedMetadata is mandatory
2154 if (!result.attributes.get("orderedMetadata"))
2155 result.addAttribute("orderedMetadata", builder.getUnitAttr());
2156
2157 result.addTypes(resultTypes);
2158 result.addAttribute(MmaSpBlockScaleOp::getOperandSegmentSizeAttr(),
2159 builder.getDenseI32ArrayAttr({
2160 static_cast<int32_t>(frags[0].regs.size()),
2161 static_cast<int32_t>(frags[1].regs.size()),
2162 static_cast<int32_t>(frags[2].regs.size()),
2163 1, // sparseMetadata
2164 1, // sparsitySelector
2165 1, // scaleAData
2166 1, // byteIdA
2167 1, // threadIdA
2168 1, // scaleBData
2169 1, // byteIdB
2170 1 // threadIdB
2171 }));
2172 return success();
2173}
2174
2175void MmaSpBlockScaleOp::build(
2176 OpBuilder &builder, OperationState &result, Type resultType,
2177 ValueRange operandA, ValueRange operandB, ValueRange operandC,
2178 Value sparseMetadata, Value sparsitySelector, Value scaleAData,
2179 Value byteIdA, Value threadIdA, Value scaleBData, Value byteIdB,
2180 Value threadIdB, ArrayRef<int64_t> shape,
2181 std::optional<std::array<MMATypes, 2>> multiplicandPtxTypes,
2182 ScaleVecSize scaleVecSize, BlockScaleFormat blockScaleFormat,
2183 MMABlockScaleKind kind) {
2184 assert(shape.size() == 3 && "expected shape to have size 3 (m, n, k)");
2185
2187 builder, result, shape, scaleVecSize, blockScaleFormat, kind);
2188 result.addAttribute("orderedMetadata", builder.getUnitAttr());
2189
2190 result.addOperands(operandA);
2191 result.addOperands(operandB);
2192 result.addOperands(operandC);
2193 result.addOperands({sparseMetadata, sparsitySelector, scaleAData, byteIdA,
2194 threadIdA, scaleBData, byteIdB, threadIdB});
2195
2196 addInferredMultiplicandTypes(builder.getContext(), result, operandA, operandB,
2197 multiplicandPtxTypes);
2198
2199 result.addTypes(resultType);
2200 result.addAttribute(MmaSpBlockScaleOp::getOperandSegmentSizeAttr(),
2201 builder.getDenseI32ArrayAttr({
2202 static_cast<int32_t>(operandA.size()),
2203 static_cast<int32_t>(operandB.size()),
2204 static_cast<int32_t>(operandC.size()),
2205 1, // sparseMetadata
2206 1, // sparsitySelector
2207 1, // scaleAData
2208 1, // byteIdA
2209 1, // threadIdA
2210 1, // scaleBData
2211 1, // byteIdB
2212 1 // threadIdB
2213 }));
2214}
2215
2216NVVM::IDArgPair MmaSpBlockScaleOp::getIntrinsicIDAndArgs(
2217 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
2218 auto curOp = cast<NVVM::MmaSpBlockScaleOp>(op);
2219
2221 // Add A, B, C operands
2222 for (Value operand : curOp.getOperandA())
2223 args.push_back(mt.lookupValue(operand));
2224 for (Value operand : curOp.getOperandB())
2225 args.push_back(mt.lookupValue(operand));
2226 for (Value operand : curOp.getOperandC())
2227 args.push_back(mt.lookupValue(operand));
2228
2229 // Add sparse metadata and selector
2230 args.push_back(mt.lookupValue(curOp.getSparseMetadata()));
2231 args.push_back(mt.lookupValue(curOp.getSparsitySelector()));
2232
2233 // Add scale operands
2234 args.push_back(mt.lookupValue(curOp.getScaleAData()));
2235 args.push_back(mt.lookupValue(curOp.getByteIdA()));
2236 args.push_back(mt.lookupValue(curOp.getThreadIdA()));
2237 args.push_back(mt.lookupValue(curOp.getScaleBData()));
2238 args.push_back(mt.lookupValue(curOp.getByteIdB()));
2239 args.push_back(mt.lookupValue(curOp.getThreadIdB()));
2240
2241 unsigned intId = MmaSpBlockScaleOp::getIntrinsicID(
2242 curOp.getShape().getM(), curOp.getShape().getN(), curOp.getShape().getK(),
2243 *curOp.getMultiplicandAPtxType(), *curOp.getMultiplicandBPtxType(),
2244 inferPtxTypeFromResult(curOp), curOp.getScaleVecSize(),
2245 curOp.getBlockScaleFormat(), curOp.getKind());
2246
2247 return {intId, args};
2248}
2249
2250LogicalResult MmaSpBlockScaleOp::verify() {
2251 // Check that orderedMetadata is present
2252 if (!getOrderedMetadata()) {
2253 return emitOpError("'orderedMetadata' attribute is mandatory");
2254 }
2255
2256 LogicalResult result = success();
2257 int m = getShape().getM();
2258 int n = getShape().getN();
2259 int k = getShape().getK();
2260
2261 if (m == 16 && n == 8 && k == 128) {
2262 if (getMultiplicandAPtxType() != NVVM::MMATypes::e2m1 ||
2263 getMultiplicandBPtxType() != NVVM::MMATypes::e2m1)
2265 "unsupported MMATypes attribute for mma.m16n8k128.(mxf4nvf4|mxf4)");
2266 if (getKind() == NVVM::MMABlockScaleKind::MXF4) {
2267 if (getScaleVecSize() != NVVM::ScaleVecSize::X2)
2269 "unsupported ScaleVecSize attribute for mma.m16n8k128.mxf4");
2270 if (getBlockScaleFormat() != NVVM::BlockScaleFormat::UE8M0)
2272 "unsupported BlockScaleFormat attribute for mma.m16n8k128.mxf4");
2273 } else if (getKind() == NVVM::MMABlockScaleKind::MXF4NVF4) {
2274 if (!((getScaleVecSize() == NVVM::ScaleVecSize::X2 &&
2275 getBlockScaleFormat() == NVVM::BlockScaleFormat::UE8M0) ||
2276 (getScaleVecSize() == NVVM::ScaleVecSize::X4 &&
2277 (getBlockScaleFormat() == NVVM::BlockScaleFormat::UE4M3 ||
2278 getBlockScaleFormat() == NVVM::BlockScaleFormat::UE8M0))))
2279 result = emitOpError("unsupported ScaleVecSize and BlockScaleFormat "
2280 "attributes for mma.m16n8k128.mxf4nvf4");
2281 } else {
2282 result = emitOpError("unsupported Kind attribute for mma.m16n8k128");
2283 }
2284 } else if (m == 16 && n == 8 && k == 64) {
2285 if (!(getKind() == NVVM::MMABlockScaleKind::MXF8F6F4 &&
2286 getScaleVecSize() == NVVM::ScaleVecSize::X1 &&
2287 getBlockScaleFormat() == NVVM::BlockScaleFormat::UE8M0))
2288 result =
2289 emitOpError("unsupported Kind, ScaleVecSize and BlockScaleFormat "
2290 "attributes for mma.m16n8k64");
2291 } else {
2292 result = emitOpError("unsupported Geom for sparse mma with block scaling");
2293 }
2294 return result;
2295}
2296
2297LogicalResult ShflOp::verify() {
2298 auto returnStructType = llvm::dyn_cast<LLVM::LLVMStructType>(getType());
2299
2300 auto verifyTypeError = [&](Twine desc, Type expectedType,
2301 Type actualType) -> LogicalResult {
2302 return emitOpError("expected " + desc + " to be of type ")
2303 << expectedType << " but got " << actualType << " instead";
2304 };
2305
2306 if (returnStructType) {
2307 if (!getReturnValueAndIsValid())
2308 return emitOpError("\"return_value_and_is_valid\" attribute must be "
2309 "specified when the return type is a struct type");
2310
2311 if (returnStructType.getBody().size() != 2)
2312 return emitOpError("expected return type to be a two-element struct");
2313
2314 llvm::ArrayRef<Type> returnStruct = returnStructType.getBody();
2315 auto resultType = returnStruct[0];
2316 if (resultType != getVal().getType())
2317 return verifyTypeError("first element in the returned struct",
2318 getVal().getType(), resultType);
2319
2320 auto predicateType = returnStruct[1];
2321 if (!predicateType.isInteger(1))
2322 return verifyTypeError("second element in the returned struct",
2323 mlir::IntegerType::get(getContext(), 1),
2324 predicateType);
2325 } else {
2326 if (getReturnValueAndIsValid())
2327 return emitOpError("expected return type to be a two-element struct");
2328
2329 if (getType() != getVal().getType())
2330 return verifyTypeError("return type", getVal().getType(), getType());
2331 }
2332 return success();
2333}
2334
2335LogicalResult
2336ShflOp::inferReturnTypes(MLIRContext *context, std::optional<Location> location,
2337 ShflOp::Adaptor adaptor,
2338 SmallVectorImpl<Type> &inferredReturnTypes) {
2339 Type valType = adaptor.getVal().getType();
2340 if (adaptor.getReturnValueAndIsValid())
2341 inferredReturnTypes.push_back(LLVM::LLVMStructType::getLiteral(
2342 context, {valType, IntegerType::get(context, 1)}));
2343 else
2344 inferredReturnTypes.push_back(valType);
2345 return success();
2346}
2347
2348std::pair<mlir::Type, unsigned> NVVM::inferMMAType(NVVM::MMATypes type,
2349 NVVM::MMAFrag frag, int nRow,
2350 int nCol,
2351 MLIRContext *context) {
2352 unsigned numberElements = 0;
2353 Type elementType;
2354 OpBuilder builder(context);
2355 Type f16x2 = VectorType::get(2, builder.getF16Type());
2356 if (type == NVVM::MMATypes::f16) {
2357 elementType = f16x2;
2358 if (frag == NVVM::MMAFrag::a || frag == NVVM::MMAFrag::b)
2359 numberElements = 8;
2360 else
2361 numberElements = 4;
2362 } else if (type == NVVM::MMATypes::f32) {
2363 elementType = builder.getF32Type();
2364 numberElements = 8;
2365 } else if (type == NVVM::MMATypes::f64) {
2366 elementType = builder.getF64Type();
2367 if (frag == NVVM::MMAFrag::a || frag == NVVM::MMAFrag::b)
2368 numberElements = 1;
2369 else
2370 numberElements = 2;
2371 } else if (type == NVVM::MMATypes::tf32) {
2372 elementType = builder.getI32Type();
2373 numberElements = 4;
2374 } else if (type == NVVM::MMATypes::s8 || type == NVVM::MMATypes::u8) {
2375 elementType = builder.getI32Type();
2376 int parallelSize = 0;
2377 if (frag == NVVM::MMAFrag::a)
2378 parallelSize = nRow;
2379 if (frag == NVVM::MMAFrag::b)
2380 parallelSize = nCol;
2381
2382 // m == 16 && n == 16 && k == 16
2383 if (parallelSize == 16)
2384 numberElements = 2;
2385 // m == 8 && n == 32 && k == 16 or m == 32 && n == 8 && k == 16
2386 else if (parallelSize == 8)
2387 numberElements = 1;
2388 else if (parallelSize == 32)
2389 numberElements = 4;
2390 } else if (type == NVVM::MMATypes::s32) {
2391 elementType = builder.getI32Type();
2392 numberElements = 8;
2393 }
2394 assert(numberElements != 0 && elementType != nullptr);
2395 return std::make_pair(elementType, numberElements);
2396}
2397
2398static std::pair<mlir::Type, unsigned>
2399inferMMATypeFromMNK(NVVM::MMATypes type, NVVM::MMAFrag frag, int m, int n,
2400 int k, MLIRContext *context) {
2401 int nRow, nCol;
2402 if (frag == NVVM::MMAFrag::a) {
2403 nRow = m;
2404 nCol = k;
2405 } else if (frag == NVVM::MMAFrag::b) {
2406 nRow = k;
2407 nCol = n;
2408 } else {
2409 nRow = m;
2410 nCol = n;
2411 }
2412 assert(nRow && nCol);
2413 return inferMMAType(type, frag, nRow, nCol, context);
2414}
2415
2416LogicalResult NVVM::WMMALoadOp::verify() {
2417 unsigned addressSpace =
2418 llvm::cast<LLVM::LLVMPointerType>(getPtr().getType()).getAddressSpace();
2419 if (addressSpace != 0 && addressSpace != NVVMMemorySpace::Global &&
2420 addressSpace != NVVMMemorySpace::Shared)
2421 return emitOpError("expected source pointer in memory "
2422 "space 0, 1, 3");
2423
2424 if (NVVM::WMMALoadOp::getIntrinsicID(getM(), getN(), getK(), getLayout(),
2425 getEltype(), getFrag()) == 0)
2426 return emitOpError() << "invalid attribute combination";
2427 std::pair<Type, unsigned> typeInfo = inferMMATypeFromMNK(
2428 getEltype(), getFrag(), getM(), getN(), getK(), getContext());
2429 // Special case for f64 fragments
2430 Type f64Ty = Float64Type::get(getContext());
2431 if (typeInfo.first == f64Ty && typeInfo.second == 1) {
2432 if (getType() != f64Ty)
2433 return emitOpError("expected destination type to be f64");
2434 return success();
2435 }
2436 // Everything else is a struct
2437 Type dstType = LLVM::LLVMStructType::getLiteral(
2438 getContext(), SmallVector<Type, 8>(typeInfo.second, typeInfo.first));
2439 if (getType() != dstType)
2440 return emitOpError("expected destination type is a structure of ")
2441 << typeInfo.second << " elements of type " << typeInfo.first;
2442 return success();
2443}
2444
2445LogicalResult NVVM::WMMAStoreOp::verify() {
2446 unsigned addressSpace =
2447 llvm::cast<LLVM::LLVMPointerType>(getPtr().getType()).getAddressSpace();
2448 if (addressSpace != 0 && addressSpace != NVVMMemorySpace::Global &&
2449 addressSpace != NVVMMemorySpace::Shared)
2450 return emitOpError("expected operands to be a source pointer in memory "
2451 "space 0, 1, 3");
2452
2453 if (NVVM::WMMAStoreOp::getIntrinsicID(getM(), getN(), getK(), getLayout(),
2454 getEltype()) == 0)
2455 return emitOpError() << "invalid attribute combination";
2456 std::pair<Type, unsigned> typeInfo = inferMMATypeFromMNK(
2457 getEltype(), NVVM::MMAFrag::c, getM(), getN(), getK(), getContext());
2458 if (getArgs().size() != typeInfo.second)
2459 return emitOpError() << "expected " << typeInfo.second << " data operands";
2460 if (llvm::any_of(getArgs(), [&typeInfo](Value operands) {
2461 return operands.getType() != typeInfo.first;
2462 }))
2463 return emitOpError() << "expected data operands of type " << typeInfo.first;
2464 return success();
2465}
2466
2467LogicalResult NVVM::WMMAMmaOp::verify() {
2468 if (NVVM::WMMAMmaOp::getIntrinsicID(getM(), getN(), getK(), getLayoutA(),
2469 getLayoutB(), getEltypeA(),
2470 getEltypeB()) == 0)
2471 return emitOpError() << "invalid attribute combination";
2472 std::pair<Type, unsigned> typeInfoA = inferMMATypeFromMNK(
2473 getEltypeA(), NVVM::MMAFrag::a, getM(), getN(), getK(), getContext());
2474 std::pair<Type, unsigned> typeInfoB = inferMMATypeFromMNK(
2475 getEltypeA(), NVVM::MMAFrag::b, getM(), getN(), getK(), getContext());
2476 std::pair<Type, unsigned> typeInfoC = inferMMATypeFromMNK(
2477 getEltypeB(), NVVM::MMAFrag::c, getM(), getN(), getK(), getContext());
2478 SmallVector<Type, 32> arguments;
2479 arguments.append(typeInfoA.second, typeInfoA.first);
2480 arguments.append(typeInfoB.second, typeInfoB.first);
2481 arguments.append(typeInfoC.second, typeInfoC.first);
2482 unsigned numArgs = arguments.size();
2483 if (getArgs().size() != numArgs)
2484 return emitOpError() << "expected " << numArgs << " arguments";
2485 for (unsigned i = 0; i < numArgs; i++) {
2486 if (getArgs()[i].getType() != arguments[i])
2487 return emitOpError() << "expected argument " << i << " to be of type "
2488 << arguments[i];
2489 }
2490 Type dstType = LLVM::LLVMStructType::getLiteral(
2491 getContext(), SmallVector<Type, 8>(typeInfoC.second, typeInfoC.first));
2492 if (getType() != dstType)
2493 return emitOpError("expected destination type is a structure of ")
2494 << typeInfoC.second << " elements of type " << typeInfoC.first;
2495 return success();
2496}
2497
2498LogicalResult NVVM::LdMatrixOp::verify() {
2499 uint32_t num = getNum(), m = getShape().getM(), n = getShape().getN();
2500 if (m == 8 && n == 8) {
2501 if (num != 1 && num != 2 && num != 4) {
2502 return emitOpError("expected num attribute to be 1, 2 or 4 for 8x8 "
2503 "matrix");
2504 }
2505 if (getEltType() != LdStMatrixEltType::B16) {
2506 return emitOpError("expected element type to be b16 for 8x8 matrix");
2507 }
2508 } else if (m == 8 && n == 16) {
2509 if (num != 1 && num != 2 && num != 4) {
2510 return emitOpError("expected num attribute to be 1, 2 or 4 for 8x16 "
2511 "matrix");
2512 }
2513 if (getLayout() != MMALayout::row) {
2514 return emitOpError("expected layout to be row for 8x16 matrix");
2515 }
2516 if (getEltType() != LdStMatrixEltType::B8X16_B4X16_P64 &&
2517 getEltType() != LdStMatrixEltType::B8X16_B6X16_P32) {
2518 return emitOpError("expected element type to be b8x16.b4x16_p64 or "
2519 "b8x16.b6x16_p32 for 8x16 matrix");
2520 }
2521 } else if (m == 16 && n == 16) {
2522 if (num != 1 && num != 2) {
2523 return emitOpError("expected num attribute to be 1 or 2 for 16x16 "
2524 "matrix");
2525 }
2526 if (getLayout() != MMALayout::col) {
2527 return emitOpError("expected layout to be col for 16x16 matrix");
2528 }
2529 if (getEltType() != LdStMatrixEltType::B8 &&
2530 getEltType() != LdStMatrixEltType::B8X16_B4X16_P64 &&
2531 getEltType() != LdStMatrixEltType::B8X16_B6X16_P32) {
2532 return emitOpError("expected element type to be b8, b8x16.b4x16_p64 or "
2533 "b8x16.b6x16_p32 for 16x16 matrix");
2534 }
2535 } else {
2536 return emitOpError("expected shape to be 8x8, 8x16 or 16x16");
2537 }
2538
2539 Type i32 = IntegerType::get(getContext(), 32);
2540 uint32_t numElements = (m == 16 && n == 16 ? num * 2 : num);
2541 if (numElements == 1 && getType() != i32)
2542 return emitOpError("expected destination type is i32");
2543 if (numElements == 2 || numElements == 4) {
2544 Type dstType = LLVM::LLVMStructType::getLiteral(
2545 getContext(), SmallVector<Type>(numElements, i32));
2546 if (getType() != dstType)
2547 return emitOpError("expected destination type is a structure of ")
2548 << numElements << " elements of type i32";
2549 }
2550
2551 return success();
2552}
2553
2554LogicalResult LdMatrixOp::inferReturnTypes(
2555 MLIRContext *context, std::optional<Location> location,
2556 LdMatrixOp::Adaptor adaptor, SmallVectorImpl<Type> &inferredReturnTypes) {
2557 uint32_t num = adaptor.getNum();
2558 uint32_t m = adaptor.getShape().getM();
2559 uint32_t n = adaptor.getShape().getN();
2560 uint32_t numElements = (m == 16 && n == 16) ? num * 2 : num;
2561
2562 Type i32 = IntegerType::get(context, 32);
2563 if (numElements == 1)
2564 inferredReturnTypes.push_back(i32);
2565 else
2566 inferredReturnTypes.push_back(LLVM::LLVMStructType::getLiteral(
2567 context, SmallVector<Type>(numElements, i32)));
2568 return success();
2569}
2570
2571LogicalResult NVVM::StMatrixOp::verify() {
2572 int numMatrix = getSources().size();
2573 if (numMatrix != 1 && numMatrix != 2 && numMatrix != 4)
2574 return emitOpError("expected num attribute to be 1, 2 or 4");
2575
2576 int m = getShape().getM(), n = getShape().getN();
2577 if (m == 8 && n == 8) {
2578 if (getEltType() != NVVM::LdStMatrixEltType::B16) {
2579 return emitOpError("expected element type to be B16 for 8x8 matrix");
2580 }
2581 } else if (m == 16 && n == 8) {
2582 if (getEltType() != NVVM::LdStMatrixEltType::B8) {
2583 return emitOpError("expected element type to be B8 for 16x8 matrix");
2584 }
2585 if (getLayout() != NVVM::MMALayout::col) {
2586 return emitOpError("expected layout to be col for 16x8 matrix");
2587 }
2588 } else {
2589 return emitOpError("expected shape to be 8x8 or 16x8");
2590 }
2591
2592 return success();
2593}
2594
2595LogicalResult NVVM::MovMatrixOp::verify() {
2596 int m = getShape().getM(), n = getShape().getN();
2597 if (m != 8 || n != 8)
2598 return emitOpError("expected shape to be 8x8");
2599 if (getLayout() != NVVM::MMALayout::col)
2600 return emitOpError("expected layout to be col");
2601 if (getEltType() != NVVM::LdStMatrixEltType::B16)
2602 return emitOpError("expected element type to be b16");
2603 return success();
2604}
2605
2606static FailureOr<int> getAllowedSizeK(NVVM::WGMMATypes typeA) {
2607 if (typeA == NVVM::WGMMATypes::tf32)
2608 return 8;
2609 if (typeA == NVVM::WGMMATypes::f16 || typeA == NVVM::WGMMATypes::bf16)
2610 return 16;
2611 if (typeA == NVVM::WGMMATypes::s8 || typeA == NVVM::WGMMATypes::u8)
2612 return 32;
2613 if (typeA == NVVM::WGMMATypes::e4m3 || typeA == NVVM::WGMMATypes::e5m2)
2614 return 32;
2615 if (typeA == NVVM::WGMMATypes::b1)
2616 return 256;
2617 return failure();
2618}
2619
2620static LogicalResult isAllowedWGMMADataType(NVVM::WGMMATypes typeD,
2621 NVVM::WGMMATypes typeA,
2622 NVVM::WGMMATypes typeB) {
2623 switch (typeA) {
2624 case NVVM::WGMMATypes::f16:
2625 if ((typeD == NVVM::WGMMATypes::f32 || typeD == NVVM::WGMMATypes::f16) &&
2626 typeB == NVVM::WGMMATypes::f16)
2627 return success();
2628 break;
2629 case NVVM::WGMMATypes::tf32:
2630 if (typeD == NVVM::WGMMATypes::f32 && typeB == NVVM::WGMMATypes::tf32)
2631 return success();
2632 break;
2633 case NVVM::WGMMATypes::u8:
2634 case NVVM::WGMMATypes::s8:
2635 if (typeD == NVVM::WGMMATypes::s32 &&
2636 (typeB == NVVM::WGMMATypes::u8 || typeB == NVVM::WGMMATypes::s8))
2637 return success();
2638 break;
2639 case NVVM::WGMMATypes::b1:
2640 if (typeD == NVVM::WGMMATypes::s32 && typeB == NVVM::WGMMATypes::b1)
2641 return success();
2642 break;
2643 case NVVM::WGMMATypes::bf16:
2644 if ((typeD == NVVM::WGMMATypes::f32 || typeD == NVVM::WGMMATypes::f16) &&
2645 typeB == NVVM::WGMMATypes::bf16)
2646 return success();
2647 break;
2648 case NVVM::WGMMATypes::e4m3:
2649 case NVVM::WGMMATypes::e5m2:
2650 if ((typeD == NVVM::WGMMATypes::f32 || typeD == NVVM::WGMMATypes::f16) &&
2651 (typeB == NVVM::WGMMATypes::e5m2 || typeB == NVVM::WGMMATypes::e4m3))
2652 return success();
2653 break;
2654 case WGMMATypes::f32:
2655 case WGMMATypes::s32:
2656 llvm_unreachable("unsupported input types");
2657 break;
2658 }
2659 return failure();
2660}
2661
2662static LogicalResult isAllowedSizeN(int sizeN, NVVM::WGMMATypes typeA) {
2663 SmallVector<int> allowedN = {8, 16, 24, 32, 40, 48, 56, 64,
2664 72, 80, 88, 96, 104, 112, 120, 128,
2665 136, 144, 152, 160, 168, 176, 184, 192,
2666 200, 208, 216, 224, 232, 240, 248, 256};
2667 SmallVector<int> allowedNshort = {8, 16, 24, 32, 48, 64,
2668 80, 96, 112, 128, 144, 160,
2669 176, 192, 208, 224, 240, 256};
2670 switch (typeA) {
2671 case WGMMATypes::f16:
2672 case WGMMATypes::tf32:
2673 case WGMMATypes::bf16:
2674 case WGMMATypes::e4m3:
2675 case WGMMATypes::e5m2:
2676 if (llvm::is_contained(allowedN, sizeN))
2677 return success();
2678 break;
2679 case WGMMATypes::u8:
2680 case WGMMATypes::s8:
2681 case WGMMATypes::b1:
2682 if (llvm::is_contained(allowedNshort, sizeN))
2683 return success();
2684 break;
2685 case WGMMATypes::f32:
2686 case WGMMATypes::s32:
2687 llvm_unreachable("unsupported input types");
2688 break;
2689 }
2690 return failure();
2691}
2692
2693LogicalResult NVVM::WgmmaMmaAsyncOp::verify() {
2694 Value outValue = getResults();
2695 auto stype = dyn_cast<LLVM::LLVMStructType>(outValue.getType());
2696 if (!stype)
2697 return emitOpError() << "expected results to be struct";
2698 int outputSize = stype.getBody().size();
2699 WGMMATypes typeD = getTypeD();
2700 WGMMATypes typeA = getTypeA();
2701 WGMMATypes typeB = getTypeB();
2702
2703 for (Type t : stype.getBody()) {
2704 if (t != stype.getBody().front())
2705 return emitOpError()
2706 << "all elements in struct must be same type but there is " << t;
2707 }
2708
2709 if (typeD != WGMMATypes::f32 && typeD != WGMMATypes::f16 &&
2710 typeD != WGMMATypes::s32) {
2711 return emitOpError() << "does not support the given output type " << typeD;
2712 }
2713 if (typeD == WGMMATypes::s32 &&
2714 (getScaleA() == WGMMAScaleIn::neg || getScaleB() == WGMMAScaleIn::neg)) {
2715 return emitOpError() << "has s32 output, scaleA and scaleB cannot be neg";
2716 }
2717
2718 if (failed(isAllowedWGMMADataType(typeD, typeA, typeB))) {
2719 return emitOpError() << typeD << " += " << typeA << " * " << typeB
2720 << ", it is not supported.";
2721 }
2722
2723 // Check M
2724 if (getShape().getM() != 64)
2725 return emitOpError() << "shape 'm' must be 64";
2726
2727 // Check K
2728 FailureOr<int> allowedK = getAllowedSizeK(typeA);
2729 if (failed(allowedK) || allowedK.value() != getShape().getK())
2730 return emitOpError() << "shape 'k' must be " << allowedK.value()
2731 << " for input type " << typeA;
2732
2733 // Check N
2734 if (failed(isAllowedSizeN(getShape().getN(), typeA))) {
2735 return emitOpError() << "has input type " << typeA << " n is set to "
2736 << getShape().getN() << ", it is not supported.";
2737 }
2738
2739 // Check transpose (only available for f16/bf16)
2740 // Matrices A should be stored in row-major and B in column-major.
2741 // Only f16/bf16 matrices can be stored in either column-major or row-major
2742 // by setting the transpose value(imm-trans-a,imm-trans-b) in PTX code.
2743 if ((typeA != WGMMATypes::f16 && typeA != WGMMATypes::bf16) &&
2744 (getLayoutA() == mlir::NVVM::MMALayout::col ||
2745 getLayoutB() == mlir::NVVM::MMALayout::row)) {
2746 return emitOpError()
2747 << "given layouts layout_a = " << getLayoutA()
2748 << " and layout_b = " << getLayoutB() << " for input types " << typeA
2749 << " and " << typeB
2750 << " requires transpose. However, this is only supported for: "
2751 << MMATypes::f16 << " and " << MMATypes::bf16;
2752 }
2753
2754 // Check result registers
2755 int expectedOutput = 0;
2756 if (typeD == WGMMATypes::f32 || typeD == WGMMATypes::s32)
2757 expectedOutput = getShape().getN() / 2;
2758 if (typeD == WGMMATypes::f16)
2759 expectedOutput = getShape().getN() / 4;
2760 if (outputSize != expectedOutput) {
2761 return emitOpError() << "results " << expectedOutput
2762 << ", however output struct has " << outputSize
2763 << " elements";
2764 }
2765 // Check satfinite (only available for s32 accumulator)
2766 if (typeD != WGMMATypes::s32 &&
2767 getSatfinite().value_or(NVVM::MMAIntOverflow::wrapped) ==
2768 NVVM::MMAIntOverflow::satfinite) {
2769 return emitOpError()
2770 << " `satfinite` can be only used with s32 accumulator, however "
2771 "the current accumulator is "
2772 << typeD;
2773 }
2774
2775 return success();
2776}
2777
2778std::string NVVM::WgmmaMmaAsyncOp::getPtx() {
2779
2780 int m = getShape().getM(), n = getShape().getN(), k = getShape().getK();
2781 bool isF16 = getTypeA() == WGMMATypes::f16 || getTypeA() == WGMMATypes::bf16;
2782
2783 StringRef outputTypeName = stringifyWGMMATypes(getTypeD());
2784
2785 int expectedOutputRegisters = 0;
2786 if (getTypeD() == WGMMATypes::f16)
2787 expectedOutputRegisters = getShape().getN() / 4;
2788 else
2789 expectedOutputRegisters = getShape().getN() / 2;
2790
2791 std::string ptx;
2792 llvm::raw_string_ostream ss(ptx);
2793
2794 ss << "{\n"
2795 ".reg .pred p;\n"
2796 "setp.ne.b32 p, $"
2797 << ((expectedOutputRegisters * 2) + 2)
2798 << ", 0;\n"
2799 "wgmma.mma_async.sync.aligned.m"
2800 << m << "n" << n << "k" << k << "." << outputTypeName << "." << getTypeA()
2801 << "." << getTypeB();
2802 if (getSatfinite().value_or(NVVM::MMAIntOverflow::wrapped) ==
2803 NVVM::MMAIntOverflow::satfinite)
2804 ss << ".satfinite";
2805 ss << " {";
2806 int regCnt = 0;
2807 for (; regCnt < expectedOutputRegisters; ++regCnt) {
2808 ss << "$" << regCnt;
2809 if (regCnt != expectedOutputRegisters - 1)
2810 ss << ", ";
2811 }
2812
2813 ss << "},";
2814 // Need to map read/write registers correctly.
2815 regCnt = (regCnt * 2);
2816 ss << " $" << (regCnt) << "," << " $" << (regCnt + 1) << "," << " p";
2817 if (getTypeD() != WGMMATypes::s32) {
2818 ss << ", $" << (regCnt + 3) << ", $" << (regCnt + 4);
2819 }
2820 // Don't add transpose parameters unless needed.
2821 if (isF16) {
2822 ss << ", $" << (regCnt + 5) << ", $" << (regCnt + 6);
2823 }
2824 ss << ";\n"
2825 << "}\n";
2826 return ptx;
2827}
2828
2829bool NVVM::WgmmaMmaAsyncOp::getAsmValues(
2830 RewriterBase &rewriter,
2831 llvm::SmallVectorImpl<std::pair<mlir::Value, mlir::NVVM::PTXRegisterMod>>
2832 &asmValues) {
2833 bool isF16 = getTypeA() == WGMMATypes::f16 || getTypeA() == WGMMATypes::bf16;
2834 if (getResults())
2835 asmValues.push_back({getResults(), mlir::NVVM::PTXRegisterMod::Write});
2836 if (getInouts())
2837 asmValues.push_back({getInouts(), mlir::NVVM::PTXRegisterMod::ReadWrite});
2838 asmValues.push_back({getDescriptorA(), mlir::NVVM::PTXRegisterMod::Read});
2839 asmValues.push_back({getDescriptorB(), mlir::NVVM::PTXRegisterMod::Read});
2840 asmValues.push_back({makeConstantI32(rewriter, static_cast<int>(getScaleD())),
2842 if (getTypeD() != WGMMATypes::s32) {
2843 asmValues.push_back(
2844 {makeConstantI32(rewriter,
2845 getScaleA() == NVVM::WGMMAScaleIn::neg ? -1 : 1),
2847 asmValues.push_back(
2848 {makeConstantI32(rewriter,
2849 getScaleB() == NVVM::WGMMAScaleIn::neg ? -1 : 1),
2851 }
2852 if (isF16) {
2853 asmValues.push_back(
2854 {makeConstantI32(rewriter, static_cast<int>(getLayoutA())),
2856 asmValues.push_back(
2857 {makeConstantI32(rewriter, 1 - static_cast<int>(getLayoutB())),
2859 }
2860 return true; // Has manual mapping
2861}
2862
2863LogicalResult NVVM::FenceProxyOp::verify() {
2864 if (getKind() == NVVM::ProxyKind::async_shared && !getSpace().has_value()) {
2865 return emitOpError() << "async_shared fence requires space attribute";
2866 }
2867 if (getKind() != NVVM::ProxyKind::async_shared && getSpace().has_value()) {
2868 return emitOpError() << "only async_shared fence can have space attribute";
2869 }
2870 return success();
2871}
2872
2873LogicalResult NVVM::FenceProxyAcquireOp::verify() {
2874 if (getFromProxy() != NVVM::ProxyKind::GENERIC)
2875 return emitOpError("uni-directional proxies only support generic for "
2876 "from_proxy attribute");
2877
2878 if (getToProxy() != NVVM::ProxyKind::TENSORMAP)
2879 return emitOpError("uni-directional proxies only support tensormap "
2880 "for to_proxy attribute");
2881 return success();
2882}
2883
2884LogicalResult NVVM::FenceProxyReleaseOp::verify() {
2885 if (getFromProxy() != NVVM::ProxyKind::GENERIC)
2886 return emitOpError("uni-directional proxies only support generic for "
2887 "from_proxy attribute");
2888
2889 if (getToProxy() != NVVM::ProxyKind::TENSORMAP)
2890 return emitOpError("uni-directional proxies only support tensormap "
2891 "for to_proxy attribute");
2892 return success();
2893}
2894
2895LogicalResult NVVM::FenceProxySyncRestrictOp::verify() {
2896 if (getFromProxy() != NVVM::ProxyKind::GENERIC)
2897 return emitOpError("only generic is support for from_proxy attribute");
2898
2899 if (getToProxy() != NVVM::ProxyKind::async)
2900 return emitOpError("only async is supported for to_proxy attribute");
2901 return success();
2902}
2903
2904LogicalResult NVVM::SetMaxRegisterOp::verify() {
2905 if (getRegCount() % 8)
2906 return emitOpError("new register size must be multiple of 8");
2907 if (getRegCount() < 24 || getRegCount() > 256)
2908 return emitOpError("new register size must be in between 24 to 256");
2909 return success();
2910}
2911
2912LogicalResult NVVM::Tcgen05CpOp::verify() {
2913 auto mc = getMulticast();
2914
2915 using SH = Tcgen05CpShape;
2916 using MC = Tcgen05CpMulticast;
2917 switch (getShape()) {
2918 case SH::SHAPE_128x256b:
2919 case SH::SHAPE_128x128b:
2920 case SH::SHAPE_4x256b:
2921 if (mc != MC::NONE)
2922 return emitError("Invalid multicast type for tcgen05.cp Op");
2923 break;
2924 case SH::SHAPE_64x128b:
2925 if (mc != MC::WARPX2_01_23 && mc != MC::WARPX2_02_13)
2926 return emitError("Shape 64x128b requires multicast warpx2_01_23 or "
2927 "warpx2_02_13 for tcgen05.cp Op");
2928 break;
2929 case SH::SHAPE_32x128b:
2930 if (mc != MC::WARPX4)
2931 return emitError(
2932 "Shape 32x128b requires multicast warpx4 for tcgen05.cp Op");
2933 break;
2934 }
2935 return success();
2936}
2937
2938LogicalResult NVVM::MatchSyncOp::verify() {
2939 if (getKind() == NVVM::MatchSyncKind::all) {
2940 auto type = llvm::dyn_cast<LLVM::LLVMStructType>(getType());
2941 if (!type || type.getBody().size() != 2 ||
2942 !type.getBody()[0].isInteger(32) || !type.getBody()[1].isInteger(1)) {
2943 return emitOpError("match.sync 'all' returns a two element struct with "
2944 "first element as i32 and second element as i1");
2945 }
2946 } else {
2947 if (!getType().isInteger(32)) {
2948 return emitOpError("match.sync 'any' returns an i32");
2949 }
2950 }
2951 return success();
2952}
2953
2954LogicalResult MatchSyncOp::inferReturnTypes(
2955 MLIRContext *context, std::optional<Location> location,
2956 MatchSyncOp::Adaptor adaptor, SmallVectorImpl<Type> &inferredReturnTypes) {
2957 if (adaptor.getKind() == NVVM::MatchSyncKind::all)
2958 inferredReturnTypes.push_back(LLVM::LLVMStructType::getLiteral(
2959 context,
2960 {IntegerType::get(context, 32), IntegerType::get(context, 1)}));
2961 else
2962 inferredReturnTypes.push_back(IntegerType::get(context, 32));
2963 return success();
2964}
2965
2966LogicalResult NVVM::VoteSyncOp::verify() {
2967 if (getKind() == NVVM::VoteSyncKind::ballot) {
2968 if (!getType().isInteger(32)) {
2969 return emitOpError("vote.sync 'ballot' returns an i32");
2970 }
2971 } else {
2972 if (!getType().isInteger(1)) {
2973 return emitOpError("vote.sync 'any', 'all' and 'uni' returns an i1");
2974 }
2975 }
2976 return success();
2977}
2978
2979LogicalResult VoteSyncOp::inferReturnTypes(
2980 MLIRContext *context, std::optional<Location> location,
2981 VoteSyncOp::Adaptor adaptor, SmallVectorImpl<Type> &inferredReturnTypes) {
2982 unsigned width = adaptor.getKind() == NVVM::VoteSyncKind::ballot ? 32 : 1;
2983 inferredReturnTypes.push_back(IntegerType::get(context, width));
2984 return success();
2985}
2986
2987LogicalResult NVVM::PrefetchOp::verify() {
2988 using MemSpace = NVVM::NVVMMemorySpace;
2989 using CacheLevel = NVVM::PrefetchCacheLevel;
2990
2991 unsigned addressSpace =
2992 llvm::cast<LLVM::LLVMPointerType>(getAddr().getType()).getAddressSpace();
2993 std::optional<NVVM::CacheEvictionPriority> evictPriority = getEvictPriority();
2994 std::optional<NVVM::PrefetchCacheLevel> cacheLevel = getCacheLevel();
2995
2996 if (getTensormap() && cacheLevel)
2997 return emitOpError("cannot specify both tensormap and cache level");
2998
2999 if (getTensormap()) {
3000 if (addressSpace != MemSpace::Generic &&
3001 addressSpace != MemSpace::Constant) {
3002 return emitOpError(
3003 "prefetch tensormap requires a generic or constant pointer");
3004 }
3005
3006 if (evictPriority) {
3007 return emitOpError(
3008 "prefetch tensormap does not support eviction priority");
3009 }
3010
3011 if (getInParamSpace() && addressSpace != MemSpace::Generic) {
3012 return emitOpError(
3013 "in_param_space can only be specified for a generic pointer");
3014 }
3015
3016 } else if (cacheLevel) {
3017 if (addressSpace != MemSpace::Generic && addressSpace != MemSpace::Global &&
3018 addressSpace != MemSpace::Local) {
3019 return emitOpError("prefetch to cache level requires a generic, global, "
3020 "or local pointer");
3021 }
3022
3023 if (getUniform()) {
3024 if (*cacheLevel != CacheLevel::L1) {
3025 return emitOpError(
3026 "unsupported cache level, the only supported uniform "
3027 "cache level is L1");
3028 }
3029
3030 if (addressSpace != MemSpace::Generic) {
3031 return emitOpError(
3032 "prefetch to uniform cache requires a generic pointer");
3033 }
3034 }
3035
3036 if (evictPriority) {
3037 if (*cacheLevel != CacheLevel::L2)
3038 return emitOpError(
3039 "cache eviction priority supported only for cache level L2");
3040
3041 if (addressSpace != MemSpace::Global)
3042 return emitOpError("cache eviction priority requires a global pointer");
3043
3044 if (*evictPriority != NVVM::CacheEvictionPriority::EvictNormal &&
3045 *evictPriority != NVVM::CacheEvictionPriority::EvictLast)
3046 return emitOpError(
3047 "unsupported cache eviction priority, only evict_last and "
3048 "evict_normal are supported");
3049 }
3050
3051 if (getPredicate())
3052 return emitOpError("predicate supported only on prefetch tensormap");
3053
3054 } else {
3055 return emitOpError(
3056 "requires specification of either cache level or tensormap");
3057 }
3058
3059 return success();
3060}
3061
3062LogicalResult NVVM::ClusterLaunchControlQueryCancelOp::verify() {
3063 switch (getQueryType()) {
3064 case NVVM::ClusterLaunchControlQueryType::IS_CANCELED:
3065 if (!getType().isInteger(1))
3066 return emitOpError("is_canceled query type returns an i1");
3067 break;
3068 case NVVM::ClusterLaunchControlQueryType::GET_FIRST_CTA_ID_X:
3069 case NVVM::ClusterLaunchControlQueryType::GET_FIRST_CTA_ID_Y:
3070 case NVVM::ClusterLaunchControlQueryType::GET_FIRST_CTA_ID_Z:
3071 if (!getType().isInteger(32)) {
3072 return emitOpError("get_first_cta_id_x, get_first_cta_id_y, "
3073 "get_first_cta_id_z query types return an i32");
3074 }
3075 break;
3076 }
3077 return success();
3078}
3079
3080LogicalResult ClusterLaunchControlQueryCancelOp::inferReturnTypes(
3081 MLIRContext *context, std::optional<Location> location,
3082 ClusterLaunchControlQueryCancelOp::Adaptor adaptor,
3083 SmallVectorImpl<Type> &inferredReturnTypes) {
3084 unsigned width =
3085 adaptor.getQueryType() == NVVM::ClusterLaunchControlQueryType::IS_CANCELED
3086 ? 1
3087 : 32;
3088 inferredReturnTypes.push_back(IntegerType::get(context, width));
3089 return success();
3090}
3091
3092LogicalResult NVVM::ReduxOp::verify() {
3093 mlir::Type reduxType = getType();
3094
3095 if (!reduxType.isF32()) {
3096 if (getAbs())
3097 return emitOpError("abs attribute is supported only for f32 type");
3098 if (getNan())
3099 return emitOpError("nan attribute is supported only for f32 type");
3100 }
3101
3102 NVVM::ReductionKind kind = getKind();
3103 switch (kind) {
3104 case NVVM::ReductionKind::ADD:
3105 case NVVM::ReductionKind::AND:
3106 case NVVM::ReductionKind::OR:
3107 case NVVM::ReductionKind::XOR:
3108 case NVVM::ReductionKind::MAX:
3109 case NVVM::ReductionKind::MIN:
3110 case NVVM::ReductionKind::UMAX:
3111 case NVVM::ReductionKind::UMIN:
3112 if (!reduxType.isInteger(32))
3113 return emitOpError("'")
3114 << kind << "' reduction kind unsupported with " << reduxType
3115 << " type. Only supported type is 'i32'.";
3116 break;
3117 case NVVM::ReductionKind::FMIN:
3118 case NVVM::ReductionKind::FMAX:
3119 if (!reduxType.isF32())
3120 return emitOpError("'")
3121 << kind << "' reduction kind unsupported with " << reduxType
3122 << " type. Only supported type is 'f32'.";
3123 break;
3124 }
3125
3126 return success();
3127}
3128
3129LogicalResult NVVM::TensormapReplaceOp::verify() {
3130 auto ord = getOrd();
3131 Value newVal = getNewValue();
3132 auto newValAttr = getNewValueAttr();
3133 auto fieldName = stringifyEnum(getField());
3134
3135 if (ord && !llvm::is_contained({NVVM::TensormapField::BOX_DIM,
3136 NVVM::TensormapField::GLOBAL_DIM,
3137 NVVM::TensormapField::GLOBAL_STRIDE,
3138 NVVM::TensormapField::ELEMENT_STRIDE},
3139 getField()))
3140 return emitOpError("ordinal is not supported for ")
3141 << fieldName << " field";
3142
3143 auto invalidNewVal = [&](llvm::Twine type) -> std::string {
3144 return llvm::Twine("new_value must be specified and must be an " + type +
3145 " for " + llvm::Twine(fieldName) + " field")
3146 .str();
3147 };
3148
3149 auto invalidNewValAttr = [&]() -> std::string {
3150 return (llvm::Twine(
3151 "new_value_attr must be specified and must be a valid ") +
3152 llvm::Twine(fieldName) + " attribute for " + fieldName + " field")
3153 .str();
3154 };
3155
3156 switch (getField()) {
3157 case NVVM::TensormapField::GLOBAL_ADDRESS:
3158 if (!(newVal && newVal.getType().isInteger(64)))
3159 return emitOpError(invalidNewVal("i64"));
3160 break;
3161 case NVVM::TensormapField::RANK:
3162 if (!(newVal && newVal.getType().isInteger(32)))
3163 return emitOpError(invalidNewVal("i32"));
3164 break;
3165 case NVVM::TensormapField::GLOBAL_STRIDE:
3166 if (!ord)
3167 return emitOpError("ordinal is required for global_stride field");
3168 if (!(newVal && newVal.getType().isInteger(64)))
3169 return emitOpError(invalidNewVal("i64"));
3170 break;
3171 case NVVM::TensormapField::BOX_DIM:
3172 case NVVM::TensormapField::GLOBAL_DIM:
3173 case NVVM::TensormapField::ELEMENT_STRIDE:
3174 if (!ord)
3175 return emitOpError("ordinal is required for ")
3176 << stringifyEnum(getField()) << " field";
3177 if (!(newVal && newVal.getType().isInteger(32)))
3178 return emitOpError(invalidNewVal("i32"));
3179 break;
3180 case NVVM::TensormapField::ELEMTYPE:
3181 if (!(newValAttr && llvm::isa<TensormapElemtypeAttr>(*newValAttr)))
3182 return emitOpError(invalidNewValAttr());
3183 break;
3184 case NVVM::TensormapField::INTERLEAVE_LAYOUT:
3185 if (!(newValAttr && llvm::isa<TensormapInterleaveLayoutAttr>(*newValAttr)))
3186 return emitOpError(invalidNewValAttr());
3187 break;
3188 case NVVM::TensormapField::SWIZZLE_MODE:
3189 if (!(newValAttr && llvm::isa<TensormapSwizzleModeAttr>(*newValAttr)))
3190 return emitOpError(invalidNewValAttr());
3191 break;
3192 case NVVM::TensormapField::SWIZZLE_ATOMICITY:
3193 if (!(newValAttr && llvm::isa<TensormapSwizzleAtomicityAttr>(*newValAttr)))
3194 return emitOpError(invalidNewValAttr());
3195 break;
3196 case NVVM::TensormapField::FILL_MODE:
3197 if (!(newValAttr && llvm::isa<TensormapFillModeAttr>(*newValAttr)))
3198 return emitOpError(invalidNewValAttr());
3199 break;
3200 }
3201
3202 return success();
3203}
3204
3205template <typename OpType>
3206static LogicalResult verifyAddSubFOp(OpType op) {
3207 mlir::NVVM::FPRoundingMode rndMode = op.getRnd();
3208 mlir::NVVM::SaturationMode satMode = op.getSat();
3209 bool isFTZ = op.getFtz();
3210
3211 mlir::Type opType = op.getRes().getType();
3212 mlir::Type opBaseType = isa<VectorType>(opType)
3213 ? cast<VectorType>(opType).getElementType()
3214 : opType;
3215
3216 if (opBaseType.isF64() && (satMode != NVVM::SaturationMode::NONE || isFTZ))
3217 return op.emitOpError("FTZ and saturation are not supported for "
3218 "additions/subtractions involving f64 type");
3219
3220 if (opBaseType.isF16() && !(rndMode == NVVM::FPRoundingMode::RN ||
3221 rndMode == NVVM::FPRoundingMode::NONE))
3222 return op.emitOpError("only RN rounding mode is supported for f16 and "
3223 "vector<2xf16> additions/subtractions");
3224
3225 if (opBaseType.isBF16()) {
3226 if (rndMode != NVVM::FPRoundingMode::RN &&
3227 rndMode != NVVM::FPRoundingMode::NONE)
3228 return op.emitOpError("only RN rounding mode is supported for bf16 and "
3229 "vector<2xbf16> additions/subtractions");
3230 if (satMode != NVVM::SaturationMode::NONE || isFTZ)
3231 return op.emitOpError("FTZ and saturation are not supported for bf16 and "
3232 "vector<2xbf16> additions/subtractions");
3233 }
3234
3235 // FIXME: This is a temporary check disallowing lowering to add.rn.ftz.f16(x2)
3236 // PTX instructions since the corresponding LLVM intrinsic is missing. This
3237 // should be removed once the intrinsics for f16 addition (with FTZ only) are
3238 // available.
3239 if (opBaseType.isF16() && isFTZ && satMode == NVVM::SaturationMode::NONE)
3240 return op.emitOpError("FTZ with no saturation is not supported for f16 and "
3241 "vector<2xf16> additions/subtractions");
3242
3243 return success();
3244}
3245
3246LogicalResult NVVM::AddFOp::verify() { return verifyAddSubFOp<AddFOp>(*this); }
3247
3248LogicalResult NVVM::SubFOp::verify() { return verifyAddSubFOp<SubFOp>(*this); }
3249
3250LogicalResult NVVM::FmaOp::verify() {
3251 auto opType = getRes().getType();
3252 mlir::NVVM::FPRoundingMode rndMode = getRnd();
3253 mlir::NVVM::SaturationMode satMode = getSat();
3254 bool isFTZ = getFtz();
3255 bool isRelu = getRelu();
3256 bool hasOOB = getOob();
3257
3258 auto getBaseFType = [](Type type) -> Type {
3259 if (isa<VectorType>(type))
3260 return cast<VectorType>(type).getElementType();
3261 return type;
3262 };
3263
3264 auto opBaseType = getBaseFType(opType);
3265
3266 if (rndMode == NVVM::FPRoundingMode::NONE)
3267 return emitOpError("rounding mode must be specified");
3268
3269 if (isRelu && satMode == NVVM::SaturationMode::SAT)
3270 return emitOpError("relu and saturation are not supported together");
3271
3272 if (hasOOB && (satMode == NVVM::SaturationMode::SAT || isFTZ))
3273 return emitOpError("oob is not supported with saturation or FTZ");
3274
3275 if (!(opBaseType.isF16() || opBaseType.isBF16()) && (isRelu || hasOOB))
3276 return emitOpError("relu and oob are only supported for f16 and bf16");
3277
3278 if (opBaseType.isF64() && (satMode != NVVM::SaturationMode::NONE || isFTZ))
3279 return emitOpError("FTZ and saturation are not supported for f64 type");
3280
3281 if (opBaseType.isF16() && rndMode != NVVM::FPRoundingMode::RN)
3282 return emitOpError(
3283 "only RN rounding mode is supported for f16 and vector<2xf16>");
3284
3285 if (opBaseType.isBF16()) {
3286 if (rndMode != NVVM::FPRoundingMode::RN)
3287 return emitOpError(
3288 "only RN rounding mode is supported for bf16 and vector<2xbf16>");
3289 if (satMode != NVVM::SaturationMode::NONE || isFTZ)
3290 return emitOpError(
3291 "FTZ and saturation are not supported for bf16 and vector<2xbf16>");
3292 }
3293
3294 return success();
3295}
3296
3297LogicalResult NVVM::SqrtOp::verify() {
3298 if (getRnd() == NVVM::FPRoundingMode::NONE)
3299 return emitOpError("rounding mode cannot be None");
3300
3301 if (getRes().getType().isF64() && getFtz())
3302 return emitOpError("FTZ is not supported for f64");
3303
3304 return success();
3305}
3306
3307LogicalResult NVVM::DivFOp::verify() {
3308 bool isApprox = getApprox();
3309 bool isFull = getFull();
3310 bool isF64 = getRes().getType().isF64();
3311 bool isFtz = getFtz();
3312 NVVM::FPRoundingMode rndMode = getRnd();
3313
3314 if (isApprox && isFull)
3315 return emitOpError("'approx' and 'full' are mutually exclusive");
3316
3317 if (isApprox || isFull) {
3318 if (isF64)
3319 return emitOpError("'approx' and 'full' forms are f32-only");
3320 if (rndMode != NVVM::FPRoundingMode::NONE)
3321 return emitOpError(
3322 "'approx' and 'full' forms do not accept a rounding mode");
3323 return success();
3324 }
3325
3326 // Rounded form below.
3327 if (rndMode == NVVM::FPRoundingMode::NONE)
3328 return emitOpError("rounding mode cannot be None for the rounded divide");
3329 if (isF64 && isFtz)
3330 return emitOpError("FTZ is not supported for f64");
3331
3332 return success();
3333}
3334
3335/// Packs the given `field` into the `result`.
3336/// The `result` is 64-bits and each `field` can be 32-bits or narrower.
3337static llvm::Value *
3338packValInto64Bits(llvm::IRBuilderBase &builder,
3339 llvm::Value *result, // the `result` (unset bits are zero)
3340 llvm::Value *field, // `field` to pack into `result`
3341 unsigned sizeInBits, // Size of `field` in bits
3342 unsigned start) { // Starting bit within `result`
3343 field = builder.CreateZExtOrBitCast(field, builder.getInt32Ty());
3344
3345 unsigned mask = (sizeInBits < 32 ? ((1u << sizeInBits) - 1) : 0xffffffffu);
3346 if (mask != 0xffffffffu)
3347 field = builder.CreateAnd(field, builder.getInt32(mask));
3348
3349 field = builder.CreateZExtOrBitCast(field, builder.getInt64Ty());
3350 field = builder.CreateShl(field, start);
3351
3352 return builder.CreateOr(result, field);
3353}
3354
3355void Tcgen05MmaSmemDescOp::createSmemDescriptor(Operation &op,
3357 llvm::IRBuilderBase &builder) {
3358 auto thisOp = cast<NVVM::Tcgen05MmaSmemDescOp>(op);
3359 llvm::Value *smemDesc = builder.getInt64(0);
3360
3361 smemDesc = packValInto64Bits(builder, smemDesc,
3362 mt.lookupValue(thisOp.getStartAddr()), 14, 0);
3363 smemDesc = packValInto64Bits(
3364 builder, smemDesc, mt.lookupValue(thisOp.getLeadingDimOffset()), 14, 16);
3365 smemDesc = packValInto64Bits(
3366 builder, smemDesc, mt.lookupValue(thisOp.getStrideDimOffset()), 14, 32);
3367
3368 smemDesc = packValInto64Bits(builder, smemDesc, builder.getInt32(1), 3, 46);
3369 smemDesc = packValInto64Bits(builder, smemDesc,
3370 mt.lookupValue(thisOp.getBaseOffset()), 3, 49);
3371 smemDesc = packValInto64Bits(
3372 builder, smemDesc, mt.lookupValue(thisOp.getLeadingDimMode()), 1, 52);
3373 smemDesc = packValInto64Bits(builder, smemDesc,
3374 mt.lookupValue(thisOp.getSwizzleMode()), 3, 61);
3375
3376 mt.mapValue(thisOp.getRes()) = smemDesc;
3377}
3378
3379//===----------------------------------------------------------------------===//
3380// getPtx methods
3381//===----------------------------------------------------------------------===//
3382
3383std::string NVVM::MBarrierInitOp::getPtx() {
3384 bool isShared = isPtrInSharedCTASpace(getAddr());
3385 return isShared ? std::string("mbarrier.init.shared.b64 [%0], %1;")
3386 : std::string("mbarrier.init.b64 [%0], %1;");
3387}
3388
3389std::string NVVM::MBarrierArriveExpectTxOp::getPtx() {
3390 bool isShared = isPtrInSharedCTASpace(getAddr());
3391 return isShared
3392 ? std::string("mbarrier.arrive.expect_tx.shared.b64 _, [%0], %1;")
3393 : std::string("mbarrier.arrive.expect_tx.b64 _, [%0], %1;");
3394}
3395
3396std::string NVVM::MBarrierTryWaitParityOp::getPtx() {
3397 bool isShared = isPtrInSharedCTASpace(getAddr());
3398 llvm::StringRef space = isShared ? ".shared" : "";
3399
3400 return llvm::formatv("{\n\t"
3401 ".reg .pred P1; \n\t"
3402 "LAB_WAIT: \n\t"
3403 "mbarrier.try_wait.parity{0}.b64 P1, [%0], %1, %2; \n\t"
3404 "@P1 bra.uni DONE; \n\t"
3405 "bra.uni LAB_WAIT; \n\t"
3406 "DONE: \n\t"
3407 "}",
3408 space);
3409}
3410
3411//===----------------------------------------------------------------------===//
3412// Canonicalization patterns
3413//===----------------------------------------------------------------------===//
3414
3417
3418 LogicalResult matchAndRewrite(SubFOp op,
3419 PatternRewriter &rewriter) const override {
3420 Location loc = op.getLoc();
3421 Value negRhs =
3422 LLVM::FNegOp::create(rewriter, loc, op.getRhs().getType(), op.getRhs());
3423
3424 rewriter.replaceOpWithNewOp<AddFOp>(op, op.getType(), op.getLhs(), negRhs,
3425 op.getRnd(), op.getSat(), op.getFtz());
3426 return success();
3427 }
3428};
3429
3430void SubFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
3431 MLIRContext *context) {
3432 patterns.add<ConvertFsubToFnegFadd>(context);
3433}
3434
3435//===----------------------------------------------------------------------===//
3436// getIntrinsicID/getIntrinsicIDAndArgs methods
3437//===----------------------------------------------------------------------===//
3438
3439/// Maps the (aligned, hasCount) pair to the `@llvm.nvvm.barrier.cta.sync.*`
3440/// intrinsic ID.
3441static llvm::Intrinsic::ID getBarrierSyncIntrinsic(bool aligned,
3442 bool hasCount) {
3443 if (hasCount) {
3444 return aligned ? llvm::Intrinsic::nvvm_barrier_cta_sync_aligned_count
3445 : llvm::Intrinsic::nvvm_barrier_cta_sync_count;
3446 }
3447 return aligned ? llvm::Intrinsic::nvvm_barrier_cta_sync_aligned_all
3448 : llvm::Intrinsic::nvvm_barrier_cta_sync_all;
3449}
3450
3451/// Maps the (aligned, kind) pair to the `@llvm.nvvm.barrier.cta.red.*`
3452/// intrinsic ID.
3453static llvm::Intrinsic::ID
3454getBarrierReductionIntrinsic(bool aligned, NVVM::BarrierReduction kind) {
3455 switch (kind) {
3456 case NVVM::BarrierReduction::AND:
3457 return aligned ? llvm::Intrinsic::nvvm_barrier_cta_red_and_aligned_all
3458 : llvm::Intrinsic::nvvm_barrier_cta_red_and_all;
3459 case NVVM::BarrierReduction::OR:
3460 return aligned ? llvm::Intrinsic::nvvm_barrier_cta_red_or_aligned_all
3461 : llvm::Intrinsic::nvvm_barrier_cta_red_or_all;
3462 case NVVM::BarrierReduction::POPC:
3463 return aligned ? llvm::Intrinsic::nvvm_barrier_cta_red_popc_aligned_all
3464 : llvm::Intrinsic::nvvm_barrier_cta_red_popc_all;
3465 }
3466 llvm_unreachable("unknown BarrierReduction kind");
3467}
3468
3469mlir::NVVM::IDArgPair NVVM::BarrierOp::getIntrinsicIDAndArgs(
3470 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3471 auto thisOp = cast<NVVM::BarrierOp>(op);
3472 llvm::Value *barrierId = thisOp.getBarrierId()
3473 ? mt.lookupValue(thisOp.getBarrierId())
3474 : builder.getInt32(0);
3475 bool hasCount = static_cast<bool>(thisOp.getNumberOfThreads());
3476 llvm::Intrinsic::ID id =
3477 getBarrierSyncIntrinsic(thisOp.getAligned(), hasCount);
3478 llvm::SmallVector<llvm::Value *> args = {barrierId};
3479 if (hasCount)
3480 args.push_back(mt.lookupValue(thisOp.getNumberOfThreads()));
3481 return {id, std::move(args)};
3482}
3483
3484mlir::NVVM::IDArgPair NVVM::BarrierArriveOp::getIntrinsicIDAndArgs(
3485 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3486 auto thisOp = cast<NVVM::BarrierArriveOp>(op);
3487 llvm::Value *barrierId = thisOp.getBarrierId()
3488 ? mt.lookupValue(thisOp.getBarrierId())
3489 : builder.getInt32(0);
3490 llvm::Value *numThreads = mt.lookupValue(thisOp.getNumberOfThreads());
3491 llvm::Intrinsic::ID id =
3492 thisOp.getAligned()
3493 ? llvm::Intrinsic::nvvm_barrier_cta_arrive_aligned_count
3494 : llvm::Intrinsic::nvvm_barrier_cta_arrive_count;
3495 return {id, {barrierId, numThreads}};
3496}
3497
3498mlir::NVVM::IDArgPair NVVM::BarrierReductionOp::getIntrinsicIDAndArgs(
3499 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3500 auto thisOp = cast<NVVM::BarrierReductionOp>(op);
3501 llvm::Intrinsic::ID id = getBarrierReductionIntrinsic(
3502 thisOp.getAligned(), thisOp.getReductionOp());
3503 llvm::Value *barrierId = thisOp.getBarrierId()
3504 ? mt.lookupValue(thisOp.getBarrierId())
3505 : builder.getInt32(0);
3507 barrierId,
3508 builder.CreateICmpNE(mt.lookupValue(thisOp.getReductionPredicate()),
3509 builder.getInt32(0))};
3510 return {id, std::move(args)};
3511}
3512
3514CosOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
3515 llvm::IRBuilderBase &builder) {
3516 auto thisOp = cast<NVVM::CosOp>(op);
3517 llvm::Intrinsic::ID id = thisOp.getFtz()
3518 ? llvm::Intrinsic::nvvm_cos_approx_ftz_f
3519 : llvm::Intrinsic::nvvm_cos_approx_f;
3520 return {id, {mt.lookupValue(thisOp.getSrc())}};
3521}
3522
3524SinOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
3525 llvm::IRBuilderBase &builder) {
3526 auto thisOp = cast<NVVM::SinOp>(op);
3527 llvm::Intrinsic::ID id = thisOp.getFtz()
3528 ? llvm::Intrinsic::nvvm_sin_approx_ftz_f
3529 : llvm::Intrinsic::nvvm_sin_approx_f;
3530 return {id, {mt.lookupValue(thisOp.getSrc())}};
3531}
3532
3534Log2Op::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
3535 llvm::IRBuilderBase &builder) {
3536 auto thisOp = cast<NVVM::Log2Op>(op);
3537 llvm::Intrinsic::ID id = thisOp.getFtz()
3538 ? llvm::Intrinsic::nvvm_lg2_approx_ftz_f
3539 : llvm::Intrinsic::nvvm_lg2_approx_f;
3540 return {id, {mt.lookupValue(thisOp.getSrc())}};
3541}
3542
3544Ex2Op::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
3545 llvm::IRBuilderBase &builder) {
3546 auto thisOp = cast<NVVM::Ex2Op>(op);
3547 llvm::Intrinsic::ID id = thisOp.getFtz()
3548 ? llvm::Intrinsic::nvvm_ex2_approx_ftz
3549 : llvm::Intrinsic::nvvm_ex2_approx;
3550 return {id, {mt.lookupValue(thisOp.getSrc())}};
3551}
3552
3554RsqrtOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
3555 llvm::IRBuilderBase &builder) {
3556 auto thisOp = cast<NVVM::RsqrtOp>(op);
3557 Type t = thisOp.getRes().getType();
3558 bool isFtz = thisOp.getFtz();
3559
3560 llvm::Intrinsic::ID id = [&] {
3561 if (t.isF32()) {
3562 return isFtz ? llvm::Intrinsic::nvvm_rsqrt_approx_ftz_f
3563 : llvm::Intrinsic::nvvm_rsqrt_approx_f;
3564 }
3565 // f64
3566 return isFtz ? llvm::Intrinsic::nvvm_rsqrt_approx_ftz_d
3567 : llvm::Intrinsic::nvvm_rsqrt_approx_d;
3568 }();
3569
3570 return {id, {mt.lookupValue(thisOp.getSrc())}};
3571}
3572
3574SqrtOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
3575 llvm::IRBuilderBase &builder) {
3576 auto thisOp = cast<NVVM::SqrtOp>(op);
3577 Type t = thisOp.getRes().getType();
3578 NVVM::FPRoundingMode rndMode = thisOp.getRnd();
3579 bool isFtz = thisOp.getFtz();
3580
3581 // RM is one of RN/RM/RP/RZ (verifier rejects NONE).
3582 // Subtracting 1 maps RN=1..RZ=4 to 0..3.
3583 unsigned rndIndex = static_cast<unsigned>(rndMode) - 1;
3584
3585 static constexpr llvm::Intrinsic::ID f32IDs[] = {
3586 llvm::Intrinsic::nvvm_sqrt_rn_f,
3587 llvm::Intrinsic::nvvm_sqrt_rm_f,
3588 llvm::Intrinsic::nvvm_sqrt_rp_f,
3589 llvm::Intrinsic::nvvm_sqrt_rz_f,
3590 };
3591 static constexpr llvm::Intrinsic::ID f32FTZIDs[] = {
3592 llvm::Intrinsic::nvvm_sqrt_rn_ftz_f,
3593 llvm::Intrinsic::nvvm_sqrt_rm_ftz_f,
3594 llvm::Intrinsic::nvvm_sqrt_rp_ftz_f,
3595 llvm::Intrinsic::nvvm_sqrt_rz_ftz_f,
3596 };
3597 static constexpr llvm::Intrinsic::ID f64IDs[] = {
3598 llvm::Intrinsic::nvvm_sqrt_rn_d,
3599 llvm::Intrinsic::nvvm_sqrt_rm_d,
3600 llvm::Intrinsic::nvvm_sqrt_rp_d,
3601 llvm::Intrinsic::nvvm_sqrt_rz_d,
3602 };
3603
3604 llvm::Intrinsic::ID id =
3605 t.isF32() ? (isFtz ? f32FTZIDs[rndIndex] : f32IDs[rndIndex])
3606 : f64IDs[rndIndex];
3607
3608 return {id, {mt.lookupValue(thisOp.getSrc())}};
3609}
3610
3612SqrtApproxOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
3613 llvm::IRBuilderBase &builder) {
3614 auto thisOp = cast<NVVM::SqrtApproxOp>(op);
3615 llvm::Intrinsic::ID id = thisOp.getFtz()
3616 ? llvm::Intrinsic::nvvm_sqrt_approx_ftz_f
3617 : llvm::Intrinsic::nvvm_sqrt_approx_f;
3618 return {id, {mt.lookupValue(thisOp.getSrc())}};
3619}
3620
3622DivFOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
3623 llvm::IRBuilderBase &builder) {
3624 auto thisOp = cast<NVVM::DivFOp>(op);
3625 bool isFtz = thisOp.getFtz();
3626
3627 llvm::Intrinsic::ID id;
3628
3629 if (thisOp.getApprox()) {
3630 id = isFtz ? llvm::Intrinsic::nvvm_div_approx_ftz_f
3631 : llvm::Intrinsic::nvvm_div_approx_f;
3632 } else if (thisOp.getFull()) {
3633 // Intrinsic Naming quirk: int_nvvm_div_full has no `_f` suffix (unlike
3634 // approx).
3635 id = isFtz ? llvm::Intrinsic::nvvm_div_full_ftz
3636 : llvm::Intrinsic::nvvm_div_full;
3637 } else {
3638 // Rounded form — three 4-entry tables indexed by (rndMode - 1).
3639 unsigned rndIndex = static_cast<unsigned>(thisOp.getRnd()) - 1;
3640
3641 static constexpr llvm::Intrinsic::ID f32IDs[] = {
3642 llvm::Intrinsic::nvvm_div_rn_f,
3643 llvm::Intrinsic::nvvm_div_rm_f,
3644 llvm::Intrinsic::nvvm_div_rp_f,
3645 llvm::Intrinsic::nvvm_div_rz_f,
3646 };
3647 static constexpr llvm::Intrinsic::ID f32FTZIDs[] = {
3648 llvm::Intrinsic::nvvm_div_rn_ftz_f,
3649 llvm::Intrinsic::nvvm_div_rm_ftz_f,
3650 llvm::Intrinsic::nvvm_div_rp_ftz_f,
3651 llvm::Intrinsic::nvvm_div_rz_ftz_f,
3652 };
3653 static constexpr llvm::Intrinsic::ID f64IDs[] = {
3654 llvm::Intrinsic::nvvm_div_rn_d,
3655 llvm::Intrinsic::nvvm_div_rm_d,
3656 llvm::Intrinsic::nvvm_div_rp_d,
3657 llvm::Intrinsic::nvvm_div_rz_d,
3658 };
3659 Type t = thisOp.getRes().getType();
3660 id = t.isF32() ? (isFtz ? f32FTZIDs[rndIndex] : f32IDs[rndIndex])
3661 : f64IDs[rndIndex];
3662 }
3663
3664 return {id,
3665 {mt.lookupValue(thisOp.getLhs()), mt.lookupValue(thisOp.getRhs())}};
3666}
3667
3669PMEventOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
3670 llvm::IRBuilderBase &builder) {
3671 auto thisOp = cast<NVVM::PMEventOp>(op);
3672 llvm::Type *i16Ty = llvm::Type::getInt16Ty(mt.getLLVMContext());
3673
3674 // With event-id, mask is generated as (1 << event-id)
3675 llvm::Value *maskVal;
3676 if (auto eventAttr = thisOp.getEventIdAttr()) {
3677 uint16_t mask = static_cast<uint16_t>(1u << eventAttr.getInt());
3678 maskVal = llvm::ConstantInt::get(i16Ty, mask);
3679 } else {
3680 maskVal =
3681 llvm::ConstantInt::get(i16Ty, thisOp.getMaskedEventIdAttr().getValue());
3682 }
3683
3684 return {llvm::Intrinsic::nvvm_pm_event_mask, {maskVal}};
3685}
3686
3687mlir::NVVM::IDArgPair MBarrierInitOp::getIntrinsicIDAndArgs(
3688 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3689 auto thisOp = cast<NVVM::MBarrierInitOp>(op);
3690 bool isShared = isPtrInSharedCTASpace(thisOp.getAddr());
3691 llvm::Intrinsic::ID id = isShared ? llvm::Intrinsic::nvvm_mbarrier_init_shared
3692 : llvm::Intrinsic::nvvm_mbarrier_init;
3693
3694 // Fill the Intrinsic Args
3696 args.push_back(mt.lookupValue(thisOp.getAddr()));
3697 args.push_back(mt.lookupValue(thisOp.getCount()));
3698
3699 return {id, std::move(args)};
3700}
3701
3702mlir::NVVM::IDArgPair MBarrierInvalOp::getIntrinsicIDAndArgs(
3703 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3704 auto thisOp = cast<NVVM::MBarrierInvalOp>(op);
3705 bool isShared = isPtrInSharedCTASpace(thisOp.getAddr());
3706 llvm::Intrinsic::ID id = isShared
3707 ? llvm::Intrinsic::nvvm_mbarrier_inval_shared
3708 : llvm::Intrinsic::nvvm_mbarrier_inval;
3709
3710 return {id, {mt.lookupValue(thisOp.getAddr())}};
3711}
3712
3713mlir::NVVM::IDArgPair MBarrierExpectTxOp::getIntrinsicIDAndArgs(
3714 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3715 auto thisOp = cast<NVVM::MBarrierExpectTxOp>(op);
3716
3717 bool isClusterSpace = isPtrInSharedClusterSpace(thisOp.getAddr());
3718 bool isClusterScope = thisOp.getScope() == NVVM::MemScopeKind::CLUSTER;
3719 // bit-0: Space
3720 // bit-1: Scope
3721 size_t index = ((isClusterScope ? 1 : 0) << 1) | (isClusterSpace ? 1 : 0);
3722
3723 static constexpr llvm::Intrinsic::ID IDs[] = {
3724 llvm::Intrinsic::nvvm_mbarrier_expect_tx_scope_cta_space_cta,
3725 llvm::Intrinsic::nvvm_mbarrier_expect_tx_scope_cta_space_cluster,
3726 llvm::Intrinsic::nvvm_mbarrier_expect_tx_scope_cluster_space_cta,
3727 llvm::Intrinsic::nvvm_mbarrier_expect_tx_scope_cluster_space_cluster};
3728
3729 // Fill the Intrinsic Args
3731 args.push_back(mt.lookupValue(thisOp.getAddr()));
3732 args.push_back(mt.lookupValue(thisOp.getTxcount()));
3733
3734 return {IDs[index], std::move(args)};
3735}
3736
3737mlir::NVVM::IDArgPair MBarrierCompleteTxOp::getIntrinsicIDAndArgs(
3738 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3739 auto thisOp = cast<NVVM::MBarrierCompleteTxOp>(op);
3740
3741 bool isClusterSpace = isPtrInSharedClusterSpace(thisOp.getAddr());
3742 bool isClusterScope = thisOp.getScope() == NVVM::MemScopeKind::CLUSTER;
3743 // bit-0: Space
3744 // bit-1: Scope
3745 size_t index = ((isClusterScope ? 1 : 0) << 1) | (isClusterSpace ? 1 : 0);
3746
3747 static constexpr llvm::Intrinsic::ID IDs[] = {
3748 llvm::Intrinsic::nvvm_mbarrier_complete_tx_scope_cta_space_cta,
3749 llvm::Intrinsic::nvvm_mbarrier_complete_tx_scope_cta_space_cluster,
3750 llvm::Intrinsic::nvvm_mbarrier_complete_tx_scope_cluster_space_cta,
3751 llvm::Intrinsic::nvvm_mbarrier_complete_tx_scope_cluster_space_cluster};
3752
3753 // Fill the Intrinsic Args
3755 args.push_back(mt.lookupValue(thisOp.getAddr()));
3756 args.push_back(mt.lookupValue(thisOp.getTxcount()));
3757
3758 return {IDs[index], std::move(args)};
3759}
3760
3761mlir::NVVM::IDArgPair MBarrierArriveOp::getIntrinsicIDAndArgs(
3762 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3763 auto thisOp = cast<NVVM::MBarrierArriveOp>(op);
3764
3765 bool isClusterSpace = isPtrInSharedClusterSpace(thisOp.getAddr());
3766 bool isClusterScope = thisOp.getScope() == NVVM::MemScopeKind::CLUSTER;
3767 // bit-0: Space
3768 // bit-1: Scope
3769 size_t index = ((isClusterScope ? 1 : 0) << 1) | (isClusterSpace ? 1 : 0);
3770
3771 static constexpr llvm::Intrinsic::ID IDs[] = {
3772 llvm::Intrinsic::nvvm_mbarrier_arrive_scope_cta_space_cta,
3773 llvm::Intrinsic::nvvm_mbarrier_arrive_scope_cta_space_cluster,
3774 llvm::Intrinsic::nvvm_mbarrier_arrive_scope_cluster_space_cta,
3775 llvm::Intrinsic::nvvm_mbarrier_arrive_scope_cluster_space_cluster};
3776 static constexpr llvm::Intrinsic::ID relaxedIDs[] = {
3777 llvm::Intrinsic::nvvm_mbarrier_arrive_relaxed_scope_cta_space_cta,
3778 llvm::Intrinsic::nvvm_mbarrier_arrive_relaxed_scope_cta_space_cluster,
3779 llvm::Intrinsic::nvvm_mbarrier_arrive_relaxed_scope_cluster_space_cta,
3780 llvm::Intrinsic::
3781 nvvm_mbarrier_arrive_relaxed_scope_cluster_space_cluster};
3782 auto id = thisOp.getRelaxed() ? relaxedIDs[index] : IDs[index];
3783
3784 // Tidy-up the Intrinsic Args
3785 bool needCast = isPtrInGenericSpace(thisOp.getAddr());
3786 llvm::Value *mbar = mt.lookupValue(thisOp.getAddr());
3787 if (needCast)
3788 mbar = castPtrToAddrSpace(builder, mbar, NVVMMemorySpace::Shared);
3789
3790 // We have the most basic mbarrier.arrive supported on sm_80.
3791 // It supports: Space=cta, scope=cta, No relaxed, No explicit count.
3792 // So, only for this combination use the legacy intrinsic.
3793 bool hasCount = static_cast<bool>(thisOp.getCount());
3794 if (!hasCount &&
3795 (id == llvm::Intrinsic::nvvm_mbarrier_arrive_scope_cta_space_cta))
3796 return {llvm::Intrinsic::nvvm_mbarrier_arrive_shared, {mbar}};
3797
3798 // When count is not explicitly specified, the default is 1.
3799 llvm::LLVMContext &ctx = mt.getLLVMContext();
3800 llvm::Value *count =
3801 hasCount ? mt.lookupValue(thisOp.getCount())
3802 : llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), 1);
3803 return {id, {mbar, count}};
3804}
3805
3806mlir::NVVM::IDArgPair MBarrierArriveDropOp::getIntrinsicIDAndArgs(
3807 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3808 auto thisOp = cast<NVVM::MBarrierArriveDropOp>(op);
3809
3810 bool isClusterSpace = isPtrInSharedClusterSpace(thisOp.getAddr());
3811 bool isClusterScope = thisOp.getScope() == NVVM::MemScopeKind::CLUSTER;
3812 // bit-0: Space
3813 // bit-1: Scope
3814 size_t index = ((isClusterScope ? 1 : 0) << 1) | (isClusterSpace ? 1 : 0);
3815
3816 static constexpr llvm::Intrinsic::ID IDs[] = {
3817 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_scope_cta_space_cta,
3818 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_scope_cta_space_cluster,
3819 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_scope_cluster_space_cta,
3820 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_scope_cluster_space_cluster};
3821 static constexpr llvm::Intrinsic::ID relaxedIDs[] = {
3822 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_relaxed_scope_cta_space_cta,
3823 llvm::Intrinsic::
3824 nvvm_mbarrier_arrive_drop_relaxed_scope_cta_space_cluster,
3825 llvm::Intrinsic::
3826 nvvm_mbarrier_arrive_drop_relaxed_scope_cluster_space_cta,
3827 llvm::Intrinsic::
3828 nvvm_mbarrier_arrive_drop_relaxed_scope_cluster_space_cluster};
3829 auto id = thisOp.getRelaxed() ? relaxedIDs[index] : IDs[index];
3830
3831 // Tidy-up the Intrinsic Args
3832 bool needCast = isPtrInGenericSpace(thisOp.getAddr());
3833 llvm::Value *mbar = mt.lookupValue(thisOp.getAddr());
3834 if (needCast)
3835 mbar = castPtrToAddrSpace(builder, mbar, NVVMMemorySpace::Shared);
3836
3837 // When count is not explicitly specified, the default is 1.
3838 llvm::LLVMContext &ctx = mt.getLLVMContext();
3839 bool hasCount = static_cast<bool>(thisOp.getCount());
3840 llvm::Value *count =
3841 hasCount ? mt.lookupValue(thisOp.getCount())
3842 : llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), 1);
3843
3844 return {id, {mbar, count}};
3845}
3846
3847bool MBarrierArriveExpectTxOp::getAsmValues(
3848 RewriterBase &rewriter,
3849 llvm::SmallVectorImpl<std::pair<mlir::Value, mlir::NVVM::PTXRegisterMod>>
3850 &asmValues) {
3851 // Add all the operands but not the attrs to the asmValues list.
3852 // The attrs here are used to generate the right variants for
3853 // intrinsics-lowering. So, we ignore them while generating inline-PTX.
3854 for (auto val : getOperands())
3855 asmValues.push_back({val, mlir::NVVM::PTXRegisterMod::Read});
3856
3857 return false;
3858}
3859
3860mlir::NVVM::IDArgPair MBarrierArriveExpectTxOp::getIntrinsicIDAndArgs(
3861 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3862 auto thisOp = cast<NVVM::MBarrierArriveExpectTxOp>(op);
3863
3864 bool isClusterSpace = isPtrInSharedClusterSpace(thisOp.getAddr());
3865 bool isClusterScope = thisOp.getScope() == NVVM::MemScopeKind::CLUSTER;
3866 // bit-0: Space
3867 // bit-1: Scope
3868 size_t index = ((isClusterScope ? 1 : 0) << 1) | (isClusterSpace ? 1 : 0);
3869
3870 // clang-format off
3871 static constexpr llvm::Intrinsic::ID IDs[] = {
3872 llvm::Intrinsic::nvvm_mbarrier_arrive_expect_tx_scope_cta_space_cta,
3873 llvm::Intrinsic::nvvm_mbarrier_arrive_expect_tx_scope_cta_space_cluster,
3874 llvm::Intrinsic::nvvm_mbarrier_arrive_expect_tx_scope_cluster_space_cta,
3875 llvm::Intrinsic::nvvm_mbarrier_arrive_expect_tx_scope_cluster_space_cluster};
3876 static constexpr llvm::Intrinsic::ID relaxedIDs[] = {
3877 llvm::Intrinsic::nvvm_mbarrier_arrive_expect_tx_relaxed_scope_cta_space_cta,
3878 llvm::Intrinsic::nvvm_mbarrier_arrive_expect_tx_relaxed_scope_cta_space_cluster,
3879 llvm::Intrinsic::nvvm_mbarrier_arrive_expect_tx_relaxed_scope_cluster_space_cta,
3880 llvm::Intrinsic::nvvm_mbarrier_arrive_expect_tx_relaxed_scope_cluster_space_cluster};
3881 // clang-format on
3882 auto id = thisOp.getRelaxed() ? relaxedIDs[index] : IDs[index];
3883
3884 // Tidy-up the Intrinsic Args
3885 llvm::Value *txcount = mt.lookupValue(thisOp.getTxcount());
3886 llvm::Value *mbar = mt.lookupValue(thisOp.getAddr());
3887 bool needCast = isPtrInGenericSpace(thisOp.getAddr());
3888 if (needCast)
3889 mbar = castPtrToAddrSpace(builder, mbar, NVVMMemorySpace::Shared);
3890
3891 return {id, {mbar, txcount}};
3892}
3893
3894mlir::NVVM::IDArgPair MBarrierArriveDropExpectTxOp::getIntrinsicIDAndArgs(
3895 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3896 auto thisOp = cast<NVVM::MBarrierArriveDropExpectTxOp>(op);
3897
3898 bool isClusterSpace = isPtrInSharedClusterSpace(thisOp.getAddr());
3899 bool isClusterScope = thisOp.getScope() == NVVM::MemScopeKind::CLUSTER;
3900 // bit-0: Space
3901 // bit-1: Scope
3902 size_t index = ((isClusterScope ? 1 : 0) << 1) | (isClusterSpace ? 1 : 0);
3903
3904 // clang-format off
3905 static constexpr llvm::Intrinsic::ID IDs[] = {
3906 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_expect_tx_scope_cta_space_cta,
3907 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_expect_tx_scope_cta_space_cluster,
3908 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_expect_tx_scope_cluster_space_cta,
3909 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_expect_tx_scope_cluster_space_cluster};
3910 static constexpr llvm::Intrinsic::ID relaxedIDs[] = {
3911 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_expect_tx_relaxed_scope_cta_space_cta,
3912 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_expect_tx_relaxed_scope_cta_space_cluster,
3913 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_expect_tx_relaxed_scope_cluster_space_cta,
3914 llvm::Intrinsic::nvvm_mbarrier_arrive_drop_expect_tx_relaxed_scope_cluster_space_cluster};
3915 // clang-format on
3916 auto id = thisOp.getRelaxed() ? relaxedIDs[index] : IDs[index];
3917
3918 // Tidy-up the Intrinsic Args
3919 llvm::Value *txcount = mt.lookupValue(thisOp.getTxcount());
3920 llvm::Value *mbar = mt.lookupValue(thisOp.getAddr());
3921 bool needCast = isPtrInGenericSpace(thisOp.getAddr());
3922 if (needCast)
3923 mbar = castPtrToAddrSpace(builder, mbar, NVVMMemorySpace::Shared);
3924
3925 return {id, {mbar, txcount}};
3926}
3927
3928mlir::NVVM::IDArgPair MBarrierArriveNocompleteOp::getIntrinsicIDAndArgs(
3929 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3930 auto thisOp = cast<NVVM::MBarrierArriveNocompleteOp>(op);
3931 bool isShared = isPtrInSharedCTASpace(thisOp.getAddr());
3932 llvm::Intrinsic::ID id =
3933 isShared ? llvm::Intrinsic::nvvm_mbarrier_arrive_noComplete_shared
3934 : llvm::Intrinsic::nvvm_mbarrier_arrive_noComplete;
3935 // Fill the Intrinsic Args
3937 args.push_back(mt.lookupValue(thisOp.getAddr()));
3938 args.push_back(mt.lookupValue(thisOp.getCount()));
3939
3940 return {id, std::move(args)};
3941}
3942
3943mlir::NVVM::IDArgPair MBarrierArriveDropNocompleteOp::getIntrinsicIDAndArgs(
3944 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3945 auto thisOp = cast<NVVM::MBarrierArriveDropNocompleteOp>(op);
3946 bool isShared = isPtrInSharedCTASpace(thisOp.getAddr());
3947 llvm::Intrinsic::ID id =
3948 isShared ? llvm::Intrinsic::nvvm_mbarrier_arrive_drop_noComplete_shared
3949 : llvm::Intrinsic::nvvm_mbarrier_arrive_drop_noComplete;
3950 // Fill the Intrinsic Args
3952 args.push_back(mt.lookupValue(thisOp.getAddr()));
3953 args.push_back(mt.lookupValue(thisOp.getCount()));
3954
3955 return {id, std::move(args)};
3956}
3957
3958mlir::NVVM::IDArgPair MBarrierTestWaitOp::getIntrinsicIDAndArgs(
3959 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3960 auto thisOp = cast<NVVM::MBarrierTestWaitOp>(op);
3961 bool isPhaseParity = thisOp.getStateOrPhase().getType().isInteger(32);
3962 bool isClusterScope = thisOp.getScope() == NVVM::MemScopeKind::CLUSTER;
3963 // bit-0: isPhaseParity
3964 // bit-1: Scope
3965 size_t index = ((isClusterScope ? 1 : 0) << 1) | (isPhaseParity ? 1 : 0);
3966
3967 // clang-format off
3968 static constexpr llvm::Intrinsic::ID IDs[] = {
3969 llvm::Intrinsic::nvvm_mbarrier_test_wait_scope_cta_space_cta,
3970 llvm::Intrinsic::nvvm_mbarrier_test_wait_parity_scope_cta_space_cta,
3971 llvm::Intrinsic::nvvm_mbarrier_test_wait_scope_cluster_space_cta,
3972 llvm::Intrinsic::nvvm_mbarrier_test_wait_parity_scope_cluster_space_cta};
3973 static constexpr llvm::Intrinsic::ID relaxedIDs[] = {
3974 llvm::Intrinsic::nvvm_mbarrier_test_wait_relaxed_scope_cta_space_cta,
3975 llvm::Intrinsic::nvvm_mbarrier_test_wait_parity_relaxed_scope_cta_space_cta,
3976 llvm::Intrinsic::nvvm_mbarrier_test_wait_relaxed_scope_cluster_space_cta,
3977 llvm::Intrinsic::nvvm_mbarrier_test_wait_parity_relaxed_scope_cluster_space_cta};
3978 // clang-format on
3979 auto id = thisOp.getRelaxed() ? relaxedIDs[index] : IDs[index];
3980
3981 // Tidy-up the Intrinsic Args
3982 llvm::Value *mbar = mt.lookupValue(thisOp.getAddr());
3983 llvm::Value *input = mt.lookupValue(thisOp.getStateOrPhase());
3984 bool needCast = isPtrInGenericSpace(thisOp.getAddr());
3985 if (needCast)
3986 mbar = castPtrToAddrSpace(builder, mbar, NVVMMemorySpace::Shared);
3987
3988 return {id, {mbar, input}};
3989}
3990
3991mlir::NVVM::IDArgPair MBarrierTryWaitOp::getIntrinsicIDAndArgs(
3992 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
3993 auto thisOp = cast<NVVM::MBarrierTryWaitOp>(op);
3994 bool isPhaseParity = thisOp.getStateOrPhase().getType().isInteger(32);
3995 bool isClusterScope = thisOp.getScope() == NVVM::MemScopeKind::CLUSTER;
3996 bool hasTicks = static_cast<bool>(thisOp.getTicks());
3997 // bit-0: isPhaseParity
3998 // bit-1: Scope
3999 // bit-2: hasTicks
4000 size_t index = ((hasTicks ? 1 : 0) << 2) | ((isClusterScope ? 1 : 0) << 1) |
4001 (isPhaseParity ? 1 : 0);
4002
4003 // clang-format off
4004 static constexpr llvm::Intrinsic::ID IDs[] = {
4005 llvm::Intrinsic::nvvm_mbarrier_try_wait_scope_cta_space_cta,
4006 llvm::Intrinsic::nvvm_mbarrier_try_wait_parity_scope_cta_space_cta,
4007 llvm::Intrinsic::nvvm_mbarrier_try_wait_scope_cluster_space_cta,
4008 llvm::Intrinsic::nvvm_mbarrier_try_wait_parity_scope_cluster_space_cta,
4009 llvm::Intrinsic::nvvm_mbarrier_try_wait_tl_scope_cta_space_cta,
4010 llvm::Intrinsic::nvvm_mbarrier_try_wait_parity_tl_scope_cta_space_cta,
4011 llvm::Intrinsic::nvvm_mbarrier_try_wait_tl_scope_cluster_space_cta,
4012 llvm::Intrinsic::nvvm_mbarrier_try_wait_parity_tl_scope_cluster_space_cta};
4013 static constexpr llvm::Intrinsic::ID relaxedIDs[] = {
4014 llvm::Intrinsic::nvvm_mbarrier_try_wait_relaxed_scope_cta_space_cta,
4015 llvm::Intrinsic::nvvm_mbarrier_try_wait_parity_relaxed_scope_cta_space_cta,
4016 llvm::Intrinsic::nvvm_mbarrier_try_wait_relaxed_scope_cluster_space_cta,
4017 llvm::Intrinsic::nvvm_mbarrier_try_wait_parity_relaxed_scope_cluster_space_cta,
4018 llvm::Intrinsic::nvvm_mbarrier_try_wait_tl_relaxed_scope_cta_space_cta,
4019 llvm::Intrinsic::nvvm_mbarrier_try_wait_parity_tl_relaxed_scope_cta_space_cta,
4020 llvm::Intrinsic::nvvm_mbarrier_try_wait_tl_relaxed_scope_cluster_space_cta,
4021 llvm::Intrinsic::nvvm_mbarrier_try_wait_parity_tl_relaxed_scope_cluster_space_cta};
4022 // clang-format on
4023 auto id = thisOp.getRelaxed() ? relaxedIDs[index] : IDs[index];
4024
4025 // Tidy-up the mbarrier pointer
4026 llvm::Value *mbar = mt.lookupValue(thisOp.getAddr());
4027 bool needCast = isPtrInGenericSpace(thisOp.getAddr());
4028 if (needCast)
4029 mbar = castPtrToAddrSpace(builder, mbar, NVVMMemorySpace::Shared);
4030
4031 // Fill the Intrinsic Args
4033 args.push_back(mbar);
4034 args.push_back(mt.lookupValue(thisOp.getStateOrPhase()));
4035 if (hasTicks)
4036 args.push_back(mt.lookupValue(thisOp.getTicks()));
4037
4038 return {id, std::move(args)};
4039}
4040
4041mlir::NVVM::IDArgPair CpAsyncMBarrierArriveOp::getIntrinsicIDAndArgs(
4042 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4043 auto thisOp = cast<NVVM::CpAsyncMBarrierArriveOp>(op);
4044 bool isShared = isPtrInSharedCTASpace(thisOp.getAddr());
4045
4046 llvm::Intrinsic::ID id;
4047 if (thisOp.getNoinc()) {
4048 id = isShared ? llvm::Intrinsic::nvvm_cp_async_mbarrier_arrive_noinc_shared
4049 : llvm::Intrinsic::nvvm_cp_async_mbarrier_arrive_noinc;
4050 } else {
4051 id = isShared ? llvm::Intrinsic::nvvm_cp_async_mbarrier_arrive_shared
4052 : llvm::Intrinsic::nvvm_cp_async_mbarrier_arrive;
4053 }
4054
4055 return {id, {mt.lookupValue(thisOp.getAddr())}};
4056}
4057
4059MovMatrixOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
4060 llvm::IRBuilderBase &builder) {
4061 auto thisOp = cast<NVVM::MovMatrixOp>(op);
4062 return {llvm::Intrinsic::nvvm_movmatrix_sync_aligned_m8n8_trans_b16,
4063 {mt.lookupValue(thisOp.getSrc())}};
4064}
4065
4066#define CP_ASYNC_ID_IMPL(mod, size, suffix) \
4067 llvm::Intrinsic::nvvm_cp_async_##mod##_shared_global_##size##suffix
4068
4069#define GET_CP_ASYNC_ID(mod, size, has_cpsize) \
4070 has_cpsize ? CP_ASYNC_ID_IMPL(mod, size, _s) : CP_ASYNC_ID_IMPL(mod, size, )
4071
4072llvm::Intrinsic::ID
4073CpAsyncOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
4075 llvm::Intrinsic::ID id;
4076
4077 auto cpAsyncOp = cast<NVVM::CpAsyncOp>(op);
4078 bool hasCpSize = static_cast<bool>(cpAsyncOp.getCpSize());
4079 switch (cpAsyncOp.getSize()) {
4080 case 4:
4081 id = GET_CP_ASYNC_ID(ca, 4, hasCpSize);
4082 break;
4083 case 8:
4084 id = GET_CP_ASYNC_ID(ca, 8, hasCpSize);
4085 break;
4086 case 16:
4087 id = (cpAsyncOp.getModifier() == NVVM::LoadCacheModifierKind::CG)
4088 ? GET_CP_ASYNC_ID(cg, 16, hasCpSize)
4089 : GET_CP_ASYNC_ID(ca, 16, hasCpSize);
4090 break;
4091 default:
4092 llvm_unreachable("Invalid copy size in CpAsyncOp.");
4093 }
4094
4095 // Fill the Intrinsic Args
4096 args.push_back(mt.lookupValue(cpAsyncOp.getDst()));
4097 args.push_back(mt.lookupValue(cpAsyncOp.getSrc()));
4098 if (hasCpSize)
4099 args.push_back(mt.lookupValue(cpAsyncOp.getCpSize()));
4100
4101 return id;
4102}
4103
4104mlir::NVVM::IDArgPair CpAsyncBulkPrefetchOp::getIntrinsicIDAndArgs(
4105 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4106 auto thisOp = cast<NVVM::CpAsyncBulkPrefetchOp>(op);
4108 llvm::Intrinsic::ID id = llvm::Intrinsic::nvvm_cp_async_bulk_prefetch_L2;
4109
4110 // Fill the Intrinsic Args
4111 args.push_back(mt.lookupValue(thisOp.getSrcMem()));
4112 args.push_back(mt.lookupValue(thisOp.getSize()));
4113
4114 mlir::Value cacheHint = thisOp.getL2CacheHint();
4115 const bool hasCacheHint = static_cast<bool>(cacheHint);
4116 llvm::Value *i64Unused =
4117 llvm::ConstantInt::get(llvm::Type::getInt64Ty(mt.getLLVMContext()), 0);
4118 args.push_back(hasCacheHint ? mt.lookupValue(cacheHint) : i64Unused);
4119 args.push_back(builder.getInt1(hasCacheHint));
4120
4121 return {id, std::move(args)};
4122}
4123
4124mlir::NVVM::IDArgPair CpAsyncBulkGlobalToSharedClusterOp::getIntrinsicIDAndArgs(
4125 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4126 auto thisOp = cast<NVVM::CpAsyncBulkGlobalToSharedClusterOp>(op);
4128
4129 // Fill the Intrinsic Args: dst, mbar, src, size.
4130 args.push_back(mt.lookupValue(thisOp.getDstMem()));
4131 args.push_back(mt.lookupValue(thisOp.getMbar()));
4132 args.push_back(mt.lookupValue(thisOp.getSrcMem()));
4133 args.push_back(mt.lookupValue(thisOp.getSize()));
4134
4135 // Multicast mask for shared::cluster only, if available.
4136 mlir::Value multicastMask = thisOp.getMulticastMask();
4137 const bool hasMulticastMask = static_cast<bool>(multicastMask);
4138 const bool isSharedCTA = isPtrInSharedCTASpace(thisOp.getDstMem());
4139 if (!isSharedCTA) {
4140 llvm::Value *i16Unused = llvm::ConstantInt::get(builder.getInt16Ty(), 0);
4141 args.push_back(hasMulticastMask ? mt.lookupValue(multicastMask)
4142 : i16Unused);
4143 }
4144
4145 // Cache hint, if available.
4146 mlir::Value cacheHint = thisOp.getL2CacheHint();
4147 const bool hasCacheHint = static_cast<bool>(cacheHint);
4148 llvm::Value *i64Unused = llvm::ConstantInt::get(builder.getInt64Ty(), 0);
4149 args.push_back(hasCacheHint ? mt.lookupValue(cacheHint) : i64Unused);
4150
4151 // Flag arguments for multicast and cachehint.
4152 if (!isSharedCTA)
4153 args.push_back(builder.getInt1(hasMulticastMask));
4154 args.push_back(builder.getInt1(hasCacheHint));
4155
4156 llvm::Intrinsic::ID id =
4157 isSharedCTA
4158 ? llvm::Intrinsic::nvvm_cp_async_bulk_global_to_shared_cta
4159 : llvm::Intrinsic::nvvm_cp_async_bulk_global_to_shared_cluster;
4160
4161 return {id, std::move(args)};
4162}
4163
4164mlir::NVVM::IDArgPair CpAsyncBulkSharedCTAToGlobalOp::getIntrinsicIDAndArgs(
4165 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4166 auto thisOp = cast<NVVM::CpAsyncBulkSharedCTAToGlobalOp>(op);
4168 llvm::Intrinsic::ID id =
4169 llvm::Intrinsic::nvvm_cp_async_bulk_shared_cta_to_global;
4170
4171 // Fill the Intrinsic Args
4172 args.push_back(mt.lookupValue(thisOp.getDstMem()));
4173 args.push_back(mt.lookupValue(thisOp.getSrcMem()));
4174 args.push_back(mt.lookupValue(thisOp.getSize()));
4175
4176 mlir::Value cacheHint = thisOp.getL2CacheHint();
4177 const bool hasCacheHint = static_cast<bool>(cacheHint);
4178 llvm::Value *i64Unused =
4179 llvm::ConstantInt::get(llvm::Type::getInt64Ty(mt.getLLVMContext()), 0);
4180 args.push_back(hasCacheHint ? mt.lookupValue(cacheHint) : i64Unused);
4181 args.push_back(builder.getInt1(hasCacheHint));
4182
4183 // Choose the bytemask variant
4184 if (mlir::Value byteMask = thisOp.getByteMask()) {
4185 args.push_back(mt.lookupValue(byteMask));
4186 id = llvm::Intrinsic::nvvm_cp_async_bulk_shared_cta_to_global_bytemask;
4187 }
4188
4189 return {id, std::move(args)};
4190}
4191
4192bool CpAsyncBulkTensorGlobalToSharedClusterOp::getAsmValues(
4193 RewriterBase &rewriter,
4194 llvm::SmallVectorImpl<std::pair<mlir::Value, mlir::NVVM::PTXRegisterMod>>
4195 &asmValues) {
4196 // Add all the operands but not the attrs to the asmValues list.
4197 // The attrs here are used to generate the right variants for
4198 // intrinsics-lowering. So, we ignore them while generating inline-PTX.
4199 for (auto val : getOperands())
4200 asmValues.push_back({val, mlir::NVVM::PTXRegisterMod::Read});
4201
4202 return false;
4203}
4204
4206CpAsyncBulkTensorGlobalToSharedClusterOp::getIntrinsicIDAndArgs(
4207 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4208 auto thisOp = cast<NVVM::CpAsyncBulkTensorGlobalToSharedClusterOp>(op);
4209 const bool isCTAOnly = thisOp.getIsCTAOnly();
4211
4212 // Fill the Intrinsic Args
4213 args.push_back(mt.lookupValue(thisOp.getDstMem()));
4214 args.push_back(mt.lookupValue(thisOp.getMbar()));
4215 args.push_back(mt.lookupValue(thisOp.getTmaDescriptor()));
4216
4217 // Coordinates and im2col-offsets
4218 for (mlir::Value v : thisOp.getCoordinates())
4219 args.push_back(mt.lookupValue(v));
4220 for (mlir::Value v : thisOp.getIm2colOffsets())
4221 args.push_back(mt.lookupValue(v));
4222
4223 // MulticastMask, if available
4224 mlir::Value mcMask = thisOp.getMulticastMask();
4225 const bool hasMC = static_cast<bool>(mcMask);
4226 llvm::Value *i16Zero =
4227 llvm::ConstantInt::get(llvm::Type::getInt16Ty(mt.getLLVMContext()), 0);
4228
4229 // CacheHint, if available
4230 mlir::Value cacheHint = thisOp.getL2CacheHint();
4231 const bool hasCacheHint = static_cast<bool>(cacheHint);
4232 llvm::Value *i64Zero =
4233 llvm::ConstantInt::get(llvm::Type::getInt64Ty(mt.getLLVMContext()), 0);
4234
4235 // Flag argument CTAGroup
4236 // CTA_1/2 is mapped to values 1 and 2 for the intrinsics.
4237 // Hence, the +1 to getGroup().
4238 const int32_t val =
4239 thisOp.getGroup() ? (static_cast<int32_t>(*thisOp.getGroup()) + 1) : 0;
4240 llvm::Value *cg =
4241 llvm::ConstantInt::get(llvm::Type::getInt32Ty(mt.getLLVMContext()), val);
4242
4243 if (!isCTAOnly) {
4244 // For shared::cluster, all the arguments that we build are applicable.
4245 args.push_back(hasMC ? mt.lookupValue(mcMask) : i16Zero);
4246 args.push_back(hasCacheHint ? mt.lookupValue(cacheHint) : i64Zero);
4247 args.push_back(builder.getInt1(hasMC));
4248 args.push_back(builder.getInt1(hasCacheHint));
4249 args.push_back(cg);
4250 } else {
4251 // For shared::cta, only cache-hint is applicable.
4252 args.push_back(hasCacheHint ? mt.lookupValue(cacheHint) : i64Zero);
4253 args.push_back(builder.getInt1(hasCacheHint));
4254 }
4255
4256 constexpr size_t numDims = 5; // 1D to 5D
4257 constexpr size_t numModes = 5; // Tile, Im2col, w, w_128, gather4
4258 using rowTy = std::array<llvm::Intrinsic::ID, numDims + 1>;
4259 using TableTy = std::array<rowTy, numModes>;
4260 static constexpr TableTy IDTable{
4261 {{notIntrinsic, llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_tile_1d,
4262 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_tile_2d,
4263 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_tile_3d,
4264 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_tile_4d,
4265 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_tile_5d},
4267 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_im2col_3d,
4268 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_im2col_4d,
4269 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_im2col_5d},
4271 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_im2col_w_3d,
4272 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_im2col_w_4d,
4273 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_im2col_w_5d},
4275 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_im2col_w_128_3d,
4276 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_im2col_w_128_4d,
4277 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_im2col_w_128_5d},
4279 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_tile_gather4_2d}}};
4280
4281 static constexpr TableTy IDTableCTA{
4282 {{notIntrinsic,
4283 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_tile_1d,
4284 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_tile_2d,
4285 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_tile_3d,
4286 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_tile_4d,
4287 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_tile_5d},
4289 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_im2col_3d,
4290 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_im2col_4d,
4291 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_im2col_5d},
4293 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_im2col_w_3d,
4294 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_im2col_w_4d,
4295 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_im2col_w_5d},
4297 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_im2col_w_128_3d,
4298 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_im2col_w_128_4d,
4299 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_im2col_w_128_5d},
4301 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_g2s_cta_tile_gather4_2d}}};
4302
4303 static_assert(
4304 (getMaxEnumValForTMALoadMode() == std::size(IDTable) - 1) &&
4305 (getMaxEnumValForTMALoadMode() == std::size(IDTableCTA) - 1),
4306 "TMALoadModes must match number of rows in IDTable and IDTableCTA");
4307 size_t mode = static_cast<size_t>(thisOp.getMode());
4308 size_t dim = thisOp.getCoordinates().size();
4309 auto id = isCTAOnly ? IDTableCTA[mode][dim] : IDTable[mode][dim];
4310 assert(id != notIntrinsic &&
4311 "Invalid intrinsic for CpAsyncBulkTensorGlobalToSharedClusterOp.");
4312
4313 return {id, std::move(args)};
4314}
4315
4316mlir::NVVM::IDArgPair CpAsyncBulkTensorPrefetchOp::getIntrinsicIDAndArgs(
4317 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4318 auto thisOp = cast<NVVM::CpAsyncBulkTensorPrefetchOp>(op);
4320
4321 // Fill the Intrinsic Args
4322 args.push_back(mt.lookupValue(thisOp.getTmaDescriptor()));
4323
4324 for (auto v : thisOp.getCoordinates())
4325 args.push_back(mt.lookupValue(v));
4326 for (auto v : thisOp.getIm2colOffsets())
4327 args.push_back(mt.lookupValue(v));
4328
4329 mlir::Value cacheHint = thisOp.getL2CacheHint();
4330 const bool hasCacheHint = static_cast<bool>(cacheHint);
4331 llvm::Value *i64Unused =
4332 llvm::ConstantInt::get(llvm::Type::getInt64Ty(mt.getLLVMContext()), 0);
4333 args.push_back(hasCacheHint ? mt.lookupValue(cacheHint) : i64Unused);
4334 args.push_back(builder.getInt1(hasCacheHint));
4335
4336 const unsigned NI = llvm::Intrinsic::not_intrinsic;
4337 static constexpr llvm::Intrinsic::ID IDTable[][6] = {
4338 {NI, llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_tile_1d,
4339 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_tile_2d,
4340 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_tile_3d,
4341 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_tile_4d,
4342 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_tile_5d},
4343 {NI, NI, NI,
4344 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_im2col_3d,
4345 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_im2col_4d,
4346 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_im2col_5d},
4347 {NI, NI, NI,
4348 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_im2col_w_3d,
4349 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_im2col_w_4d,
4350 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_im2col_w_5d},
4351 {NI, NI, NI,
4352 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_im2col_w_128_3d,
4353 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_im2col_w_128_4d,
4354 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_im2col_w_128_5d},
4355 {NI, NI, NI, NI, NI,
4356 llvm::Intrinsic::nvvm_cp_async_bulk_tensor_prefetch_tile_gather4_2d}};
4357
4358 static_assert(getMaxEnumValForTMALoadMode() == std::size(IDTable) - 1,
4359 "TMALoadModes must match number of rows in IDTable");
4360 size_t mode = static_cast<size_t>(thisOp.getMode());
4361 size_t dim = thisOp.getCoordinates().size();
4362 llvm::Intrinsic::ID id = IDTable[mode][dim];
4363 if (id == llvm::Intrinsic::not_intrinsic)
4364 llvm_unreachable("Invalid intrinsic for CpAsyncBulkTensorPrefetchOp.");
4365
4366 return {id, std::move(args)};
4367}
4368
4370CpAsyncBulkTensorSharedCTAToGlobalOp::getIntrinsicIDAndArgs(
4371 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4372 auto thisOp = cast<NVVM::CpAsyncBulkTensorSharedCTAToGlobalOp>(op);
4374
4375 // Fill the Intrinsic Args
4376 args.push_back(mt.lookupValue(thisOp.getSrcMem()));
4377 args.push_back(mt.lookupValue(thisOp.getTmaDescriptor()));
4378
4379 for (auto v : thisOp.getCoordinates())
4380 args.push_back(mt.lookupValue(v));
4381
4382 mlir::Value cacheHint = thisOp.getL2CacheHint();
4383 const bool hasCacheHint = static_cast<bool>(cacheHint);
4384 llvm::Value *i64Unused =
4385 llvm::ConstantInt::get(llvm::Type::getInt64Ty(mt.getLLVMContext()), 0);
4386 args.push_back(hasCacheHint ? mt.lookupValue(cacheHint) : i64Unused);
4387 args.push_back(builder.getInt1(hasCacheHint));
4388
4389 using namespace llvm::Intrinsic;
4390 const unsigned NI = not_intrinsic;
4391 static constexpr ID IDTable[][6] = {
4392 {NI, nvvm_cp_async_bulk_tensor_s2g_tile_1d,
4393 nvvm_cp_async_bulk_tensor_s2g_tile_2d,
4394 nvvm_cp_async_bulk_tensor_s2g_tile_3d,
4395 nvvm_cp_async_bulk_tensor_s2g_tile_4d,
4396 nvvm_cp_async_bulk_tensor_s2g_tile_5d},
4397 {NI, NI, NI, nvvm_cp_async_bulk_tensor_s2g_im2col_3d,
4398 nvvm_cp_async_bulk_tensor_s2g_im2col_4d,
4399 nvvm_cp_async_bulk_tensor_s2g_im2col_5d},
4400 {NI, NI, NI, NI, NI, nvvm_cp_async_bulk_tensor_s2g_tile_scatter4_2d},
4401 {NI, NI, NI, nvvm_cp_async_bulk_tensor_s2g_im2col_w_3d,
4402 nvvm_cp_async_bulk_tensor_s2g_im2col_w_4d,
4403 nvvm_cp_async_bulk_tensor_s2g_im2col_w_5d}};
4404
4405 static_assert(getMaxEnumValForTMAStoreMode() == std::size(IDTable) - 1,
4406 "TMAStoreModes must match number of rows in IDTable");
4407 size_t mode = static_cast<size_t>(thisOp.getMode());
4408 size_t dim = thisOp.getCoordinates().size();
4409 ID id = IDTable[mode][dim];
4410 if (id == llvm::Intrinsic::not_intrinsic)
4411 llvm_unreachable(
4412 "Invalid intrinsic for CpAsyncBulkTensorSharedCTAToGlobalOp.");
4413
4414 return {id, std::move(args)};
4415}
4416
4417NVVM::IDArgPair CpAsyncBulkTensorReduceOp::getIntrinsicIDAndArgs(
4418 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4419 auto thisOp = cast<NVVM::CpAsyncBulkTensorReduceOp>(op);
4420
4422 args.push_back(mt.lookupValue(thisOp.getSrcMem()));
4423 args.push_back(mt.lookupValue(thisOp.getTmaDescriptor()));
4424 for (Value v : thisOp.getCoordinates())
4425 args.push_back(mt.lookupValue(v));
4426
4427 mlir::Value cacheHint = thisOp.getL2CacheHint();
4428 const bool hasCacheHint = static_cast<bool>(cacheHint);
4429 args.push_back(hasCacheHint ? mt.lookupValue(cacheHint)
4430 : builder.getInt64(0));
4431 args.push_back(builder.getInt32(static_cast<uint32_t>(thisOp.getRedKind())));
4432 args.push_back(builder.getInt1(hasCacheHint));
4433
4434 using namespace llvm::Intrinsic;
4435 const unsigned NI = not_intrinsic;
4436 static constexpr ID IDTable[][6] = {
4437 {NI, nvvm_cp_async_bulk_tensor_reduce_tile_1d,
4438 nvvm_cp_async_bulk_tensor_reduce_tile_2d,
4439 nvvm_cp_async_bulk_tensor_reduce_tile_3d,
4440 nvvm_cp_async_bulk_tensor_reduce_tile_4d,
4441 nvvm_cp_async_bulk_tensor_reduce_tile_5d},
4442 {NI, NI, NI, nvvm_cp_async_bulk_tensor_reduce_im2col_3d,
4443 nvvm_cp_async_bulk_tensor_reduce_im2col_4d,
4444 nvvm_cp_async_bulk_tensor_reduce_im2col_5d},
4445 {NI, NI, NI, NI, NI, NI}, // scatter4 not supported for reduce
4446 {NI, NI, NI, nvvm_cp_async_bulk_tensor_reduce_im2col_w_3d,
4447 nvvm_cp_async_bulk_tensor_reduce_im2col_w_4d,
4448 nvvm_cp_async_bulk_tensor_reduce_im2col_w_5d}};
4449
4450 size_t mode = static_cast<size_t>(thisOp.getMode());
4451 size_t dim = thisOp.getCoordinates().size();
4452 assert(mode < std::size(IDTable) &&
4453 "Invalid mode for CpAsyncBulkTensorReduceOp");
4454 assert(dim < std::size(IDTable[mode]) &&
4455 "Invalid dim for CpAsyncBulkTensorReduceOp");
4456
4457 ID intrinsicID = IDTable[mode][dim];
4458 assert(intrinsicID != NI &&
4459 "Invalid intrinsic for CpAsyncBulkTensorReduceOp");
4460 return {intrinsicID, std::move(args)};
4461}
4462
4463#define _none
4464
4465#define CVT_F2TF32_ID_IMPL(rnd, relu, sf) \
4466 hasRelu ? llvm::Intrinsic::nvvm_f2tf32_##rnd##relu##sf \
4467 : llvm::Intrinsic::nvvm_f2tf32_##rnd##sf
4468
4469#define GET_CVT_F2TF32_ID(rnd, relu, sf) \
4470 hasSatFinite ? CVT_F2TF32_ID_IMPL(rnd, relu, sf) \
4471 : CVT_F2TF32_ID_IMPL(rnd, relu, )
4472
4473llvm::Intrinsic::ID
4474ConvertFloatToTF32Op::getIntrinsicID(NVVM::FPRoundingMode rnd,
4475 NVVM::SaturationMode sat, bool hasRelu) {
4476 using RndMode = NVVM::FPRoundingMode;
4477 bool hasSatFinite = (sat == NVVM::SaturationMode::SATFINITE);
4478 switch (rnd) {
4479 case RndMode::RN:
4480 return GET_CVT_F2TF32_ID(rn, _relu, _satfinite);
4481 case RndMode::RZ:
4482 return GET_CVT_F2TF32_ID(rz, _relu, _satfinite);
4483 case RndMode::RNA:
4484 return GET_CVT_F2TF32_ID(rna, _none, _satfinite);
4485 default:
4486 llvm_unreachable("Invalid RoundingMode for CvtFloatToTF32Op");
4487 }
4488}
4489
4491ConvertF32x2ToF4x2Op::getIntrinsicIDAndArgs(NVVM::ConvertF32x2ToF4x2Op op,
4493 llvm::IRBuilderBase &builder) {
4495 args.push_back(mt.lookupValue(op.getA()));
4496 args.push_back(mt.lookupValue(op.getB()));
4497
4498 bool hasRelu = op.getRelu();
4499
4500 llvm::Intrinsic::ID intId =
4501 hasRelu ? llvm::Intrinsic::nvvm_ff_to_e2m1x2_rn_relu_satfinite
4502 : llvm::Intrinsic::nvvm_ff_to_e2m1x2_rn_satfinite;
4503
4504 return {intId, std::move(args)};
4505}
4506
4507#define GET_F32x2_TO_F6x2_ID(type, has_relu) \
4508 has_relu ? llvm::Intrinsic::nvvm_ff_to_##type##_rn_relu_satfinite \
4509 : llvm::Intrinsic::nvvm_ff_to_##type##_rn_satfinite
4510
4511llvm::Intrinsic::ID ConvertF32x2ToF6x2Op::getIntrinsicID(mlir::Type dstTy,
4512 bool hasRelu) {
4514 .Case([&](mlir::Float6E2M3FNType) {
4515 return GET_F32x2_TO_F6x2_ID(e2m3x2, hasRelu);
4516 })
4517 .Case([&](mlir::Float6E3M2FNType) {
4518 return GET_F32x2_TO_F6x2_ID(e3m2x2, hasRelu);
4519 })
4520 .Default([](mlir::Type) {
4521 llvm_unreachable("Invalid conversion in ConvertF32x2ToF6x2Op");
4522 return llvm::Intrinsic::not_intrinsic;
4523 });
4524}
4525
4527ConvertF16x2ToF4x2Op::getIntrinsicIDAndArgs(NVVM::ConvertF16x2ToF4x2Op &op,
4529 llvm::IRBuilderBase &builder) {
4530 mlir::Type dstTy = op.getDstTy();
4531 bool hasRelu = op.getRelu();
4532
4533 llvm::Intrinsic::ID intId = llvm::Intrinsic::not_intrinsic;
4534
4535 if (llvm::isa<mlir::Float4E2M1FNType>(dstTy))
4536 intId = hasRelu ? llvm::Intrinsic::nvvm_f16x2_to_e2m1x2_rn_relu_satfinite
4537 : llvm::Intrinsic::nvvm_f16x2_to_e2m1x2_rn_satfinite;
4538
4540 args.push_back(mt.lookupValue(op.getSrc()));
4541
4542 return {intId, std::move(args)};
4543}
4544
4546ConvertBF16x2ToF4x2Op::getIntrinsicIDAndArgs(NVVM::ConvertBF16x2ToF4x2Op &op,
4548 llvm::IRBuilderBase &builder) {
4549 mlir::Type dstTy = op.getDstTy();
4550 bool hasRelu = op.getRelu();
4551
4552 llvm::Intrinsic::ID intId = llvm::Intrinsic::not_intrinsic;
4553
4554 if (llvm::isa<mlir::Float4E2M1FNType>(dstTy))
4555 intId = hasRelu ? llvm::Intrinsic::nvvm_bf16x2_to_e2m1x2_rn_relu_satfinite
4556 : llvm::Intrinsic::nvvm_bf16x2_to_e2m1x2_rn_satfinite;
4557
4559 args.push_back(mt.lookupValue(op.getSrc()));
4560
4561 return {intId, std::move(args)};
4562}
4563
4564llvm::Intrinsic::ID ConvertF16x2ToF6x2Op::getIntrinsicID(mlir::Type dstTy,
4565 bool hasRelu) {
4567 .Case<mlir::Float6E2M3FNType>([&](mlir::Float6E2M3FNType) {
4568 return hasRelu ? llvm::Intrinsic::nvvm_f16x2_to_e2m3x2_rn_relu_satfinite
4569 : llvm::Intrinsic::nvvm_f16x2_to_e2m3x2_rn_satfinite;
4570 })
4571 .Case<mlir::Float6E3M2FNType>([&](mlir::Float6E3M2FNType) {
4572 return hasRelu ? llvm::Intrinsic::nvvm_f16x2_to_e3m2x2_rn_relu_satfinite
4573 : llvm::Intrinsic::nvvm_f16x2_to_e3m2x2_rn_satfinite;
4574 })
4575 .Default([](mlir::Type) {
4576 llvm_unreachable("Invalid conversion in ConvertF16x2ToF6x2Op");
4577 return llvm::Intrinsic::not_intrinsic;
4578 });
4579}
4580
4581llvm::Intrinsic::ID ConvertBF16x2ToF6x2Op::getIntrinsicID(mlir::Type dstTy,
4582 bool hasRelu) {
4584 .Case<mlir::Float6E2M3FNType>([&](mlir::Float6E2M3FNType) {
4585 return hasRelu
4586 ? llvm::Intrinsic::nvvm_bf16x2_to_e2m3x2_rn_relu_satfinite
4587 : llvm::Intrinsic::nvvm_bf16x2_to_e2m3x2_rn_satfinite;
4588 })
4589 .Case<mlir::Float6E3M2FNType>([&](mlir::Float6E3M2FNType) {
4590 return hasRelu
4591 ? llvm::Intrinsic::nvvm_bf16x2_to_e3m2x2_rn_relu_satfinite
4592 : llvm::Intrinsic::nvvm_bf16x2_to_e3m2x2_rn_satfinite;
4593 })
4594 .Default([](mlir::Type) {
4595 llvm_unreachable("Invalid conversion in ConvertBF16x2ToF6x2Op");
4596 return llvm::Intrinsic::not_intrinsic;
4597 });
4598}
4599
4600#define GET_F32x2_TO_F8X2_US_ID(rnd, has_satf) \
4601 has_satf ? llvm::Intrinsic::nvvm_ff_to_ue8m0x2_##rnd##_satfinite \
4602 : llvm::Intrinsic::nvvm_ff_to_ue8m0x2_##rnd
4603
4604#define GET_F32x2_TO_F8X2_S_ID(type, has_relu) \
4605 has_relu ? llvm::Intrinsic::nvvm_ff_to_##type##_rn_relu \
4606 : llvm::Intrinsic::nvvm_ff_to_##type##_rn
4607
4608llvm::Intrinsic::ID
4609ConvertF32x2ToF8x2Op::getIntrinsicID(mlir::Type dstTy, NVVM::FPRoundingMode rnd,
4610 NVVM::SaturationMode sat, bool hasRelu) {
4611 bool hasSatFinite = (sat == NVVM::SaturationMode::SATFINITE);
4612 bool hasRoundingModeRZ = (rnd == NVVM::FPRoundingMode::RZ);
4613 bool hasRoundingModeRP = (rnd == NVVM::FPRoundingMode::RP);
4614
4616 .Case([&](mlir::Float8E4M3FNType) {
4617 return GET_F32x2_TO_F8X2_S_ID(e4m3x2, hasRelu);
4618 })
4619 .Case([&](mlir::Float8E5M2Type) {
4620 return GET_F32x2_TO_F8X2_S_ID(e5m2x2, hasRelu);
4621 })
4622 .Case([&](mlir::Float8E8M0FNUType) {
4623 if (hasRoundingModeRZ)
4624 return GET_F32x2_TO_F8X2_US_ID(rz, hasSatFinite);
4625 else if (hasRoundingModeRP)
4626 return GET_F32x2_TO_F8X2_US_ID(rp, hasSatFinite);
4627
4628 llvm_unreachable("Invalid conversion in ConvertF32x2ToF8x2Op");
4629 })
4630 .Default([](mlir::Type) {
4631 llvm_unreachable("Invalid conversion in ConvertF32x2ToF8x2Op");
4632 return llvm::Intrinsic::not_intrinsic;
4633 });
4634}
4635
4636#define GET_F16x2_TO_F8X2_ID(type, has_relu) \
4637 has_relu ? llvm::Intrinsic::nvvm_f16x2_to_##type##_rn_relu \
4638 : llvm::Intrinsic::nvvm_f16x2_to_##type##_rn
4639
4640llvm::Intrinsic::ID ConvertF16x2ToF8x2Op::getIntrinsicID(mlir::Type dstTy,
4641 bool hasRelu) {
4643 .Case([&](mlir::Float8E4M3FNType) {
4644 return GET_F16x2_TO_F8X2_ID(e4m3x2, hasRelu);
4645 })
4646 .Case([&](mlir::Float8E5M2Type) {
4647 return GET_F16x2_TO_F8X2_ID(e5m2x2, hasRelu);
4648 })
4649 .Default([](mlir::Type) {
4650 llvm_unreachable("Invalid conversion in ConvertF16x2ToF8x2Op");
4651 return llvm::Intrinsic::not_intrinsic;
4652 });
4653}
4654
4655llvm::Intrinsic::ID
4656ConvertBF16x2ToF8x2Op::getIntrinsicID(mlir::Type dstTy,
4657 NVVM::FPRoundingMode rnd,
4658 NVVM::SaturationMode sat, bool hasRelu) {
4659 bool hasSatFinite = (sat == NVVM::SaturationMode::SATFINITE);
4660
4661 static constexpr llvm::Intrinsic::ID ue8m0x2IDs[] = {
4662 llvm::Intrinsic::nvvm_bf16x2_to_ue8m0x2_rz,
4663 llvm::Intrinsic::nvvm_bf16x2_to_ue8m0x2_rp,
4664 llvm::Intrinsic::nvvm_bf16x2_to_ue8m0x2_rz_satfinite,
4665 llvm::Intrinsic::nvvm_bf16x2_to_ue8m0x2_rp_satfinite,
4666 };
4667
4669 .Case<mlir::Float8E4M3FNType>([&](mlir::Float8E4M3FNType) {
4670 return hasRelu
4671 ? llvm::Intrinsic::nvvm_bf16x2_to_e4m3x2_rn_relu_satfinite
4672 : llvm::Intrinsic::nvvm_bf16x2_to_e4m3x2_rn_satfinite;
4673 })
4674 .Case<mlir::Float8E5M2Type>([&](mlir::Float8E5M2Type) {
4675 return hasRelu
4676 ? llvm::Intrinsic::nvvm_bf16x2_to_e5m2x2_rn_relu_satfinite
4677 : llvm::Intrinsic::nvvm_bf16x2_to_e5m2x2_rn_satfinite;
4678 })
4679 .Case<mlir::Float8E8M0FNUType>([&](mlir::Float8E8M0FNUType) {
4680 bool hasRoundingModeRP = (rnd == NVVM::FPRoundingMode::RP);
4681 unsigned index = (hasSatFinite << 1) | hasRoundingModeRP;
4682 return ue8m0x2IDs[index];
4683 })
4684 .Default([](mlir::Type) {
4685 llvm_unreachable("Invalid conversion in ConvertBF16x2ToF8x2Op");
4686 return llvm::Intrinsic::not_intrinsic;
4687 });
4688}
4689
4690NVVM::IDArgPair ConvertF8x2ToF16x2Op::getIntrinsicIDAndArgs(
4691 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4692 auto curOp = cast<NVVM::ConvertF8x2ToF16x2Op>(op);
4693
4694 bool hasRelu = curOp.getRelu();
4695
4696 llvm::Intrinsic::ID intId =
4698 .Case([&](Float8E4M3FNType type) {
4699 return hasRelu ? llvm::Intrinsic::nvvm_e4m3x2_to_f16x2_rn_relu
4700 : llvm::Intrinsic::nvvm_e4m3x2_to_f16x2_rn;
4701 })
4702 .Case([&](Float8E5M2Type type) {
4703 return hasRelu ? llvm::Intrinsic::nvvm_e5m2x2_to_f16x2_rn_relu
4704 : llvm::Intrinsic::nvvm_e5m2x2_to_f16x2_rn;
4705 })
4706 .Default([](mlir::Type type) {
4707 llvm_unreachable("Invalid type for ConvertF8x2ToF16x2Op");
4708 return llvm::Intrinsic::not_intrinsic;
4709 });
4710
4711 llvm::Value *packedI16 =
4712 builder.CreateBitCast(mt.lookupValue(curOp.getSrc()),
4713 llvm::Type::getInt16Ty(builder.getContext()));
4714
4715 return {intId, {packedI16}};
4716}
4717
4718NVVM::IDArgPair ConvertF8x2ToBF16x2Op::getIntrinsicIDAndArgs(
4719 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4720 auto curOp = cast<NVVM::ConvertF8x2ToBF16x2Op>(op);
4721 bool hasScale = static_cast<bool>(curOp.getScaleFactor());
4722 bool hasSatfinite = curOp.getSat() == NVVM::SaturationMode::SATFINITE;
4723 bool hasRelu = curOp.getRelu();
4724
4725 static constexpr llvm::Intrinsic::ID E4M3Ids[] = {
4726 llvm::Intrinsic::nvvm_e4m3x2_to_bf16x2_rn_scale_n2_ue8m0,
4727 llvm::Intrinsic::nvvm_e4m3x2_to_bf16x2_rn_relu_scale_n2_ue8m0,
4728 llvm::Intrinsic::nvvm_e4m3x2_to_bf16x2_rn_satfinite_scale_n2_ue8m0,
4729 llvm::Intrinsic::nvvm_e4m3x2_to_bf16x2_rn_relu_satfinite_scale_n2_ue8m0,
4730 };
4731
4732 static constexpr llvm::Intrinsic::ID E5M2Ids[] = {
4733 llvm::Intrinsic::nvvm_e5m2x2_to_bf16x2_rn_scale_n2_ue8m0,
4734 llvm::Intrinsic::nvvm_e5m2x2_to_bf16x2_rn_relu_scale_n2_ue8m0,
4735 llvm::Intrinsic::nvvm_e5m2x2_to_bf16x2_rn_satfinite_scale_n2_ue8m0,
4736 llvm::Intrinsic::nvvm_e5m2x2_to_bf16x2_rn_relu_satfinite_scale_n2_ue8m0,
4737 };
4738
4739 llvm::Intrinsic::ID intId =
4741 .Case([&](Float8E8M0FNUType type) {
4742 return llvm::Intrinsic::nvvm_ue8m0x2_to_bf16x2;
4743 })
4744 .Case([&](Float8E4M3FNType type) {
4745 return E4M3Ids[hasSatfinite << 1 | hasRelu];
4746 })
4747 .Case([&](Float8E5M2Type type) {
4748 return E5M2Ids[hasSatfinite << 1 | hasRelu];
4749 })
4750 .Default([](mlir::Type type) {
4751 llvm_unreachable("Invalid type for ConvertF8x2ToBF16x2Op");
4752 return llvm::Intrinsic::not_intrinsic;
4753 });
4754 llvm::Value *packedI16 =
4755 builder.CreateBitCast(mt.lookupValue(curOp.getSrc()),
4756 llvm::Type::getInt16Ty(builder.getContext()));
4757
4759 args.push_back(packedI16);
4760 if (!isa<Float8E8M0FNUType>(curOp.getSrcType()))
4761 args.push_back(
4762 hasScale ? mt.lookupValue(curOp.getScaleFactor())
4763 : builder.getInt16(0x7f7f)); // default scale factor (value of
4764 // 1 for both elements)
4765
4766 return {intId, std::move(args)};
4767}
4768
4769NVVM::IDArgPair ConvertF6x2ToF16x2Op::getIntrinsicIDAndArgs(
4770 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4771 auto curOp = cast<NVVM::ConvertF6x2ToF16x2Op>(op);
4772
4773 bool hasRelu = curOp.getRelu();
4774
4775 llvm::Intrinsic::ID intId =
4777 .Case([&](Float6E2M3FNType type) {
4778 return hasRelu ? llvm::Intrinsic::nvvm_e2m3x2_to_f16x2_rn_relu
4779 : llvm::Intrinsic::nvvm_e2m3x2_to_f16x2_rn;
4780 })
4781 .Case([&](Float6E3M2FNType type) {
4782 return hasRelu ? llvm::Intrinsic::nvvm_e3m2x2_to_f16x2_rn_relu
4783 : llvm::Intrinsic::nvvm_e3m2x2_to_f16x2_rn;
4784 })
4785 .Default([](mlir::Type type) {
4786 llvm_unreachable("Invalid type for ConvertF6x2ToF16x2Op");
4787 return llvm::Intrinsic::not_intrinsic;
4788 });
4789
4790 llvm::Value *packedI16 =
4791 builder.CreateBitCast(mt.lookupValue(curOp.getSrc()),
4792 llvm::Type::getInt16Ty(builder.getContext()));
4793
4794 return {intId, {packedI16}};
4795}
4796
4797NVVM::IDArgPair ConvertF6x2ToBF16x2Op::getIntrinsicIDAndArgs(
4798 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4799 auto curOp = cast<NVVM::ConvertF6x2ToBF16x2Op>(op);
4800 bool hasScale = static_cast<bool>(curOp.getScaleFactor());
4801 bool hasSatfinite = curOp.getSat() == NVVM::SaturationMode::SATFINITE;
4802 bool hasRelu = curOp.getRelu();
4803
4804 static constexpr llvm::Intrinsic::ID E2M3Ids[] = {
4805 llvm::Intrinsic::nvvm_e2m3x2_to_bf16x2_rn_scale_n2_ue8m0,
4806 llvm::Intrinsic::nvvm_e2m3x2_to_bf16x2_rn_relu_scale_n2_ue8m0,
4807 llvm::Intrinsic::nvvm_e2m3x2_to_bf16x2_rn_satfinite_scale_n2_ue8m0,
4808 llvm::Intrinsic::nvvm_e2m3x2_to_bf16x2_rn_relu_satfinite_scale_n2_ue8m0,
4809 };
4810
4811 static constexpr llvm::Intrinsic::ID E3M2Ids[] = {
4812 llvm::Intrinsic::nvvm_e3m2x2_to_bf16x2_rn_scale_n2_ue8m0,
4813 llvm::Intrinsic::nvvm_e3m2x2_to_bf16x2_rn_relu_scale_n2_ue8m0,
4814 llvm::Intrinsic::nvvm_e3m2x2_to_bf16x2_rn_satfinite_scale_n2_ue8m0,
4815 llvm::Intrinsic::nvvm_e3m2x2_to_bf16x2_rn_relu_satfinite_scale_n2_ue8m0,
4816 };
4817
4818 unsigned idx = (hasSatfinite << 1) | hasRelu;
4819 llvm::Intrinsic::ID intId =
4821 .Case([&](Float6E2M3FNType type) { return E2M3Ids[idx]; })
4822 .Case([&](Float6E3M2FNType type) { return E3M2Ids[idx]; })
4823 .Default([](mlir::Type type) {
4824 llvm_unreachable("Invalid type for ConvertF6x2ToBF16x2Op");
4825 return llvm::Intrinsic::not_intrinsic;
4826 });
4827
4828 llvm::Value *packedI16 =
4829 builder.CreateBitCast(mt.lookupValue(curOp.getSrc()),
4830 llvm::Type::getInt16Ty(builder.getContext()));
4831
4833 args.push_back(packedI16);
4834 args.push_back(
4835 hasScale
4836 ? mt.lookupValue(curOp.getScaleFactor())
4837 : builder.getInt16(
4838 0x7f7f)); // default scale factor (value of 1 for both elements)
4839
4840 return {intId, std::move(args)};
4841}
4842
4843NVVM::IDArgPair ConvertF4x2ToF16x2Op::getIntrinsicIDAndArgs(
4844 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4845 auto curOp = cast<NVVM::ConvertF4x2ToF16x2Op>(op);
4846
4847 bool hasRelu = curOp.getRelu();
4848
4849 llvm::Intrinsic::ID intId =
4851 .Case([&](Float4E2M1FNType type) {
4852 return hasRelu ? llvm::Intrinsic::nvvm_e2m1x2_to_f16x2_rn_relu
4853 : llvm::Intrinsic::nvvm_e2m1x2_to_f16x2_rn;
4854 })
4855 .Default([](mlir::Type type) {
4856 llvm_unreachable("Invalid type for ConvertF4x2ToF16x2Op");
4857 return llvm::Intrinsic::not_intrinsic;
4858 });
4859
4860 llvm::Value *extendedI16 =
4861 builder.CreateZExt(mt.lookupValue(curOp.getSrc()),
4862 llvm::Type::getInt16Ty(builder.getContext()));
4863
4864 return {intId, {extendedI16}};
4865}
4866
4867NVVM::IDArgPair ConvertF4x2ToBF16x2Op::getIntrinsicIDAndArgs(
4868 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4869 auto curOp = cast<NVVM::ConvertF4x2ToBF16x2Op>(op);
4870 bool hasScale = static_cast<bool>(curOp.getScaleFactor());
4871 bool hasSatfinite = curOp.getSat() == NVVM::SaturationMode::SATFINITE;
4872 bool hasRelu = curOp.getRelu();
4873
4874 static constexpr llvm::Intrinsic::ID E2M1Ids[] = {
4875 llvm::Intrinsic::nvvm_e2m1x2_to_bf16x2_rn_scale_n2_ue8m0,
4876 llvm::Intrinsic::nvvm_e2m1x2_to_bf16x2_rn_relu_scale_n2_ue8m0,
4877 llvm::Intrinsic::nvvm_e2m1x2_to_bf16x2_rn_satfinite_scale_n2_ue8m0,
4878 llvm::Intrinsic::nvvm_e2m1x2_to_bf16x2_rn_relu_satfinite_scale_n2_ue8m0,
4879 };
4880
4881 unsigned idx = (hasSatfinite << 1) | hasRelu;
4882 llvm::Intrinsic::ID intId =
4884 .Case([&](Float4E2M1FNType type) { return E2M1Ids[idx]; })
4885 .Default([](mlir::Type type) {
4886 llvm_unreachable("Invalid type for ConvertF4x2ToBF16x2Op");
4887 return llvm::Intrinsic::not_intrinsic;
4888 });
4889
4890 llvm::Value *extendedI16 =
4891 builder.CreateZExt(mt.lookupValue(curOp.getSrc()),
4892 llvm::Type::getInt16Ty(builder.getContext()));
4893
4895 args.push_back(extendedI16);
4896 args.push_back(
4897 hasScale
4898 ? mt.lookupValue(curOp.getScaleFactor())
4899 : builder.getInt16(
4900 0x7f7f)); // default scale factor (value of 1 for both elements)
4901
4902 return {intId, std::move(args)};
4903}
4904
4905NVVM::IDArgPair ConvertF32x2ToS2F6x2Op::getIntrinsicIDAndArgs(
4906 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4907 auto thisOp = cast<NVVM::ConvertF32x2ToS2F6x2Op>(op);
4908 bool hasRelu = thisOp.getRelu();
4909 bool hasScale = static_cast<bool>(thisOp.getScaleFactor());
4910
4911 llvm::Intrinsic::ID id =
4912 hasRelu
4913 ? llvm::Intrinsic::nvvm_ff_to_s2f6x2_rn_relu_satfinite_scale_n2_ue8m0
4914 : llvm::Intrinsic::nvvm_ff_to_s2f6x2_rn_satfinite_scale_n2_ue8m0;
4915
4916 // Fill the Intrinsic Args
4918 args.push_back(mt.lookupValue(thisOp.getA()));
4919 args.push_back(mt.lookupValue(thisOp.getB()));
4920 args.push_back(hasScale ? mt.lookupValue(thisOp.getScaleFactor())
4921 : builder.getInt16(0x7f7f));
4922 return {id, std::move(args)};
4923}
4924
4925NVVM::IDArgPair ConvertBF16x2ToS2F6x2Op::getIntrinsicIDAndArgs(
4926 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4927 auto thisOp = cast<NVVM::ConvertBF16x2ToS2F6x2Op>(op);
4928 bool hasRelu = thisOp.getRelu();
4929 bool hasScale = static_cast<bool>(thisOp.getScaleFactor());
4930
4931 llvm::Intrinsic::ID id =
4932 hasRelu
4933 ? llvm::Intrinsic::
4934 nvvm_bf16x2_to_s2f6x2_rn_relu_satfinite_scale_n2_ue8m0
4935 : llvm::Intrinsic::nvvm_bf16x2_to_s2f6x2_rn_satfinite_scale_n2_ue8m0;
4936
4937 // Fill the Intrinsic Args
4939 args.push_back(mt.lookupValue(thisOp.getSrc()));
4940 args.push_back(hasScale ? mt.lookupValue(thisOp.getScaleFactor())
4941 : builder.getInt16(0x7f7f));
4942 return {id, std::move(args)};
4943}
4944
4945NVVM::IDArgPair ConvertS2F6x2ToBF16x2Op::getIntrinsicIDAndArgs(
4946 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
4947 auto thisOp = cast<NVVM::ConvertS2F6x2ToBF16x2Op>(op);
4948 bool hasRelu = thisOp.getRelu();
4949 bool hasScale = static_cast<bool>(thisOp.getScaleFactor());
4950 bool hasSat = thisOp.getSat() == NVVM::SaturationMode::SATFINITE;
4951
4952 static constexpr llvm::Intrinsic::ID ids[] = {
4953 llvm::Intrinsic::nvvm_s2f6x2_to_bf16x2_rn_scale_n2_ue8m0,
4954 llvm::Intrinsic::nvvm_s2f6x2_to_bf16x2_rn_relu_scale_n2_ue8m0,
4955 llvm::Intrinsic::nvvm_s2f6x2_to_bf16x2_rn_satfinite_scale_n2_ue8m0,
4956 llvm::Intrinsic::nvvm_s2f6x2_to_bf16x2_rn_relu_satfinite_scale_n2_ue8m0,
4957 };
4958
4959 unsigned idx = (hasSat << 1) | hasRelu;
4960
4961 // Fill the Intrinsic Args
4963 llvm::Value *packedI16 =
4964 builder.CreateBitCast(mt.lookupValue(thisOp.getSrc()),
4965 llvm::Type::getInt16Ty(builder.getContext()));
4966 args.push_back(packedI16);
4967 args.push_back(hasScale ? mt.lookupValue(thisOp.getScaleFactor())
4968 : builder.getInt16(0x7f7f));
4969
4970 return {ids[idx], std::move(args)};
4971}
4972
4973llvm::Intrinsic::ID
4974Tcgen05AllocOp::getIntrinsicIDAndArgs(Operation &op,
4977 auto curOp = cast<NVVM::Tcgen05AllocOp>(op);
4978 unsigned as = llvm::cast<LLVM::LLVMPointerType>(curOp.getAddr().getType())
4979 .getAddressSpace();
4980 bool isShared = as == NVVMMemorySpace::Shared;
4981 bool is2CTAMode = curOp.getGroup() == CTAGroupKind::CTA_2;
4982
4983 llvm::Intrinsic::ID id;
4984 if (isShared) {
4985 id = is2CTAMode ? llvm::Intrinsic::nvvm_tcgen05_alloc_shared_cg2
4986 : llvm::Intrinsic::nvvm_tcgen05_alloc_shared_cg1;
4987 } else {
4988 id = is2CTAMode ? llvm::Intrinsic::nvvm_tcgen05_alloc_cg2
4989 : llvm::Intrinsic::nvvm_tcgen05_alloc_cg1;
4990 }
4991
4992 // Fill the Intrinsic Args
4993 args.push_back(mt.lookupValue(curOp.getAddr()));
4994 args.push_back(mt.lookupValue(curOp.getNCols()));
4995
4996 return id;
4997}
4998
4999llvm::Intrinsic::ID Tcgen05DeallocOp::getIntrinsicIDAndArgs(
5002 auto curOp = cast<NVVM::Tcgen05DeallocOp>(op);
5003 auto id = (curOp.getGroup() == CTAGroupKind::CTA_1)
5004 ? llvm::Intrinsic::nvvm_tcgen05_dealloc_cg1
5005 : llvm::Intrinsic::nvvm_tcgen05_dealloc_cg2;
5006
5007 // Fill the Intrinsic Args
5008 args.push_back(mt.lookupValue(curOp.getTaddr()));
5009 args.push_back(mt.lookupValue(curOp.getNCols()));
5010
5011 return id;
5012}
5013
5014llvm::Intrinsic::ID
5015Tcgen05CommitOp::getIntrinsicIDAndArgs(Operation &op,
5018 auto curOp = cast<NVVM::Tcgen05CommitOp>(op);
5019 bool hasMulticast = static_cast<bool>(curOp.getMulticastMask());
5020 bool is2CTAMode = curOp.getGroup() == CTAGroupKind::CTA_2;
5021 bool hasSmemARead = curOp.getSmemARead();
5022 unsigned index = (static_cast<unsigned>(hasSmemARead) << 1) |
5023 static_cast<unsigned>(is2CTAMode);
5024
5025 using namespace llvm::Intrinsic;
5026 static constexpr ID IDs[] = {
5027 nvvm_tcgen05_commit_cg1,
5028 nvvm_tcgen05_commit_cg2,
5029 nvvm_tcgen05_commit_smem_a_read_cg1,
5030 nvvm_tcgen05_commit_smem_a_read_cg2,
5031 };
5032
5033 static constexpr ID multicastIDs[] = {
5034 nvvm_tcgen05_commit_mc_cg1,
5035 nvvm_tcgen05_commit_mc_cg2,
5036 nvvm_tcgen05_commit_smem_a_read_mc_cg1,
5037 nvvm_tcgen05_commit_smem_a_read_mc_cg2,
5038 };
5039
5040 ID id = hasMulticast ? multicastIDs[index] : IDs[index];
5041 // Fill the Intrinsic Args
5042 args.push_back(mt.lookupValue(curOp.getAddr()));
5043 if (hasMulticast)
5044 args.push_back(mt.lookupValue(curOp.getMulticastMask()));
5045
5046 return id;
5047}
5048
5049#define TCGEN05_CP_IMPL(shape_mc, src_fmt, cg) \
5050 llvm::Intrinsic::nvvm_tcgen05_cp##shape_mc##src_fmt##cg
5051
5052#define TCGEN05_CP_2CTA(shape_mc, src_fmt, is_2cta) \
5053 is_2cta ? TCGEN05_CP_IMPL(shape_mc, src_fmt, _cg2) \
5054 : TCGEN05_CP_IMPL(shape_mc, src_fmt, _cg1)
5055
5056#define GET_TCGEN05_CP_ID(shape_mc, src_fmt, is_2cta) \
5057 [&]() -> auto { \
5058 if ((src_fmt) == Tcgen05CpSrcFormat::B6x16_P32) \
5059 return TCGEN05_CP_2CTA(shape_mc, _b6x16_p32, is_2cta); \
5060 if ((src_fmt) == Tcgen05CpSrcFormat::B4x16_P64) \
5061 return TCGEN05_CP_2CTA(shape_mc, _b4x16_p64, is_2cta); \
5062 return TCGEN05_CP_2CTA(shape_mc, , is_2cta); \
5063 }()
5064
5066ConvertF32x2ToF16x2Op::getIntrinsicIDAndArgs(NVVM::ConvertF32x2ToF16x2Op &op,
5068 llvm::IRBuilderBase &builder) {
5069 static constexpr llvm::Intrinsic::ID rndRNIds[] = {
5070 llvm::Intrinsic::nvvm_ff2f16x2_rn,
5071 llvm::Intrinsic::nvvm_ff2f16x2_rn_relu,
5072 llvm::Intrinsic::nvvm_ff2f16x2_rn_satfinite,
5073 llvm::Intrinsic::nvvm_ff2f16x2_rn_relu_satfinite,
5074 };
5075 static constexpr llvm::Intrinsic::ID rndRZIds[] = {
5076 llvm::Intrinsic::nvvm_ff2f16x2_rz,
5077 llvm::Intrinsic::nvvm_ff2f16x2_rz_relu,
5078 llvm::Intrinsic::nvvm_ff2f16x2_rz_satfinite,
5079 llvm::Intrinsic::nvvm_ff2f16x2_rz_relu_satfinite,
5080 };
5081 static constexpr llvm::Intrinsic::ID rndRSIds[] = {
5082 llvm::Intrinsic::nvvm_ff2f16x2_rs,
5083 llvm::Intrinsic::nvvm_ff2f16x2_rs_relu,
5084 llvm::Intrinsic::nvvm_ff2f16x2_rs_satfinite,
5085 llvm::Intrinsic::nvvm_ff2f16x2_rs_relu_satfinite,
5086 };
5087
5088 unsigned hasRelu = op.getRelu() ? 1 : 0;
5089 unsigned hasSatFinite =
5090 (op.getSat() == NVVM::SaturationMode::SATFINITE) ? 1 : 0;
5091 // idx: bit-0 - relu
5092 // bit-1 - satfinite
5093 unsigned idx = (hasSatFinite << 1) | hasRelu;
5094
5096 args.push_back(mt.lookupValue(op.getSrcHi()));
5097 args.push_back(mt.lookupValue(op.getSrcLo()));
5098 if (op.getRandomBits())
5099 args.push_back(mt.lookupValue(op.getRandomBits()));
5100
5101 switch (op.getRnd()) {
5102 case FPRoundingMode::RN:
5103 return {rndRNIds[idx], std::move(args)};
5104 case FPRoundingMode::RZ:
5105 return {rndRZIds[idx], std::move(args)};
5106 case FPRoundingMode::RS:
5107 return {rndRSIds[idx], std::move(args)};
5108 default:
5109 llvm_unreachable("Invalid rounding mode for ConvertF32x2ToF16x2Op");
5110 }
5111}
5112
5114ConvertF32x2ToBF16x2Op::getIntrinsicIDAndArgs(NVVM::ConvertF32x2ToBF16x2Op &op,
5116 llvm::IRBuilderBase &builder) {
5117 static constexpr llvm::Intrinsic::ID rndRNIds[] = {
5118 llvm::Intrinsic::nvvm_ff2bf16x2_rn,
5119 llvm::Intrinsic::nvvm_ff2bf16x2_rn_relu,
5120 llvm::Intrinsic::nvvm_ff2bf16x2_rn_satfinite,
5121 llvm::Intrinsic::nvvm_ff2bf16x2_rn_relu_satfinite,
5122 };
5123 static constexpr llvm::Intrinsic::ID rndRZIds[] = {
5124 llvm::Intrinsic::nvvm_ff2bf16x2_rz,
5125 llvm::Intrinsic::nvvm_ff2bf16x2_rz_relu,
5126 llvm::Intrinsic::nvvm_ff2bf16x2_rz_satfinite,
5127 llvm::Intrinsic::nvvm_ff2bf16x2_rz_relu_satfinite,
5128 };
5129 static constexpr llvm::Intrinsic::ID rndRSIds[] = {
5130 llvm::Intrinsic::nvvm_ff2bf16x2_rs,
5131 llvm::Intrinsic::nvvm_ff2bf16x2_rs_relu,
5132 llvm::Intrinsic::nvvm_ff2bf16x2_rs_satfinite,
5133 llvm::Intrinsic::nvvm_ff2bf16x2_rs_relu_satfinite,
5134 };
5135
5136 unsigned hasRelu = op.getRelu() ? 1 : 0;
5137 unsigned hasSatFinite =
5138 (op.getSat() == NVVM::SaturationMode::SATFINITE) ? 1 : 0;
5139 // idx: bit-0 - relu
5140 // bit-1 - satfinite
5141 unsigned idx = (hasSatFinite << 1) | hasRelu;
5142
5144 args.push_back(mt.lookupValue(op.getSrcHi()));
5145 args.push_back(mt.lookupValue(op.getSrcLo()));
5146 if (op.getRandomBits())
5147 args.push_back(mt.lookupValue(op.getRandomBits()));
5148
5149 switch (op.getRnd()) {
5150 case FPRoundingMode::RN:
5151 return {rndRNIds[idx], std::move(args)};
5152 case FPRoundingMode::RZ:
5153 return {rndRZIds[idx], std::move(args)};
5154 case FPRoundingMode::RS:
5155 return {rndRSIds[idx], std::move(args)};
5156 default:
5157 llvm_unreachable("Invalid rounding mode for ConvertF32x2ToBF16x2Op");
5158 }
5159}
5160
5161llvm::Intrinsic::ID ConvertF32x4ToF8x4Op::getIntrinsicID() {
5162 mlir::Type dstTy = getDstTy();
5163 bool hasRelu = getRelu();
5164
5166 .Case([&](mlir::Float8E4M3FNType) {
5167 return hasRelu ? llvm::Intrinsic::nvvm_f32x4_to_e4m3x4_rs_relu_satfinite
5168 : llvm::Intrinsic::nvvm_f32x4_to_e4m3x4_rs_satfinite;
5169 })
5170 .Case([&](mlir::Float8E5M2Type) {
5171 return hasRelu ? llvm::Intrinsic::nvvm_f32x4_to_e5m2x4_rs_relu_satfinite
5172 : llvm::Intrinsic::nvvm_f32x4_to_e5m2x4_rs_satfinite;
5173 })
5174 .Default([](mlir::Type) {
5175 llvm_unreachable("Invalid F8 type in ConvertF32x4ToF8x4Op");
5176 return llvm::Intrinsic::not_intrinsic;
5177 });
5178}
5179
5180llvm::Intrinsic::ID ConvertF32x4ToF6x4Op::getIntrinsicID() {
5181 mlir::Type dstTy = getDstTy();
5182 bool hasRelu = getRelu();
5183
5185 .Case([&](mlir::Float6E2M3FNType) {
5186 return hasRelu ? llvm::Intrinsic::nvvm_f32x4_to_e2m3x4_rs_relu_satfinite
5187 : llvm::Intrinsic::nvvm_f32x4_to_e2m3x4_rs_satfinite;
5188 })
5189 .Case([&](mlir::Float6E3M2FNType) {
5190 return hasRelu ? llvm::Intrinsic::nvvm_f32x4_to_e3m2x4_rs_relu_satfinite
5191 : llvm::Intrinsic::nvvm_f32x4_to_e3m2x4_rs_satfinite;
5192 })
5193 .Default([](mlir::Type) {
5194 llvm_unreachable("Invalid F6 type in ConvertF32x4ToF6x4Op");
5195 return llvm::Intrinsic::not_intrinsic;
5196 });
5197}
5198
5199llvm::Intrinsic::ID ConvertF32x4ToF4x4Op::getIntrinsicID() {
5200 mlir::Type dstTy = getDstTy();
5201 bool hasRelu = getRelu();
5202
5204 .Case([&](mlir::Float4E2M1FNType) {
5205 return hasRelu ? llvm::Intrinsic::nvvm_f32x4_to_e2m1x4_rs_relu_satfinite
5206 : llvm::Intrinsic::nvvm_f32x4_to_e2m1x4_rs_satfinite;
5207 })
5208 .Default([](mlir::Type) {
5209 llvm_unreachable("Invalid F4 type in ConvertF32x4ToF4x4Op");
5210 return llvm::Intrinsic::not_intrinsic;
5211 });
5212}
5213
5214llvm::Intrinsic::ID Tcgen05CpOp::getIntrinsicID(Operation &op) {
5215 auto curOp = cast<NVVM::Tcgen05CpOp>(op);
5216 bool is2CTA = curOp.getGroup() == CTAGroupKind::CTA_2;
5217 auto srcFmt = curOp.getSrcFormat();
5218 auto mc = curOp.getMulticast();
5219
5220 switch (curOp.getShape()) {
5221 case Tcgen05CpShape::SHAPE_128x256b:
5222 return GET_TCGEN05_CP_ID(_128x256b, srcFmt, is2CTA);
5223 case Tcgen05CpShape::SHAPE_128x128b:
5224 return GET_TCGEN05_CP_ID(_128x128b, srcFmt, is2CTA);
5225 case Tcgen05CpShape::SHAPE_4x256b:
5226 return GET_TCGEN05_CP_ID(_4x256b, srcFmt, is2CTA);
5227 case Tcgen05CpShape::SHAPE_32x128b:
5228 return GET_TCGEN05_CP_ID(_32x128b_warpx4, srcFmt, is2CTA);
5229 case Tcgen05CpShape::SHAPE_64x128b:
5230 return (mc == Tcgen05CpMulticast::WARPX2_01_23)
5231 ? GET_TCGEN05_CP_ID(_64x128b_warpx2_01_23, srcFmt, is2CTA)
5232 : GET_TCGEN05_CP_ID(_64x128b_warpx2_02_13, srcFmt, is2CTA);
5233 }
5234 llvm_unreachable("Invalid shape in tcgen05 cp Op");
5235}
5236
5237// Returns the valid vector length for a given shape and vector length, the
5238// function models the table mentioned in the tcgen05.{ld, st} Op description
5239static unsigned isValidVectorLength(NVVM::Tcgen05LdStShape shape,
5240 unsigned vecLen) {
5241 if (shape == NVVM::Tcgen05LdStShape::SHAPE_16X128B)
5242 return vecLen >= 2;
5243 if (shape == NVVM::Tcgen05LdStShape::SHAPE_16X256B)
5244 return vecLen >= 4;
5245 return true;
5246}
5247
5248LogicalResult Tcgen05LdOp::verify() {
5249 LogicalResult result = success();
5250 if (getShape() == NVVM::Tcgen05LdStShape::SHAPE_16X32BX2 && !getOffset())
5251 result = emitError("shape 16x32bx2 requires offset argument");
5252
5253 if (getShape() != NVVM::Tcgen05LdStShape::SHAPE_16X32BX2 && getOffset())
5254 result = emitError("offset argument is only supported for shape 16x32bx2");
5255
5256 auto resTy = getRes().getType();
5257 unsigned resLen = isa<VectorType>(resTy)
5258 ? llvm::cast<VectorType>(resTy).getNumElements()
5259 : 1;
5260 if (!isValidVectorLength(getShape(), resLen))
5261 result = emitError(llvm::formatv("invalid result type length {0} for shape "
5262 "{1} in tcgen05.ld Op",
5263 resLen, stringifyEnum(getShape())));
5264
5265 return result;
5266}
5267
5268LogicalResult Tcgen05StOp::verify() {
5269 LogicalResult result = success();
5270 if (getShape() == NVVM::Tcgen05LdStShape::SHAPE_16X32BX2 && !getOffset())
5271 result = emitError("shape 16x32bx2 requires offset argument");
5272
5273 auto valTy = getVal().getType();
5274 unsigned valLen = isa<VectorType>(valTy)
5275 ? llvm::cast<VectorType>(valTy).getNumElements()
5276 : 1;
5277 if (!isValidVectorLength(getShape(), valLen))
5278 result = emitError(llvm::formatv("invalid input length {0} for shape "
5279 "{1} in tcgen05.st Op",
5280 valLen, stringifyEnum(getShape())));
5281
5282 return result;
5283}
5284
5285/// Infer the result ranges for the NVVM SpecialRangeableRegisterOp that might
5286/// have ConstantRangeAttr.
5289 SetIntRangeFn setResultRanges) {
5290 if (auto rangeAttr = op->getAttrOfType<LLVM::ConstantRangeAttr>("range")) {
5291 setResultRanges(result, {rangeAttr.getLower(), rangeAttr.getUpper(),
5292 rangeAttr.getLower(), rangeAttr.getUpper()});
5293 } else {
5294 setResultRanges(result, IntegerValueRange::getMaxRange(result).getValue());
5295 }
5296}
5297
5298/// Verify the range attribute satisfies LLVM ConstantRange constructor
5299/// requirements for NVVM SpecialRangeableRegisterOp.
5300static LogicalResult
5302 std::optional<LLVM::ConstantRangeAttr> rangeAttr) {
5303 if (!rangeAttr)
5304 return success();
5305
5306 const llvm::APInt &lower = rangeAttr->getLower();
5307 const llvm::APInt &upper = rangeAttr->getUpper();
5308
5309 // Check LLVM ConstantRange constructor condition
5310 if (lower == upper && !lower.isMaxValue() && !lower.isMinValue()) {
5311 unsigned bitWidth = lower.getBitWidth();
5312 llvm::APInt minVal = llvm::APInt::getMinValue(bitWidth);
5313 llvm::APInt maxVal = llvm::APInt::getMaxValue(bitWidth);
5314 return op->emitOpError(
5315 "invalid range attribute: Lower == Upper, but they aren't min (")
5316 << llvm::toString(minVal, 10, false) << ") or max ("
5317 << llvm::toString(maxVal, 10, false)
5318 << ") value! This is an invalid constant range.";
5319 }
5320
5321 return success();
5322}
5323
5324static llvm::Value *getAsPackedI32(llvm::Value *arg,
5325 llvm::IRBuilderBase &builder) {
5326 return builder.CreateBitCast(arg,
5327 llvm::Type::getInt32Ty(builder.getContext()));
5328}
5329
5330NVVM::IDArgPair DotAccumulate4WayOp::getIntrinsicIDAndArgs(
5331 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
5332 auto curOp = cast<NVVM::DotAccumulate4WayOp>(op);
5333
5335 args.push_back(getAsPackedI32(mt.lookupValue(curOp.getA()), builder));
5336 args.push_back(getAsPackedI32(mt.lookupValue(curOp.getB()), builder));
5337 args.push_back(mt.lookupValue(curOp.getC()));
5338
5339 bool isASigned = curOp.getAType() == NVVM::DotAccumulateType::SIGNED;
5340 bool isBSigned = curOp.getBType() == NVVM::DotAccumulateType::SIGNED;
5341 unsigned type = (isASigned << 1) | isBSigned;
5342 const llvm::Intrinsic::ID ids[] = {
5343 llvm::Intrinsic::nvvm_idp4a_u_u,
5344 llvm::Intrinsic::nvvm_idp4a_u_s,
5345 llvm::Intrinsic::nvvm_idp4a_s_u,
5346 llvm::Intrinsic::nvvm_idp4a_s_s,
5347 };
5348 return {ids[type], args};
5349}
5350
5351NVVM::IDArgPair DotAccumulate2WayOp::getIntrinsicIDAndArgs(
5352 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
5353 auto curOp = cast<NVVM::DotAccumulate2WayOp>(op);
5354
5356 args.push_back(getAsPackedI32(mt.lookupValue(curOp.getA()), builder));
5357 args.push_back(getAsPackedI32(mt.lookupValue(curOp.getB()), builder));
5358 args.push_back(builder.getInt1(curOp.getBHi()));
5359 args.push_back(mt.lookupValue(curOp.getC()));
5360
5361 bool isASigned = curOp.getAType() == NVVM::DotAccumulateType::SIGNED;
5362 bool isBSigned = curOp.getBType() == NVVM::DotAccumulateType::SIGNED;
5363 unsigned type = (isASigned << 1) | isBSigned;
5364 const llvm::Intrinsic::ID ids[] = {
5365 llvm::Intrinsic::nvvm_idp2a_u_u,
5366 llvm::Intrinsic::nvvm_idp2a_u_s,
5367 llvm::Intrinsic::nvvm_idp2a_s_u,
5368 llvm::Intrinsic::nvvm_idp2a_s_s,
5369 };
5370 return {ids[type], args};
5371}
5372
5373static llvm::Value *getParamCastedAddr(llvm::Value *addr,
5374 llvm::IRBuilderBase &builder) {
5375 return builder.CreateAddrSpaceCast(
5376 addr, builder.getPtrTy(llvm::NVPTXAS::ADDRESS_SPACE_ENTRY_PARAM));
5377}
5378
5380PrefetchOp::getIntrinsicIDAndArgs(NVVM::PrefetchOp &op,
5382 llvm::IRBuilderBase &builder) {
5383 using MemSpace = NVVM::NVVMMemorySpace;
5384 using CacheLevel = NVVM::PrefetchCacheLevel;
5385
5386 std::optional<NVVM::PrefetchCacheLevel> cacheLevel = op.getCacheLevel();
5387 std::optional<NVVM::CacheEvictionPriority> evictPriority =
5388 op.getEvictPriority();
5389 unsigned addressSpace =
5390 llvm::cast<LLVM::LLVMPointerType>(op.getAddr().getType())
5391 .getAddressSpace();
5392
5394 llvm::Value *addr = mt.lookupValue(op.getAddr());
5395 args.push_back(op.getInParamSpace() ? getParamCastedAddr(addr, builder)
5396 : addr);
5397
5398 if (op.getTensormap())
5399 return {llvm::Intrinsic::nvvm_prefetch_tensormap, args};
5400
5401 assert(cacheLevel && "expected cache level for non-tensormap prefetch");
5402
5403 if (op.getUniform() && *cacheLevel == CacheLevel::L1)
5404 return {llvm::Intrinsic::nvvm_prefetchu_L1, args};
5405
5406 if (evictPriority && *cacheLevel == CacheLevel::L2) {
5407 switch (*evictPriority) {
5408 case NVVM::CacheEvictionPriority::EvictLast:
5409 return {llvm::Intrinsic::nvvm_prefetch_global_L2_evict_last, args};
5410 case NVVM::CacheEvictionPriority::EvictNormal:
5411 return {llvm::Intrinsic::nvvm_prefetch_global_L2_evict_normal, args};
5412 default:
5413 llvm_unreachable("Invalid cache eviction priority");
5414 }
5415 }
5416
5417 switch (static_cast<MemSpace>(addressSpace)) {
5418 case MemSpace::Generic:
5419 return *cacheLevel == CacheLevel::L1
5420 ? NVVM::IDArgPair({llvm::Intrinsic::nvvm_prefetch_L1, args})
5421 : NVVM::IDArgPair({llvm::Intrinsic::nvvm_prefetch_L2, args});
5422 case MemSpace::Global:
5423 return *cacheLevel == CacheLevel::L1
5425 {llvm::Intrinsic::nvvm_prefetch_global_L1, args})
5426 : NVVM::IDArgPair(
5427 {llvm::Intrinsic::nvvm_prefetch_global_L2, args});
5428 case MemSpace::Local:
5429 return *cacheLevel == CacheLevel::L1
5431 {llvm::Intrinsic::nvvm_prefetch_local_L1, args})
5432 : NVVM::IDArgPair(
5433 {llvm::Intrinsic::nvvm_prefetch_local_L2, args});
5434 default:
5435 llvm_unreachable("Invalid pointer address space");
5436 }
5437}
5438
5439bool NVVM::InlinePtxOp::getAsmValues(
5440 RewriterBase &rewriter,
5441 llvm::SmallVectorImpl<std::pair<mlir::Value, mlir::NVVM::PTXRegisterMod>>
5442 &asmValues) {
5443 for (auto arg : getReadWriteArgs())
5444 asmValues.push_back({arg, mlir::NVVM::PTXRegisterMod::ReadWrite});
5445 for (auto arg : getResults())
5446 asmValues.push_back({arg, mlir::NVVM::PTXRegisterMod::Write});
5447 for (auto arg : getReadOnlyArgs())
5448 asmValues.push_back({arg, mlir::NVVM::PTXRegisterMod::Read});
5449 if (getPredicate())
5450 asmValues.push_back({getPredicate(), mlir::NVVM::PTXRegisterMod::Read});
5451 return false; // No manual mapping needed
5452}
5453
5454NVVM::IDArgPair ClusterLaunchControlTryCancelOp::getIntrinsicIDAndArgs(
5455 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
5456 auto curOp = cast<NVVM::ClusterLaunchControlTryCancelOp>(op);
5458 args.push_back(mt.lookupValue(curOp.getSmemAddress()));
5459 args.push_back(mt.lookupValue(curOp.getMbarrier()));
5460
5461 llvm::Intrinsic::ID intrinsicID =
5462 curOp.getMulticast()
5463 ? llvm::Intrinsic::
5464 nvvm_clusterlaunchcontrol_try_cancel_async_multicast_shared
5465 : llvm::Intrinsic::nvvm_clusterlaunchcontrol_try_cancel_async_shared;
5466
5467 return {intrinsicID, args};
5468}
5469
5470NVVM::IDArgPair ClusterLaunchControlQueryCancelOp::getIntrinsicIDAndArgs(
5471 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
5472 auto curOp = cast<NVVM::ClusterLaunchControlQueryCancelOp>(op);
5474 args.push_back(mt.lookupValue(curOp.getTryCancelResponse()));
5475
5476 llvm::Intrinsic::ID intrinsicID;
5477
5478 switch (curOp.getQueryType()) {
5479 case NVVM::ClusterLaunchControlQueryType::IS_CANCELED:
5480 intrinsicID =
5481 llvm::Intrinsic::nvvm_clusterlaunchcontrol_query_cancel_is_canceled;
5482 break;
5483 case NVVM::ClusterLaunchControlQueryType::GET_FIRST_CTA_ID_X:
5484 intrinsicID = llvm::Intrinsic::
5485 nvvm_clusterlaunchcontrol_query_cancel_get_first_ctaid_x;
5486 break;
5487 case NVVM::ClusterLaunchControlQueryType::GET_FIRST_CTA_ID_Y:
5488 intrinsicID = llvm::Intrinsic::
5489 nvvm_clusterlaunchcontrol_query_cancel_get_first_ctaid_y;
5490 break;
5491 case NVVM::ClusterLaunchControlQueryType::GET_FIRST_CTA_ID_Z:
5492 intrinsicID = llvm::Intrinsic::
5493 nvvm_clusterlaunchcontrol_query_cancel_get_first_ctaid_z;
5494 break;
5495 }
5496 return {intrinsicID, args};
5497}
5498
5500PermuteOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
5501 llvm::IRBuilderBase &builder) {
5502 auto thisOp = cast<NVVM::PermuteOp>(op);
5503 NVVM::PermuteMode mode = thisOp.getMode();
5504
5505 static constexpr llvm::Intrinsic::ID IDs[] = {
5506 llvm::Intrinsic::nvvm_prmt, llvm::Intrinsic::nvvm_prmt_f4e,
5507 llvm::Intrinsic::nvvm_prmt_b4e, llvm::Intrinsic::nvvm_prmt_rc8,
5508 llvm::Intrinsic::nvvm_prmt_ecl, llvm::Intrinsic::nvvm_prmt_ecr,
5509 llvm::Intrinsic::nvvm_prmt_rc16};
5510
5511 unsigned modeIndex = static_cast<unsigned>(mode);
5513 args.push_back(mt.lookupValue(thisOp.getLo()));
5514
5515 // Only first 3 modes (Default, f4e, b4e) need the hi operand.
5516 if (modeIndex < 3)
5517 args.push_back(mt.lookupValue(thisOp.getHi()));
5518
5519 args.push_back(mt.lookupValue(thisOp.getSelector()));
5520
5521 return {IDs[modeIndex], args};
5522}
5523
5524mlir::NVVM::IDArgPair TensormapReplaceOp::getIntrinsicIDAndArgs(
5525 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
5526 auto thisOp = cast<NVVM::TensormapReplaceOp>(op);
5527
5529 args.push_back(mt.lookupValue(thisOp.getAddr()));
5530 if (thisOp.getOrd())
5531 args.push_back(builder.getInt32(thisOp.getOrd().value()));
5532 if (thisOp.getNewValue())
5533 args.push_back(mt.lookupValue(thisOp.getNewValue()));
5534 if (auto attr = thisOp.getNewValueAttr()) {
5535 auto val =
5537 .Case<TensormapElemtypeAttr, TensormapInterleaveLayoutAttr,
5538 TensormapSwizzleModeAttr, TensormapSwizzleAtomicityAttr,
5539 TensormapFillModeAttr>([](auto attr) {
5540 return static_cast<unsigned>(attr.getValue());
5541 })
5542 .Default([](auto attr) {
5543 llvm_unreachable("Invalid attribute type");
5544 return 0;
5545 });
5546 args.push_back(builder.getInt32(val));
5547 }
5548
5549 static constexpr llvm::Intrinsic::ID IDs[] = {
5550 llvm::Intrinsic::nvvm_tensormap_replace_global_address,
5551 llvm::Intrinsic::nvvm_tensormap_replace_rank,
5552 llvm::Intrinsic::nvvm_tensormap_replace_box_dim,
5553 llvm::Intrinsic::nvvm_tensormap_replace_global_dim,
5554 llvm::Intrinsic::nvvm_tensormap_replace_global_stride,
5555 llvm::Intrinsic::nvvm_tensormap_replace_element_stride,
5556 llvm::Intrinsic::nvvm_tensormap_replace_elemtype,
5557 llvm::Intrinsic::nvvm_tensormap_replace_interleave_layout,
5558 llvm::Intrinsic::nvvm_tensormap_replace_swizzle_mode,
5559 llvm::Intrinsic::nvvm_tensormap_replace_swizzle_atomicity,
5560 llvm::Intrinsic::nvvm_tensormap_replace_fill_mode,
5561 };
5562
5563 unsigned fieldIndex = static_cast<unsigned>(thisOp.getField());
5564
5565 return {IDs[fieldIndex], args};
5566}
5567
5568//===----------------------------------------------------------------------===//
5569// NVVM tcgen05.mma functions
5570//===----------------------------------------------------------------------===//
5571
5573Tcgen05MMAOp::getIntrinsicIDAndArgs(Operation &op, LLVM::ModuleTranslation &mt,
5574 llvm::IRBuilderBase &builder) {
5575
5576 auto thisOp = cast<NVVM::Tcgen05MMAOp>(op);
5578
5579 args.push_back(mt.lookupValue(thisOp.getMatrixD()));
5580
5581 llvm::Value *A = mt.lookupValue(thisOp.getMatrixA());
5582 const bool isATensor = isa<llvm::PointerType>(A->getType());
5583 args.push_back(A);
5584
5585 args.push_back(mt.lookupValue(thisOp.getMatrixB()));
5586 args.push_back(mt.lookupValue(thisOp.getIdesc()));
5587 args.push_back(mt.lookupValue(thisOp.getEnableInputD()));
5588
5589 using EnableAShiftArray = std::array<llvm::Intrinsic::ID, 2>;
5590 using CtaGroupArray = std::array<EnableAShiftArray, 2>;
5591 using IsATensorArray = std::array<CtaGroupArray, 2>;
5592 using HasScaleInputDArray = std::array<IsATensorArray, 2>;
5593 using HasDisableOutputLaneArray = std::array<HasScaleInputDArray, 2>;
5594
5595 // [hasDisableOutputLane][hasScaleInputD][isATensor][CtaGroup][EnableAShift]
5596 static constexpr HasDisableOutputLaneArray tcgen05MMAIDs = {
5597 { // without diable output lane
5598 {{// without scale input D
5599 {{
5600 // shared
5601 {{// cg1
5602 {llvm::Intrinsic::nvvm_tcgen05_mma_shared, notIntrinsic},
5603 // cg2
5604 {llvm::Intrinsic::nvvm_tcgen05_mma_shared, notIntrinsic}}},
5605 {{// tensor
5606 {
5607 // cg1
5608 llvm::Intrinsic::nvvm_tcgen05_mma_tensor,
5609 llvm::Intrinsic::nvvm_tcgen05_mma_tensor_ashift,
5610 },
5611 {
5612 // cg2
5613 llvm::Intrinsic::nvvm_tcgen05_mma_tensor,
5614 llvm::Intrinsic::nvvm_tcgen05_mma_tensor_ashift,
5615 }}},
5616 }},
5617 // with scale input D
5618 {{ // shared
5619 {{// cg1
5620 {llvm::Intrinsic::nvvm_tcgen05_mma_shared_scale_d, notIntrinsic},
5621 // cg2
5622 {llvm::Intrinsic::nvvm_tcgen05_mma_shared_scale_d, notIntrinsic}}},
5623 {{// tensor
5624 {
5625 // cg1
5626 llvm::Intrinsic::nvvm_tcgen05_mma_tensor_scale_d,
5627 llvm::Intrinsic::nvvm_tcgen05_mma_tensor_scale_d_ashift,
5628 },
5629 {
5630 // cg2
5631 llvm::Intrinsic::nvvm_tcgen05_mma_tensor_scale_d,
5632 llvm::Intrinsic::nvvm_tcgen05_mma_tensor_scale_d_ashift,
5633 }}}}}}},
5634 // with disable output lane
5635 {{ // without scale input D
5636 {{ // shared
5637 {{// cg1
5638 {llvm::Intrinsic::nvvm_tcgen05_mma_shared_disable_output_lane_cg1,
5639 notIntrinsic},
5640 // cg2
5641 {llvm::Intrinsic::nvvm_tcgen05_mma_shared_disable_output_lane_cg2,
5642 notIntrinsic}}},
5643 {{// cg1
5644 {
5645 llvm::Intrinsic::
5646 nvvm_tcgen05_mma_tensor_disable_output_lane_cg1,
5647 llvm::Intrinsic::
5648 nvvm_tcgen05_mma_tensor_disable_output_lane_cg1_ashift,
5649 },
5650 // cg2
5651 {
5652 llvm::Intrinsic::
5653 nvvm_tcgen05_mma_tensor_disable_output_lane_cg2,
5654 llvm::Intrinsic::
5655 nvvm_tcgen05_mma_tensor_disable_output_lane_cg2_ashift,
5656 }}}}},
5657 // with scale input D
5658 {{ // shared
5659 {{// cg1
5660 {llvm::Intrinsic::
5661 nvvm_tcgen05_mma_shared_scale_d_disable_output_lane_cg1,
5662 notIntrinsic},
5663 // cg2
5664 {llvm::Intrinsic::
5665 nvvm_tcgen05_mma_shared_scale_d_disable_output_lane_cg2,
5666 notIntrinsic}}},
5667 // tensor
5668 {{// cg1
5669 {llvm::Intrinsic::
5670 nvvm_tcgen05_mma_tensor_scale_d_disable_output_lane_cg1,
5671 llvm::Intrinsic::
5672 nvvm_tcgen05_mma_tensor_scale_d_disable_output_lane_cg1_ashift},
5673 // cg2
5674 {
5675 llvm::Intrinsic::
5676 nvvm_tcgen05_mma_tensor_scale_d_disable_output_lane_cg2,
5677 llvm::Intrinsic::
5678 nvvm_tcgen05_mma_tensor_scale_d_disable_output_lane_cg2_ashift,
5679 }}}}}}}}};
5680
5681 llvm::Value *ScaleInputD = mt.lookupValue(thisOp.getScaleInputD());
5682 bool hasScaleInputD = ScaleInputD != nullptr;
5683
5684 llvm::Value *DisableOutputLane =
5685 mt.lookupValue(thisOp.getDisableOutputLane());
5686 bool hasDisableOutputLane = DisableOutputLane != nullptr;
5687
5688 const unsigned ctaGroup =
5689 static_cast<unsigned>(getNVVMCtaGroupKind(thisOp.getCtaGroup()));
5690
5691 llvm::Intrinsic::ID ID =
5692 tcgen05MMAIDs[hasDisableOutputLane][hasScaleInputD][isATensor]
5693 [ctaGroup - 1][thisOp.getAShift()];
5694
5695 assert(ID != notIntrinsic && "Invalid intrinsic for Tcgen05MMAOp.");
5696
5697 if (hasScaleInputD)
5698 args.push_back(ScaleInputD);
5699
5700 if (hasDisableOutputLane)
5701 args.push_back(DisableOutputLane);
5702
5703 args.push_back(builder.getInt32(static_cast<unsigned>(thisOp.getKind())));
5704
5705 if (!hasDisableOutputLane)
5706 args.push_back(builder.getInt32(ctaGroup));
5707
5708 args.push_back(
5709 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorOp())));
5710
5711 args.push_back(
5712 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorOpB())));
5713
5714 return {ID, args};
5715}
5716
5717static LogicalResult
5718verifyTcgen05MMAOp(bool isATensor, mlir::Value disableOutputLane,
5719 NVVM::CTAGroupKind ctaGroup, bool hasAShift,
5720 NVVM::Tcgen05MMACollectorOp collectorOp, Location loc) {
5721
5722 if (disableOutputLane) {
5723 mlir::VectorType disableOutputLaneType =
5724 cast<mlir::VectorType>(disableOutputLane.getType());
5725 if ((ctaGroup == NVVM::CTAGroupKind::CTA_1 &&
5726 disableOutputLaneType.getNumElements() != 4) ||
5727 (ctaGroup == NVVM::CTAGroupKind::CTA_2 &&
5728 disableOutputLaneType.getNumElements() != 8))
5729 return emitError(loc) << "Disable Output Lane of length "
5730 << disableOutputLaneType.getNumElements()
5731 << " is incompatible with CtaGroupAttr";
5732 }
5733
5734 if (hasAShift && !isATensor)
5735 return emitError(
5736 loc, "A-shift can be applied only when matrix A is in tensor memory");
5737
5738 if (hasAShift == true && (collectorOp == Tcgen05MMACollectorOp::FILL ||
5739 collectorOp == Tcgen05MMACollectorOp::USE))
5740 return emitError(
5741 loc, "Cannot use collector buffer operation fill or use with ashift");
5742
5743 return success();
5744}
5745
5746LogicalResult Tcgen05MMAOp::verify() {
5747 return verifyTcgen05MMAOp(isa<LLVM::LLVMPointerType>(getMatrixA().getType()),
5748 getDisableOutputLane(), getCtaGroup(), getAShift(),
5749 getCollectorOp(), getLoc());
5750}
5751
5752//===----------------------------------------------------------------------===//
5753// NVVM tcgen05.mma.sp functions
5754//===----------------------------------------------------------------------===//
5755
5756mlir::NVVM::IDArgPair Tcgen05MMASparseOp::getIntrinsicIDAndArgs(
5757 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
5758
5759 auto thisOp = cast<NVVM::Tcgen05MMASparseOp>(op);
5761
5762 args.push_back(mt.lookupValue(thisOp.getMatrixD()));
5763
5764 llvm::Value *A = mt.lookupValue(thisOp.getMatrixA());
5765 bool isATensor = isa<llvm::PointerType>(A->getType());
5766 args.push_back(A);
5767
5768 args.push_back(mt.lookupValue(thisOp.getMatrixB()));
5769 args.push_back(mt.lookupValue(thisOp.getIdesc()));
5770 args.push_back(mt.lookupValue(thisOp.getEnableInputD()));
5771 args.push_back(mt.lookupValue(thisOp.getSparseMetadata()));
5772
5773 using EnableAShiftArray = std::array<llvm::Intrinsic::ID, 2>;
5774 using CtaGroupArray = std::array<EnableAShiftArray, 2>;
5775 using IsATensorArray = std::array<CtaGroupArray, 2>;
5776 using HasScaleInputDArray = std::array<IsATensorArray, 2>;
5777 using HasDisableOutputLaneArray = std::array<HasScaleInputDArray, 2>;
5778
5779 // [hasDisableOutputLane][hasScaleInputD][isATensor][CtaGroup][EnableAShift]
5780 static constexpr HasDisableOutputLaneArray tcgen05MMASparseIDs = {
5781 { // without diable output lane
5782 {{// without scale input D
5783 {{
5784 // shared
5785 {{// cg1
5786 {llvm::Intrinsic::nvvm_tcgen05_mma_sp_shared, notIntrinsic},
5787 // cg2
5788 {llvm::Intrinsic::nvvm_tcgen05_mma_sp_shared, notIntrinsic}}},
5789 {{// tensor
5790 {
5791 // cg1
5792 llvm::Intrinsic::nvvm_tcgen05_mma_sp_tensor,
5793 llvm::Intrinsic::nvvm_tcgen05_mma_sp_tensor_ashift,
5794 },
5795 {
5796 // cg2
5797 llvm::Intrinsic::nvvm_tcgen05_mma_sp_tensor,
5798 llvm::Intrinsic::nvvm_tcgen05_mma_sp_tensor_ashift,
5799 }}},
5800 }},
5801 // with scale input D
5802 {{ // shared
5803 {{// cg1
5804 {llvm::Intrinsic::nvvm_tcgen05_mma_sp_shared_scale_d,
5805 notIntrinsic},
5806 // cg2
5807 {llvm::Intrinsic::nvvm_tcgen05_mma_sp_shared_scale_d,
5808 notIntrinsic}}},
5809 {{// tensor
5810 {
5811 // cg1
5812 llvm::Intrinsic::nvvm_tcgen05_mma_sp_tensor_scale_d,
5813 llvm::Intrinsic::nvvm_tcgen05_mma_sp_tensor_scale_d_ashift,
5814 },
5815 {
5816 // cg2
5817 llvm::Intrinsic::nvvm_tcgen05_mma_sp_tensor_scale_d,
5818 llvm::Intrinsic::nvvm_tcgen05_mma_sp_tensor_scale_d_ashift,
5819 }}}}}}},
5820 // with disable output lane
5821 {{ // without scale input D
5822 {{ // shared
5823 {{// cg1
5824 {llvm::Intrinsic::
5825 nvvm_tcgen05_mma_sp_shared_disable_output_lane_cg1,
5826 notIntrinsic},
5827 // cg2
5828 {llvm::Intrinsic::
5829 nvvm_tcgen05_mma_sp_shared_disable_output_lane_cg2,
5830 notIntrinsic}}},
5831 {{// cg1
5832 {
5833 llvm::Intrinsic::
5834 nvvm_tcgen05_mma_sp_tensor_disable_output_lane_cg1,
5835 llvm::Intrinsic::
5836 nvvm_tcgen05_mma_sp_tensor_disable_output_lane_cg1_ashift,
5837 },
5838 // cg2
5839 {
5840 llvm::Intrinsic::
5841 nvvm_tcgen05_mma_sp_tensor_disable_output_lane_cg2,
5842 llvm::Intrinsic::
5843 nvvm_tcgen05_mma_sp_tensor_disable_output_lane_cg2_ashift,
5844 }}}}},
5845 // with scale input D
5846 {{ // shared
5847 {{// cg1
5848 {llvm::Intrinsic::
5849 nvvm_tcgen05_mma_sp_shared_scale_d_disable_output_lane_cg1,
5850 notIntrinsic},
5851 // cg2
5852 {llvm::Intrinsic::
5853 nvvm_tcgen05_mma_sp_shared_scale_d_disable_output_lane_cg2,
5854 notIntrinsic}}},
5855 // tensor
5856 {{// cg1
5857 {llvm::Intrinsic::
5858 nvvm_tcgen05_mma_sp_tensor_scale_d_disable_output_lane_cg1,
5859 llvm::Intrinsic::
5860 nvvm_tcgen05_mma_sp_tensor_scale_d_disable_output_lane_cg1_ashift},
5861 // cg2
5862 {
5863 llvm::Intrinsic::
5864 nvvm_tcgen05_mma_sp_tensor_scale_d_disable_output_lane_cg2,
5865 llvm::Intrinsic::
5866 nvvm_tcgen05_mma_sp_tensor_scale_d_disable_output_lane_cg2_ashift,
5867 }}}}}}}}};
5868
5869 llvm::Value *ScaleInputD = mt.lookupValue(thisOp.getScaleInputD());
5870 bool hasScaleInputD = ScaleInputD != nullptr;
5871
5872 llvm::Value *DisableOutputLane =
5873 mt.lookupValue(thisOp.getDisableOutputLane());
5874 bool hasDisableOutputLane = DisableOutputLane != nullptr;
5875
5876 unsigned ctaGroup =
5877 static_cast<unsigned>(getNVVMCtaGroupKind(thisOp.getCtaGroup()));
5878
5879 llvm::Intrinsic::ID ID =
5880 tcgen05MMASparseIDs[hasDisableOutputLane][hasScaleInputD][isATensor]
5881 [ctaGroup - 1][thisOp.getAShift()];
5882
5883 assert(ID != notIntrinsic && "Invalid intrinsic for Tcgen05MMASparseOp.");
5884
5885 if (hasScaleInputD)
5886 args.push_back(ScaleInputD);
5887
5888 if (hasDisableOutputLane)
5889 args.push_back(DisableOutputLane);
5890
5891 args.push_back(builder.getInt32(static_cast<unsigned>(thisOp.getKind())));
5892
5893 if (!hasDisableOutputLane)
5894 args.push_back(builder.getInt32(ctaGroup));
5895
5896 args.push_back(
5897 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorOp())));
5898
5899 args.push_back(
5900 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorOpB())));
5901
5902 return {ID, args};
5903}
5904
5905LogicalResult Tcgen05MMASparseOp::verify() {
5906 return verifyTcgen05MMAOp(isa<LLVM::LLVMPointerType>(getMatrixA().getType()),
5907 getDisableOutputLane(), getCtaGroup(), getAShift(),
5908 getCollectorOp(), getLoc());
5909}
5910
5911//===----------------------------------------------------------------------===//
5912// NVVM tcgen05.mma.block_scale functions
5913//===----------------------------------------------------------------------===//
5914
5915mlir::NVVM::IDArgPair Tcgen05MMABlockScaleOp::getIntrinsicIDAndArgs(
5916 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
5917
5918 auto thisOp = cast<NVVM::Tcgen05MMABlockScaleOp>(op);
5920
5921 args.push_back(mt.lookupValue(thisOp.getMatrixD()));
5922
5923 llvm::Value *A = mt.lookupValue(thisOp.getMatrixA());
5924 bool isATensor = isa<llvm::PointerType>(A->getType());
5925 args.push_back(A);
5926
5927 args.push_back(mt.lookupValue(thisOp.getMatrixB()));
5928 args.push_back(mt.lookupValue(thisOp.getIdesc()));
5929 args.push_back(mt.lookupValue(thisOp.getEnableInputD()));
5930 args.push_back(mt.lookupValue(thisOp.getScaleA()));
5931 args.push_back(mt.lookupValue(thisOp.getScaleB()));
5932 args.push_back(builder.getInt32(
5933 static_cast<unsigned>(getNVVMCtaGroupKind(thisOp.getCtaGroup()))));
5934 args.push_back(
5935 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorOp())));
5936 args.push_back(
5937 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorOpB())));
5938
5939 auto kind = thisOp.getKind();
5940 auto blockScale = thisOp.getBlockScale();
5941 llvm::Intrinsic::ID ID = [&]() {
5942 if (kind == NVVM::Tcgen05MMAKind::MXF8F6F4) {
5943 if (blockScale == NVVM::Tcgen05MMABlockScale::DEFAULT) {
5944 return isATensor ? llvm::Intrinsic::
5945 nvvm_tcgen05_mma_tensor_mxf8f6f4_block_scale
5946 : llvm::Intrinsic::
5947 nvvm_tcgen05_mma_shared_mxf8f6f4_block_scale;
5948 } else if (blockScale == NVVM::Tcgen05MMABlockScale::BLOCK32) {
5949 return isATensor
5950 ? llvm::Intrinsic::
5951 nvvm_tcgen05_mma_tensor_mxf8f6f4_block_scale_block32
5952 : llvm::Intrinsic::
5953 nvvm_tcgen05_mma_shared_mxf8f6f4_block_scale_block32;
5954 }
5955 } else if (kind == NVVM::Tcgen05MMAKind::MXF4) {
5956 if (blockScale == NVVM::Tcgen05MMABlockScale::DEFAULT) {
5957 return isATensor
5958 ? llvm::Intrinsic::nvvm_tcgen05_mma_tensor_mxf4_block_scale
5959 : llvm::Intrinsic::nvvm_tcgen05_mma_shared_mxf4_block_scale;
5960 } else if (blockScale == NVVM::Tcgen05MMABlockScale::BLOCK32) {
5961 return isATensor ? llvm::Intrinsic::
5962 nvvm_tcgen05_mma_tensor_mxf4_block_scale_block32
5963 : llvm::Intrinsic::
5964 nvvm_tcgen05_mma_shared_mxf4_block_scale_block32;
5965 }
5966 } else if (kind == NVVM::Tcgen05MMAKind::MXF4NVF4) {
5967 if (blockScale == NVVM::Tcgen05MMABlockScale::BLOCK32) {
5968 return isATensor
5969 ? llvm::Intrinsic::
5970 nvvm_tcgen05_mma_tensor_mxf4nvf4_block_scale_block32
5971 : llvm::Intrinsic::
5972 nvvm_tcgen05_mma_shared_mxf4nvf4_block_scale_block32;
5973
5974 } else if (blockScale == NVVM::Tcgen05MMABlockScale::BLOCK16) {
5975 return isATensor
5976 ? llvm::Intrinsic::
5977 nvvm_tcgen05_mma_tensor_mxf4nvf4_block_scale_block16
5978 : llvm::Intrinsic::
5979 nvvm_tcgen05_mma_shared_mxf4nvf4_block_scale_block16;
5980 }
5981 }
5982 llvm_unreachable("Invalid tcgen05.mma.block_scale attributes");
5983 }();
5984
5985 return {ID, args};
5986}
5987
5988static LogicalResult verifyTcgen05MMABlockScaleOp(
5989 NVVM::Tcgen05MMACollectorOp collectorOp, NVVM::Tcgen05MMAKind kind,
5990 NVVM::Tcgen05MMABlockScale blockScale, Location loc) {
5991 if (blockScale == NVVM::Tcgen05MMABlockScale::DEFAULT &&
5992 kind == NVVM::Tcgen05MMAKind::MXF4NVF4)
5993 return emitError(loc, "mxf4nvf4 requires block scale attribute");
5994
5995 if (blockScale == NVVM::Tcgen05MMABlockScale::BLOCK16 &&
5996 kind != NVVM::Tcgen05MMAKind::MXF4NVF4)
5997 return emitError(loc,
5998 llvm::formatv("{} kind does not support block16 attribute",
5999 stringifyEnum(kind)));
6000
6001 return success();
6002}
6003
6004LogicalResult Tcgen05MMABlockScaleOp::verify() {
6005 return verifyTcgen05MMABlockScaleOp(getCollectorOp(), getKind(),
6006 getBlockScale(), getLoc());
6007}
6008
6009//===----------------------------------------------------------------------===//
6010// NVVM tcgen05.mma.sp.block_scale functions
6011//===----------------------------------------------------------------------===//
6012
6013mlir::NVVM::IDArgPair Tcgen05MMASparseBlockScaleOp::getIntrinsicIDAndArgs(
6014 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
6015
6016 auto thisOp = cast<NVVM::Tcgen05MMASparseBlockScaleOp>(op);
6018
6019 args.push_back(mt.lookupValue(thisOp.getMatrixD()));
6020
6021 llvm::Value *A = mt.lookupValue(thisOp.getMatrixA());
6022 bool isATensor = isa<llvm::PointerType>(A->getType());
6023 args.push_back(A);
6024
6025 args.push_back(mt.lookupValue(thisOp.getMatrixB()));
6026 args.push_back(mt.lookupValue(thisOp.getIdesc()));
6027 args.push_back(mt.lookupValue(thisOp.getEnableInputD()));
6028 args.push_back(mt.lookupValue(thisOp.getSparseMetadata()));
6029 args.push_back(mt.lookupValue(thisOp.getScaleA()));
6030 args.push_back(mt.lookupValue(thisOp.getScaleB()));
6031 args.push_back(builder.getInt32(
6032 static_cast<unsigned>(getNVVMCtaGroupKind(thisOp.getCtaGroup()))));
6033 args.push_back(
6034 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorOp())));
6035 args.push_back(
6036 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorOpB())));
6037
6038 auto kind = thisOp.getKind();
6039 auto blockScale = thisOp.getBlockScale();
6040 llvm::Intrinsic::ID ID = [&]() {
6041 if (kind == NVVM::Tcgen05MMAKind::MXF8F6F4) {
6042 if (blockScale == NVVM::Tcgen05MMABlockScale::DEFAULT) {
6043 return isATensor ? llvm::Intrinsic::
6044 nvvm_tcgen05_mma_sp_tensor_mxf8f6f4_block_scale
6045 : llvm::Intrinsic::
6046 nvvm_tcgen05_mma_sp_shared_mxf8f6f4_block_scale;
6047 } else if (blockScale == NVVM::Tcgen05MMABlockScale::BLOCK32) {
6048 return isATensor
6049 ? llvm::Intrinsic::
6050 nvvm_tcgen05_mma_sp_tensor_mxf8f6f4_block_scale_block32
6051 : llvm::Intrinsic::
6052 nvvm_tcgen05_mma_sp_shared_mxf8f6f4_block_scale_block32;
6053 }
6054 } else if (kind == NVVM::Tcgen05MMAKind::MXF4) {
6055 if (blockScale == NVVM::Tcgen05MMABlockScale::DEFAULT) {
6056 return isATensor ? llvm::Intrinsic::
6057 nvvm_tcgen05_mma_sp_tensor_mxf4_block_scale
6058 : llvm::Intrinsic::
6059 nvvm_tcgen05_mma_sp_shared_mxf4_block_scale;
6060 } else if (blockScale == NVVM::Tcgen05MMABlockScale::BLOCK32) {
6061 return isATensor
6062 ? llvm::Intrinsic::
6063 nvvm_tcgen05_mma_sp_tensor_mxf4_block_scale_block32
6064 : llvm::Intrinsic::
6065 nvvm_tcgen05_mma_sp_shared_mxf4_block_scale_block32;
6066 }
6067 } else if (kind == NVVM::Tcgen05MMAKind::MXF4NVF4) {
6068 if (blockScale == NVVM::Tcgen05MMABlockScale::BLOCK32) {
6069 return isATensor
6070 ? llvm::Intrinsic::
6071 nvvm_tcgen05_mma_sp_tensor_mxf4nvf4_block_scale_block32
6072 : llvm::Intrinsic::
6073 nvvm_tcgen05_mma_sp_shared_mxf4nvf4_block_scale_block32;
6074
6075 } else if (blockScale == NVVM::Tcgen05MMABlockScale::BLOCK16) {
6076 return isATensor
6077 ? llvm::Intrinsic::
6078 nvvm_tcgen05_mma_sp_tensor_mxf4nvf4_block_scale_block16
6079 : llvm::Intrinsic::
6080 nvvm_tcgen05_mma_sp_shared_mxf4nvf4_block_scale_block16;
6081 }
6082 }
6083 llvm_unreachable("Invalid tcgen05.mma.sp.block_scale attributes");
6084 }();
6085
6086 return {ID, args};
6087}
6088
6089LogicalResult Tcgen05MMASparseBlockScaleOp::verify() {
6090 return verifyTcgen05MMABlockScaleOp(getCollectorOp(), getKind(),
6091 getBlockScale(), getLoc());
6092}
6093
6094//===----------------------------------------------------------------------===//
6095// NVVM tcgen05.mma.ws functions
6096//===----------------------------------------------------------------------===//
6097
6098mlir::NVVM::IDArgPair Tcgen05MMAWsOp::getIntrinsicIDAndArgs(
6099 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
6100
6101 auto thisOp = cast<NVVM::Tcgen05MMAWsOp>(op);
6103
6104 args.push_back(mt.lookupValue(thisOp.getMatrixD()));
6105
6106 llvm::Value *A = mt.lookupValue(thisOp.getMatrixA());
6107 bool isATensor = isa<llvm::PointerType>(A->getType());
6108 args.push_back(A);
6109
6110 args.push_back(mt.lookupValue(thisOp.getMatrixB()));
6111 args.push_back(mt.lookupValue(thisOp.getIdesc()));
6112 args.push_back(mt.lookupValue(thisOp.getEnableInputD()));
6113
6114 mlir::Value ZeroColMask = thisOp.getZeroColMask();
6115 llvm::Intrinsic::ID ID = notIntrinsic;
6116 if (ZeroColMask) {
6117 args.push_back(mt.lookupValue(ZeroColMask));
6118 ID = isATensor ? llvm::Intrinsic::nvvm_tcgen05_mma_ws_tensor_zero_col_mask
6119 : llvm::Intrinsic::nvvm_tcgen05_mma_ws_shared_zero_col_mask;
6120 } else
6121 ID = isATensor ? llvm::Intrinsic::nvvm_tcgen05_mma_ws_tensor
6122 : llvm::Intrinsic::nvvm_tcgen05_mma_ws_shared;
6123
6124 args.push_back(builder.getInt32(static_cast<unsigned>(thisOp.getKind())));
6125 args.push_back(
6126 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorBBuffer())));
6127 args.push_back(
6128 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorOp())));
6129
6130 return {ID, args};
6131}
6132
6133//===----------------------------------------------------------------------===//
6134// NVVM tcgen05.mma.ws.sp functions
6135//===----------------------------------------------------------------------===//
6136
6137mlir::NVVM::IDArgPair Tcgen05MMAWsSparseOp::getIntrinsicIDAndArgs(
6138 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
6139
6140 auto thisOp = cast<NVVM::Tcgen05MMAWsSparseOp>(op);
6142
6143 args.push_back(mt.lookupValue(thisOp.getMatrixD()));
6144
6145 llvm::Value *A = mt.lookupValue(thisOp.getMatrixA());
6146 bool isATensor = isa<llvm::PointerType>(A->getType());
6147 args.push_back(A);
6148
6149 args.push_back(mt.lookupValue(thisOp.getMatrixB()));
6150 args.push_back(mt.lookupValue(thisOp.getIdesc()));
6151 args.push_back(mt.lookupValue(thisOp.getEnableInputD()));
6152 args.push_back(mt.lookupValue(thisOp.getSparseMetadata()));
6153
6154 mlir::Value ZeroColMask = thisOp.getZeroColMask();
6155 llvm::Intrinsic::ID ID = notIntrinsic;
6156 if (ZeroColMask) {
6157 args.push_back(mt.lookupValue(ZeroColMask));
6158 ID = isATensor
6159 ? llvm::Intrinsic::nvvm_tcgen05_mma_ws_sp_tensor_zero_col_mask
6160 : llvm::Intrinsic::nvvm_tcgen05_mma_ws_sp_shared_zero_col_mask;
6161 } else
6162 ID = isATensor ? llvm::Intrinsic::nvvm_tcgen05_mma_ws_sp_tensor
6163 : llvm::Intrinsic::nvvm_tcgen05_mma_ws_sp_shared;
6164
6165 args.push_back(builder.getInt32(static_cast<unsigned>(thisOp.getKind())));
6166 args.push_back(
6167 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorBBuffer())));
6168 args.push_back(
6169 builder.getInt32(static_cast<unsigned>(thisOp.getCollectorOp())));
6170
6171 return {ID, args};
6172}
6173
6174//===----------------------------------------------------------------------===//
6175// NVVM tcgen05.ld.red functions
6176//===----------------------------------------------------------------------===//
6177
6178#define TCGEN05LDRED(SHAPE, NUM, TYPE) \
6179 llvm::Intrinsic::nvvm_tcgen05_ld_red_##SHAPE##_##NUM##_##TYPE
6180
6181mlir::NVVM::IDArgPair NVVM::Tcgen05LdRedOp::getIntrinsicIDAndArgs(
6182 Operation &op, LLVM::ModuleTranslation &mt, llvm::IRBuilderBase &builder) {
6183 auto thisOp = cast<NVVM::Tcgen05LdRedOp>(op);
6185
6186 mlir::VectorType VecResTy =
6187 cast<mlir::VectorType>(thisOp.getData().getType());
6188 unsigned Num = VecResTy.getNumElements();
6189 bool IsFloat = thisOp.getRedVal().getType().isF32();
6190
6191 llvm::Intrinsic::ID Shape32x32b[][2] = {
6193 {TCGEN05LDRED(32x32b, x2, i32), TCGEN05LDRED(32x32b, x2, f32)},
6194 {TCGEN05LDRED(32x32b, x4, i32), TCGEN05LDRED(32x32b, x4, f32)},
6195 {TCGEN05LDRED(32x32b, x8, i32), TCGEN05LDRED(32x32b, x8, f32)},
6196 {TCGEN05LDRED(32x32b, x16, i32), TCGEN05LDRED(32x32b, x16, f32)},
6197 {TCGEN05LDRED(32x32b, x32, i32), TCGEN05LDRED(32x32b, x32, f32)},
6198 {TCGEN05LDRED(32x32b, x64, i32), TCGEN05LDRED(32x32b, x64, f32)},
6199 {TCGEN05LDRED(32x32b, x128, i32), TCGEN05LDRED(32x32b, x128, f32)},
6200 };
6201
6202 llvm::Intrinsic::ID Shape16x32bx2[][2] = {
6204 {TCGEN05LDRED(16x32bx2, x2, i32), TCGEN05LDRED(16x32bx2, x2, f32)},
6205 {TCGEN05LDRED(16x32bx2, x4, i32), TCGEN05LDRED(16x32bx2, x4, f32)},
6206 {TCGEN05LDRED(16x32bx2, x8, i32), TCGEN05LDRED(16x32bx2, x8, f32)},
6207 {TCGEN05LDRED(16x32bx2, x16, i32), TCGEN05LDRED(16x32bx2, x16, f32)},
6208 {TCGEN05LDRED(16x32bx2, x32, i32), TCGEN05LDRED(16x32bx2, x32, f32)},
6209 {TCGEN05LDRED(16x32bx2, x64, i32), TCGEN05LDRED(16x32bx2, x64, f32)},
6210 {TCGEN05LDRED(16x32bx2, x128, i32), TCGEN05LDRED(16x32bx2, x128, f32)},
6211 };
6212
6213 NVVM::Tcgen05LdStShape shape = thisOp.getShape();
6214 unsigned ID = [&]() {
6215 // `num` contains the length of vector and log2 of `num` returns the index
6216 // into the shape array
6217 unsigned idx = std::log2(Num);
6218 switch (shape) {
6219 case NVVM::Tcgen05LdStShape::SHAPE_32X32B:
6220 return Shape32x32b[idx][IsFloat];
6221 case NVVM::Tcgen05LdStShape::SHAPE_16X32BX2:
6222 return Shape16x32bx2[idx][IsFloat];
6223 default:
6224 llvm_unreachable("unhandled tcgen05.ld lowering");
6225 }
6226 }();
6227
6228 args.push_back(mt.lookupValue(thisOp.getAddr()));
6229
6230 if (shape == NVVM::Tcgen05LdStShape::SHAPE_16X32BX2)
6231 args.push_back(mt.lookupValue(thisOp.getOffset()));
6232
6233 args.push_back(
6234 builder.getInt32(thisOp.getOp() == NVVM::ReductionKind::MIN ? 0 : 1));
6235
6236 if (IsFloat) {
6237 args.push_back(builder.getInt1(static_cast<unsigned>(thisOp.getAbs())));
6238 args.push_back(builder.getInt1(static_cast<unsigned>(thisOp.getNan())));
6239 }
6240 return {ID, args};
6241}
6242
6243LogicalResult Tcgen05LdRedOp::verify() {
6244 VectorType data = cast<VectorType>(getData().getType());
6245 Type redVal = getRedVal().getType();
6246
6247 if (data.getElementType() != redVal)
6248 return emitError(
6249 "type of reduction value and element type of vector data should match");
6250
6251 if (getOp() != NVVM::ReductionKind::MIN &&
6252 getOp() != NVVM::ReductionKind::MAX)
6253 return emitError("only min and max reduction kinds are supported");
6254
6255 if (redVal.isInteger() && (getAbs() || getNan())) {
6256 return emitError("abs or nan is only applicable for f32 type");
6257 }
6258 return success();
6259}
6260
6261//===----------------------------------------------------------------------===//
6262// NVVMDialect initialization, type parsing, and registration.
6263//===----------------------------------------------------------------------===//
6264
6265namespace {
6266struct NVVMInlinerInterface final : DialectInlinerInterface {
6267 using DialectInlinerInterface::DialectInlinerInterface;
6268 bool isLegalToInline(Operation *, Region *, bool, IRMapping &) const final {
6269 return true;
6270 }
6271};
6272} // namespace
6273
6274// TODO: This should be the llvm.nvvm dialect once this is supported.
6275void NVVMDialect::initialize() {
6276 addOperations<
6277#define GET_OP_LIST
6278#include "mlir/Dialect/LLVMIR/NVVMOps.cpp.inc"
6279 >();
6280 addAttributes<
6281#define GET_ATTRDEF_LIST
6282#include "mlir/Dialect/LLVMIR/NVVMOpsAttributes.cpp.inc"
6283 >();
6284
6285 // Support unknown operations because not all NVVM operations are
6286 // registered.
6287 allowUnknownOperations();
6288 addInterfaces<NVVMInlinerInterface>();
6289 declarePromisedInterface<ConvertToLLVMPatternInterface, NVVMDialect>();
6290 declarePromisedInterface<gpu::TargetAttrInterface, NVVMTargetAttr>();
6291}
6292
6293LogicalResult NVVMDialect::verifyOperationAttribute(Operation *op,
6294 NamedAttribute attr) {
6295 StringAttr attrName = attr.getName();
6296 // Kernel function attribute should be attached to functions.
6297 if (attrName == NVVMDialect::getKernelFuncAttrName()) {
6298 if (!isa<LLVM::LLVMFuncOp>(op)) {
6299 return op->emitError() << "'" << NVVMDialect::getKernelFuncAttrName()
6300 << "' attribute attached to unexpected op";
6301 }
6302 }
6303 // If maxntid / reqntid / cluster_dim exist, it must be an array with max 3
6304 // dim
6305 if (attrName == NVVMDialect::getMaxntidAttrName() ||
6306 attrName == NVVMDialect::getReqntidAttrName() ||
6307 attrName == NVVMDialect::getClusterDimAttrName()) {
6308 auto values = llvm::dyn_cast<DenseI32ArrayAttr>(attr.getValue());
6309 if (!values || values.empty() || values.size() > 3) {
6310 return op->emitError()
6311 << "'" << attrName
6312 << "' attribute must be integer array with maximum 3 index";
6313 }
6314 }
6315 // If minctasm / maxnreg / cluster_max_blocks exist, it must be an integer
6316 // attribute
6317 if (attrName == NVVMDialect::getMinctasmAttrName() ||
6318 attrName == NVVMDialect::getMaxnregAttrName() ||
6319 attrName == NVVMDialect::getClusterMaxBlocksAttrName()) {
6320 if (!llvm::dyn_cast<IntegerAttr>(attr.getValue())) {
6321 return op->emitError()
6322 << "'" << attrName << "' attribute must be integer constant";
6323 }
6324 }
6325 // blocksareclusters must be used along with reqntid and cluster_dim
6326 if (attrName == NVVMDialect::getBlocksAreClustersAttrName()) {
6327 if (!op->hasAttr(NVVMDialect::getReqntidAttrName()) ||
6328 !op->hasAttr(NVVMDialect::getClusterDimAttrName())) {
6329 return op->emitError()
6330 << "'" << attrName << "' attribute must be used along with " << "'"
6331 << NVVMDialect::getReqntidAttrName() << "' and " << "'"
6332 << NVVMDialect::getClusterDimAttrName() << "'";
6333 }
6334 }
6335
6336 return success();
6337}
6338
6339LogicalResult NVVMDialect::verifyRegionArgAttribute(Operation *op,
6340 unsigned regionIndex,
6341 unsigned argIndex,
6342 NamedAttribute argAttr) {
6343 auto funcOp = dyn_cast<FunctionOpInterface>(op);
6344 if (!funcOp)
6345 return success();
6346
6347 bool isKernel = op->hasAttr(NVVMDialect::getKernelFuncAttrName());
6348 StringAttr attrName = argAttr.getName();
6349 if (attrName == NVVM::NVVMDialect::getGridConstantAttrName()) {
6350 if (!isKernel) {
6351 return op->emitError()
6352 << "'" << attrName
6353 << "' attribute must be present only on kernel arguments";
6354 }
6355 if (!isa<UnitAttr>(argAttr.getValue()))
6356 return op->emitError() << "'" << attrName << "' must be a unit attribute";
6357 if (!funcOp.getArgAttr(argIndex, LLVM::LLVMDialect::getByValAttrName())) {
6358 return op->emitError()
6359 << "'" << attrName
6360 << "' attribute requires the argument to also have attribute '"
6361 << LLVM::LLVMDialect::getByValAttrName() << "'";
6362 }
6363 }
6364
6365 return success();
6366}
6367
6368//===----------------------------------------------------------------------===//
6369// NVVM Address Space Attr
6370//===----------------------------------------------------------------------===//
6371
6372unsigned NVVMMemorySpaceAttr::getAddressSpace() const {
6373 return static_cast<unsigned>(getValue());
6374}
6375
6376bool NVVMMemorySpaceAttr::isValidLoad(
6377 Type type, ptr::AtomicOrdering ordering, std::optional<int64_t> alignment,
6378 const ::mlir::DataLayout *dataLayout,
6380 return LLVM::detail::isValidLoadStoreImpl(type, ordering, alignment,
6381 dataLayout, emitError);
6382}
6383
6384bool NVVMMemorySpaceAttr::isValidStore(
6385 Type type, ptr::AtomicOrdering ordering, std::optional<int64_t> alignment,
6386 const ::mlir::DataLayout *dataLayout,
6388 return LLVM::detail::isValidLoadStoreImpl(type, ordering, alignment,
6389 dataLayout, emitError);
6390}
6391
6392bool NVVMMemorySpaceAttr::isValidAtomicOp(
6393 ptr::AtomicBinOp op, Type type, ptr::AtomicOrdering ordering,
6394 std::optional<int64_t> alignment, const ::mlir::DataLayout *dataLayout,
6396 // TODO: update this method once `ptr.atomic_rmw` is implemented.
6397 assert(false && "unimplemented, see TODO in the source.");
6398 return false;
6399}
6400
6401bool NVVMMemorySpaceAttr::isValidAtomicXchg(
6402 Type type, ptr::AtomicOrdering successOrdering,
6403 ptr::AtomicOrdering failureOrdering, std::optional<int64_t> alignment,
6404 const ::mlir::DataLayout *dataLayout,
6406 // TODO: update this method once `ptr.atomic_cmpxchg` is implemented.
6407 assert(false && "unimplemented, see TODO in the source.");
6408 return false;
6409}
6410
6411bool NVVMMemorySpaceAttr::isValidAddrSpaceCast(
6412 Type tgt, Type src, function_ref<InFlightDiagnostic()> emitError) const {
6413 // TODO: update this method once the `ptr.addrspace_cast` op is added to the
6414 // dialect.
6415 assert(false && "unimplemented, see TODO in the source.");
6416 return false;
6417}
6418
6419bool NVVMMemorySpaceAttr::isValidPtrIntCast(
6420 Type intLikeTy, Type ptrLikeTy,
6422 // TODO: update this method once the int-cast ops are added to the `ptr`
6423 // dialect.
6424 assert(false && "unimplemented, see TODO in the source.");
6425 return false;
6426}
6427
6428//===----------------------------------------------------------------------===//
6429// NVVM target attribute.
6430//===----------------------------------------------------------------------===//
6431LogicalResult
6432NVVMTargetAttr::verify(function_ref<InFlightDiagnostic()> emitError,
6433 int optLevel, StringRef triple, StringRef chip,
6434 StringRef features, DictionaryAttr flags,
6435 ArrayAttr files, bool verifyTarget) {
6436 if (optLevel < 0 || optLevel > 3) {
6437 emitError() << "The optimization level must be a number between 0 and 3.";
6438 return failure();
6439 }
6440 if (triple.empty()) {
6441 emitError() << "The target triple cannot be empty.";
6442 return failure();
6443 }
6444 if (chip.empty()) {
6445 emitError() << "The target chip cannot be empty.";
6446 return failure();
6447 }
6448 if (files && !llvm::all_of(files, [](::mlir::Attribute attr) {
6449 return mlir::isa_and_nonnull<StringAttr>(attr);
6450 })) {
6451 emitError() << "All the elements in the `link` array must be strings.";
6452 return failure();
6453 }
6454 return success();
6455}
6456
6457LogicalResult NVVMTargetAttr::verifyTarget(Operation *gpuModule) {
6458 if (!getVerifyTarget())
6459 return success();
6460
6461 auto gpuModuleOp = llvm::dyn_cast<gpu::GPUModuleOp>(gpuModule);
6462 if (!gpuModuleOp) {
6463 return emitError(gpuModule->getLoc(),
6464 "NVVM target attribute must be attached to a GPU module");
6465 }
6466
6467 const unsigned targetFullSmVersion =
6469 if (!NVVMCheckSMVersion::isMinimumSMVersion(targetFullSmVersion)) {
6470 return emitError(gpuModule->getLoc(),
6471 "Minimum NVVM target SM version is sm_20");
6472 }
6473
6474 if (gpuModuleOp
6475 ->walk([&](Operation *op) {
6476 if (auto reqOp = llvm::dyn_cast<NVVM::RequiresSMInterface>(op)) {
6477 const NVVMCheckSMVersion requirement =
6478 reqOp.getRequiredMinSMVersion();
6479 if (!requirement.isCompatibleWith(targetFullSmVersion)) {
6480 op->emitOpError() << "is not supported on " << getChip();
6481 return WalkResult::interrupt();
6482 }
6483 }
6484 return WalkResult::advance();
6485 })
6486 .wasInterrupted())
6487 return failure();
6488
6489 return success();
6490}
6491
6492#define GET_OP_CLASSES
6493#include "mlir/Dialect/LLVMIR/NVVMOps.cpp.inc"
6494
6495#define GET_ATTRDEF_CLASSES
6496#include "mlir/Dialect/LLVMIR/NVVMOpsAttributes.cpp.inc"
for(Operation *op :ops)
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.
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
ArrayAttr()
b getContext())
#define GET_TCGEN05_CP_ID(shape_mc, src_fmt, is_2cta)
static LogicalResult verifyTMALoadParams(size_t tensorDims, size_t numIm2colOff, TMALoadMode mode, Location loc)
static LogicalResult verifyTcgen05MMAOp(bool isATensor, mlir::Value disableOutputLane, NVVM::CTAGroupKind ctaGroup, bool hasAShift, NVVM::Tcgen05MMACollectorOp collectorOp, Location loc)
#define _none
static bool isPtrInAddrSpace(mlir::Value ptr, NVVMMemorySpace targetAS)
static bool isCompatibleReturnTypesOptionalResult(TypeRange inferred, TypeRange actual)
For ops with optional results, allow the user to omit the result even when inference would produce on...
static bool isPtrInSharedCTASpace(mlir::Value ptr)
static LogicalResult isAllowedSizeN(int sizeN, NVVM::WGMMATypes typeA)
static llvm::nvvm::CTAGroupKind getNVVMCtaGroupKind(NVVM::CTAGroupKind ctaGroup)
static void addInferredMultiplicandTypes(MLIRContext *ctx, OperationState &result, ValueRange operandA, ValueRange operandB, std::optional< std::array< MMATypes, 2 > > multiplicandPtxTypes)
#define GET_CVT_F2TF32_ID(rnd, relu, sf)
static void addBlockScaleProperties(OpBuilder &builder, OperationState &result, ArrayRef< int64_t > shape, ScaleVecSize scaleVecSize, BlockScaleFormat blockScaleFormat, MMABlockScaleKind kind)
#define GET_F32x2_TO_F8X2_US_ID(rnd, has_satf)
static llvm::Value * getParamCastedAddr(llvm::Value *addr, llvm::IRBuilderBase &builder)
static LogicalResult verifyAddSubFOp(OpType op)
static LogicalResult verifyTcgen05MMABlockScaleOp(NVVM::Tcgen05MMACollectorOp collectorOp, NVVM::Tcgen05MMAKind kind, NVVM::Tcgen05MMABlockScale blockScale, Location loc)
static llvm::Value * packValInto64Bits(llvm::IRBuilderBase &builder, llvm::Value *result, llvm::Value *field, unsigned sizeInBits, unsigned start)
Packs the given field into the result.
static void printOperandList(OpAsmPrinter &p, StringRef name, ArrayRef< Value > operands)
#define GET_F32x2_TO_F6x2_ID(type, has_relu)
static llvm::Value * getAsPackedI32(llvm::Value *arg, llvm::IRBuilderBase &builder)
#define GET_F16x2_TO_F8X2_ID(type, has_relu)
static LogicalResult verifyMBarrierArriveLikeOp(Operation *op, Value addr, NVVM::MemScopeKind scope, Value retVal=nullptr)
static llvm::Value * castPtrToAddrSpace(llvm::IRBuilderBase &builder, llvm::Value *ptr, NVVMMemorySpace targetAS)
static LogicalResult isAllowedWGMMADataType(NVVM::WGMMATypes typeD, NVVM::WGMMATypes typeA, NVVM::WGMMATypes typeB)
static llvm::Intrinsic::ID getBarrierReductionIntrinsic(bool aligned, NVVM::BarrierReduction kind)
Maps the (aligned, kind) pair to the @llvm.nvvm.barrier.cta.red.
static void inferAndSetMultiplicandTypes(MLIRContext *ctx, NamedAttrList &attrs, const SmallVectorImpl< Type > &operandTypes)
static LogicalResult parseMmaOperand(OpAsmParser &parser, StringRef operandName, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &regs)
static std::pair< mlir::Type, unsigned > inferMMATypeFromMNK(NVVM::MMATypes type, NVVM::MMAFrag frag, int m, int n, int k, MLIRContext *context)
static bool isInt8PtxType(MMATypes type)
#define TCGEN05LDRED(SHAPE, NUM, TYPE)
static bool isInt4PtxType(MMATypes type)
static bool isIntegerPtxType(MMATypes type)
#define GET_F32x2_TO_F8X2_S_ID(type, has_relu)
static MMATypes inferPtxTypeFromResult(OpTy op)
static LogicalResult verifyConstantRangeAttr(Operation *op, std::optional< LLVM::ConstantRangeAttr > rangeAttr)
Verify the range attribute satisfies LLVM ConstantRange constructor requirements for NVVM SpecialRang...
static LogicalResult parseMmaTypeSignature(OpAsmParser &parser, SmallVectorImpl< Type > &operandTypes)
static FailureOr< int > getAllowedSizeK(NVVM::WGMMATypes typeA)
static bool isPtrInSharedClusterSpace(mlir::Value ptr)
#define GET_CP_ASYNC_ID(mod, size, has_cpsize)
static unsigned isValidVectorLength(NVVM::Tcgen05LdStShape shape, unsigned vecLen)
static LogicalResult verifyConvertF32x2ToFP16x2Op(Twine dstType, FPRoundingMode rnd, bool hasRandomBits, Operation *op)
static void nvvmInferResultRanges(Operation *op, Value result, ArrayRef<::mlir::ConstantIntRanges > argRanges, SetIntRangeFn setResultRanges)
Infer the result ranges for the NVVM SpecialRangeableRegisterOp that might have ConstantRangeAttr.
static LogicalResult cpAsyncBulkTensorCommonVerifier(size_t tensorDims, bool isIm2Col, size_t numIm2ColOffsets, Location loc)
static bool isPtrInGenericSpace(mlir::Value ptr)
static void processOperandFragments(Op &op, std::array< MMAOperandFragment, 3 > &frags, SmallVectorImpl< Type > &regTypes, SmallVectorImpl< StringRef > &ignoreAttrNames)
static llvm::Intrinsic::ID getBarrierSyncIntrinsic(bool aligned, bool hasCount)
Maps the (aligned, hasCount) pair to the @llvm.nvvm.barrier.cta.sync.
static constexpr unsigned notIntrinsic
static LogicalResult inferMBarrierArriveResultTypes(MLIRContext *context, Value addr, SmallVectorImpl< Type > &inferredReturnTypes)
Only shared_cluster (ptr<7>) produces zero results; all other address spaces (including generic) retu...
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Definition Traits.cpp:117
@ OptionalSquare
Square brackets supporting zero or more ops, or nothing.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
MLIRContext * getContext() const
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseColon()=0
Parse a : token.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseArrow()=0
Parse a '->' token.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an arrow followed by a type list.
ParseResult parseTypeList(SmallVectorImpl< Type > &result)
Parse a type list.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
void printArrowTypeList(TypeRange &&types)
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
IntegerType getI16Type()
Definition Builders.cpp:69
UnitAttr getUnitAttr()
Definition Builders.cpp:106
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:171
IntegerType getI32Type()
Definition Builders.cpp:71
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:75
MLIRContext * getContext() const
Definition Builders.h:56
Attr getAttr(Args &&...args)
Get or construct an instance of the attribute Attr with provided arguments.
Definition Builders.h:101
This class represents a diagnostic that is inflight and set to be reported.
static IntegerValueRange getMaxRange(Value value)
Create a maximal range ([0, uint_max(t)] / [int_min(t), int_max(t)]) range that is used to mark the v...
Implementation class for module translation.
llvm::Value * lookupValue(Value value) const
Finds an LLVM IR value corresponding to the given MLIR value.
void mapValue(Value mlir, llvm::Value *llvm)
Stores the mapping between an MLIR value and its LLVM IR counterpart.
llvm::LLVMContext & getLLVMContext() const
Returns the LLVM context in which the IR is being constructed.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
std::optional< NamedAttribute > getNamed(StringRef name) const
Return the specified named attribute if present, std::nullopt otherwise.
Attribute get(StringAttr name) const
Return the specified attribute if present, null otherwise.
Attribute set(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
StringAttr getName() const
Return the name of the attribute.
Attribute getValue() const
Return the value of the attribute.
Definition Attributes.h:179
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
void printOperands(const ContainerType &container)
Print a comma separated list of operands.
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
This class helps build Operations.
Definition Builders.h:210
This provides public APIs that all operations should have.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
AttrClass getAttrOfType(StringAttr name)
Definition Operation.h:575
bool hasAttr(StringAttr name)
Return true if the operation has an attribute with the provided name, false otherwise.
Definition Operation.h:585
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
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
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition Types.cpp:35
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
bool isBF16() const
Definition Types.cpp:37
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
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
static WalkResult advance()
Definition WalkResult.h:47
static WalkResult interrupt()
Definition WalkResult.h:46
bool isValidLoadStoreImpl(Type type, ptr::AtomicOrdering ordering, std::optional< int64_t > alignment, const ::mlir::DataLayout *dataLayout, function_ref< InFlightDiagnostic()> emitError)
Checks whether the given type is an LLVM type that can be loaded or stored.
Definition LLVMAttrs.cpp:60
SmallVector< int64_t, 4 > getCoordinates(ArrayRef< int64_t > basis, unsigned linearIndex)
@ Write
Write register with '=' modifier.
@ ReadWrite
ReadWrite register with '+' modifier.
@ Read
Read register with no modifier.
std::pair< mlir::Type, unsigned > inferMMAType(mlir::NVVM::MMATypes type, mlir::NVVM::MMAFrag frag, int nRow, int nCol, mlir::MLIRContext *context)
Return the element type and number of elements associated with a wmma matrix of given chracteristics.
std::pair< llvm::Intrinsic::ID, llvm::SmallVector< llvm::Value * > > IDArgPair
A pair type of LLVM's Intrinsic ID and args (which are llvm values).
Definition NVVMDialect.h:53
void walk(Operation *op, function_ref< void(Region *)> callback, WalkOrder order)
Walk all of the regions, blocks, or operations nested under (and including) the given operation.
Definition Visitors.h:102
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
uint64_t getN(LevelType lt)
Definition Enums.h:442
uint64_t getM(LevelType lt)
Definition Enums.h:443
Include the generated interface declarations.
llvm::function_ref< void(Value, const ConstantIntRanges &)> SetIntRangeFn
The type of the setResultRanges callback provided to ops implementing InferIntRangeInterface.
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.
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
LogicalResult matchAndRewrite(SubFOp op, PatternRewriter &rewriter) const override
static bool isMinimumSMVersion(unsigned fullSmVersion)
static unsigned getTargetFullSmVersionFromStr(StringRef smVersionString)
bool isCompatibleWith(const unsigned &targetFullSmVersion) const
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
This represents an operation in an abstracted form, suitable for use with the builder APIs.