LLVM 24.0.0git
NVPTXISelDAGToDAG.cpp
Go to the documentation of this file.
1//===-- NVPTXISelDAGToDAG.cpp - A dag to dag inst selector for NVPTX ------===//
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 an instruction selector for the NVPTX target.
10//
11//===----------------------------------------------------------------------===//
12
13#include "NVPTXISelDAGToDAG.h"
14#include "NVPTX.h"
15#include "NVPTXUtilities.h"
16#include "llvm/ADT/APInt.h"
21#include "llvm/IR/GlobalValue.h"
23#include "llvm/IR/IntrinsicsNVPTX.h"
30#include <optional>
31
32using namespace llvm;
33
34#define DEBUG_TYPE "nvptx-isel"
35#define PASS_NAME "NVPTX DAG->DAG Pattern Instruction Selection"
36
37static cl::opt<bool>
38 EnableRsqrtOpt("nvptx-rsqrt-approx-opt", cl::init(true), cl::Hidden,
39 cl::desc("Enable reciprocal sqrt optimization"));
40
41// FIXME: This is a WAR to recover lost performance from #155024.
42// We still need to investigate the regression and find a more permanent
43// solution.
44static cl::opt<bool> EnableMADWide("nvptx-mad-wide-opt", cl::init(false),
46 cl::desc("Enable MAD wide optimization"));
47
48/// createNVPTXISelDag - This pass converts a legalized DAG into a
49/// NVPTX-specific DAG, ready for instruction scheduling.
54
59
61
63
67
69 Subtarget = &MF.getSubtarget<NVPTXSubtarget>();
70 Scopes = NVPTXScopes(MF.getFunction().getContext());
72}
73
75NVPTXDAGToDAGISel::getDivF32Level(const SDNode *N) const {
77}
78
79bool NVPTXDAGToDAGISel::usePrecSqrtF32(const SDNode *N) const {
81}
82
83bool NVPTXDAGToDAGISel::useF32FTZ() const {
84 return Subtarget->getTargetLowering()->useF32FTZ(*MF);
85}
86
87bool NVPTXDAGToDAGISel::allowFMA() const {
88 const NVPTXTargetLowering *TL = Subtarget->getTargetLowering();
89 return TL->allowFMA(*MF, OptLevel);
90}
91
92bool NVPTXDAGToDAGISel::doRsqrtOpt() const { return EnableRsqrtOpt; }
93
94bool NVPTXDAGToDAGISel::doMADWideOpt() const { return EnableMADWide; }
95
96/// Select - Select instructions not customized! Used for
97/// expanded, promoted and normal instructions.
98void NVPTXDAGToDAGISel::Select(SDNode *N) {
99
100 if (N->isMachineOpcode()) {
101 N->setNodeId(-1);
102 return; // Already selected.
103 }
104
105 switch (N->getOpcode()) {
106 case ISD::LOAD:
107 case ISD::ATOMIC_LOAD:
108 case NVPTXISD::MLoad:
109 if (tryLoad(N))
110 return;
111 break;
112 case ISD::STORE:
114 if (tryStore(N))
115 return;
116 break;
118 if (tryFence(N))
119 return;
120 break;
122 tryUNPACK_VECTOR(N);
123 return;
125 if (tryEXTRACT_VECTOR_ELEMENT(N))
126 return;
127 break;
129 SelectSETP_F16X2(N);
130 return;
132 SelectSETP_BF16X2(N);
133 return;
134 case NVPTXISD::LoadV2:
135 case NVPTXISD::LoadV4:
136 case NVPTXISD::LoadV8:
137 if (tryLoadVector(N))
138 return;
139 break;
140 case NVPTXISD::LDUV2:
141 case NVPTXISD::LDUV4:
142 if (tryLDU(N))
143 return;
144 break;
148 if (tryStoreVector(N))
149 return;
150 break;
152 if (tryIntrinsicChain(N))
153 return;
154 break;
156 if (tryIntrinsicVoid(N))
157 return;
158 break;
159 case ISD::AND:
160 case ISD::SRA:
161 case ISD::SRL:
162 // Try to select BFE
163 if (tryBFE(N))
164 return;
165 break;
166 case ISD::CopyToReg: {
167 if (N->getOperand(1).getValueType() == MVT::i128) {
168 SelectV2I64toI128(N);
169 return;
170 }
171 break;
172 }
173 case ISD::CopyFromReg: {
174 if (N->getOperand(1).getValueType() == MVT::i128) {
175 SelectI128toV2I64(N);
176 return;
177 }
178 break;
179 }
182 selectAtomicSwap128(N);
183 return;
184 case ISD::FADD:
185 case ISD::FMUL:
186 case ISD::FSUB:
187 if (tryBF16ArithToFMA(N))
188 return;
189 break;
190 default:
191 break;
192 }
193 SelectCode(N);
194}
195
196#define TCGEN05_LD_OPCODE(SHAPE, NUM) \
197 (enablePack ? NVPTX::TCGEN05_LD_##SHAPE##_##NUM##_PACK \
198 : NVPTX::TCGEN05_LD_##SHAPE##_##NUM)
199
200static unsigned getTcgen05LdOpcode(unsigned IID, bool enablePack) {
201 switch (IID) {
202 case Intrinsic::nvvm_tcgen05_ld_16x64b_x1:
203 return TCGEN05_LD_OPCODE(16x64b, x1);
204 case Intrinsic::nvvm_tcgen05_ld_16x64b_x2:
205 return TCGEN05_LD_OPCODE(16x64b, x2);
206 case Intrinsic::nvvm_tcgen05_ld_16x64b_x4:
207 return TCGEN05_LD_OPCODE(16x64b, x4);
208 case Intrinsic::nvvm_tcgen05_ld_16x64b_x8:
209 return TCGEN05_LD_OPCODE(16x64b, x8);
210 case Intrinsic::nvvm_tcgen05_ld_16x64b_x16:
211 return TCGEN05_LD_OPCODE(16x64b, x16);
212 case Intrinsic::nvvm_tcgen05_ld_16x64b_x32:
213 return TCGEN05_LD_OPCODE(16x64b, x32);
214 case Intrinsic::nvvm_tcgen05_ld_16x64b_x64:
215 return TCGEN05_LD_OPCODE(16x64b, x64);
216 case Intrinsic::nvvm_tcgen05_ld_16x64b_x128:
217 return TCGEN05_LD_OPCODE(16x64b, x128);
218 case Intrinsic::nvvm_tcgen05_ld_16x128b_x1:
219 return TCGEN05_LD_OPCODE(16x128b, x1);
220 case Intrinsic::nvvm_tcgen05_ld_16x128b_x2:
221 return TCGEN05_LD_OPCODE(16x128b, x2);
222 case Intrinsic::nvvm_tcgen05_ld_16x128b_x4:
223 return TCGEN05_LD_OPCODE(16x128b, x4);
224 case Intrinsic::nvvm_tcgen05_ld_16x128b_x8:
225 return TCGEN05_LD_OPCODE(16x128b, x8);
226 case Intrinsic::nvvm_tcgen05_ld_16x128b_x16:
227 return TCGEN05_LD_OPCODE(16x128b, x16);
228 case Intrinsic::nvvm_tcgen05_ld_16x128b_x32:
229 return TCGEN05_LD_OPCODE(16x128b, x32);
230 case Intrinsic::nvvm_tcgen05_ld_16x128b_x64:
231 return TCGEN05_LD_OPCODE(16x128b, x64);
232 case Intrinsic::nvvm_tcgen05_ld_16x256b_x1:
233 return TCGEN05_LD_OPCODE(16x256b, x1);
234 case Intrinsic::nvvm_tcgen05_ld_16x256b_x2:
235 return TCGEN05_LD_OPCODE(16x256b, x2);
236 case Intrinsic::nvvm_tcgen05_ld_16x256b_x4:
237 return TCGEN05_LD_OPCODE(16x256b, x4);
238 case Intrinsic::nvvm_tcgen05_ld_16x256b_x8:
239 return TCGEN05_LD_OPCODE(16x256b, x8);
240 case Intrinsic::nvvm_tcgen05_ld_16x256b_x16:
241 return TCGEN05_LD_OPCODE(16x256b, x16);
242 case Intrinsic::nvvm_tcgen05_ld_16x256b_x32:
243 return TCGEN05_LD_OPCODE(16x256b, x32);
244 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x1:
245 return TCGEN05_LD_OPCODE(16x32bx2, x1);
246 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x2:
247 return TCGEN05_LD_OPCODE(16x32bx2, x2);
248 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x4:
249 return TCGEN05_LD_OPCODE(16x32bx2, x4);
250 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x8:
251 return TCGEN05_LD_OPCODE(16x32bx2, x8);
252 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x16:
253 return TCGEN05_LD_OPCODE(16x32bx2, x16);
254 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x32:
255 return TCGEN05_LD_OPCODE(16x32bx2, x32);
256 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x64:
257 return TCGEN05_LD_OPCODE(16x32bx2, x64);
258 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x128:
259 return TCGEN05_LD_OPCODE(16x32bx2, x128);
260 case Intrinsic::nvvm_tcgen05_ld_32x32b_x1:
261 return TCGEN05_LD_OPCODE(32x32b, x1);
262 case Intrinsic::nvvm_tcgen05_ld_32x32b_x2:
263 return TCGEN05_LD_OPCODE(32x32b, x2);
264 case Intrinsic::nvvm_tcgen05_ld_32x32b_x4:
265 return TCGEN05_LD_OPCODE(32x32b, x4);
266 case Intrinsic::nvvm_tcgen05_ld_32x32b_x8:
267 return TCGEN05_LD_OPCODE(32x32b, x8);
268 case Intrinsic::nvvm_tcgen05_ld_32x32b_x16:
269 return TCGEN05_LD_OPCODE(32x32b, x16);
270 case Intrinsic::nvvm_tcgen05_ld_32x32b_x32:
271 return TCGEN05_LD_OPCODE(32x32b, x32);
272 case Intrinsic::nvvm_tcgen05_ld_32x32b_x64:
273 return TCGEN05_LD_OPCODE(32x32b, x64);
274 case Intrinsic::nvvm_tcgen05_ld_32x32b_x128:
275 return TCGEN05_LD_OPCODE(32x32b, x128);
276 }
277 llvm_unreachable("unhandled tcgen05.ld lowering");
278}
279
280void NVPTXDAGToDAGISel::SelectTcgen05Ld(SDNode *N, bool hasOffset) {
281 if (!Subtarget->hasTcgen05InstSupport())
283 "tcgen05.ld is not supported on this architecture variant");
284
285 SDLoc DL(N);
286 unsigned IID = cast<ConstantSDNode>(N->getOperand(1))->getZExtValue();
287
288 if (hasOffset) {
289 bool enablePack = cast<ConstantSDNode>(N->getOperand(4))->getZExtValue();
290 auto OffsetNode = CurDAG->getTargetConstant(
291 cast<ConstantSDNode>(N->getOperand(3))->getZExtValue(), DL, MVT::i32);
292 ReplaceNode(N, CurDAG->getMachineNode(
293 getTcgen05LdOpcode(IID, enablePack), DL, N->getVTList(),
294 {N->getOperand(2), OffsetNode, N->getOperand(0)}));
295 } else {
296 bool enablePack = cast<ConstantSDNode>(N->getOperand(3))->getZExtValue();
297 ReplaceNode(N, CurDAG->getMachineNode(
298 getTcgen05LdOpcode(IID, enablePack), DL, N->getVTList(),
299 {N->getOperand(2), N->getOperand(0)}));
300 }
301}
302
303bool NVPTXDAGToDAGISel::tryIntrinsicChain(SDNode *N) {
304 unsigned IID = N->getConstantOperandVal(1);
305 switch (IID) {
306 default:
307 return false;
308 case Intrinsic::nvvm_ldu_global_f:
309 case Intrinsic::nvvm_ldu_global_i:
310 case Intrinsic::nvvm_ldu_global_p:
311 return tryLDU(N);
312
313 case Intrinsic::nvvm_tcgen05_ld_16x64b_x1:
314 case Intrinsic::nvvm_tcgen05_ld_16x64b_x2:
315 case Intrinsic::nvvm_tcgen05_ld_16x64b_x4:
316 case Intrinsic::nvvm_tcgen05_ld_16x64b_x8:
317 case Intrinsic::nvvm_tcgen05_ld_16x64b_x16:
318 case Intrinsic::nvvm_tcgen05_ld_16x64b_x32:
319 case Intrinsic::nvvm_tcgen05_ld_16x64b_x64:
320 case Intrinsic::nvvm_tcgen05_ld_16x64b_x128:
321 case Intrinsic::nvvm_tcgen05_ld_16x128b_x1:
322 case Intrinsic::nvvm_tcgen05_ld_16x128b_x2:
323 case Intrinsic::nvvm_tcgen05_ld_16x128b_x4:
324 case Intrinsic::nvvm_tcgen05_ld_16x128b_x16:
325 case Intrinsic::nvvm_tcgen05_ld_16x128b_x32:
326 case Intrinsic::nvvm_tcgen05_ld_16x128b_x64:
327 case Intrinsic::nvvm_tcgen05_ld_16x256b_x1:
328 case Intrinsic::nvvm_tcgen05_ld_16x128b_x8:
329 case Intrinsic::nvvm_tcgen05_ld_16x256b_x2:
330 case Intrinsic::nvvm_tcgen05_ld_16x256b_x4:
331 case Intrinsic::nvvm_tcgen05_ld_16x256b_x8:
332 case Intrinsic::nvvm_tcgen05_ld_16x256b_x16:
333 case Intrinsic::nvvm_tcgen05_ld_16x256b_x32:
334 case Intrinsic::nvvm_tcgen05_ld_32x32b_x1:
335 case Intrinsic::nvvm_tcgen05_ld_32x32b_x2:
336 case Intrinsic::nvvm_tcgen05_ld_32x32b_x4:
337 case Intrinsic::nvvm_tcgen05_ld_32x32b_x8:
338 case Intrinsic::nvvm_tcgen05_ld_32x32b_x16:
339 case Intrinsic::nvvm_tcgen05_ld_32x32b_x32:
340 case Intrinsic::nvvm_tcgen05_ld_32x32b_x64:
341 case Intrinsic::nvvm_tcgen05_ld_32x32b_x128: {
342 SelectTcgen05Ld(N);
343 return true;
344 }
345
346 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x1:
347 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x2:
348 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x4:
349 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x8:
350 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x16:
351 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x32:
352 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x64:
353 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x128: {
354 SelectTcgen05Ld(N, /* hasOffset */ true);
355 return true;
356 }
357 }
358}
359
360// Map ISD:CONDCODE value to appropriate CmpMode expected by
361// NVPTXInstPrinter::printCmpMode()
362SDValue NVPTXDAGToDAGISel::getPTXCmpMode(const CondCodeSDNode &CondCode) {
364 const unsigned PTXCmpMode = [](ISD::CondCode CC) {
365 switch (CC) {
366 default:
367 llvm_unreachable("Unexpected condition code.");
368 case ISD::SETOEQ:
369 case ISD::SETEQ:
370 return CmpMode::EQ;
371 case ISD::SETOGT:
372 case ISD::SETGT:
373 return CmpMode::GT;
374 case ISD::SETOGE:
375 case ISD::SETGE:
376 return CmpMode::GE;
377 case ISD::SETOLT:
378 case ISD::SETLT:
379 return CmpMode::LT;
380 case ISD::SETOLE:
381 case ISD::SETLE:
382 return CmpMode::LE;
383 case ISD::SETONE:
384 case ISD::SETNE:
385 return CmpMode::NE;
386 case ISD::SETO:
387 return CmpMode::NUM;
388 case ISD::SETUO:
389 return CmpMode::NotANumber;
390 case ISD::SETUEQ:
391 return CmpMode::EQU;
392 case ISD::SETUGT:
393 return CmpMode::GTU;
394 case ISD::SETUGE:
395 return CmpMode::GEU;
396 case ISD::SETULT:
397 return CmpMode::LTU;
398 case ISD::SETULE:
399 return CmpMode::LEU;
400 case ISD::SETUNE:
401 return CmpMode::NEU;
402 }
403 }(CondCode.get());
404 return CurDAG->getTargetConstant(PTXCmpMode, SDLoc(), MVT::i32);
405}
406
407bool NVPTXDAGToDAGISel::SelectSETP_F16X2(SDNode *N) {
408 SDValue PTXCmpMode = getPTXCmpMode(*cast<CondCodeSDNode>(N->getOperand(2)));
409 SDLoc DL(N);
410 SDNode *SetP = CurDAG->getMachineNode(
411 NVPTX::SETP_f16x2rr, DL, MVT::i1, MVT::i1,
412 {N->getOperand(0), N->getOperand(1), PTXCmpMode,
413 CurDAG->getTargetConstant(useF32FTZ() ? 1 : 0, DL, MVT::i1)});
414 ReplaceNode(N, SetP);
415 return true;
416}
417
418bool NVPTXDAGToDAGISel::SelectSETP_BF16X2(SDNode *N) {
419 SDValue PTXCmpMode = getPTXCmpMode(*cast<CondCodeSDNode>(N->getOperand(2)));
420 SDLoc DL(N);
421 SDNode *SetP =
422 CurDAG->getMachineNode(NVPTX::SETP_bf16x2rr, DL, MVT::i1, MVT::i1,
423 {N->getOperand(0), N->getOperand(1), PTXCmpMode});
424 ReplaceNode(N, SetP);
425 return true;
426}
427
428bool NVPTXDAGToDAGISel::tryUNPACK_VECTOR(SDNode *N) {
429 SDValue Vector = N->getOperand(0);
430 MVT EltVT = N->getSimpleValueType(0);
431
432 MachineSDNode *N2 =
433 CurDAG->getMachineNode(NVPTX::I64toV2I32, SDLoc(N), EltVT, EltVT, Vector);
434
435 ReplaceNode(N, N2);
436 return true;
437}
438
439// Find all instances of extract_vector_elt that use this v2f16 vector
440// and coalesce them into a scattering move instruction.
441bool NVPTXDAGToDAGISel::tryEXTRACT_VECTOR_ELEMENT(SDNode *N) {
442 SDValue Vector = N->getOperand(0);
443
444 MVT VT = Vector.getSimpleValueType();
445 if (!(NVPTX::isPackedVectorTy(VT) && VT.getVectorNumElements() == 2))
446 return false;
447
448 unsigned Opcode;
449 if (VT.is32BitVector())
450 Opcode = NVPTX::I32toV2I16;
451 else if (VT.is64BitVector())
452 Opcode = NVPTX::I64toV2I32;
453 else
454 llvm_unreachable("Unhandled packed type");
455
456 // Find and record all uses of this vector that extract element 0 or 1.
458 for (auto *U : Vector.getNode()->users()) {
459 if (U->getOpcode() != ISD::EXTRACT_VECTOR_ELT)
460 continue;
461 if (U->getOperand(0) != Vector)
462 continue;
463 if (const ConstantSDNode *IdxConst =
464 dyn_cast<ConstantSDNode>(U->getOperand(1))) {
465 if (IdxConst->getZExtValue() == 0)
466 E0.push_back(U);
467 else if (IdxConst->getZExtValue() == 1)
468 E1.push_back(U);
469 else
470 llvm_unreachable("Invalid vector index.");
471 }
472 }
473
474 // There's no point scattering f16x2 if we only ever access one
475 // element of it.
476 if (E0.empty() || E1.empty())
477 return false;
478
479 // Merge (EltTy extractelt(V, 0), EltTy extractelt(V,1))
480 // into EltTy,EltTy Split[EltTy]x2(V)
481 MVT EltVT = VT.getVectorElementType();
482 SDNode *ScatterOp =
483 CurDAG->getMachineNode(Opcode, SDLoc(N), EltVT, EltVT, Vector);
484 for (auto *Node : E0)
485 ReplaceUses(SDValue(Node, 0), SDValue(ScatterOp, 0));
486 for (auto *Node : E1)
487 ReplaceUses(SDValue(Node, 0), SDValue(ScatterOp, 1));
488
489 return true;
490}
491
493 auto AS =
494 static_cast<NVPTX::AddressSpace>(N->getMemOperand()->getAddrSpace());
495 switch (AS) {
504 return AS;
505 }
506 llvm_unreachable("Unexpected address space");
507}
508
509NVPTX::Ordering NVPTXDAGToDAGISel::getMemOrder(const MemSDNode *N) const {
510 // No "sem" orderings for SM/PTX versions which do not support memory ordering
513 auto Ordering = N->getMergedOrdering();
514 switch (Ordering) {
528 }
529 llvm_unreachable("Invalid atomic ordering");
530}
531
532// Clusters contain exactly 1 block on targets without cluster support.
534 if (S == NVPTX::Scope::Cluster && !T->hasClusters())
535 return NVPTX::Scope::Block;
536 return S;
537}
538
539NVPTX::Scope NVPTXDAGToDAGISel::getAtomicScope(const MemSDNode *N) const {
540 if (!Subtarget->hasAtomScope())
542 return resolveScope(Scopes[N->getSyncScopeID()], Subtarget);
543}
544
545namespace {
546
547struct OperationOrderings {
548 NVPTX::Ordering InstructionOrdering, FenceOrdering;
549 OperationOrderings(NVPTX::Ordering IO = NVPTX::Ordering::NotAtomic,
550 NVPTX::Ordering FO = NVPTX::Ordering::NotAtomic)
551 : InstructionOrdering(IO), FenceOrdering(FO) {}
552};
553
554static OperationOrderings
555getOperationOrderings(MemSDNode *N, const NVPTXSubtarget *Subtarget) {
556 AtomicOrdering Ordering = N->getSuccessOrdering();
557 auto CodeAddrSpace = NVPTXDAGToDAGISel::getAddrSpace(N);
558
559 bool HasMemoryOrdering = Subtarget->hasMemoryOrdering();
560 bool HasRelaxedMMIO = Subtarget->hasRelaxedMMIO();
561
562 // clang-format off
563
564 // Lowering for Load/Store Operations (note: AcquireRelease Loads or Stores error).
565 // Note: uses of Relaxed in the Atomic column of this table refer
566 // to LLVM AtomicOrdering::Monotonic.
567 //
568 // | Atomic | Volatile | Statespace | PTX sm_60- | PTX sm_70+ |
569 // |---------|----------|--------------------|------------|------------------------------|
570 // | No | No | All | plain | .weak |
571 // | No | Yes | Generic,Shared, | .volatile | .volatile |
572 // | | | Global [0] | | |
573 // | No | Yes | Local,Const,Param | plain [1] | .weak [1] |
574 // | Unorder | Yes/No | All | == Relaxed | == Relaxed |
575 // | Relaxed | No | Generic,Shared, | .volatile | <atomic sem> |
576 // | | | Global [0] | | |
577 // | Other | No | Generic,Shared, | Error [2] | <atomic sem> |
578 // | | | Global [0] | | |
579 // | Yes | No | Local,Const,Param | plain [1] | .weak [1] |
580 // | Relaxed | Yes | Generic,Shared [0] | .volatile | .volatile |
581 // | Relaxed | Yes | Global [0] | .volatile | .mmio.relaxed.sys (PTX 8.2+) |
582 // | | | | | or .volatile (PTX 8.1-) |
583 // | Relaxed | Yes | Local,Const,Param | plain [1] | .weak [1] |
584 // | Other | Yes | Generic, Shared, | Error [2] | <atomic sem> [3] |
585 // | | | / Global [0] | | |
586
587 // Lowering of CUDA C++ SequentiallyConsistent Operations and Fences to PTX
588 // by following the ABI proven sound in:
589 // Lustig et al, A Formal Analysis of the NVIDIA PTX Memory Consistency Model, ASPLOS’19.
590 // https://dl.acm.org/doi/pdf/10.1145/3297858.3304043
591 //
592 // | CUDA C++ Atomic Operation or Atomic Fence | PTX Atomic Operation or Fence |
593 // |------------------------------------------------------|-------------------------------|
594 // | cuda::atomic_thread_fence | fence.sc.<scope>; |
595 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | |
596 // |------------------------------------------------------|-------------------------------|
597 // | cuda::atomic_load | fence.sc.<scope>; |
598 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | ld.acquire.<scope>; |
599 // |------------------------------------------------------|-------------------------------|
600 // | cuda::atomic_store | fence.sc.<scope>; |
601 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | st.release.<scope>; |
602 // |------------------------------------------------------|-------------------------------|
603 // | cuda::atomic_fetch_<op> | fence.sc.<scope>; |
604 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | atom.acq_rel.<scope>; |
605
606 // clang-format on
607
608 // [0]: volatile and atomics are only supported on global or shared
609 // memory locations, accessed via generic/shared/global pointers.
610 // MMIO is only supported on global memory locations,
611 // accessed via generic/global pointers.
612 // TODO: Implement MMIO access via generic pointer to global.
613 // Currently implemented for global pointers only.
614
615 // [1]: Lowering volatile/atomic operations to non-volatile/non-atomic
616 // PTX instructions fails to preserve their C++ side-effects.
617 //
618 // Example (https://github.com/llvm/llvm-project/issues/62057):
619 //
620 // void example() {
621 // std::atomic<bool> True = true;
622 // while (True.load(std::memory_order_relaxed));
623 // }
624 //
625 // A C++ program that calls "example" is well-defined: the infinite loop
626 // performs an atomic operation. By lowering volatile/atomics to
627 // "weak" memory operations, we are transforming the above into:
628 //
629 // void undefined_behavior() {
630 // bool True = true;
631 // while (True);
632 // }
633 //
634 // which exhibits undefined behavior in both C++ and PTX.
635 //
636 // Calling "example" in CUDA C++ compiled for sm_60- exhibits undefined
637 // behavior due to lack of Independent Forward Progress. Lowering these
638 // to weak memory operations in sm_60- is therefore fine.
639 //
640 // TODO: lower atomic and volatile operations to memory locations
641 // in local, const, and param to two PTX instructions in sm_70+:
642 // - the "weak" memory instruction we are currently lowering to, and
643 // - some other instruction that preserves the side-effect, e.g.,
644 // a dead dummy volatile load.
645 if (CodeAddrSpace == NVPTX::AddressSpace::Local ||
646 CodeAddrSpace == NVPTX::AddressSpace::Const ||
647 CodeAddrSpace == NVPTX::AddressSpace::EntryParam ||
648 CodeAddrSpace == NVPTX::AddressSpace::DeviceParam) {
650 }
651
652 // [2]: Atomics with Ordering different than Unordered or Relaxed are not
653 // supported on sm_60 and older; this includes volatile atomics.
654 if (!(Ordering == AtomicOrdering::NotAtomic ||
655 Ordering == AtomicOrdering::Unordered ||
656 Ordering == AtomicOrdering::Monotonic) &&
657 !HasMemoryOrdering) {
659 formatv("PTX does not support \"atomic\" for orderings different than"
660 "\"NotAtomic\" or \"Monotonic\" for sm_60 or older, but order "
661 "is: \"{}\".",
662 toIRString(Ordering)));
663 }
664
665 // [3]: TODO: these should eventually use .mmio<.atomic sem>; for now we drop
666 // the volatile semantics and preserve the atomic ones.
667
668 // PTX volatile and PTX atomics are not available for statespace that differ
669 // from .generic, .global, or .shared. The behavior of PTX volatile and PTX
670 // atomics is undefined if the generic address does not refer to a .global or
671 // .shared memory location.
672 bool AddrGenericOrGlobalOrShared =
673 (CodeAddrSpace == NVPTX::AddressSpace::Generic ||
674 CodeAddrSpace == NVPTX::AddressSpace::Global ||
675 CodeAddrSpace == NVPTX::AddressSpace::Shared ||
676 CodeAddrSpace == NVPTX::AddressSpace::SharedCluster);
677 if (!AddrGenericOrGlobalOrShared)
679
680 bool UseRelaxedMMIO =
681 HasRelaxedMMIO && CodeAddrSpace == NVPTX::AddressSpace::Global;
682
683 switch (Ordering) {
685 return N->isVolatile() ? NVPTX::Ordering::Volatile
688 // We lower unordered in the exact same way as 'monotonic' to respect
689 // LLVM IR atomicity requirements.
691 if (N->isVolatile())
692 return UseRelaxedMMIO ? NVPTX::Ordering::RelaxedMMIO
694 else
695 return HasMemoryOrdering ? NVPTX::Ordering::Relaxed
697 // case AtomicOrdering::Consume: // If LLVM ever provides this, lower it to
698 // Acquire.
700 if (!N->readMem())
702 formatv("PTX only supports Acquire Ordering on reads: {}",
703 N->getOperationName()));
706 if (!N->writeMem())
708 formatv("PTX only supports Release Ordering on writes: {}",
709 N->getOperationName()));
713 formatv("NVPTX does not support AcquireRelease Ordering on "
714 "read-modify-write "
715 "yet and PTX does not support it on loads or stores: {}",
716 N->getOperationName()));
717 }
719 // LLVM-IR SequentiallyConsistent atomics map to a two-instruction PTX
720 // sequence including a "fence.sc.sco" and the memory instruction with an
721 // Ordering that differs from "sc": acq, rel, or acq_rel, depending on
722 // whether the memory operation is a read, write, or read-modify-write.
723 //
724 // This sets the ordering of the fence to SequentiallyConsistent, and
725 // sets the corresponding ordering for the instruction.
726 NVPTX::Ordering InstrOrder;
727 if (N->readMem())
728 InstrOrder = NVPTX::Ordering::Acquire;
729 else if (N->writeMem())
730 InstrOrder = NVPTX::Ordering::Release;
731 else
733 formatv("NVPTX does not support SequentiallyConsistent Ordering on "
734 "read-modify-writes yet: {}",
735 N->getOperationName()));
736 return OperationOrderings(InstrOrder,
738 }
739 }
741 formatv("NVPTX backend does not support AtomicOrdering \"{}\" yet.",
742 toIRString(Ordering)));
743}
744
745} // namespace
746
747NVPTX::Scope NVPTXDAGToDAGISel::getOperationScope(MemSDNode *N,
748 NVPTX::Ordering O) const {
749 switch (O) {
751 case NVPTX::Ordering::Volatile: // Non-atomic volatile operations
752 // NVPTX uses Thread scope as the scope of non-atomic operations.
755 // RelaxedMMIO operations are always system scope.
756 // If a RelaxedMMIO order was generated from an atomic volatile operation
757 // with a smaller thread scope, we bump it here to system scope.
764 auto S = Scopes[N->getSyncScopeID()];
765
766 S = resolveScope(S, Subtarget);
767
768 // If operation is volatile, then its scope is system.
769 return N->isVolatile() ? NVPTX::Scope::System : S;
770 }
771 llvm_unreachable("unhandled ordering");
772}
773
774static bool canLowerToLDG(const MemSDNode &N, const NVPTXSubtarget &Subtarget,
775 NVPTX::AddressSpace CodeAddrSpace) {
776 // We use ldg (i.e. ld.global.nc) for invariant loads from the global address
777 // space.
778 return Subtarget.hasLDG() && CodeAddrSpace == NVPTX::AddressSpace::Global &&
779 N.isInvariant();
780}
781
782static unsigned int getFenceOp(NVPTX::Ordering O, NVPTX::Scope S,
783 NVPTXSubtarget const *T) {
784 S = resolveScope(S, T);
785
786 // Fall back to .acq_rel if .acquire, .release is not supported.
787 if (!T->hasSplitAcquireAndReleaseFences() &&
790
791 switch (O) {
793 switch (S) {
795 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acquire_sys
796 : NVPTX::INT_MEMBAR_SYS;
798 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acquire_cta
799 : NVPTX::INT_MEMBAR_CTA;
801 return NVPTX::atomic_thread_fence_acquire_cluster;
803 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acquire_gpu
804 : NVPTX::INT_MEMBAR_GL;
808 formatv("Unsupported scope \"{}\" for acquire/release/acq_rel fence.",
809 ScopeToString(S)));
810 }
811 break;
813 switch (S) {
815 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_release_sys
816 : NVPTX::INT_MEMBAR_SYS;
818 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_release_cta
819 : NVPTX::INT_MEMBAR_CTA;
821 return NVPTX::atomic_thread_fence_release_cluster;
823 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_release_gpu
824 : NVPTX::INT_MEMBAR_GL;
828 formatv("Unsupported scope \"{}\" for acquire/release/acq_rel fence.",
829 ScopeToString(S)));
830 }
831 break;
833 switch (S) {
835 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acq_rel_sys
836 : NVPTX::INT_MEMBAR_SYS;
838 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acq_rel_cta
839 : NVPTX::INT_MEMBAR_CTA;
841 return NVPTX::atomic_thread_fence_acq_rel_cluster;
843 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acq_rel_gpu
844 : NVPTX::INT_MEMBAR_GL;
848 formatv("Unsupported scope \"{}\" for acquire/release/acq_rel fence.",
849 ScopeToString(S)));
850 }
851 break;
852 }
854 switch (S) {
856 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_seq_cst_sys
857 : NVPTX::INT_MEMBAR_SYS;
859 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_seq_cst_cta
860 : NVPTX::INT_MEMBAR_CTA;
862 return NVPTX::atomic_thread_fence_seq_cst_cluster;
864 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_seq_cst_gpu
865 : NVPTX::INT_MEMBAR_GL;
868 report_fatal_error(formatv("Unsupported scope \"{}\" for seq_cst fence.",
869 ScopeToString(S)));
870 }
871 break;
872 }
878 formatv("Unsupported \"{}\" ordering and \"{}\" scope for fence.",
879 OrderingToString(O), ScopeToString(S)));
880 }
881 llvm_unreachable("unhandled ordering");
882}
883
884// Returns Memory Order and Scope of a memory instruction, and
885// inserts any fence before the instruction that's required to
886// implement its memory ordering.
887std::pair<NVPTX::Ordering, NVPTX::Scope>
888NVPTXDAGToDAGISel::insertMemoryInstructionFence(SDLoc DL, SDValue &Chain,
889 MemSDNode *N) {
890 auto [InstructionOrdering, FenceOrdering] =
891 getOperationOrderings(N, Subtarget);
892 auto Scope = getOperationScope(N, InstructionOrdering);
893
894 // Singlethread scope has no inter-thread synchronization requirements, so
895 // the atomic operation is lowered as plain and the fence is skipped.
896 // NotAtomic and Volatile operations naturally have Thread scope and must
897 // preserve their ordering.
898 if (Scope == NVPTX::Scope::Thread &&
899 InstructionOrdering != NVPTX::Ordering::NotAtomic &&
900 InstructionOrdering != NVPTX::Ordering::Volatile)
902
903 // If a fence is required before the operation, insert it:
904 switch (NVPTX::Ordering(FenceOrdering)) {
906 break;
908 auto Op = getFenceOp(FenceOrdering, Scope, Subtarget);
909 Chain = SDValue(CurDAG->getMachineNode(Op, DL, MVT::Other, Chain), 0);
910 break;
911 }
912 default:
914 formatv("Unexpected fence ordering: \"{}\".",
915 OrderingToString(NVPTX::Ordering(FenceOrdering))));
916 }
917 return {InstructionOrdering, Scope};
918}
919
920// Helper function template to reduce amount of boilerplate code for
921// opcode selection.
922static std::optional<unsigned>
923pickOpcodeForVT(MVT::SimpleValueType VT, std::optional<unsigned> Opcode_i16,
924 std::optional<unsigned> Opcode_i32,
925 std::optional<unsigned> Opcode_i64) {
926 switch (VT) {
927 case MVT::f16:
928 case MVT::i16:
929 case MVT::bf16:
930 return Opcode_i16;
931 case MVT::v2f16:
932 case MVT::v2bf16:
933 case MVT::v2i16:
934 case MVT::v4i8:
935 case MVT::i32:
936 case MVT::f32:
937 return Opcode_i32;
938 case MVT::v2f32:
939 case MVT::v2i32:
940 case MVT::i64:
941 case MVT::f64:
942 return Opcode_i64;
943 default:
944 return std::nullopt;
945 }
946}
947
948static inline bool isAddLike(const SDValue V) {
949 return V.getOpcode() == ISD::ADD ||
950 (V->getOpcode() == ISD::OR && V->getFlags().hasDisjoint());
951}
952
954 if (N.getOpcode() == ISD::AssertAlign)
955 N = N.getOperand(0);
956 return N;
957}
958
959// selectBaseADDR - Match a dag node which will serve as the base address for an
960// ADDR operand pair.
963 if (const auto *GA = dyn_cast<GlobalAddressSDNode>(N))
964 return DAG->getTargetGlobalAddress(GA->getGlobal(), SDLoc(N),
965 GA->getValueType(0), GA->getOffset(),
966 GA->getTargetFlags());
967 if (const auto *ES = dyn_cast<ExternalSymbolSDNode>(N))
968 return DAG->getTargetExternalSymbol(ES->getSymbol(), ES->getValueType(0),
969 ES->getTargetFlags());
970 if (const auto *FIN = dyn_cast<FrameIndexSDNode>(N))
971 return DAG->getTargetFrameIndex(FIN->getIndex(), FIN->getValueType(0));
972
973 return N;
974}
975
977 Addr = stripAssertAlign(Addr);
978 APInt AccumulatedOffset(64u, 0);
979 while (isAddLike(Addr)) {
980 const auto *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1));
981 if (!CN)
982 break;
983
984 const APInt CI = CN->getAPIntValue().sext(64);
985 if (!(CI + AccumulatedOffset).isSignedIntN(32))
986 break;
987
988 AccumulatedOffset += CI;
989 Addr = stripAssertAlign(Addr->getOperand(0));
990 }
991 return DAG->getSignedTargetConstant(AccumulatedOffset.getSExtValue(), DL,
992 MVT::i32);
993}
994
995static std::pair<SDValue, SDValue> selectADDR(SDValue Addr, SelectionDAG *DAG) {
996 SDValue Offset = accumulateOffset(Addr, SDLoc(Addr), DAG);
997 SDValue Base = selectBaseADDR(Addr, DAG);
998 return {Base, Offset};
999}
1000
1001// Select a pair of operands which represent a valid PTX address, this could be
1002// one of the following things:
1003// - [var] - Offset is simply set to 0
1004// - [reg] - Offset is simply set to 0
1005// - [reg+immOff]
1006// - [var+immOff]
1007// Note that immOff must fit into a 32-bit signed integer.
1008bool NVPTXDAGToDAGISel::SelectADDR(SDValue Addr, SDValue &Base,
1009 SDValue &Offset) {
1010 std::tie(Base, Offset) = selectADDR(Addr, CurDAG);
1011 return true;
1012}
1013
1014bool NVPTXDAGToDAGISel::tryLoad(SDNode *N) {
1015 MemSDNode *LD = cast<MemSDNode>(N);
1016 assert(LD->readMem() && "Expected load");
1017
1018 // do not support pre/post inc/dec
1019 const LoadSDNode *PlainLoad = dyn_cast<LoadSDNode>(LD);
1020 if (PlainLoad && PlainLoad->isIndexed())
1021 return false;
1022
1023 // Address Space Setting
1024 const auto CodeAddrSpace = getAddrSpace(LD);
1025 if (canLowerToLDG(*LD, *Subtarget, CodeAddrSpace))
1026 return tryLDG(LD);
1027
1028 SDLoc DL(LD);
1029 SDValue Chain = N->getOperand(0);
1030 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, LD);
1031
1032 const unsigned FromTypeWidth = LD->getMemoryVT().getSizeInBits();
1033
1034 // Vector Setting
1035 const unsigned FromType =
1036 (PlainLoad && (PlainLoad->getExtensionType() == ISD::SEXTLOAD))
1039
1040 uint32_t UsedBytesMask;
1041 switch (N->getOpcode()) {
1042 case ISD::LOAD:
1043 case ISD::ATOMIC_LOAD:
1044 UsedBytesMask = UINT32_MAX;
1045 break;
1046 case NVPTXISD::MLoad:
1047 UsedBytesMask = N->getConstantOperandVal(3);
1048 break;
1049 default:
1050 llvm_unreachable("Unexpected opcode");
1051 }
1052
1053 assert(isPowerOf2_32(FromTypeWidth) && FromTypeWidth >= 8 &&
1054 FromTypeWidth <= 128 && "Invalid width for load");
1055
1056 // Create the machine instruction DAG
1057 const auto [Base, Offset] = selectADDR(N->getOperand(1), CurDAG);
1058 SDValue Ops[] = {getI32Imm(Ordering, DL),
1059 getI32Imm(Scope, DL),
1060 getI32Imm(CodeAddrSpace, DL),
1061 getI32Imm(FromType, DL),
1062 getI32Imm(FromTypeWidth, DL),
1063 getI32Imm(UsedBytesMask, DL),
1064 Base,
1065 Offset,
1066 Chain};
1067
1068 const MVT::SimpleValueType TargetVT = LD->getSimpleValueType(0).SimpleTy;
1069 const std::optional<unsigned> Opcode =
1070 pickOpcodeForVT(TargetVT, NVPTX::LD_i16, NVPTX::LD_i32, NVPTX::LD_i64);
1071 if (!Opcode)
1072 return false;
1073
1074 SDNode *NVPTXLD = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1075 if (!NVPTXLD)
1076 return false;
1077
1078 MachineMemOperand *MemRef = LD->getMemOperand();
1079 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXLD), {MemRef});
1080
1081 ReplaceNode(LD, NVPTXLD);
1082 return true;
1083}
1084
1085static unsigned getStoreVectorNumElts(SDNode *N) {
1086 switch (N->getOpcode()) {
1087 case NVPTXISD::StoreV2:
1088 return 2;
1089 case NVPTXISD::StoreV4:
1090 return 4;
1091 case NVPTXISD::StoreV8:
1092 return 8;
1093 default:
1094 llvm_unreachable("Unexpected opcode");
1095 }
1096}
1097
1098bool NVPTXDAGToDAGISel::tryLoadVector(SDNode *N) {
1099 MemSDNode *LD = cast<MemSDNode>(N);
1100
1101 // Address Space Setting
1102 const auto CodeAddrSpace = getAddrSpace(LD);
1103 if (canLowerToLDG(*LD, *Subtarget, CodeAddrSpace))
1104 return tryLDG(LD);
1105
1106 const MVT EltVT = LD->getSimpleValueType(0);
1107 SDLoc DL(LD);
1108 SDValue Chain = LD->getChain();
1109 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, LD);
1110
1111 // Type Setting: fromType + fromTypeWidth
1112 //
1113 // Sign : ISD::SEXTLOAD
1114 // Unsign : ISD::ZEXTLOAD, ISD::NON_EXTLOAD or ISD::EXTLOAD and the
1115 // type is integer
1116 // Float : ISD::NON_EXTLOAD or ISD::EXTLOAD and the type is float
1117 // Read at least 8 bits (predicates are stored as 8-bit values)
1118 // Get the original LoadSDNode::getExtensionType() value
1119 const unsigned ExtensionType = N->getConstantOperandVal(4);
1120 const unsigned FromType = (ExtensionType == ISD::SEXTLOAD)
1122 : NVPTX::PTXLdStInstCode::Untyped;
1123
1124 const unsigned FromTypeWidth = getFromTypeWidthForLoad(LD);
1125 const uint32_t UsedBytesMask = N->getConstantOperandVal(3);
1126
1127 assert(!(EltVT.isVector() && ExtensionType != ISD::NON_EXTLOAD));
1128
1129 const auto [Base, Offset] = selectADDR(N->getOperand(1), CurDAG);
1130 SDValue Ops[] = {getI32Imm(Ordering, DL),
1131 getI32Imm(Scope, DL),
1132 getI32Imm(CodeAddrSpace, DL),
1133 getI32Imm(FromType, DL),
1134 getI32Imm(FromTypeWidth, DL),
1135 getI32Imm(UsedBytesMask, DL),
1136 Base,
1137 Offset,
1138 Chain};
1139
1140 std::optional<unsigned> Opcode;
1141 switch (N->getOpcode()) {
1142 default:
1143 llvm_unreachable("Unexpected opcode");
1144 case NVPTXISD::LoadV2:
1145 Opcode = pickOpcodeForVT(EltVT.SimpleTy, NVPTX::LDV_i16_v2,
1146 NVPTX::LDV_i32_v2, NVPTX::LDV_i64_v2);
1147 break;
1148 case NVPTXISD::LoadV4:
1149 Opcode = pickOpcodeForVT(EltVT.SimpleTy, NVPTX::LDV_i16_v4,
1150 NVPTX::LDV_i32_v4, NVPTX::LDV_i64_v4);
1151 break;
1152 case NVPTXISD::LoadV8:
1153 Opcode = pickOpcodeForVT(EltVT.SimpleTy, {/* no v8i16 */},
1154 NVPTX::LDV_i32_v8, {/* no v8i64 */});
1155 break;
1156 }
1157 if (!Opcode)
1158 return false;
1159
1160 SDNode *NVPTXLD = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1161
1162 MachineMemOperand *MemRef = LD->getMemOperand();
1163 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXLD), {MemRef});
1164
1165 ReplaceNode(LD, NVPTXLD);
1166 return true;
1167}
1168
1169bool NVPTXDAGToDAGISel::tryLDG(MemSDNode *LD) {
1170 SDLoc DL(LD);
1171
1172 unsigned ExtensionType;
1173 uint32_t UsedBytesMask;
1174 if (const auto *Load = dyn_cast<LoadSDNode>(LD)) {
1175 ExtensionType = Load->getExtensionType();
1176 UsedBytesMask = UINT32_MAX;
1177 } else {
1178 ExtensionType = LD->getConstantOperandVal(4);
1179 UsedBytesMask = LD->getConstantOperandVal(3);
1180 }
1181 const unsigned FromType = (ExtensionType == ISD::SEXTLOAD)
1183 : NVPTX::PTXLdStInstCode::Untyped;
1184
1185 const unsigned FromTypeWidth = getFromTypeWidthForLoad(LD);
1186
1187 assert(!(LD->getSimpleValueType(0).isVector() &&
1188 ExtensionType != ISD::NON_EXTLOAD));
1189
1190 const auto [Base, Offset] = selectADDR(LD->getOperand(1), CurDAG);
1191 SDValue Ops[] = {getI32Imm(FromType, DL),
1192 getI32Imm(FromTypeWidth, DL),
1193 getI32Imm(UsedBytesMask, DL),
1194 Base,
1195 Offset,
1196 LD->getChain()};
1197
1198 const MVT::SimpleValueType TargetVT = LD->getSimpleValueType(0).SimpleTy;
1199 std::optional<unsigned> Opcode;
1200 switch (LD->getOpcode()) {
1201 default:
1202 llvm_unreachable("Unexpected opcode");
1203 case ISD::LOAD:
1204 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LD_GLOBAL_NC_i16,
1205 NVPTX::LD_GLOBAL_NC_i32, NVPTX::LD_GLOBAL_NC_i64);
1206 break;
1207 case NVPTXISD::MLoad:
1208 Opcode = pickOpcodeForVT(TargetVT, std::nullopt, NVPTX::LD_GLOBAL_NC_i32,
1209 NVPTX::LD_GLOBAL_NC_i64);
1210 break;
1211 case NVPTXISD::LoadV2:
1212 Opcode =
1213 pickOpcodeForVT(TargetVT, NVPTX::LD_GLOBAL_NC_v2i16,
1214 NVPTX::LD_GLOBAL_NC_v2i32, NVPTX::LD_GLOBAL_NC_v2i64);
1215 break;
1216 case NVPTXISD::LoadV4:
1217 Opcode =
1218 pickOpcodeForVT(TargetVT, NVPTX::LD_GLOBAL_NC_v4i16,
1219 NVPTX::LD_GLOBAL_NC_v4i32, NVPTX::LD_GLOBAL_NC_v4i64);
1220 break;
1221 case NVPTXISD::LoadV8:
1222 Opcode = pickOpcodeForVT(TargetVT, {/* no v8i16 */},
1223 NVPTX::LD_GLOBAL_NC_v8i32, {/* no v8i64 */});
1224 break;
1225 }
1226 if (!Opcode)
1227 return false;
1228
1229 SDNode *NVPTXLDG = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1230
1231 ReplaceNode(LD, NVPTXLDG);
1232 return true;
1233}
1234
1236 auto TotalWidth = Mem->getMemoryVT().getSizeInBits();
1237 auto NumElts = Mem->getNumValues() - 1;
1238 auto ElementBitWidth = TotalWidth / NumElts;
1239 assert(isPowerOf2_32(ElementBitWidth) && ElementBitWidth >= 8 &&
1240 ElementBitWidth <= 128 && TotalWidth <= 256 &&
1241 "Invalid width for load");
1242 return ElementBitWidth;
1243}
1244
1245bool NVPTXDAGToDAGISel::tryLDU(SDNode *N) {
1246 auto *LD = cast<MemSDNode>(N);
1247
1248 SDLoc DL(N);
1249 const unsigned FromTypeWidth = getFromTypeWidthForLoad(LD);
1250 const MVT::SimpleValueType TargetVT = LD->getSimpleValueType(0).SimpleTy;
1251
1252 // If this is an LDU intrinsic, the address is the third operand. If its an
1253 // LDU SD node (from custom vector handling), then its the second operand
1254 SDValue Addr =
1255 LD->getOperand(LD->getOpcode() == ISD::INTRINSIC_W_CHAIN ? 2 : 1);
1256
1257 const auto [Base, Offset] = selectADDR(Addr, CurDAG);
1258 SDValue Ops[] = {getI32Imm(FromTypeWidth, DL), Base, Offset, LD->getChain()};
1259
1260 std::optional<unsigned> Opcode;
1261 switch (N->getOpcode()) {
1262 default:
1263 llvm_unreachable("Unexpected opcode");
1265 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LDU_GLOBAL_i16,
1266 NVPTX::LDU_GLOBAL_i32, NVPTX::LDU_GLOBAL_i64);
1267 break;
1268 case NVPTXISD::LDUV2:
1269 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LDU_GLOBAL_v2i16,
1270 NVPTX::LDU_GLOBAL_v2i32, NVPTX::LDU_GLOBAL_v2i64);
1271 break;
1272 case NVPTXISD::LDUV4:
1273 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LDU_GLOBAL_v4i16,
1274 NVPTX::LDU_GLOBAL_v4i32, {/* no v4i64 */});
1275 break;
1276 }
1277 if (!Opcode)
1278 return false;
1279
1280 SDNode *NVPTXLDU = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1281
1282 ReplaceNode(LD, NVPTXLDU);
1283 return true;
1284}
1285
1286bool NVPTXDAGToDAGISel::tryStore(SDNode *N) {
1287 MemSDNode *ST = cast<MemSDNode>(N);
1288 assert(ST->writeMem() && "Expected store");
1289 StoreSDNode *PlainStore = dyn_cast<StoreSDNode>(ST);
1290 AtomicSDNode *AtomicStore = dyn_cast<AtomicSDNode>(ST);
1291 assert((PlainStore || AtomicStore) && "Expected store");
1292
1293 // do not support pre/post inc/dec
1294 if (PlainStore && PlainStore->isIndexed())
1295 return false;
1296
1297 // Address Space Setting
1298 const auto CodeAddrSpace = getAddrSpace(ST);
1299
1300 SDLoc DL(ST);
1301 SDValue Chain = ST->getChain();
1302 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, ST);
1303
1304 // Vector Setting
1305 const unsigned ToTypeWidth = ST->getMemoryVT().getSizeInBits();
1306
1307 // Create the machine instruction DAG
1308 SDValue Value = PlainStore ? PlainStore->getValue() : AtomicStore->getVal();
1309
1310 assert(isPowerOf2_32(ToTypeWidth) && ToTypeWidth >= 8 && ToTypeWidth <= 128 &&
1311 "Invalid width for store");
1312
1313 const auto [Base, Offset] = selectADDR(ST->getBasePtr(), CurDAG);
1314 SDValue Ops[] = {selectPossiblyImm(Value),
1315 getI32Imm(Ordering, DL),
1316 getI32Imm(Scope, DL),
1317 getI32Imm(CodeAddrSpace, DL),
1318 getI32Imm(ToTypeWidth, DL),
1319 Base,
1320 Offset,
1321 Chain};
1322
1323 const std::optional<unsigned> Opcode =
1324 pickOpcodeForVT(Value.getSimpleValueType().SimpleTy, NVPTX::ST_i16,
1325 NVPTX::ST_i32, NVPTX::ST_i64);
1326 if (!Opcode)
1327 return false;
1328
1329 SDNode *NVPTXST = CurDAG->getMachineNode(*Opcode, DL, MVT::Other, Ops);
1330
1331 if (!NVPTXST)
1332 return false;
1333
1334 MachineMemOperand *MemRef = ST->getMemOperand();
1335 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXST), {MemRef});
1336 ReplaceNode(ST, NVPTXST);
1337 return true;
1338}
1339
1340bool NVPTXDAGToDAGISel::tryStoreVector(SDNode *N) {
1341 MemSDNode *ST = cast<MemSDNode>(N);
1342 const unsigned TotalWidth = ST->getMemoryVT().getSizeInBits();
1343
1344 // Address Space Setting
1345 const auto CodeAddrSpace = getAddrSpace(ST);
1346 if (CodeAddrSpace == NVPTX::AddressSpace::Const) {
1347 report_fatal_error("Cannot store to pointer that points to constant "
1348 "memory space");
1349 }
1350
1351 SDLoc DL(ST);
1352 SDValue Chain = ST->getChain();
1353 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, ST);
1354
1355 const unsigned NumElts = getStoreVectorNumElts(ST);
1356
1358 for (auto &V : ST->ops().slice(1, NumElts))
1359 Ops.push_back(selectPossiblyImm(V));
1360 SDValue Addr = N->getOperand(NumElts + 1);
1361 const unsigned ToTypeWidth = TotalWidth / NumElts;
1362
1363 assert(isPowerOf2_32(ToTypeWidth) && ToTypeWidth >= 8 && ToTypeWidth <= 128 &&
1364 TotalWidth <= 256 && "Invalid width for store");
1365
1366 const auto [Base, Offset] = selectADDR(Addr, CurDAG);
1367 Ops.append({getI32Imm(Ordering, DL), getI32Imm(Scope, DL),
1368 getI32Imm(CodeAddrSpace, DL), getI32Imm(ToTypeWidth, DL), Base,
1369 Offset, Chain});
1370
1371 const MVT::SimpleValueType EltVT =
1372 ST->getOperand(1).getSimpleValueType().SimpleTy;
1373 std::optional<unsigned> Opcode;
1374 switch (ST->getOpcode()) {
1375 default:
1376 return false;
1377 case NVPTXISD::StoreV2:
1378 Opcode = pickOpcodeForVT(EltVT, NVPTX::STV_i16_v2, NVPTX::STV_i32_v2,
1379 NVPTX::STV_i64_v2);
1380 break;
1381 case NVPTXISD::StoreV4:
1382 Opcode = pickOpcodeForVT(EltVT, NVPTX::STV_i16_v4, NVPTX::STV_i32_v4,
1383 NVPTX::STV_i64_v4);
1384 break;
1385 case NVPTXISD::StoreV8:
1386 Opcode = pickOpcodeForVT(EltVT, {/* no v8i16 */}, NVPTX::STV_i32_v8,
1387 {/* no v8i64 */});
1388 break;
1389 }
1390
1391 if (!Opcode)
1392 return false;
1393
1394 SDNode *NVPTXST = CurDAG->getMachineNode(*Opcode, DL, MVT::Other, Ops);
1395
1396 MachineMemOperand *MemRef = ST->getMemOperand();
1397 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXST), {MemRef});
1398
1399 ReplaceNode(ST, NVPTXST);
1400 return true;
1401}
1402
1403/// SelectBFE - Look for instruction sequences that can be made more efficient
1404/// by using the 'bfe' (bit-field extract) PTX instruction
1405bool NVPTXDAGToDAGISel::tryBFE(SDNode *N) {
1406 SDLoc DL(N);
1407 SDValue LHS = N->getOperand(0);
1408 SDValue RHS = N->getOperand(1);
1409 SDValue Len;
1410 SDValue Start;
1411 SDValue Val;
1412 bool IsSigned = false;
1413
1414 if (N->getOpcode() == ISD::AND) {
1415 // Canonicalize the operands
1416 // We want 'and %val, %mask'
1418 std::swap(LHS, RHS);
1419 }
1420
1421 ConstantSDNode *Mask = dyn_cast<ConstantSDNode>(RHS);
1422 if (!Mask) {
1423 // We need a constant mask on the RHS of the AND
1424 return false;
1425 }
1426
1427 // Extract the mask bits
1428 uint64_t MaskVal = Mask->getZExtValue();
1429 if (!isMask_64(MaskVal)) {
1430 // We *could* handle shifted masks here, but doing so would require an
1431 // 'and' operation to fix up the low-order bits so we would trade
1432 // shr+and for bfe+and, which has the same throughput
1433 return false;
1434 }
1435
1436 // How many bits are in our mask?
1437 int64_t NumBits = countr_one(MaskVal);
1438 Len = CurDAG->getTargetConstant(NumBits, DL, MVT::i32);
1439
1440 if (LHS.getOpcode() == ISD::SRL || LHS.getOpcode() == ISD::SRA) {
1441 // We have a 'srl/and' pair, extract the effective start bit and length
1442 Val = LHS.getNode()->getOperand(0);
1443 Start = LHS.getNode()->getOperand(1);
1444 ConstantSDNode *StartConst = dyn_cast<ConstantSDNode>(Start);
1445 if (StartConst) {
1446 uint64_t StartVal = StartConst->getZExtValue();
1447 // How many "good" bits do we have left? "good" is defined here as bits
1448 // that exist in the original value, not shifted in.
1449 int64_t GoodBits = Start.getValueSizeInBits() - StartVal;
1450 if (NumBits > GoodBits) {
1451 // Do not handle the case where bits have been shifted in. In theory
1452 // we could handle this, but the cost is likely higher than just
1453 // emitting the srl/and pair.
1454 return false;
1455 }
1456 Start = CurDAG->getTargetConstant(StartVal, DL, MVT::i32);
1457 } else {
1458 // Do not handle the case where the shift amount (can be zero if no srl
1459 // was found) is not constant. We could handle this case, but it would
1460 // require run-time logic that would be more expensive than just
1461 // emitting the srl/and pair.
1462 return false;
1463 }
1464 } else {
1465 // Do not handle the case where the LHS of the and is not a shift. While
1466 // it would be trivial to handle this case, it would just transform
1467 // 'and' -> 'bfe', but 'and' has higher-throughput.
1468 return false;
1469 }
1470 } else if (N->getOpcode() == ISD::SRL || N->getOpcode() == ISD::SRA) {
1471 if (LHS->getOpcode() == ISD::AND) {
1472 ConstantSDNode *ShiftCnst = dyn_cast<ConstantSDNode>(RHS);
1473 if (!ShiftCnst) {
1474 // Shift amount must be constant
1475 return false;
1476 }
1477
1478 uint64_t ShiftAmt = ShiftCnst->getZExtValue();
1479
1480 SDValue AndLHS = LHS->getOperand(0);
1481 SDValue AndRHS = LHS->getOperand(1);
1482
1483 // Canonicalize the AND to have the mask on the RHS
1484 if (isa<ConstantSDNode>(AndLHS)) {
1485 std::swap(AndLHS, AndRHS);
1486 }
1487
1488 ConstantSDNode *MaskCnst = dyn_cast<ConstantSDNode>(AndRHS);
1489 if (!MaskCnst) {
1490 // Mask must be constant
1491 return false;
1492 }
1493
1494 uint64_t MaskVal = MaskCnst->getZExtValue();
1495 uint64_t NumZeros;
1496 uint64_t NumBits;
1497 if (isMask_64(MaskVal)) {
1498 NumZeros = 0;
1499 // The number of bits in the result bitfield will be the number of
1500 // trailing ones (the AND) minus the number of bits we shift off
1501 NumBits = llvm::countr_one(MaskVal) - ShiftAmt;
1502 } else if (isShiftedMask_64(MaskVal)) {
1503 NumZeros = llvm::countr_zero(MaskVal);
1504 unsigned NumOnes = llvm::countr_one(MaskVal >> NumZeros);
1505 // The number of bits in the result bitfield will be the number of
1506 // trailing zeros plus the number of set bits in the mask minus the
1507 // number of bits we shift off
1508 NumBits = NumZeros + NumOnes - ShiftAmt;
1509 } else {
1510 // This is not a mask we can handle
1511 return false;
1512 }
1513
1514 if (ShiftAmt < NumZeros) {
1515 // Handling this case would require extra logic that would make this
1516 // transformation non-profitable
1517 return false;
1518 }
1519
1520 Val = AndLHS;
1521 Start = CurDAG->getTargetConstant(ShiftAmt, DL, MVT::i32);
1522 Len = CurDAG->getTargetConstant(NumBits, DL, MVT::i32);
1523
1524 // If pre-shift AND includes the sign bit in the bitfield, we must use
1525 // signed BFE to replicate that bit during bitfield extraction. If the
1526 // sign bit is not part of the mask, unsigned BFE will zero out upper bits
1527 // of the result
1528 if (N->getOpcode() == ISD::SRA)
1529 IsSigned = (ShiftAmt + NumBits) == Val.getValueSizeInBits();
1530 } else if (LHS->getOpcode() == ISD::SHL) {
1531 // Here, we have a pattern like:
1532 //
1533 // (sra (shl val, NN), MM)
1534 // or
1535 // (srl (shl val, NN), MM)
1536 //
1537 // If MM >= NN, we can efficiently optimize this with bfe
1538 Val = LHS->getOperand(0);
1539
1540 SDValue ShlRHS = LHS->getOperand(1);
1541 ConstantSDNode *ShlCnst = dyn_cast<ConstantSDNode>(ShlRHS);
1542 if (!ShlCnst) {
1543 // Shift amount must be constant
1544 return false;
1545 }
1546 uint64_t InnerShiftAmt = ShlCnst->getZExtValue();
1547
1548 SDValue ShrRHS = RHS;
1549 ConstantSDNode *ShrCnst = dyn_cast<ConstantSDNode>(ShrRHS);
1550 if (!ShrCnst) {
1551 // Shift amount must be constant
1552 return false;
1553 }
1554 uint64_t OuterShiftAmt = ShrCnst->getZExtValue();
1555
1556 // To avoid extra codegen and be profitable, we need Outer >= Inner
1557 if (OuterShiftAmt < InnerShiftAmt) {
1558 return false;
1559 }
1560
1561 // If the outer shift is more than the type size, we have no bitfield to
1562 // extract (since we also check that the inner shift is <= the outer shift
1563 // then this also implies that the inner shift is < the type size)
1564 if (OuterShiftAmt >= Val.getValueSizeInBits()) {
1565 return false;
1566 }
1567
1568 Start = CurDAG->getTargetConstant(OuterShiftAmt - InnerShiftAmt, DL,
1569 MVT::i32);
1570 Len = CurDAG->getTargetConstant(Val.getValueSizeInBits() - OuterShiftAmt,
1571 DL, MVT::i32);
1572
1573 if (N->getOpcode() == ISD::SRA) {
1574 // If we have a arithmetic right shift, we need to use the signed bfe
1575 // variant
1576 IsSigned = true;
1577 }
1578 } else {
1579 // No can do...
1580 return false;
1581 }
1582 } else {
1583 // No can do...
1584 return false;
1585 }
1586
1587
1588 unsigned Opc;
1589 // For the BFE operations we form here from "and" and "srl", always use the
1590 // unsigned variants.
1591 if (Val.getValueType() == MVT::i32) {
1592 if (IsSigned) {
1593 Opc = NVPTX::BFE_S32rii;
1594 } else {
1595 Opc = NVPTX::BFE_U32rii;
1596 }
1597 } else if (Val.getValueType() == MVT::i64) {
1598 if (IsSigned) {
1599 Opc = NVPTX::BFE_S64rii;
1600 } else {
1601 Opc = NVPTX::BFE_U64rii;
1602 }
1603 } else {
1604 // We cannot handle this type
1605 return false;
1606 }
1607
1608 SDValue Ops[] = {
1609 Val, Start, Len
1610 };
1611
1612 ReplaceNode(N, CurDAG->getMachineNode(Opc, DL, N->getVTList(), Ops));
1613 return true;
1614}
1615
1616// Select bf16/bf16v2 FADD, FSUB, FMUL as fma on targets with only fma
1617bool NVPTXDAGToDAGISel::tryBF16ArithToFMA(SDNode *N) {
1618 EVT VT = SDValue(N, 0).getValueType();
1619 if (VT.getScalarType() != MVT::bf16)
1620 return false;
1621
1622 const NVPTXSubtarget *STI = TM.getSubtargetImpl();
1623 if (STI->hasNativeBF16Support(N->getOpcode()))
1624 return false;
1625
1626 const bool IsVec = VT.isVector();
1627 assert(!IsVec || VT.getVectorNumElements() == 2);
1628 SDLoc DL(N);
1629 SDValue N0 = N->getOperand(0);
1630 SDValue N1 = N->getOperand(1);
1631 SmallVector<SDValue, 3> Operands;
1632 auto GetConstant = [&](float Value) -> SDValue {
1633 // BF16 immediates must be legalized to integer register values
1634 APFloat APF(Value);
1635 bool LosesInfo;
1636 APF.convert(APFloat::BFloat(), APFloat::rmNearestTiesToEven, &LosesInfo);
1637 assert(!LosesInfo);
1638 if (IsVec) {
1639 auto API = APF.bitcastToAPInt();
1640 API = API.concat(API);
1641 auto Const = CurDAG->getTargetConstant(API, DL, MVT::i32);
1642 return SDValue(CurDAG->getMachineNode(NVPTX::MOV_B32_i, DL, VT, Const),
1643 0);
1644 }
1645 auto Const = CurDAG->getTargetConstantFP(APF, DL, VT);
1646 return SDValue(CurDAG->getMachineNode(NVPTX::MOV_BF16_i, DL, VT, Const), 0);
1647 };
1648
1649 switch (N->getOpcode()) {
1650 case ISD::FADD:
1651 // add(a, b) -> fma(a, 1.0, b)
1652 Operands = {N0, GetConstant(1.0), N1};
1653 break;
1654 case ISD::FSUB:
1655 // sub(a, b) -> fma(b, -1.0, a)
1656 Operands = {N1, GetConstant(-1.0), N0};
1657 break;
1658 case ISD::FMUL:
1659 // mul(a, b) -> fma(a, b, -0.0)
1660 // NOTE: The identity is -0, not 0, because -0 + 0 == 0 for floats
1661 Operands = {N0, N1, GetConstant(-0.0)};
1662 break;
1663 default:
1664 llvm_unreachable("Unexpected opcode");
1665 };
1666
1667 int Opcode = IsVec ? NVPTX::FMA_BF16x2rrr : NVPTX::FMA_BF16rrr;
1668 MachineSDNode *FMA = CurDAG->getMachineNode(Opcode, DL, VT, Operands);
1669 ReplaceNode(N, FMA);
1670 return true;
1671}
1672
1673SDValue NVPTXDAGToDAGISel::selectPossiblyImm(SDValue V) {
1674 if (V.getOpcode() == ISD::BITCAST)
1675 V = V.getOperand(0);
1676
1677 if (auto *CN = dyn_cast<ConstantSDNode>(V))
1678 return CurDAG->getTargetConstant(CN->getAPIntValue(), SDLoc(V),
1679 V.getValueType());
1680 if (auto *CN = dyn_cast<ConstantFPSDNode>(V))
1681 return CurDAG->getTargetConstantFP(CN->getValueAPF(), SDLoc(V),
1682 V.getValueType());
1683 return V;
1684}
1685
1686/// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
1687/// inline asm expressions.
1689 const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
1690 std::vector<SDValue> &OutOps) {
1691 switch (ConstraintID) {
1692 default:
1693 return true;
1694 case InlineAsm::ConstraintCode::m: { // memory
1695 const auto [Base, Offset] = selectADDR(Op, CurDAG);
1696 OutOps.push_back(Base);
1697 OutOps.push_back(Offset);
1698 return false;
1699 }
1700 }
1701 return true;
1702}
1703
1704void NVPTXDAGToDAGISel::SelectV2I64toI128(SDNode *N) {
1705 // Lower a CopyToReg with two 64-bit inputs
1706 // Dst:i128, lo:i64, hi:i64
1707 //
1708 // CopyToReg Dst, lo, hi;
1709 //
1710 // ==>
1711 //
1712 // tmp = V2I64toI128 {lo, hi};
1713 // CopyToReg Dst, tmp;
1714 SDValue Dst = N->getOperand(1);
1715 SDValue Lo = N->getOperand(2);
1716 SDValue Hi = N->getOperand(3);
1717
1718 SDLoc DL(N);
1719 SDNode *Mov =
1720 CurDAG->getMachineNode(NVPTX::V2I64toI128, DL, MVT::i128, {Lo, Hi});
1721
1722 SmallVector<SDValue, 4> NewOps(N->getNumOperands() - 1);
1723 NewOps[0] = N->getOperand(0);
1724 NewOps[1] = Dst;
1725 NewOps[2] = SDValue(Mov, 0);
1726 if (N->getNumOperands() == 5)
1727 NewOps[3] = N->getOperand(4);
1728 SDValue NewValue = CurDAG->getNode(ISD::CopyToReg, DL, SmallVector<EVT>(N->values()), NewOps);
1729
1730 ReplaceNode(N, NewValue.getNode());
1731}
1732
1733void NVPTXDAGToDAGISel::SelectI128toV2I64(SDNode *N) {
1734 // Lower CopyFromReg from a 128-bit regs to two 64-bit regs
1735 // Dst:i128, Src:i128
1736 //
1737 // {lo, hi} = CopyFromReg Src
1738 //
1739 // ==>
1740 //
1741 // {lo, hi} = I128toV2I64 Src
1742 //
1743 SDValue Ch = N->getOperand(0);
1744 SDValue Src = N->getOperand(1);
1745 SDValue Glue = N->getOperand(2);
1746 SDLoc DL(N);
1747
1748 // Add Glue and Ch to the operands and results to avoid break the execution
1749 // order
1750 SDNode *Mov = CurDAG->getMachineNode(
1751 NVPTX::I128toV2I64, DL,
1752 {MVT::i64, MVT::i64, Ch.getValueType(), Glue.getValueType()},
1753 {Src, Ch, Glue});
1754
1755 ReplaceNode(N, Mov);
1756}
1757
1758bool NVPTXDAGToDAGISel::tryFence(SDNode *N) {
1759 SDLoc DL(N);
1760 assert(N->getOpcode() == ISD::ATOMIC_FENCE);
1761 auto Scope = Scopes[N->getConstantOperandVal(2)];
1762
1763 // Singlethread fences have no inter-thread synchronization requirements.
1764 // Note: std::atomic_signal_fence lowers to singlethread LLVM IR fences;
1765 // this intentionally drops these before emitting PTX.
1766 if (Scope == NVPTX::Scope::Thread) {
1767 CurDAG->ReplaceAllUsesOfValueWith(SDValue(N, 0), N->getOperand(0));
1768 CurDAG->RemoveDeadNode(N);
1769 return true;
1770 }
1771
1772 unsigned int FenceOp = getFenceOp(
1773 NVPTX::Ordering(N->getConstantOperandVal(1)), Scope, Subtarget);
1774 SDValue Chain = N->getOperand(0);
1775 SDNode *FenceNode = CurDAG->getMachineNode(FenceOp, DL, MVT::Other, Chain);
1776 ReplaceNode(N, FenceNode);
1777 return true;
1778}
1779
1781 Scopes[C.getOrInsertSyncScopeID("singlethread")] = NVPTX::Scope::Thread;
1782 Scopes[C.getOrInsertSyncScopeID("")] = NVPTX::Scope::System;
1783 Scopes[C.getOrInsertSyncScopeID("block")] = NVPTX::Scope::Block;
1784 Scopes[C.getOrInsertSyncScopeID("cluster")] = NVPTX::Scope::Cluster;
1785 Scopes[C.getOrInsertSyncScopeID("device")] = NVPTX::Scope::Device;
1786}
1787
1789 if (Scopes.empty())
1790 llvm_unreachable("NVPTX Scopes must be initialized before calling "
1791 "NVPTXScopes::operator[]");
1792
1793 auto S = Scopes.find(ID);
1794 if (S == Scopes.end()) {
1795 auto scopeName = Context->getSyncScopeName(ID);
1796 assert(scopeName.has_value() && "Scope name must exist.");
1797
1798 // Build list of supported syncscopes programmatically
1799 SmallVector<StringRef> supportedScopes;
1800 for (const auto &Entry : Scopes) {
1801 if (auto name = Context->getSyncScopeName(Entry.first))
1802 supportedScopes.push_back(name->empty() ? "<empty string>" : *name);
1803 }
1804
1806 formatv("NVPTX backend does not support syncscope \"{0}\" (ID={1}).\n"
1807 "Supported syncscopes are: {2}.",
1808 scopeName.value(), int(ID),
1809 make_range(supportedScopes.begin(), supportedScopes.end())));
1810 }
1811 return S->second;
1812}
1813
1814bool NVPTXScopes::empty() const { return Scopes.size() == 0; }
1815
1816#define CP_ASYNC_BULK_TENSOR_OPCODE(dir, dim, mode, is_s32, suffix) \
1817 (is_s32 \
1818 ? NVPTX::CP_ASYNC_BULK_TENSOR_##dir##_##dim##_SHARED32_##mode##suffix \
1819 : NVPTX::CP_ASYNC_BULK_TENSOR_##dir##_##dim##_##mode##suffix)
1820
1821#define GET_CP_ASYNC_BULK_TENSOR_OPCODE_S2G_RED(dim, mode, is_ch, is_s32) \
1822 (is_ch ? (CP_ASYNC_BULK_TENSOR_OPCODE(RED, dim, mode, is_s32, _CH)) \
1823 : (CP_ASYNC_BULK_TENSOR_OPCODE(RED, dim, mode, is_s32, )))
1824
1826 bool IsShared32,
1827 bool IsCacheHint,
1828 bool IsIm2Col) {
1829 if (IsIm2Col) {
1830 switch (Dim) {
1831 case 3:
1832 return GET_CP_ASYNC_BULK_TENSOR_OPCODE_S2G_RED(3D, IM2COL, IsCacheHint,
1833 IsShared32);
1834 case 4:
1835 return GET_CP_ASYNC_BULK_TENSOR_OPCODE_S2G_RED(4D, IM2COL, IsCacheHint,
1836 IsShared32);
1837 case 5:
1838 return GET_CP_ASYNC_BULK_TENSOR_OPCODE_S2G_RED(5D, IM2COL, IsCacheHint,
1839 IsShared32);
1840 default:
1841 llvm_unreachable("Invalid Dimension in im2col mode for "
1842 "GetCpAsyncBulkTensorS2GReductionOpcode.");
1843 }
1844 } else {
1845 switch (Dim) {
1846 case 1:
1847 return GET_CP_ASYNC_BULK_TENSOR_OPCODE_S2G_RED(1D, TILE, IsCacheHint,
1848 IsShared32);
1849 case 2:
1850 return GET_CP_ASYNC_BULK_TENSOR_OPCODE_S2G_RED(2D, TILE, IsCacheHint,
1851 IsShared32);
1852 case 3:
1853 return GET_CP_ASYNC_BULK_TENSOR_OPCODE_S2G_RED(3D, TILE, IsCacheHint,
1854 IsShared32);
1855 case 4:
1856 return GET_CP_ASYNC_BULK_TENSOR_OPCODE_S2G_RED(4D, TILE, IsCacheHint,
1857 IsShared32);
1858 case 5:
1859 return GET_CP_ASYNC_BULK_TENSOR_OPCODE_S2G_RED(5D, TILE, IsCacheHint,
1860 IsShared32);
1861 default:
1862 llvm_unreachable("Invalid Dimension in tile mode for "
1863 "GetCpAsyncBulkTensorS2GReductionOpcode.");
1864 }
1865 }
1866}
1867
1868void NVPTXDAGToDAGISel::SelectCpAsyncBulkTensorReduceCommon(SDNode *N,
1869 unsigned RedOp,
1870 bool IsIm2Col) {
1871 // We have {Chain, Intrinsic-ID} followed by the actual intrisic args:
1872 // src, dst, dims{d0...dN}, cache_hint, cache_hint_flag
1873 // NumOperands = {Chain, IID} + {Actual intrinsic args}
1874 // = {2} + {4 + dims}
1875 size_t NumOps = N->getNumOperands();
1876 size_t NumDims = NumOps - 6;
1877 bool IsCacheHint = N->getConstantOperandVal(NumOps - 1) == 1;
1878 size_t NumArgs = NumDims + (IsCacheHint ? 3 : 2); // src, dst, cache_hint
1879
1880 SDLoc DL(N);
1881 SmallVector<SDValue, 12> Ops(N->ops().slice(2, NumArgs));
1882 Ops.push_back(getI32Imm(RedOp, DL)); // Reduction Op
1883 Ops.push_back(N->getOperand(0)); // Chain operand
1884
1885 bool IsShared32 =
1886 CurDAG->getDataLayout().getPointerSizeInBits(ADDRESS_SPACE_SHARED) == 32;
1888 NumDims, IsShared32, IsCacheHint, IsIm2Col);
1889 ReplaceNode(N, CurDAG->getMachineNode(Opcode, DL, N->getVTList(), Ops));
1890}
1891
1892#define TCGEN05_ST_OPCODE(SHAPE, NUM) \
1893 (enableUnpack ? NVPTX::TCGEN05_ST_##SHAPE##_##NUM##_UNPACK \
1894 : NVPTX::TCGEN05_ST_##SHAPE##_##NUM)
1895
1896static unsigned getTcgen05StOpcode(unsigned IID, bool enableUnpack) {
1897 switch (IID) {
1898 case Intrinsic::nvvm_tcgen05_st_16x64b_x1:
1899 return TCGEN05_ST_OPCODE(16x64b, x1);
1900 case Intrinsic::nvvm_tcgen05_st_16x64b_x2:
1901 return TCGEN05_ST_OPCODE(16x64b, x2);
1902 case Intrinsic::nvvm_tcgen05_st_16x64b_x4:
1903 return TCGEN05_ST_OPCODE(16x64b, x4);
1904 case Intrinsic::nvvm_tcgen05_st_16x64b_x8:
1905 return TCGEN05_ST_OPCODE(16x64b, x8);
1906 case Intrinsic::nvvm_tcgen05_st_16x64b_x16:
1907 return TCGEN05_ST_OPCODE(16x64b, x16);
1908 case Intrinsic::nvvm_tcgen05_st_16x64b_x32:
1909 return TCGEN05_ST_OPCODE(16x64b, x32);
1910 case Intrinsic::nvvm_tcgen05_st_16x64b_x64:
1911 return TCGEN05_ST_OPCODE(16x64b, x64);
1912 case Intrinsic::nvvm_tcgen05_st_16x64b_x128:
1913 return TCGEN05_ST_OPCODE(16x64b, x128);
1914 case Intrinsic::nvvm_tcgen05_st_16x128b_x1:
1915 return TCGEN05_ST_OPCODE(16x128b, x1);
1916 case Intrinsic::nvvm_tcgen05_st_16x128b_x2:
1917 return TCGEN05_ST_OPCODE(16x128b, x2);
1918 case Intrinsic::nvvm_tcgen05_st_16x128b_x4:
1919 return TCGEN05_ST_OPCODE(16x128b, x4);
1920 case Intrinsic::nvvm_tcgen05_st_16x128b_x8:
1921 return TCGEN05_ST_OPCODE(16x128b, x8);
1922 case Intrinsic::nvvm_tcgen05_st_16x128b_x16:
1923 return TCGEN05_ST_OPCODE(16x128b, x16);
1924 case Intrinsic::nvvm_tcgen05_st_16x128b_x32:
1925 return TCGEN05_ST_OPCODE(16x128b, x32);
1926 case Intrinsic::nvvm_tcgen05_st_16x128b_x64:
1927 return TCGEN05_ST_OPCODE(16x128b, x64);
1928 case Intrinsic::nvvm_tcgen05_st_16x256b_x1:
1929 return TCGEN05_ST_OPCODE(16x256b, x1);
1930 case Intrinsic::nvvm_tcgen05_st_16x256b_x2:
1931 return TCGEN05_ST_OPCODE(16x256b, x2);
1932 case Intrinsic::nvvm_tcgen05_st_16x256b_x4:
1933 return TCGEN05_ST_OPCODE(16x256b, x4);
1934 case Intrinsic::nvvm_tcgen05_st_16x256b_x8:
1935 return TCGEN05_ST_OPCODE(16x256b, x8);
1936 case Intrinsic::nvvm_tcgen05_st_16x256b_x16:
1937 return TCGEN05_ST_OPCODE(16x256b, x16);
1938 case Intrinsic::nvvm_tcgen05_st_16x256b_x32:
1939 return TCGEN05_ST_OPCODE(16x256b, x32);
1940 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x1:
1941 return TCGEN05_ST_OPCODE(16x32bx2, x1);
1942 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x2:
1943 return TCGEN05_ST_OPCODE(16x32bx2, x2);
1944 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x4:
1945 return TCGEN05_ST_OPCODE(16x32bx2, x4);
1946 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x8:
1947 return TCGEN05_ST_OPCODE(16x32bx2, x8);
1948 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x16:
1949 return TCGEN05_ST_OPCODE(16x32bx2, x16);
1950 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x32:
1951 return TCGEN05_ST_OPCODE(16x32bx2, x32);
1952 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x64:
1953 return TCGEN05_ST_OPCODE(16x32bx2, x64);
1954 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x128:
1955 return TCGEN05_ST_OPCODE(16x32bx2, x128);
1956 case Intrinsic::nvvm_tcgen05_st_32x32b_x1:
1957 return TCGEN05_ST_OPCODE(32x32b, x1);
1958 case Intrinsic::nvvm_tcgen05_st_32x32b_x2:
1959 return TCGEN05_ST_OPCODE(32x32b, x2);
1960 case Intrinsic::nvvm_tcgen05_st_32x32b_x4:
1961 return TCGEN05_ST_OPCODE(32x32b, x4);
1962 case Intrinsic::nvvm_tcgen05_st_32x32b_x8:
1963 return TCGEN05_ST_OPCODE(32x32b, x8);
1964 case Intrinsic::nvvm_tcgen05_st_32x32b_x16:
1965 return TCGEN05_ST_OPCODE(32x32b, x16);
1966 case Intrinsic::nvvm_tcgen05_st_32x32b_x32:
1967 return TCGEN05_ST_OPCODE(32x32b, x32);
1968 case Intrinsic::nvvm_tcgen05_st_32x32b_x64:
1969 return TCGEN05_ST_OPCODE(32x32b, x64);
1970 case Intrinsic::nvvm_tcgen05_st_32x32b_x128:
1971 return TCGEN05_ST_OPCODE(32x32b, x128);
1972 }
1973 llvm_unreachable("unhandled tcgen05.st lowering");
1974}
1975
1976void NVPTXDAGToDAGISel::SelectTcgen05St(SDNode *N, bool hasOffset) {
1977 if (!Subtarget->hasTcgen05InstSupport())
1979 "tcgen05.st is not supported on this architecture variant");
1980
1981 SDLoc DL(N);
1982 unsigned IID = cast<ConstantSDNode>(N->getOperand(1))->getZExtValue();
1983
1984 SmallVector<SDValue, 128> Operands = {
1985 N->getOperand(2) // taddr
1986 };
1987
1988 if (hasOffset)
1989 Operands.push_back(CurDAG->getTargetConstant(
1990 cast<ConstantSDNode>(N->getOperand(3))->getZExtValue(), DL,
1991 MVT::i32)); // Offset
1992
1993 for (unsigned I = hasOffset ? 4 : 3; I < (N->getNumOperands() - 1); I++)
1994 Operands.push_back(N->getOperand(I));
1995
1996 bool enableUnpack =
1997 cast<ConstantSDNode>(N->getOperand(N->getNumOperands() - 1))
1998 ->getZExtValue();
1999
2000 Operands.push_back(N->getOperand(0)); // Chain
2001 ReplaceNode(N, CurDAG->getMachineNode(getTcgen05StOpcode(IID, enableUnpack),
2002 DL, N->getVTList(), Operands));
2003}
2004
2005bool NVPTXDAGToDAGISel::tryIntrinsicVoid(SDNode *N) {
2006 unsigned IID = N->getConstantOperandVal(1);
2007 using TMARedTy = llvm::nvvm::TMAReductionOp;
2008 auto CastTy = [](TMARedTy Op) { return static_cast<unsigned>(Op); };
2009 switch (IID) {
2010 default:
2011 return false;
2012 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_add_tile_1d:
2013 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_add_tile_2d:
2014 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_add_tile_3d:
2015 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_add_tile_4d:
2016 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_add_tile_5d:
2017 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::ADD));
2018 return true;
2019 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_add_im2col_3d:
2020 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_add_im2col_4d:
2021 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_add_im2col_5d:
2022 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::ADD),
2023 /*IsIm2Col=*/true);
2024 return true;
2025 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_min_tile_1d:
2026 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_min_tile_2d:
2027 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_min_tile_3d:
2028 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_min_tile_4d:
2029 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_min_tile_5d:
2030 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::MIN));
2031 return true;
2032 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_min_im2col_3d:
2033 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_min_im2col_4d:
2034 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_min_im2col_5d:
2035 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::MIN),
2036 /*IsIm2Col=*/true);
2037 return true;
2038 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_max_tile_1d:
2039 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_max_tile_2d:
2040 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_max_tile_3d:
2041 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_max_tile_4d:
2042 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_max_tile_5d:
2043 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::MAX));
2044 return true;
2045 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_max_im2col_3d:
2046 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_max_im2col_4d:
2047 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_max_im2col_5d:
2048 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::MAX),
2049 /*IsIm2Col=*/true);
2050 return true;
2051 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_inc_tile_1d:
2052 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_inc_tile_2d:
2053 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_inc_tile_3d:
2054 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_inc_tile_4d:
2055 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_inc_tile_5d:
2056 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::INC));
2057 return true;
2058 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_inc_im2col_3d:
2059 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_inc_im2col_4d:
2060 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_inc_im2col_5d:
2061 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::INC),
2062 /*IsIm2Col=*/true);
2063 return true;
2064 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_dec_tile_1d:
2065 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_dec_tile_2d:
2066 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_dec_tile_3d:
2067 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_dec_tile_4d:
2068 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_dec_tile_5d:
2069 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::DEC));
2070 return true;
2071 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_dec_im2col_3d:
2072 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_dec_im2col_4d:
2073 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_dec_im2col_5d:
2074 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::DEC),
2075 /*IsIm2Col=*/true);
2076 return true;
2077 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_and_tile_1d:
2078 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_and_tile_2d:
2079 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_and_tile_3d:
2080 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_and_tile_4d:
2081 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_and_tile_5d:
2082 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::AND));
2083 return true;
2084 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_and_im2col_3d:
2085 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_and_im2col_4d:
2086 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_and_im2col_5d:
2087 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::AND),
2088 /*IsIm2Col=*/true);
2089 return true;
2090 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_or_tile_1d:
2091 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_or_tile_2d:
2092 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_or_tile_3d:
2093 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_or_tile_4d:
2094 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_or_tile_5d:
2095 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::OR));
2096 return true;
2097 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_or_im2col_3d:
2098 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_or_im2col_4d:
2099 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_or_im2col_5d:
2100 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::OR),
2101 /*IsIm2Col=*/true);
2102 return true;
2103 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_xor_tile_1d:
2104 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_xor_tile_2d:
2105 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_xor_tile_3d:
2106 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_xor_tile_4d:
2107 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_xor_tile_5d:
2108 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::XOR));
2109 return true;
2110 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_xor_im2col_3d:
2111 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_xor_im2col_4d:
2112 case Intrinsic::nvvm_cp_async_bulk_tensor_reduce_xor_im2col_5d:
2113 SelectCpAsyncBulkTensorReduceCommon(N, CastTy(TMARedTy::XOR),
2114 /*IsIm2Col=*/true);
2115 return true;
2116
2117 case Intrinsic::nvvm_tcgen05_st_16x64b_x1:
2118 case Intrinsic::nvvm_tcgen05_st_16x64b_x2:
2119 case Intrinsic::nvvm_tcgen05_st_16x64b_x4:
2120 case Intrinsic::nvvm_tcgen05_st_16x64b_x8:
2121 case Intrinsic::nvvm_tcgen05_st_16x64b_x16:
2122 case Intrinsic::nvvm_tcgen05_st_16x64b_x32:
2123 case Intrinsic::nvvm_tcgen05_st_16x64b_x64:
2124 case Intrinsic::nvvm_tcgen05_st_16x64b_x128:
2125 case Intrinsic::nvvm_tcgen05_st_32x32b_x1:
2126 case Intrinsic::nvvm_tcgen05_st_32x32b_x2:
2127 case Intrinsic::nvvm_tcgen05_st_32x32b_x4:
2128 case Intrinsic::nvvm_tcgen05_st_32x32b_x8:
2129 case Intrinsic::nvvm_tcgen05_st_32x32b_x16:
2130 case Intrinsic::nvvm_tcgen05_st_32x32b_x32:
2131 case Intrinsic::nvvm_tcgen05_st_32x32b_x64:
2132 case Intrinsic::nvvm_tcgen05_st_32x32b_x128:
2133 case Intrinsic::nvvm_tcgen05_st_16x128b_x1:
2134 case Intrinsic::nvvm_tcgen05_st_16x128b_x2:
2135 case Intrinsic::nvvm_tcgen05_st_16x128b_x4:
2136 case Intrinsic::nvvm_tcgen05_st_16x128b_x8:
2137 case Intrinsic::nvvm_tcgen05_st_16x128b_x16:
2138 case Intrinsic::nvvm_tcgen05_st_16x128b_x32:
2139 case Intrinsic::nvvm_tcgen05_st_16x128b_x64:
2140 case Intrinsic::nvvm_tcgen05_st_16x256b_x1:
2141 case Intrinsic::nvvm_tcgen05_st_16x256b_x2:
2142 case Intrinsic::nvvm_tcgen05_st_16x256b_x4:
2143 case Intrinsic::nvvm_tcgen05_st_16x256b_x8:
2144 case Intrinsic::nvvm_tcgen05_st_16x256b_x16:
2145 case Intrinsic::nvvm_tcgen05_st_16x256b_x32: {
2146 SelectTcgen05St(N);
2147 return true;
2148 }
2149
2150 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x1:
2151 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x2:
2152 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x4:
2153 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x8:
2154 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x16:
2155 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x32:
2156 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x64:
2157 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x128: {
2158 SelectTcgen05St(N, /* hasOffset */ true);
2159 return true;
2160 }
2161 }
2162}
2163
2164void NVPTXDAGToDAGISel::selectAtomicSwap128(SDNode *N) {
2165 MemSDNode *AN = cast<MemSDNode>(N);
2166 SDLoc dl(N);
2167
2168 const SDValue Chain = N->getOperand(0);
2169 const auto [Base, Offset] = selectADDR(N->getOperand(1), CurDAG);
2171 Ops.append(N->op_begin() + 2, N->op_end());
2172 Ops.append({
2173 getI32Imm(getMemOrder(AN), dl),
2174 getI32Imm(getAtomicScope(AN), dl),
2175 getI32Imm(getAddrSpace(AN), dl),
2176 Chain,
2177 });
2178
2179 assert(N->getOpcode() == NVPTXISD::ATOMIC_CMP_SWAP_B128 ||
2180 N->getOpcode() == NVPTXISD::ATOMIC_SWAP_B128);
2181 unsigned Opcode = N->getOpcode() == NVPTXISD::ATOMIC_SWAP_B128
2182 ? NVPTX::ATOM_EXCH_B128
2183 : NVPTX::ATOM_CAS_B128;
2184
2185 auto *ATOM = CurDAG->getMachineNode(Opcode, dl, N->getVTList(), Ops);
2186 CurDAG->setNodeMemRefs(ATOM, AN->getMemOperand());
2187
2188 ReplaceNode(N, ATOM);
2189}
return SDValue()
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file implements a class to represent arbitrary precision integral constant values and operations...
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Atomic ordering constants.
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
#define DEBUG_TYPE
const size_t AbstractManglingParser< Derived, Alloc >::NumOps
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
#define I(x, y, z)
Definition MD5.cpp:57
#define T
static NVPTX::Scope resolveScope(NVPTX::Scope S, const NVPTXSubtarget *T)
static unsigned getStoreVectorNumElts(SDNode *N)
static bool isAddLike(const SDValue V)
static SDValue selectBaseADDR(SDValue N, SelectionDAG *DAG)
static SDValue accumulateOffset(SDValue &Addr, SDLoc DL, SelectionDAG *DAG)
static unsigned getTcgen05StOpcode(unsigned IID, bool enableUnpack)
static std::optional< unsigned > pickOpcodeForVT(MVT::SimpleValueType VT, std::optional< unsigned > Opcode_i16, std::optional< unsigned > Opcode_i32, std::optional< unsigned > Opcode_i64)
static cl::opt< bool > EnableMADWide("nvptx-mad-wide-opt", cl::init(false), cl::Hidden, cl::desc("Enable MAD wide optimization"))
static unsigned GetCpAsyncBulkTensorS2GReductionOpcode(size_t Dim, bool IsShared32, bool IsCacheHint, bool IsIm2Col)
#define TCGEN05_LD_OPCODE(SHAPE, NUM)
static SDValue stripAssertAlign(SDValue N)
static cl::opt< bool > EnableRsqrtOpt("nvptx-rsqrt-approx-opt", cl::init(true), cl::Hidden, cl::desc("Enable reciprocal sqrt optimization"))
static unsigned int getFenceOp(NVPTX::Ordering O, NVPTX::Scope S, NVPTXSubtarget const *T)
#define GET_CP_ASYNC_BULK_TENSOR_OPCODE_S2G_RED(dim, mode, is_ch, is_s32)
#define TCGEN05_ST_OPCODE(SHAPE, NUM)
static std::pair< SDValue, SDValue > selectADDR(SDValue Addr, SelectionDAG *DAG)
static unsigned getTcgen05LdOpcode(unsigned IID, bool enablePack)
static bool canLowerToLDG(const MemSDNode &N, const NVPTXSubtarget &Subtarget, NVPTX::AddressSpace CodeAddrSpace)
This file contains the definitions of the enumerations and flags associated with NVVM Intrinsics,...
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
static const char * name
#define PASS_NAME
Value * RHS
Value * LHS
static const fltSemantics & BFloat()
Definition APFloat.h:296
static constexpr roundingMode rmNearestTiesToEven
Definition APFloat.h:345
Class for arbitrary precision integers.
Definition APInt.h:78
LLVM_ABI APInt sext(unsigned width) const
Sign extend to a new width.
Definition APInt.cpp:1028
int64_t getSExtValue() const
Get sign extended value.
Definition APInt.h:1587
const SDValue & getVal() const
uint64_t getZExtValue() const
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
bool isIndexed() const
Return true if this is a pre/post inc/dec load/store.
ISD::LoadExtType getExtensionType() const
Return whether this is a plain node, or one of the varieties of value-extending loads.
SimpleValueType SimpleTy
unsigned getVectorNumElements() const
bool isVector() const
Return true if this is a vector value type.
bool is32BitVector() const
Return true if this is a 32-bit vector type.
MVT getVectorElementType() const
bool is64BitVector() const
Return true if this is a 64-bit vector type.
This is an abstract virtual class for memory operations.
MachineMemOperand * getMemOperand() const
Return the unique MachineMemOperand object describing the memory reference performed by operation.
EVT getMemoryVT() const
Return the type of the in-memory value.
NVPTXDAGToDAGISelLegacy(NVPTXTargetMachine &tm, CodeGenOptLevel OptLevel)
bool runOnMachineFunction(MachineFunction &MF) override
static NVPTX::AddressSpace getAddrSpace(const MemSDNode *N)
bool SelectInlineAsmMemoryOperand(const SDValue &Op, InlineAsm::ConstraintCode ConstraintID, std::vector< SDValue > &OutOps) override
SelectInlineAsmMemoryOperand - Implement addressing mode selection for inline asm expressions.
static unsigned getFromTypeWidthForLoad(const MemSDNode *Mem)
const NVPTXSubtarget * Subtarget
const NVPTXTargetLowering * getTargetLowering() const override
bool hasNativeBF16Support(int Opcode) const
bool hasRelaxedMMIO() const
bool hasMemoryOrdering() const
NVPTX::DivPrecisionLevel getDivF32Level(const MachineFunction &MF, const SDNode &N) const
bool allowFMA(MachineFunction &MF, CodeGenOptLevel OptLevel) const
bool usePrecSqrtF32(const SDNode *N=nullptr) const
Wrapper class for IR location info (IR ordering and DebugLoc) to be passed into SDNode creation funct...
Represents one node in the SelectionDAG.
unsigned getNumValues() const
Return the number of values defined/returned by this operator.
const SDValue & getOperand(unsigned Num) const
Unlike LLVM values, Selection DAG nodes may return multiple values as the result of a computation.
SDNode * getNode() const
get the SDNode which holds the desired result
EVT getValueType() const
Return the ValueType of the referenced return value.
TypeSize getValueSizeInBits() const
Returns the size of the value in bits.
const SDValue & getOperand(unsigned i) const
SelectionDAGISelLegacy(char &ID, std::unique_ptr< SelectionDAGISel > S)
void ReplaceUses(SDValue F, SDValue T)
ReplaceUses - replace all uses of the old node F with the use of the new node T.
void ReplaceNode(SDNode *F, SDNode *T)
Replace all uses of F with T, then remove F from the DAG.
SelectionDAGISel(TargetMachine &tm, CodeGenOptLevel OL=CodeGenOptLevel::Default)
virtual bool runOnMachineFunction(MachineFunction &mf)
This is used to represent a portion of an LLVM function in a low-level Data Dependence DAG representa...
SDValue getTargetGlobalAddress(const GlobalValue *GV, const SDLoc &DL, EVT VT, int64_t offset=0, unsigned TargetFlags=0)
LLVM_ABI MachineSDNode * getMachineNode(unsigned Opcode, const SDLoc &dl, EVT VT)
These are used for target selectors to create a new node with specified return type(s),...
SDValue getTargetFrameIndex(int FI, EVT VT)
SDValue getSignedTargetConstant(int64_t Val, const SDLoc &DL, EVT VT, bool isOpaque=false)
LLVM_ABI SDValue getNode(unsigned Opcode, const SDLoc &DL, EVT VT, ArrayRef< SDUse > Ops)
Gets or creates the specified node.
LLVM_ABI SDValue getTargetExternalSymbol(const char *Sym, EVT VT, unsigned TargetFlags=0)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
const SDValue & getValue() const
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
@ ATOMIC_STORE
OUTCHAIN = ATOMIC_STORE(INCHAIN, val, ptr) This corresponds to "store atomic" instruction.
@ ADD
Simple integer binary arithmetic operators.
Definition ISDOpcodes.h:264
@ LOAD
LOAD and STORE have token chains as their first operand, then the same operands as an LLVM load/store...
@ FMA
FMA - Perform a * b + c with no intermediate rounding step.
Definition ISDOpcodes.h:520
@ INTRINSIC_VOID
OUTCHAIN = INTRINSIC_VOID(INCHAIN, INTRINSICID, arg1, arg2, ...) This node represents a target intrin...
Definition ISDOpcodes.h:220
@ FADD
Simple binary floating point operators.
Definition ISDOpcodes.h:417
@ ATOMIC_FENCE
OUTCHAIN = ATOMIC_FENCE(INCHAIN, ordering, scope) This corresponds to the fence instruction.
@ BITCAST
BITCAST - This operator converts between integer, vector and FP values, as if the value was stored to...
@ ATOMIC_LOAD
Val, OUTCHAIN = ATOMIC_LOAD(INCHAIN, ptr) This corresponds to "load atomic" instruction.
@ AssertAlign
AssertAlign - These nodes record if a register contains a value that has a known alignment and the tr...
Definition ISDOpcodes.h:69
@ CopyFromReg
CopyFromReg - This node indicates that the input value is a virtual or physical register that is defi...
Definition ISDOpcodes.h:230
@ SHL
Shift and rotation operations.
Definition ISDOpcodes.h:771
@ EXTRACT_VECTOR_ELT
EXTRACT_VECTOR_ELT(VECTOR, IDX) - Returns a single element from VECTOR identified by the (potentially...
Definition ISDOpcodes.h:578
@ CopyToReg
CopyToReg - This node has three operands: a chain, a register number to set to this value,...
Definition ISDOpcodes.h:224
@ AND
Bitwise operators - logical and, logical or, logical xor.
Definition ISDOpcodes.h:741
@ INTRINSIC_W_CHAIN
RESULT,OUTCHAIN = INTRINSIC_W_CHAIN(INCHAIN, INTRINSICID, arg1, ...) This node represents a target in...
Definition ISDOpcodes.h:213
CondCode
ISD::CondCode enum - These are ordered carefully to make the bitfields below work out,...
@ ATOMIC_CMP_SWAP_B128
These nodes are used to lower atomic instructions with i128 type.
@ DeviceParam
Definition NVPTX.h:217
@ SharedCluster
Definition NVPTX.h:210
@ EntryParam
Definition NVPTX.h:211
std::string OrderingToString(Ordering Order)
bool isPackedVectorTy(EVT VT)
DivPrecisionLevel
Definition NVPTX.h:280
@ DefaultDevice
Definition NVPTX.h:199
@ RelaxedMMIO
Definition NVPTX.h:189
@ AcquireRelease
Definition NVPTX.h:185
@ NotAtomic
Definition NVPTX.h:178
@ SequentiallyConsistent
Definition NVPTX.h:186
initializer< Ty > init(const Ty &Val)
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:578
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
int countr_one(T Value)
Count the number of ones from the least significant bit to the first zero bit.
Definition bit.h:315
@ Load
The value being inserted comes from a load (InsertElement only).
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
FunctionPass * createNVPTXISelDag(NVPTXTargetMachine &TM, llvm::CodeGenOptLevel OptLevel)
createNVPTXISelDag - This pass converts a legalized DAG into a NVPTX-specific DAG,...
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
int countr_zero(T Val)
Count number of 0's from the least significant bit to the most stopping at the first 1.
Definition bit.h:204
constexpr bool isShiftedMask_64(uint64_t Value)
Return true if the argument contains a non-empty sequence of ones with the remainder zero (64 bit ver...
Definition MathExtras.h:274
const char * toIRString(AtomicOrdering ao)
String used by LLVM IR to represent atomic ordering.
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
constexpr bool isPowerOf2_32(uint32_t Value)
Return true if the argument is a power of two > 0.
Definition MathExtras.h:280
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
constexpr bool isMask_64(uint64_t Value)
Return true if the argument is a non-empty sequence of ones starting at the least significant bit wit...
Definition MathExtras.h:262
CodeGenOptLevel
Code generation optimization level.
Definition CodeGen.h:82
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
AtomicOrdering
Atomic ordering for LLVM's memory model.
DWARFExpression::Operation Op
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
LLVM_ABI void reportFatalUsageError(Error Err)
Report a fatal error that does not indicate a bug in LLVM.
Definition Error.cpp:177
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:878
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:880
#define N
TypeSize getSizeInBits() const
Return the size of the specified value type in bits.
Definition ValueTypes.h:396
bool isVector() const
Return true if this is a vector value type.
Definition ValueTypes.h:176
EVT getScalarType() const
If this is a vector type, return the element type, otherwise return this.
Definition ValueTypes.h:346
unsigned getVectorNumElements() const
Given a vector type, return the number of elements it contains.
Definition ValueTypes.h:359
NVPTXScopes()=default
NVPTX::Scope operator[](SyncScope::ID ID) const