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