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