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
14#include "NVPTX.h"
15#include "NVPTXISelLowering.h"
17#include "NVPTXTargetMachine.h"
18#include "NVPTXUtilities.h"
19#include "llvm/ADT/APInt.h"
20#include "llvm/ADT/MapVector.h"
22#include "llvm/ADT/Twine.h"
28#include "llvm/IR/Constants.h"
30#include "llvm/IR/InlineAsm.h"
32#include "llvm/IR/Intrinsics.h"
33#include "llvm/IR/IntrinsicsNVPTX.h"
34#include "llvm/IR/LLVMContext.h"
35#include "llvm/IR/Metadata.h"
42#include <optional>
43
44using namespace llvm;
45
46#define DEBUG_TYPE "nvptx-isel"
47#define PASS_NAME "NVPTX DAG->DAG Pattern Instruction Selection"
48
49static cl::opt<bool>
50 EnableRsqrtOpt("nvptx-rsqrt-approx-opt", cl::init(true), cl::Hidden,
51 cl::desc("Enable reciprocal sqrt optimization"));
52
53// FIXME: This is a WAR to recover lost performance from #155024.
54// We still need to investigate the regression and find a more permanent
55// solution.
56static cl::opt<bool> EnableMADWide("nvptx-mad-wide-opt", cl::init(false),
58 cl::desc("Enable MAD wide optimization"));
59
60namespace {
61
62struct NVPTXScopes {
63 NVPTXScopes() = default;
64 NVPTXScopes(LLVMContext &C, const Triple &T);
65 NVPTX::Scope operator[](SyncScope::ID ID) const;
66 bool empty() const;
67
68private:
70 LLVMContext *Context = nullptr;
71};
72
73enum class NVPTXMemCacheHintInstruction { Ld, St, Atom };
74
75struct NVPTXMemCacheHintAccess {
76 NVPTXMemCacheHintInstruction Instruction;
77 NVPTX::AddressSpace AddrSpace;
78 unsigned NumElts;
79 unsigned EltWidth;
80 bool IsVolatile;
81};
82
83struct NVPTXMemCacheHintOperands {
84 SDValue EvictionAndPrefetchHint;
85 SDValue CachePolicyReg;
86};
87
88class NVPTXDAGToDAGISel : public SelectionDAGISel {
89 const NVPTXTargetMachine &TM;
90
91 NVPTX::DivPrecisionLevel getDivF32Level(const SDNode *N) const;
92 bool usePrecSqrtF32(const SDNode *N) const;
93 bool useF32FTZ() const;
94 bool allowFMA() const;
95 bool doRsqrtOpt() const;
96 bool doMADWideOpt() const;
97
98 NVPTXScopes Scopes{};
99
100public:
101 NVPTXDAGToDAGISel() = delete;
102
103 explicit NVPTXDAGToDAGISel(NVPTXTargetMachine &tm, CodeGenOptLevel OptLevel);
104
105 bool runOnMachineFunction(MachineFunction &MF) override;
106 const NVPTXSubtarget *Subtarget = nullptr;
107
108 bool SelectInlineAsmMemoryOperand(const SDValue &Op,
109 InlineAsm::ConstraintCode ConstraintID,
110 std::vector<SDValue> &OutOps) override;
111
112private:
113// Include the pieces autogenerated from the target description.
114#include "NVPTXGenDAGISel.inc"
115
116 void Select(SDNode *N) override;
117 bool tryIntrinsicChain(SDNode *N);
118 bool tryIntrinsicVoid(SDNode *N);
119 void SelectTexSurfHandle(SDNode *N);
120 bool tryLoad(SDNode *N);
121 bool tryLoadVector(SDNode *N);
122 bool tryLDU(SDNode *N);
123 bool tryLDG(MemSDNode *N);
124 bool tryStore(SDNode *N);
125 bool tryStoreVector(SDNode *N);
126 bool tryFence(SDNode *N);
127 bool tryBFE(SDNode *N);
128 bool tryBF16ArithToFMA(SDNode *N);
129 bool tryConstantFP(SDNode *N);
130 bool SelectSETP_F16X2(SDNode *N);
131 bool SelectSETP_BF16X2(SDNode *N);
132 bool tryUNPACK_VECTOR(SDNode *N);
133 bool tryEXTRACT_VECTOR_ELEMENT(SDNode *N);
134 void SelectV2I64toI128(SDNode *N);
135 void SelectI128toV2I64(SDNode *N);
136 void SelectCpAsyncBulkTensorReduceCommon(SDNode *N, unsigned RedOp,
137 bool IsIm2Col = false);
138 void SelectTcgen05Ld(SDNode *N, bool hasOffset = false);
139 void SelectTcgen05St(SDNode *N, bool hasOffset = false);
140 void selectAtomicSwap128(SDNode *N);
141
142 inline SDValue getI32Imm(unsigned Imm, const SDLoc &DL) {
143 return CurDAG->getTargetConstant(Imm, DL, MVT::i32);
144 }
145 NVPTX::Ordering getMemOrder(const MemSDNode *N) const;
146 NVPTX::Scope getAtomicScope(const MemSDNode *N) const;
147
148 bool SelectADDR(SDValue Addr, SDValue &Base, SDValue &Offset);
149 SDValue getPTXCmpMode(const CondCodeSDNode &CondCode);
150 SDValue selectPossiblyImm(SDValue V);
151
152 // Returns the encoded eviction/prefetch hint and cache policy register for a
153 // memory operation. Hints unsupported by the subtarget or address space are
154 // dropped. If L2::cache_hint is active, returns the hint with
155 // L2CacheHintBit set and a register containing the 64-bit cache policy
156 // value. Otherwise returns NOREG for the policy operand.
157 NVPTXMemCacheHintOperands
158 getMemCacheHintOperands(const MemSDNode *N, NVPTXMemCacheHintAccess Access,
159 const SDLoc &DL, bool EmitDiagnostics = true);
160
161 // Returns the Memory Order and Scope that the PTX memory instruction should
162 // use, and inserts appropriate fence instruction before the memory
163 // instruction, if needed to implement the instructions memory order. Required
164 // fences after the instruction need to be handled elsewhere.
165 std::pair<NVPTX::Ordering, NVPTX::Scope>
166 insertMemoryInstructionFence(SDLoc DL, SDValue &Chain, MemSDNode *N);
167 NVPTX::Scope getOperationScope(MemSDNode *N, NVPTX::Ordering O) const;
168
169public:
170 static NVPTX::AddressSpace getAddrSpace(const MemSDNode *N);
171};
172
173class NVPTXDAGToDAGISelLegacy : public SelectionDAGISelLegacy {
174public:
175 static char ID;
176 explicit NVPTXDAGToDAGISelLegacy(NVPTXTargetMachine &tm,
177 CodeGenOptLevel OptLevel);
178};
179
180} // end anonymous namespace
181
182/// createNVPTXISelDag - This pass converts a legalized DAG into a
183/// NVPTX-specific DAG, ready for instruction scheduling.
185 llvm::CodeGenOptLevel OptLevel) {
186 return new NVPTXDAGToDAGISelLegacy(TM, OptLevel);
187}
188
189NVPTXDAGToDAGISelLegacy::NVPTXDAGToDAGISelLegacy(NVPTXTargetMachine &tm,
190 CodeGenOptLevel OptLevel)
192 ID, std::make_unique<NVPTXDAGToDAGISel>(tm, OptLevel)) {}
193
194char NVPTXDAGToDAGISelLegacy::ID = 0;
195
196INITIALIZE_PASS(NVPTXDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)
197
199 CodeGenOptLevel OptLevel)
200 : SelectionDAGISelPass(std::make_unique<NVPTXDAGToDAGISel>(TM, OptLevel)) {}
201
202NVPTXDAGToDAGISel::NVPTXDAGToDAGISel(NVPTXTargetMachine &tm,
203 CodeGenOptLevel OptLevel)
204 : SelectionDAGISel(tm, OptLevel), TM(tm) {}
205
206bool NVPTXDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) {
207 Subtarget = &MF.getSubtarget<NVPTXSubtarget>();
208 Scopes = NVPTXScopes(MF.getFunction().getContext(),
211}
212
214NVPTXDAGToDAGISel::getDivF32Level(const SDNode *N) const {
215 return Subtarget->getTargetLowering()->getDivF32Level(*MF, *N);
216}
217
218bool NVPTXDAGToDAGISel::usePrecSqrtF32(const SDNode *N) const {
219 return Subtarget->getTargetLowering()->usePrecSqrtF32(N);
220}
221
222bool NVPTXDAGToDAGISel::useF32FTZ() const {
223 return Subtarget->getTargetLowering()->useF32FTZ(*MF);
224}
225
226bool NVPTXDAGToDAGISel::allowFMA() const {
227 const NVPTXTargetLowering *TL = Subtarget->getTargetLowering();
228 return TL->allowFMA(*MF, OptLevel);
229}
230
231bool NVPTXDAGToDAGISel::doRsqrtOpt() const { return EnableRsqrtOpt; }
232
233bool NVPTXDAGToDAGISel::doMADWideOpt() const { return EnableMADWide; }
234
235/// Select - Select instructions not customized! Used for
236/// expanded, promoted and normal instructions.
237void NVPTXDAGToDAGISel::Select(SDNode *N) {
238
239 if (N->isMachineOpcode()) {
240 N->setNodeId(-1);
241 return; // Already selected.
242 }
243
244 switch (N->getOpcode()) {
245 case ISD::LOAD:
246 case ISD::ATOMIC_LOAD:
247 case NVPTXISD::MLoad:
248 if (tryLoad(N))
249 return;
250 break;
251 case ISD::STORE:
253 if (tryStore(N))
254 return;
255 break;
257 if (tryFence(N))
258 return;
259 break;
261 tryUNPACK_VECTOR(N);
262 return;
264 if (tryEXTRACT_VECTOR_ELEMENT(N))
265 return;
266 break;
268 SelectSETP_F16X2(N);
269 return;
271 SelectSETP_BF16X2(N);
272 return;
273 case NVPTXISD::LoadV2:
274 case NVPTXISD::LoadV4:
275 case NVPTXISD::LoadV8:
276 if (tryLoadVector(N))
277 return;
278 break;
279 case NVPTXISD::LDUV2:
280 case NVPTXISD::LDUV4:
281 if (tryLDU(N))
282 return;
283 break;
287 if (tryStoreVector(N))
288 return;
289 break;
291 if (tryIntrinsicChain(N))
292 return;
293 break;
295 if (tryIntrinsicVoid(N))
296 return;
297 break;
298 case ISD::AND:
299 case ISD::SRA:
300 case ISD::SRL:
301 // Try to select BFE
302 if (tryBFE(N))
303 return;
304 break;
305 case ISD::CopyToReg: {
306 if (N->getOperand(1).getValueType() == MVT::i128) {
307 SelectV2I64toI128(N);
308 return;
309 }
310 break;
311 }
312 case ISD::CopyFromReg: {
313 if (N->getOperand(1).getValueType() == MVT::i128) {
314 SelectI128toV2I64(N);
315 return;
316 }
317 break;
318 }
321 selectAtomicSwap128(N);
322 return;
323 case ISD::FADD:
324 case ISD::FMUL:
325 case ISD::FSUB:
326 if (tryBF16ArithToFMA(N))
327 return;
328 break;
329 default:
330 break;
331 }
332 SelectCode(N);
333}
334
335#define TCGEN05_LD_OPCODE(SHAPE, NUM) \
336 (enablePack ? NVPTX::TCGEN05_LD_##SHAPE##_##NUM##_PACK \
337 : NVPTX::TCGEN05_LD_##SHAPE##_##NUM)
338
339static unsigned getTcgen05LdOpcode(unsigned IID, bool enablePack) {
340 switch (IID) {
341 case Intrinsic::nvvm_tcgen05_ld_16x64b_x1:
342 return TCGEN05_LD_OPCODE(16x64b, x1);
343 case Intrinsic::nvvm_tcgen05_ld_16x64b_x2:
344 return TCGEN05_LD_OPCODE(16x64b, x2);
345 case Intrinsic::nvvm_tcgen05_ld_16x64b_x4:
346 return TCGEN05_LD_OPCODE(16x64b, x4);
347 case Intrinsic::nvvm_tcgen05_ld_16x64b_x8:
348 return TCGEN05_LD_OPCODE(16x64b, x8);
349 case Intrinsic::nvvm_tcgen05_ld_16x64b_x16:
350 return TCGEN05_LD_OPCODE(16x64b, x16);
351 case Intrinsic::nvvm_tcgen05_ld_16x64b_x32:
352 return TCGEN05_LD_OPCODE(16x64b, x32);
353 case Intrinsic::nvvm_tcgen05_ld_16x64b_x64:
354 return TCGEN05_LD_OPCODE(16x64b, x64);
355 case Intrinsic::nvvm_tcgen05_ld_16x64b_x128:
356 return TCGEN05_LD_OPCODE(16x64b, x128);
357 case Intrinsic::nvvm_tcgen05_ld_16x128b_x1:
358 return TCGEN05_LD_OPCODE(16x128b, x1);
359 case Intrinsic::nvvm_tcgen05_ld_16x128b_x2:
360 return TCGEN05_LD_OPCODE(16x128b, x2);
361 case Intrinsic::nvvm_tcgen05_ld_16x128b_x4:
362 return TCGEN05_LD_OPCODE(16x128b, x4);
363 case Intrinsic::nvvm_tcgen05_ld_16x128b_x8:
364 return TCGEN05_LD_OPCODE(16x128b, x8);
365 case Intrinsic::nvvm_tcgen05_ld_16x128b_x16:
366 return TCGEN05_LD_OPCODE(16x128b, x16);
367 case Intrinsic::nvvm_tcgen05_ld_16x128b_x32:
368 return TCGEN05_LD_OPCODE(16x128b, x32);
369 case Intrinsic::nvvm_tcgen05_ld_16x128b_x64:
370 return TCGEN05_LD_OPCODE(16x128b, x64);
371 case Intrinsic::nvvm_tcgen05_ld_16x256b_x1:
372 return TCGEN05_LD_OPCODE(16x256b, x1);
373 case Intrinsic::nvvm_tcgen05_ld_16x256b_x2:
374 return TCGEN05_LD_OPCODE(16x256b, x2);
375 case Intrinsic::nvvm_tcgen05_ld_16x256b_x4:
376 return TCGEN05_LD_OPCODE(16x256b, x4);
377 case Intrinsic::nvvm_tcgen05_ld_16x256b_x8:
378 return TCGEN05_LD_OPCODE(16x256b, x8);
379 case Intrinsic::nvvm_tcgen05_ld_16x256b_x16:
380 return TCGEN05_LD_OPCODE(16x256b, x16);
381 case Intrinsic::nvvm_tcgen05_ld_16x256b_x32:
382 return TCGEN05_LD_OPCODE(16x256b, x32);
383 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x1:
384 return TCGEN05_LD_OPCODE(16x32bx2, x1);
385 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x2:
386 return TCGEN05_LD_OPCODE(16x32bx2, x2);
387 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x4:
388 return TCGEN05_LD_OPCODE(16x32bx2, x4);
389 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x8:
390 return TCGEN05_LD_OPCODE(16x32bx2, x8);
391 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x16:
392 return TCGEN05_LD_OPCODE(16x32bx2, x16);
393 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x32:
394 return TCGEN05_LD_OPCODE(16x32bx2, x32);
395 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x64:
396 return TCGEN05_LD_OPCODE(16x32bx2, x64);
397 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x128:
398 return TCGEN05_LD_OPCODE(16x32bx2, x128);
399 case Intrinsic::nvvm_tcgen05_ld_32x32b_x1:
400 return TCGEN05_LD_OPCODE(32x32b, x1);
401 case Intrinsic::nvvm_tcgen05_ld_32x32b_x2:
402 return TCGEN05_LD_OPCODE(32x32b, x2);
403 case Intrinsic::nvvm_tcgen05_ld_32x32b_x4:
404 return TCGEN05_LD_OPCODE(32x32b, x4);
405 case Intrinsic::nvvm_tcgen05_ld_32x32b_x8:
406 return TCGEN05_LD_OPCODE(32x32b, x8);
407 case Intrinsic::nvvm_tcgen05_ld_32x32b_x16:
408 return TCGEN05_LD_OPCODE(32x32b, x16);
409 case Intrinsic::nvvm_tcgen05_ld_32x32b_x32:
410 return TCGEN05_LD_OPCODE(32x32b, x32);
411 case Intrinsic::nvvm_tcgen05_ld_32x32b_x64:
412 return TCGEN05_LD_OPCODE(32x32b, x64);
413 case Intrinsic::nvvm_tcgen05_ld_32x32b_x128:
414 return TCGEN05_LD_OPCODE(32x32b, x128);
415 }
416 llvm_unreachable("unhandled tcgen05.ld lowering");
417}
418
419void NVPTXDAGToDAGISel::SelectTcgen05Ld(SDNode *N, bool hasOffset) {
420 if (!Subtarget->hasTcgen05InstSupport())
422 "tcgen05.ld is not supported on this architecture variant");
423
424 SDLoc DL(N);
425 unsigned IID = cast<ConstantSDNode>(N->getOperand(1))->getZExtValue();
426
427 if (hasOffset) {
428 bool enablePack = cast<ConstantSDNode>(N->getOperand(4))->getZExtValue();
429 auto OffsetNode = CurDAG->getTargetConstant(
430 cast<ConstantSDNode>(N->getOperand(3))->getZExtValue(), DL, MVT::i32);
431 ReplaceNode(N, CurDAG->getMachineNode(
432 getTcgen05LdOpcode(IID, enablePack), DL, N->getVTList(),
433 {N->getOperand(2), OffsetNode, N->getOperand(0)}));
434 } else {
435 bool enablePack = cast<ConstantSDNode>(N->getOperand(3))->getZExtValue();
436 ReplaceNode(N, CurDAG->getMachineNode(
437 getTcgen05LdOpcode(IID, enablePack), DL, N->getVTList(),
438 {N->getOperand(2), N->getOperand(0)}));
439 }
440}
441
442bool NVPTXDAGToDAGISel::tryIntrinsicChain(SDNode *N) {
443 unsigned IID = N->getConstantOperandVal(1);
444 switch (IID) {
445 default:
446 return false;
447 case Intrinsic::nvvm_ldu_global_f:
448 case Intrinsic::nvvm_ldu_global_i:
449 case Intrinsic::nvvm_ldu_global_p:
450 return tryLDU(N);
451
452 case Intrinsic::nvvm_tcgen05_ld_16x64b_x1:
453 case Intrinsic::nvvm_tcgen05_ld_16x64b_x2:
454 case Intrinsic::nvvm_tcgen05_ld_16x64b_x4:
455 case Intrinsic::nvvm_tcgen05_ld_16x64b_x8:
456 case Intrinsic::nvvm_tcgen05_ld_16x64b_x16:
457 case Intrinsic::nvvm_tcgen05_ld_16x64b_x32:
458 case Intrinsic::nvvm_tcgen05_ld_16x64b_x64:
459 case Intrinsic::nvvm_tcgen05_ld_16x64b_x128:
460 case Intrinsic::nvvm_tcgen05_ld_16x128b_x1:
461 case Intrinsic::nvvm_tcgen05_ld_16x128b_x2:
462 case Intrinsic::nvvm_tcgen05_ld_16x128b_x4:
463 case Intrinsic::nvvm_tcgen05_ld_16x128b_x16:
464 case Intrinsic::nvvm_tcgen05_ld_16x128b_x32:
465 case Intrinsic::nvvm_tcgen05_ld_16x128b_x64:
466 case Intrinsic::nvvm_tcgen05_ld_16x256b_x1:
467 case Intrinsic::nvvm_tcgen05_ld_16x128b_x8:
468 case Intrinsic::nvvm_tcgen05_ld_16x256b_x2:
469 case Intrinsic::nvvm_tcgen05_ld_16x256b_x4:
470 case Intrinsic::nvvm_tcgen05_ld_16x256b_x8:
471 case Intrinsic::nvvm_tcgen05_ld_16x256b_x16:
472 case Intrinsic::nvvm_tcgen05_ld_16x256b_x32:
473 case Intrinsic::nvvm_tcgen05_ld_32x32b_x1:
474 case Intrinsic::nvvm_tcgen05_ld_32x32b_x2:
475 case Intrinsic::nvvm_tcgen05_ld_32x32b_x4:
476 case Intrinsic::nvvm_tcgen05_ld_32x32b_x8:
477 case Intrinsic::nvvm_tcgen05_ld_32x32b_x16:
478 case Intrinsic::nvvm_tcgen05_ld_32x32b_x32:
479 case Intrinsic::nvvm_tcgen05_ld_32x32b_x64:
480 case Intrinsic::nvvm_tcgen05_ld_32x32b_x128: {
481 SelectTcgen05Ld(N);
482 return true;
483 }
484
485 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x1:
486 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x2:
487 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x4:
488 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x8:
489 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x16:
490 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x32:
491 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x64:
492 case Intrinsic::nvvm_tcgen05_ld_16x32bx2_x128: {
493 SelectTcgen05Ld(N, /* hasOffset */ true);
494 return true;
495 }
496 }
497}
498
499// Map ISD:CONDCODE value to appropriate CmpMode expected by
500// NVPTXInstPrinter::printCmpMode()
501SDValue NVPTXDAGToDAGISel::getPTXCmpMode(const CondCodeSDNode &CondCode) {
503 const unsigned PTXCmpMode = [](ISD::CondCode CC) {
504 switch (CC) {
505 default:
506 llvm_unreachable("Unexpected condition code.");
507 case ISD::SETOEQ:
508 case ISD::SETEQ:
509 return CmpMode::EQ;
510 case ISD::SETOGT:
511 case ISD::SETGT:
512 return CmpMode::GT;
513 case ISD::SETOGE:
514 case ISD::SETGE:
515 return CmpMode::GE;
516 case ISD::SETOLT:
517 case ISD::SETLT:
518 return CmpMode::LT;
519 case ISD::SETOLE:
520 case ISD::SETLE:
521 return CmpMode::LE;
522 case ISD::SETONE:
523 case ISD::SETNE:
524 return CmpMode::NE;
525 case ISD::SETO:
526 return CmpMode::NUM;
527 case ISD::SETUO:
528 return CmpMode::NotANumber;
529 case ISD::SETUEQ:
530 return CmpMode::EQU;
531 case ISD::SETUGT:
532 return CmpMode::GTU;
533 case ISD::SETUGE:
534 return CmpMode::GEU;
535 case ISD::SETULT:
536 return CmpMode::LTU;
537 case ISD::SETULE:
538 return CmpMode::LEU;
539 case ISD::SETUNE:
540 return CmpMode::NEU;
541 }
542 }(CondCode.get());
543 return CurDAG->getTargetConstant(PTXCmpMode, SDLoc(), MVT::i32);
544}
545
546bool NVPTXDAGToDAGISel::SelectSETP_F16X2(SDNode *N) {
547 SDValue PTXCmpMode = getPTXCmpMode(*cast<CondCodeSDNode>(N->getOperand(2)));
548 SDLoc DL(N);
549 SDNode *SetP = CurDAG->getMachineNode(
550 NVPTX::SETP_f16x2rr, DL, MVT::i1, MVT::i1,
551 {N->getOperand(0), N->getOperand(1), PTXCmpMode,
552 CurDAG->getTargetConstant(useF32FTZ() ? 1 : 0, DL, MVT::i1)});
553 ReplaceNode(N, SetP);
554 return true;
555}
556
557bool NVPTXDAGToDAGISel::SelectSETP_BF16X2(SDNode *N) {
558 SDValue PTXCmpMode = getPTXCmpMode(*cast<CondCodeSDNode>(N->getOperand(2)));
559 SDLoc DL(N);
560 SDNode *SetP =
561 CurDAG->getMachineNode(NVPTX::SETP_bf16x2rr, DL, MVT::i1, MVT::i1,
562 {N->getOperand(0), N->getOperand(1), PTXCmpMode});
563 ReplaceNode(N, SetP);
564 return true;
565}
566
567bool NVPTXDAGToDAGISel::tryUNPACK_VECTOR(SDNode *N) {
568 SDValue Vector = N->getOperand(0);
569 MVT EltVT = N->getSimpleValueType(0);
570
571 MachineSDNode *N2 =
572 CurDAG->getMachineNode(NVPTX::I64toV2I32, SDLoc(N), EltVT, EltVT, Vector);
573
574 ReplaceNode(N, N2);
575 return true;
576}
577
578// Find all instances of extract_vector_elt that use this v2f16 vector
579// and coalesce them into a scattering move instruction.
580bool NVPTXDAGToDAGISel::tryEXTRACT_VECTOR_ELEMENT(SDNode *N) {
581 SDValue Vector = N->getOperand(0);
582
583 MVT VT = Vector.getSimpleValueType();
584 if (!(NVPTX::isPackedVectorTy(VT) && VT.getVectorNumElements() == 2))
585 return false;
586
587 unsigned Opcode;
588 if (VT.is32BitVector())
589 Opcode = NVPTX::I32toV2I16;
590 else if (VT.is64BitVector())
591 Opcode = NVPTX::I64toV2I32;
592 else
593 llvm_unreachable("Unhandled packed type");
594
595 // Find and record all uses of this vector that extract element 0 or 1.
597 for (auto *U : Vector.getNode()->users()) {
598 if (U->getOpcode() != ISD::EXTRACT_VECTOR_ELT)
599 continue;
600 if (U->getOperand(0) != Vector)
601 continue;
602 if (const ConstantSDNode *IdxConst =
603 dyn_cast<ConstantSDNode>(U->getOperand(1))) {
604 if (IdxConst->getZExtValue() == 0)
605 E0.push_back(U);
606 else if (IdxConst->getZExtValue() == 1)
607 E1.push_back(U);
608 else
609 llvm_unreachable("Invalid vector index.");
610 }
611 }
612
613 // There's no point scattering f16x2 if we only ever access one
614 // element of it.
615 if (E0.empty() || E1.empty())
616 return false;
617
618 // Merge (EltTy extractelt(V, 0), EltTy extractelt(V,1))
619 // into EltTy,EltTy Split[EltTy]x2(V)
620 MVT EltVT = VT.getVectorElementType();
621 SDNode *ScatterOp =
622 CurDAG->getMachineNode(Opcode, SDLoc(N), EltVT, EltVT, Vector);
623 for (auto *Node : E0)
624 ReplaceUses(SDValue(Node, 0), SDValue(ScatterOp, 0));
625 for (auto *Node : E1)
626 ReplaceUses(SDValue(Node, 0), SDValue(ScatterOp, 1));
627
628 return true;
629}
630
631NVPTX::AddressSpace NVPTXDAGToDAGISel::getAddrSpace(const MemSDNode *N) {
632 auto AS =
633 static_cast<NVPTX::AddressSpace>(N->getMemOperand()->getAddrSpace());
634 switch (AS) {
643 return AS;
644 }
645 llvm_unreachable("Unexpected address space");
646}
647
648NVPTX::Ordering NVPTXDAGToDAGISel::getMemOrder(const MemSDNode *N) const {
649 // No "sem" orderings for SM/PTX versions which do not support memory ordering
650 if (!Subtarget->hasMemoryOrdering())
652 auto Ordering = N->getMergedOrdering();
653 switch (Ordering) {
667 }
668 llvm_unreachable("Invalid atomic ordering");
669}
670
671// Clusters contain exactly 1 block on targets without cluster support.
673 if (S == NVPTX::Scope::Cluster && !T->hasClusters())
674 return NVPTX::Scope::Block;
675 return S;
676}
677
678NVPTX::Scope NVPTXDAGToDAGISel::getAtomicScope(const MemSDNode *N) const {
679 NVPTX::Scope Scope = resolveScope(Scopes[N->getSyncScopeID()], Subtarget);
680 if (!Subtarget->hasAtomScope()) {
681 if (Scope == NVPTX::Scope::System)
682 CurDAG->getContext()->diagnose(DiagnosticInfoUnsupported(
683 CurDAG->getMachineFunction().getFunction(),
684 "NVPTX system scope atomics require sm_60 or later",
685 N->getDebugLoc()));
687 }
688 return Scope;
689}
690
691namespace {
692
693struct OperationOrderings {
694 NVPTX::Ordering InstructionOrdering, FenceOrdering;
695 OperationOrderings(NVPTX::Ordering IO = NVPTX::Ordering::NotAtomic,
696 NVPTX::Ordering FO = NVPTX::Ordering::NotAtomic)
697 : InstructionOrdering(IO), FenceOrdering(FO) {}
698};
699
700static OperationOrderings
701getOperationOrderings(MemSDNode *N, const NVPTXSubtarget *Subtarget) {
702 AtomicOrdering Ordering = N->getSuccessOrdering();
703 auto CodeAddrSpace = NVPTXDAGToDAGISel::getAddrSpace(N);
704
705 bool HasMemoryOrdering = Subtarget->hasMemoryOrdering();
706 bool HasRelaxedMMIO = Subtarget->hasRelaxedMMIO();
707 bool IsSupportedLocalVolatile = CodeAddrSpace == NVPTX::AddressSpace::Local &&
708 Subtarget->hasFeature(NVPTX::PTX91) &&
709 N->isVolatile() &&
713
714 // clang-format off
715
716 // Lowering for Load/Store Operations (note: AcquireRelease Loads or Stores error).
717 // Note: uses of Relaxed in the Atomic column of this table refer
718 // to LLVM AtomicOrdering::Monotonic.
719 //
720 // | Atomic | Volatile | Statespace | PTX sm_60- | PTX sm_70+ |
721 // |---------|----------|--------------------|------------|------------------------------|
722 // | No | No | All | plain | .weak |
723 // | No | Yes | Generic,Shared, | .volatile | .volatile |
724 // | | | Global [0] | | |
725 // | No | Yes | Local (PTX 9.0-) | plain [1] | .weak [1] |
726 // | No | Yes | Local (PTX 9.1+) | .volatile | .volatile |
727 // | No | Yes | Const,Param | plain [1] | .weak [1] |
728 // | Unorder | Yes/No | All | == Relaxed | == Relaxed |
729 // | Relaxed | No | Generic,Shared, | .volatile | <atomic sem> |
730 // | | | Global [0] | | |
731 // | Other | No | Generic,Shared, | Error [2] | <atomic sem> |
732 // | | | Global [0] | | |
733 // | Yes | No | Local,Const,Param | plain [1] | .weak [1] |
734 // | Relaxed | Yes | Generic,Shared [0] | .volatile | .volatile |
735 // | Relaxed | Yes | Global [0] | .volatile | .mmio.relaxed.sys (PTX 8.2+) |
736 // | | | | | or .volatile (PTX 8.1-) |
737 // | Relaxed | Yes | Local (PTX 9.0-) | plain [1] | .weak [1] |
738 // | Relaxed | Yes | Local (PTX 9.1+) | .volatile | .volatile |
739 // | Relaxed | Yes | Const,Param | plain [1] | .weak [1] |
740 // | Other | Yes | Generic, Shared, | Error [2] | <atomic sem> [3] |
741 // | | | / Global [0] | | |
742
743 // Lowering of CUDA C++ SequentiallyConsistent Operations and Fences to PTX
744 // by following the ABI proven sound in:
745 // Lustig et al, A Formal Analysis of the NVIDIA PTX Memory Consistency Model, ASPLOS’19.
746 // https://dl.acm.org/doi/pdf/10.1145/3297858.3304043
747 //
748 // | CUDA C++ Atomic Operation or Atomic Fence | PTX Atomic Operation or Fence |
749 // |------------------------------------------------------|-------------------------------|
750 // | cuda::atomic_thread_fence | fence.sc.<scope>; |
751 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | |
752 // |------------------------------------------------------|-------------------------------|
753 // | cuda::atomic_load | fence.sc.<scope>; |
754 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | ld.acquire.<scope>; |
755 // |------------------------------------------------------|-------------------------------|
756 // | cuda::atomic_store | fence.sc.<scope>; |
757 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | st.release.<scope>; |
758 // |------------------------------------------------------|-------------------------------|
759 // | cuda::atomic_fetch_<op> | fence.sc.<scope>; |
760 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | atom.acq_rel.<scope>; |
761
762 // clang-format on
763
764 // [0]: volatile and atomics are only supported on global or shared
765 // memory locations, accessed via generic/shared/global pointers.
766 // PTX 9.1 adds volatile support on local ld/st.
767 // MMIO is only supported on global memory locations,
768 // accessed via generic/global pointers.
769 // TODO: Implement MMIO access via generic pointer to global.
770 // Currently implemented for global pointers only.
771
772 // [1]: Lowering volatile/atomic operations to non-volatile/non-atomic
773 // PTX instructions fails to preserve their C++ side-effects.
774 //
775 // Example (https://github.com/llvm/llvm-project/issues/62057):
776 //
777 // void example() {
778 // std::atomic<bool> True = true;
779 // while (True.load(std::memory_order_relaxed));
780 // }
781 //
782 // A C++ program that calls "example" is well-defined: the infinite loop
783 // performs an atomic operation. By lowering volatile/atomics to
784 // "weak" memory operations, we are transforming the above into:
785 //
786 // void undefined_behavior() {
787 // bool True = true;
788 // while (True);
789 // }
790 //
791 // which exhibits undefined behavior in both C++ and PTX.
792 //
793 // Calling "example" in CUDA C++ compiled for sm_60- exhibits undefined
794 // behavior due to lack of Independent Forward Progress. Lowering these
795 // to weak memory operations in sm_60- is therefore fine.
796 //
797 // TODO: Where direct volatile or atomic operations are unsupported,
798 // preserve the side-effect using the weak memory instruction and
799 // another instruction, such as a dead dummy volatile load.
800
801 if ((CodeAddrSpace == NVPTX::AddressSpace::Local &&
802 !IsSupportedLocalVolatile) ||
803 CodeAddrSpace == NVPTX::AddressSpace::Const ||
804 CodeAddrSpace == NVPTX::AddressSpace::EntryParam ||
805 CodeAddrSpace == NVPTX::AddressSpace::DeviceParam) {
807 }
808
809 // [2]: Atomics with Ordering different than Unordered or Relaxed are not
810 // supported on sm_60 and older; this includes volatile atomics.
811 if (!(Ordering == AtomicOrdering::NotAtomic ||
812 Ordering == AtomicOrdering::Unordered ||
813 Ordering == AtomicOrdering::Monotonic) &&
814 !HasMemoryOrdering) {
816 formatv("PTX does not support \"atomic\" for orderings different than"
817 "\"NotAtomic\" or \"Monotonic\" for sm_60 or older, but order "
818 "is: \"{}\".",
819 toIRString(Ordering)));
820 }
821
822 // [3]: TODO: these should eventually use .mmio<.atomic sem>; for now we drop
823 // the volatile semantics and preserve the atomic ones.
824
825 // PTX atomics are not available outside generic, global, or shared memory.
826 // PTX volatile operations additionally support local memory in PTX 9.1+.
827 bool AddrSupportsVolatileOrAtomic =
828 (IsSupportedLocalVolatile ||
829 CodeAddrSpace == NVPTX::AddressSpace::Generic ||
830 CodeAddrSpace == NVPTX::AddressSpace::Global ||
831 CodeAddrSpace == NVPTX::AddressSpace::Shared ||
832 CodeAddrSpace == NVPTX::AddressSpace::SharedCluster);
833 if (!AddrSupportsVolatileOrAtomic)
835
836 bool UseRelaxedMMIO =
837 HasRelaxedMMIO && CodeAddrSpace == NVPTX::AddressSpace::Global;
838
839 switch (Ordering) {
841 return N->isVolatile() ? NVPTX::Ordering::Volatile
844 // We lower unordered in the exact same way as 'monotonic' to respect
845 // LLVM IR atomicity requirements.
847 if (N->isVolatile())
848 return UseRelaxedMMIO ? NVPTX::Ordering::RelaxedMMIO
850 else
851 return HasMemoryOrdering ? NVPTX::Ordering::Relaxed
853 // case AtomicOrdering::Consume: // If LLVM ever provides this, lower it to
854 // Acquire.
856 if (!N->readMem())
858 formatv("PTX only supports Acquire Ordering on reads: {}",
859 N->getOperationName()));
862 if (!N->writeMem())
864 formatv("PTX only supports Release Ordering on writes: {}",
865 N->getOperationName()));
869 formatv("NVPTX does not support AcquireRelease Ordering on "
870 "read-modify-write "
871 "yet and PTX does not support it on loads or stores: {}",
872 N->getOperationName()));
873 }
875 // LLVM-IR SequentiallyConsistent atomics map to a two-instruction PTX
876 // sequence including a "fence.sc.sco" and the memory instruction with an
877 // Ordering that differs from "sc": acq, rel, or acq_rel, depending on
878 // whether the memory operation is a read, write, or read-modify-write.
879 //
880 // This sets the ordering of the fence to SequentiallyConsistent, and
881 // sets the corresponding ordering for the instruction.
882 NVPTX::Ordering InstrOrder;
883 if (N->readMem())
884 InstrOrder = NVPTX::Ordering::Acquire;
885 else if (N->writeMem())
886 InstrOrder = NVPTX::Ordering::Release;
887 else
889 formatv("NVPTX does not support SequentiallyConsistent Ordering on "
890 "read-modify-writes yet: {}",
891 N->getOperationName()));
892 return OperationOrderings(InstrOrder,
894 }
895 }
897 formatv("NVPTX backend does not support AtomicOrdering \"{}\" yet.",
898 toIRString(Ordering)));
899}
900
901} // namespace
902
903NVPTX::Scope NVPTXDAGToDAGISel::getOperationScope(MemSDNode *N,
904 NVPTX::Ordering O) const {
905 switch (O) {
907 case NVPTX::Ordering::Volatile: // Non-atomic volatile operations
908 // NVPTX uses Thread scope as the scope of non-atomic operations.
911 // RelaxedMMIO operations are always system scope.
912 // If a RelaxedMMIO order was generated from an atomic volatile operation
913 // with a smaller thread scope, we bump it here to system scope.
920 auto S = Scopes[N->getSyncScopeID()];
921
922 S = resolveScope(S, Subtarget);
923
924 // If operation is volatile, then its scope is system.
925 return N->isVolatile() ? NVPTX::Scope::System : S;
926 }
927 llvm_unreachable("unhandled ordering");
928}
929
930static bool canLowerToLDG(const MemSDNode &N, const NVPTXSubtarget &Subtarget,
931 NVPTX::AddressSpace CodeAddrSpace) {
932 // We use ldg (i.e. ld.global.nc) for invariant loads from the global address
933 // space.
934 return Subtarget.hasLDG() && CodeAddrSpace == NVPTX::AddressSpace::Global &&
935 N.isInvariant();
936}
937
938static unsigned int getFenceOp(NVPTX::Ordering O, NVPTX::Scope S,
939 NVPTXSubtarget const *T) {
940 S = resolveScope(S, T);
941
942 // Fall back to .acq_rel if .acquire, .release is not supported.
943 if (!T->hasSplitAcquireAndReleaseFences() &&
946
947 switch (O) {
949 switch (S) {
951 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acquire_sys
952 : NVPTX::INT_MEMBAR_SYS;
954 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acquire_cta
955 : NVPTX::INT_MEMBAR_CTA;
957 return NVPTX::atomic_thread_fence_acquire_cluster;
959 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acquire_gpu
960 : NVPTX::INT_MEMBAR_GL;
964 formatv("Unsupported scope \"{}\" for acquire/release/acq_rel fence.",
965 ScopeToString(S)));
966 }
967 break;
969 switch (S) {
971 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_release_sys
972 : NVPTX::INT_MEMBAR_SYS;
974 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_release_cta
975 : NVPTX::INT_MEMBAR_CTA;
977 return NVPTX::atomic_thread_fence_release_cluster;
979 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_release_gpu
980 : NVPTX::INT_MEMBAR_GL;
984 formatv("Unsupported scope \"{}\" for acquire/release/acq_rel fence.",
985 ScopeToString(S)));
986 }
987 break;
989 switch (S) {
991 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acq_rel_sys
992 : NVPTX::INT_MEMBAR_SYS;
994 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acq_rel_cta
995 : NVPTX::INT_MEMBAR_CTA;
997 return NVPTX::atomic_thread_fence_acq_rel_cluster;
999 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acq_rel_gpu
1000 : NVPTX::INT_MEMBAR_GL;
1004 formatv("Unsupported scope \"{}\" for acquire/release/acq_rel fence.",
1005 ScopeToString(S)));
1006 }
1007 break;
1008 }
1010 switch (S) {
1012 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_seq_cst_sys
1013 : NVPTX::INT_MEMBAR_SYS;
1015 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_seq_cst_cta
1016 : NVPTX::INT_MEMBAR_CTA;
1018 return NVPTX::atomic_thread_fence_seq_cst_cluster;
1020 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_seq_cst_gpu
1021 : NVPTX::INT_MEMBAR_GL;
1024 report_fatal_error(formatv("Unsupported scope \"{}\" for seq_cst fence.",
1025 ScopeToString(S)));
1026 }
1027 break;
1028 }
1034 formatv("Unsupported \"{}\" ordering and \"{}\" scope for fence.",
1035 OrderingToString(O), ScopeToString(S)));
1036 }
1037 llvm_unreachable("unhandled ordering");
1038}
1039
1040// Returns Memory Order and Scope of a memory instruction, and
1041// inserts any fence before the instruction that's required to
1042// implement its memory ordering.
1043std::pair<NVPTX::Ordering, NVPTX::Scope>
1044NVPTXDAGToDAGISel::insertMemoryInstructionFence(SDLoc DL, SDValue &Chain,
1045 MemSDNode *N) {
1046 auto [InstructionOrdering, FenceOrdering] =
1047 getOperationOrderings(N, Subtarget);
1048 auto Scope = getOperationScope(N, InstructionOrdering);
1049
1050 // Singlethread scope has no inter-thread synchronization requirements, so
1051 // the atomic operation is lowered as plain and the fence is skipped.
1052 // NotAtomic and Volatile operations naturally have Thread scope and must
1053 // preserve their ordering.
1054 if (Scope == NVPTX::Scope::Thread &&
1058
1059 // If a fence is required before the operation, insert it:
1060 switch (NVPTX::Ordering(FenceOrdering)) {
1062 break;
1064 auto Op = getFenceOp(FenceOrdering, Scope, Subtarget);
1065 Chain = SDValue(CurDAG->getMachineNode(Op, DL, MVT::Other, Chain), 0);
1066 break;
1067 }
1068 default:
1070 formatv("Unexpected fence ordering: \"{}\".",
1071 OrderingToString(NVPTX::Ordering(FenceOrdering))));
1072 }
1073 return {InstructionOrdering, Scope};
1074}
1075
1076// Helper function template to reduce amount of boilerplate code for
1077// opcode selection.
1078static std::optional<unsigned>
1079pickOpcodeForVT(MVT::SimpleValueType VT, std::optional<unsigned> Opcode_i16,
1080 std::optional<unsigned> Opcode_i32,
1081 std::optional<unsigned> Opcode_i64) {
1082 switch (VT) {
1083 case MVT::f16:
1084 case MVT::i16:
1085 case MVT::bf16:
1086 return Opcode_i16;
1087 case MVT::v2f16:
1088 case MVT::v2bf16:
1089 case MVT::v2i16:
1090 case MVT::v4i8:
1091 case MVT::i32:
1092 case MVT::f32:
1093 return Opcode_i32;
1094 case MVT::v2f32:
1095 case MVT::v2i32:
1096 case MVT::i64:
1097 case MVT::f64:
1098 return Opcode_i64;
1099 default:
1100 return std::nullopt;
1101 }
1102}
1103
1104static inline bool isAddLike(const SDValue V) {
1105 return V.getOpcode() == ISD::ADD ||
1106 (V->getOpcode() == ISD::OR && V->getFlags().hasDisjoint());
1107}
1108
1110 if (N.getOpcode() == ISD::AssertAlign)
1111 N = N.getOperand(0);
1112 return N;
1113}
1114
1115// selectBaseADDR - Match a dag node which will serve as the base address for an
1116// ADDR operand pair.
1118 N = stripAssertAlign(N);
1119 if (const auto *GA = dyn_cast<GlobalAddressSDNode>(N))
1120 return DAG->getTargetGlobalAddress(GA->getGlobal(), SDLoc(N),
1121 GA->getValueType(0), GA->getOffset(),
1122 GA->getTargetFlags());
1123 if (const auto *ES = dyn_cast<ExternalSymbolSDNode>(N))
1124 return DAG->getTargetExternalSymbol(ES->getSymbol(), ES->getValueType(0),
1125 ES->getTargetFlags());
1126 if (const auto *FIN = dyn_cast<FrameIndexSDNode>(N))
1127 return DAG->getTargetFrameIndex(FIN->getIndex(), FIN->getValueType(0));
1128 if (N.getOpcode() == NVPTXISD::Symbol)
1129 return N.getOperand(0);
1130
1131 return N;
1132}
1133
1135 Addr = stripAssertAlign(Addr);
1136 APInt AccumulatedOffset(64u, 0);
1137 while (isAddLike(Addr)) {
1138 const auto *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1));
1139 if (!CN)
1140 break;
1141
1142 const APInt CI = CN->getAPIntValue().sext(64);
1143 if (!(CI + AccumulatedOffset).isSignedIntN(32))
1144 break;
1145
1146 AccumulatedOffset += CI;
1147 Addr = stripAssertAlign(Addr->getOperand(0));
1148 }
1149 return DAG->getSignedTargetConstant(AccumulatedOffset.getSExtValue(), DL,
1150 MVT::i32);
1151}
1152
1153static std::pair<SDValue, SDValue> selectADDR(SDValue Addr, SelectionDAG *DAG) {
1154 SDValue Offset = accumulateOffset(Addr, SDLoc(Addr), DAG);
1155 SDValue Base = selectBaseADDR(Addr, DAG);
1156 return {Base, Offset};
1157}
1158
1159// Select a pair of operands which represent a valid PTX address, this could be
1160// one of the following things:
1161// - [var] - Offset is simply set to 0
1162// - [reg] - Offset is simply set to 0
1163// - [reg+immOff]
1164// - [var+immOff]
1165// Note that immOff must fit into a 32-bit signed integer.
1166bool NVPTXDAGToDAGISel::SelectADDR(SDValue Addr, SDValue &Base,
1167 SDValue &Offset) {
1168 std::tie(Base, Offset) = selectADDR(Addr, CurDAG);
1169 return true;
1170}
1171
1173 Ctx.diagnose(DiagnosticInfoGeneric(
1174 Twine("invalid NVPTX !mem.cache_hint metadata: ") + Msg, DS_Warning));
1175}
1176
1177static std::optional<NVPTX::L1Eviction> parseL1Eviction(StringRef Str) {
1179 .Case("normal", NVPTX::L1Eviction::Normal)
1180 .Case("unchanged", NVPTX::L1Eviction::Unchanged)
1183 .Case("no_allocate", NVPTX::L1Eviction::NoAllocate)
1184 .Default(std::nullopt);
1185}
1186
1187static std::optional<NVPTX::L2Eviction> parseL2Eviction(StringRef Str) {
1189 .Case("normal", NVPTX::L2Eviction::Normal)
1192 .Default(std::nullopt);
1193}
1194
1195static std::optional<NVPTX::L2Prefetch> parseL2Prefetch(StringRef Str) {
1197 .Case("64B", NVPTX::L2Prefetch::Bytes64)
1200 .Default(std::nullopt);
1201}
1202
1203template <typename T>
1204static std::optional<T> parseMemCacheHintStringValue(
1205 LLVMContext &Ctx, StringRef Key, const Metadata *Value,
1206 std::optional<T> (*Parse)(StringRef), bool EmitDiagnostics) {
1207 const auto *Val = dyn_cast<MDString>(Value);
1208 if (!Val) {
1209 if (EmitDiagnostics)
1211 Twine("'") + Key + "' expects a string value");
1212 return std::nullopt;
1213 }
1214
1215 StringRef ValStr = Val->getString();
1216 auto Parsed = Parse(ValStr);
1217 if (!Parsed && EmitDiagnostics)
1218 emitInvalidMemCacheHint(Ctx, Twine("unknown value '") + ValStr + "' for '" +
1219 Key + "'");
1220 return Parsed;
1221}
1222
1224 return AddrSpace == NVPTX::AddressSpace::Global ||
1225 AddrSpace == NVPTX::AddressSpace::Generic;
1226}
1227
1228static bool isLdOrSt(NVPTXMemCacheHintAccess Access) {
1229 return Access.Instruction == NVPTXMemCacheHintInstruction::Ld ||
1230 Access.Instruction == NVPTXMemCacheHintInstruction::St;
1231}
1232
1233static bool isL1EvictionSupported(const NVPTXSubtarget &Subtarget,
1234 NVPTX::L1Eviction Eviction,
1235 NVPTXMemCacheHintAccess Access) {
1236 if (Eviction == NVPTX::L1Eviction::Normal)
1237 return true;
1238
1239 return isLdOrSt(Access) && !Access.IsVolatile &&
1240 Subtarget.hasL1EvictionHint();
1241}
1242
1243static bool isL2PrefetchSupported(const NVPTXSubtarget &Subtarget,
1245 NVPTXMemCacheHintAccess Access) {
1246 switch (Prefetch) {
1248 return true;
1250 return Access.Instruction == NVPTXMemCacheHintInstruction::Ld &&
1251 isGlobalOrGeneric(Access.AddrSpace) && Subtarget.hasL2Prefetch64B();
1253 return Access.Instruction == NVPTXMemCacheHintInstruction::Ld &&
1254 isGlobalOrGeneric(Access.AddrSpace) && Subtarget.hasL2Prefetch128B();
1256 return Access.Instruction == NVPTXMemCacheHintInstruction::Ld &&
1257 isGlobalOrGeneric(Access.AddrSpace) && Subtarget.hasL2Prefetch256B();
1258 }
1259 llvm_unreachable("Unexpected L2 prefetch hint");
1260}
1261
1262static bool isL2EvictionSupported(const NVPTXSubtarget &Subtarget,
1263 NVPTX::L2Eviction Eviction,
1264 NVPTXMemCacheHintAccess Access) {
1265 if (Eviction == NVPTX::L2Eviction::Normal)
1266 return true;
1267
1268 return isLdOrSt(Access) && !Access.IsVolatile &&
1269 Subtarget.hasL2EvictionHint() && isGlobalOrGeneric(Access.AddrSpace) &&
1270 ((Access.NumElts == 8 && Access.EltWidth == 32) ||
1271 (Access.NumElts == 4 && Access.EltWidth == 64));
1272}
1273
1274static bool isCachePolicySupported(const NVPTXSubtarget &Subtarget,
1275 NVPTXMemCacheHintAccess Access) {
1276 return !Access.IsVolatile && isGlobalOrGeneric(Access.AddrSpace) &&
1277 Subtarget.hasL2CacheHint();
1278}
1279
1280NVPTXMemCacheHintOperands NVPTXDAGToDAGISel::getMemCacheHintOperands(
1281 const MemSDNode *N, NVPTXMemCacheHintAccess Access, const SDLoc &DL,
1282 bool EmitDiagnostics) {
1283 LLVMContext &Ctx = *CurDAG->getContext();
1284 const MDNode *Node = N->getMemCacheHint();
1285 SDValue PolicyReg = CurDAG->getRegister(NVPTX::NoRegister, MVT::i64);
1286 if (!Node)
1287 return {getI32Imm(0, DL), PolicyReg};
1288 if (Node->getNumOperands() == 0) {
1289 if (EmitDiagnostics)
1290 emitInvalidMemCacheHint(Ctx, "empty hint node");
1291 return {getI32Imm(0, DL), PolicyReg};
1292 }
1293
1297 std::optional<uint64_t> CachePolicy;
1298
1299 for (unsigned I = 0; I + 1 < Node->getNumOperands(); I += 2) {
1300 const auto *Key = cast<MDString>(Node->getOperand(I));
1301 StringRef KeyStr = Key->getString();
1302 const Metadata *Value = Node->getOperand(I + 1).get();
1303
1304 if (KeyStr == "nvvm.l1_eviction") {
1305 auto ParsedL1 = parseMemCacheHintStringValue(
1306 Ctx, KeyStr, Value, parseL1Eviction, EmitDiagnostics);
1307 if (ParsedL1 && isL1EvictionSupported(*Subtarget, *ParsedL1, Access))
1308 L1 = *ParsedL1;
1309 continue;
1310 }
1311
1312 if (KeyStr == "nvvm.l2_eviction") {
1313 auto ParsedL2 = parseMemCacheHintStringValue(
1314 Ctx, KeyStr, Value, parseL2Eviction, EmitDiagnostics);
1315 if (ParsedL2 && isL2EvictionSupported(*Subtarget, *ParsedL2, Access))
1316 L2 = *ParsedL2;
1317 continue;
1318 }
1319
1320 if (KeyStr == "nvvm.l2_prefetch_size") {
1321 auto ParsedPrefetch = parseMemCacheHintStringValue(
1322 Ctx, KeyStr, Value, parseL2Prefetch, EmitDiagnostics);
1323 if (ParsedPrefetch &&
1324 isL2PrefetchSupported(*Subtarget, *ParsedPrefetch, Access))
1325 Prefetch = *ParsedPrefetch;
1326 continue;
1327 }
1328
1329 if (KeyStr == "nvvm.l2_cache_hint") {
1330 const auto *ValCI = mdconst::dyn_extract<ConstantInt>(Value);
1331 if (!ValCI) {
1332 if (EmitDiagnostics)
1334 Ctx, "'nvvm.l2_cache_hint' expects an integer value");
1335 } else if (isCachePolicySupported(*Subtarget, Access)) {
1336 CachePolicy = ValCI->getZExtValue();
1337 }
1338 continue;
1339 }
1340
1341 if (EmitDiagnostics)
1342 emitInvalidMemCacheHint(Ctx, Twine("unknown key '") + KeyStr + "'");
1343 }
1344
1345 unsigned EvictionAndPrefetchHint =
1347 if (CachePolicy) {
1348 SDValue PolicyConst = CurDAG->getTargetConstant(*CachePolicy, DL, MVT::i64);
1349 PolicyReg = SDValue(
1350 CurDAG->getMachineNode(NVPTX::MOV_B64_i, DL, MVT::i64, PolicyConst), 0);
1351 Bitfield::set<NVPTX::L2CacheHintBit>(EvictionAndPrefetchHint, true);
1352 }
1353
1354 return {getI32Imm(EvictionAndPrefetchHint, DL), PolicyReg};
1355}
1356
1357bool NVPTXDAGToDAGISel::tryLoad(SDNode *N) {
1359 assert(LD->readMem() && "Expected load");
1360
1361 // do not support pre/post inc/dec
1362 const LoadSDNode *PlainLoad = dyn_cast<LoadSDNode>(LD);
1363 if (PlainLoad && PlainLoad->isIndexed())
1364 return false;
1365
1366 // Address Space Setting
1367 const auto CodeAddrSpace = getAddrSpace(LD);
1368 if (canLowerToLDG(*LD, *Subtarget, CodeAddrSpace))
1369 return tryLDG(LD);
1370
1371 SDLoc DL(LD);
1372 SDValue Chain = N->getOperand(0);
1373 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, LD);
1374
1375 const unsigned FromTypeWidth = LD->getMemoryVT().getSizeInBits();
1376
1377 // Vector Setting
1378 const unsigned FromType =
1379 (PlainLoad && (PlainLoad->getExtensionType() == ISD::SEXTLOAD))
1382
1383 uint32_t UsedBytesMask;
1384 switch (N->getOpcode()) {
1385 case ISD::LOAD:
1386 case ISD::ATOMIC_LOAD:
1387 UsedBytesMask = UINT32_MAX;
1388 break;
1389 case NVPTXISD::MLoad:
1390 UsedBytesMask = N->getConstantOperandVal(3);
1391 break;
1392 default:
1393 llvm_unreachable("Unexpected opcode");
1394 }
1395
1396 assert(isPowerOf2_32(FromTypeWidth) && FromTypeWidth >= 8 &&
1397 FromTypeWidth <= 128 && "Invalid width for load");
1398
1399 const auto [Base, Offset] = selectADDR(N->getOperand(1), CurDAG);
1400 const auto [EvictionAndPrefetchHint, PolicyReg] = getMemCacheHintOperands(
1401 LD,
1402 {NVPTXMemCacheHintInstruction::Ld, CodeAddrSpace,
1403 /*NumElts=*/1, /*EltWidth=*/FromTypeWidth, LD->isVolatile()},
1404 DL);
1405
1406 // Create the machine instruction DAG
1407 SDValue Ops[] = {getI32Imm(Ordering, DL),
1408 getI32Imm(Scope, DL),
1409 getI32Imm(CodeAddrSpace, DL),
1410 getI32Imm(FromType, DL),
1411 getI32Imm(FromTypeWidth, DL),
1412 getI32Imm(UsedBytesMask, DL),
1413 Base,
1414 Offset,
1415 EvictionAndPrefetchHint,
1416 PolicyReg,
1417 Chain};
1418
1419 const MVT::SimpleValueType TargetVT = LD->getSimpleValueType(0).SimpleTy;
1420 const std::optional<unsigned> Opcode =
1421 pickOpcodeForVT(TargetVT, NVPTX::LD_i16, NVPTX::LD_i32, NVPTX::LD_i64);
1422 if (!Opcode)
1423 return false;
1424
1425 SDNode *NVPTXLD = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1426 if (!NVPTXLD)
1427 return false;
1428
1429 MachineMemOperand *MemRef = LD->getMemOperand();
1430 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXLD), {MemRef});
1431
1432 ReplaceNode(LD, NVPTXLD);
1433 return true;
1434}
1435
1436static unsigned getStoreVectorNumElts(SDNode *N) {
1437 switch (N->getOpcode()) {
1438 case NVPTXISD::StoreV2:
1439 return 2;
1440 case NVPTXISD::StoreV4:
1441 return 4;
1442 case NVPTXISD::StoreV8:
1443 return 8;
1444 default:
1445 llvm_unreachable("Unexpected opcode");
1446 }
1447}
1448
1449bool NVPTXDAGToDAGISel::tryLoadVector(SDNode *N) {
1451
1452 // Address Space Setting
1453 const auto CodeAddrSpace = getAddrSpace(LD);
1454 if (canLowerToLDG(*LD, *Subtarget, CodeAddrSpace))
1455 return tryLDG(LD);
1456
1457 const MVT EltVT = LD->getSimpleValueType(0);
1458 SDLoc DL(LD);
1459 SDValue Chain = LD->getChain();
1460 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, LD);
1461
1462 // Type Setting: fromType + fromTypeWidth
1463 //
1464 // Sign : ISD::SEXTLOAD
1465 // Unsign : ISD::ZEXTLOAD, ISD::NON_EXTLOAD or ISD::EXTLOAD and the
1466 // type is integer
1467 // Float : ISD::NON_EXTLOAD or ISD::EXTLOAD and the type is float
1468 // Read at least 8 bits (predicates are stored as 8-bit values)
1469 // Get the original LoadSDNode::getExtensionType() value
1470 const unsigned ExtensionType = N->getConstantOperandVal(4);
1471 const unsigned FromType = (ExtensionType == ISD::SEXTLOAD)
1473 : NVPTX::PTXLdStInstCode::Untyped;
1474
1475 const unsigned FromTypeWidth = getFromTypeWidthForLoad(LD);
1476 const uint32_t UsedBytesMask = N->getConstantOperandVal(3);
1477
1478 assert(!(EltVT.isVector() && ExtensionType != ISD::NON_EXTLOAD));
1479
1480 const auto [EvictionAndPrefetchHint, PolicyReg] =
1481 getMemCacheHintOperands(LD,
1482 {NVPTXMemCacheHintInstruction::Ld, CodeAddrSpace,
1483 /*NumElts=*/LD->getNumValues() - 1,
1484 /*EltWidth=*/FromTypeWidth, LD->isVolatile()},
1485 DL);
1486 const auto [Base, Offset] = selectADDR(N->getOperand(1), CurDAG);
1487 SDValue Ops[] = {getI32Imm(Ordering, DL),
1488 getI32Imm(Scope, DL),
1489 getI32Imm(CodeAddrSpace, DL),
1490 getI32Imm(FromType, DL),
1491 getI32Imm(FromTypeWidth, DL),
1492 getI32Imm(UsedBytesMask, DL),
1493 Base,
1494 Offset,
1495 EvictionAndPrefetchHint,
1496 PolicyReg,
1497 Chain};
1498
1499 std::optional<unsigned> Opcode;
1500 switch (N->getOpcode()) {
1501 default:
1502 llvm_unreachable("Unexpected opcode");
1503 case NVPTXISD::LoadV2:
1504 Opcode = pickOpcodeForVT(EltVT.SimpleTy, NVPTX::LDV_i16_v2,
1505 NVPTX::LDV_i32_v2, NVPTX::LDV_i64_v2);
1506 break;
1507 case NVPTXISD::LoadV4:
1508 Opcode = pickOpcodeForVT(EltVT.SimpleTy, NVPTX::LDV_i16_v4,
1509 NVPTX::LDV_i32_v4, NVPTX::LDV_i64_v4);
1510 break;
1511 case NVPTXISD::LoadV8:
1512 Opcode = pickOpcodeForVT(EltVT.SimpleTy, {/* no v8i16 */},
1513 NVPTX::LDV_i32_v8, {/* no v8i64 */});
1514 break;
1515 }
1516 if (!Opcode)
1517 return false;
1518
1519 SDNode *NVPTXLD = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1520
1521 MachineMemOperand *MemRef = LD->getMemOperand();
1522 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXLD), {MemRef});
1523
1524 ReplaceNode(LD, NVPTXLD);
1525 return true;
1526}
1527
1528bool NVPTXDAGToDAGISel::tryLDG(MemSDNode *LD) {
1529 SDLoc DL(LD);
1530
1531 unsigned ExtensionType;
1532 uint32_t UsedBytesMask;
1533 if (const auto *Load = dyn_cast<LoadSDNode>(LD)) {
1534 ExtensionType = Load->getExtensionType();
1535 UsedBytesMask = UINT32_MAX;
1536 } else {
1537 ExtensionType = LD->getConstantOperandVal(4);
1538 UsedBytesMask = LD->getConstantOperandVal(3);
1539 }
1540 const unsigned FromType = (ExtensionType == ISD::SEXTLOAD)
1542 : NVPTX::PTXLdStInstCode::Untyped;
1543
1544 const unsigned FromTypeWidth = getFromTypeWidthForLoad(LD);
1545
1546 assert(!(LD->getSimpleValueType(0).isVector() &&
1547 ExtensionType != ISD::NON_EXTLOAD));
1548
1549 const auto [Base, Offset] = selectADDR(LD->getOperand(1), CurDAG);
1550 const auto [EvictionAndPrefetchHint, PolicyReg] = getMemCacheHintOperands(
1551 LD,
1552 {NVPTXMemCacheHintInstruction::Ld, NVPTX::AddressSpace::Global,
1553 LD->getNumValues() - 1, FromTypeWidth, LD->isVolatile()},
1554 DL);
1555 SDValue Ops[] = {getI32Imm(FromType, DL),
1556 getI32Imm(FromTypeWidth, DL),
1557 getI32Imm(UsedBytesMask, DL),
1558 Base,
1559 Offset,
1560 EvictionAndPrefetchHint,
1561 PolicyReg,
1562 LD->getChain()};
1563
1564 const MVT::SimpleValueType TargetVT = LD->getSimpleValueType(0).SimpleTy;
1565 std::optional<unsigned> Opcode;
1566 switch (LD->getOpcode()) {
1567 default:
1568 llvm_unreachable("Unexpected opcode");
1569 case ISD::LOAD:
1570 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LD_GLOBAL_NC_i16,
1571 NVPTX::LD_GLOBAL_NC_i32, NVPTX::LD_GLOBAL_NC_i64);
1572 break;
1573 case NVPTXISD::MLoad:
1574 Opcode = pickOpcodeForVT(TargetVT, std::nullopt, NVPTX::LD_GLOBAL_NC_i32,
1575 NVPTX::LD_GLOBAL_NC_i64);
1576 break;
1577 case NVPTXISD::LoadV2:
1578 Opcode =
1579 pickOpcodeForVT(TargetVT, NVPTX::LD_GLOBAL_NC_v2i16,
1580 NVPTX::LD_GLOBAL_NC_v2i32, NVPTX::LD_GLOBAL_NC_v2i64);
1581 break;
1582 case NVPTXISD::LoadV4:
1583 Opcode =
1584 pickOpcodeForVT(TargetVT, NVPTX::LD_GLOBAL_NC_v4i16,
1585 NVPTX::LD_GLOBAL_NC_v4i32, NVPTX::LD_GLOBAL_NC_v4i64);
1586 break;
1587 case NVPTXISD::LoadV8:
1588 Opcode = pickOpcodeForVT(TargetVT, {/* no v8i16 */},
1589 NVPTX::LD_GLOBAL_NC_v8i32, {/* no v8i64 */});
1590 break;
1591 }
1592 if (!Opcode)
1593 return false;
1594
1595 SDNode *NVPTXLDG = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1596
1597 ReplaceNode(LD, NVPTXLDG);
1598 return true;
1599}
1600
1601bool NVPTXDAGToDAGISel::tryLDU(SDNode *N) {
1602 auto *LD = cast<MemSDNode>(N);
1603
1604 SDLoc DL(N);
1605 const unsigned FromTypeWidth = getFromTypeWidthForLoad(LD);
1606 const MVT::SimpleValueType TargetVT = LD->getSimpleValueType(0).SimpleTy;
1607
1608 // If this is an LDU intrinsic, the address is the third operand. If its an
1609 // LDU SD node (from custom vector handling), then its the second operand
1610 SDValue Addr =
1611 LD->getOperand(LD->getOpcode() == ISD::INTRINSIC_W_CHAIN ? 2 : 1);
1612
1613 const auto [Base, Offset] = selectADDR(Addr, CurDAG);
1614 SDValue Ops[] = {getI32Imm(FromTypeWidth, DL), Base, Offset, LD->getChain()};
1615
1616 std::optional<unsigned> Opcode;
1617 switch (N->getOpcode()) {
1618 default:
1619 llvm_unreachable("Unexpected opcode");
1621 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LDU_GLOBAL_i16,
1622 NVPTX::LDU_GLOBAL_i32, NVPTX::LDU_GLOBAL_i64);
1623 break;
1624 case NVPTXISD::LDUV2:
1625 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LDU_GLOBAL_v2i16,
1626 NVPTX::LDU_GLOBAL_v2i32, NVPTX::LDU_GLOBAL_v2i64);
1627 break;
1628 case NVPTXISD::LDUV4:
1629 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LDU_GLOBAL_v4i16,
1630 NVPTX::LDU_GLOBAL_v4i32, {/* no v4i64 */});
1631 break;
1632 }
1633 if (!Opcode)
1634 return false;
1635
1636 SDNode *NVPTXLDU = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1637
1638 ReplaceNode(LD, NVPTXLDU);
1639 return true;
1640}
1641
1642bool NVPTXDAGToDAGISel::tryStore(SDNode *N) {
1644 assert(ST->writeMem() && "Expected store");
1645 StoreSDNode *PlainStore = dyn_cast<StoreSDNode>(ST);
1646 AtomicSDNode *AtomicStore = dyn_cast<AtomicSDNode>(ST);
1647 assert((PlainStore || AtomicStore) && "Expected store");
1648
1649 // do not support pre/post inc/dec
1650 if (PlainStore && PlainStore->isIndexed())
1651 return false;
1652
1653 // Address Space Setting
1654 const auto CodeAddrSpace = getAddrSpace(ST);
1655
1656 SDLoc DL(ST);
1657 SDValue Chain = ST->getChain();
1658 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, ST);
1659
1660 // Vector Setting
1661 const unsigned ToTypeWidth = ST->getMemoryVT().getSizeInBits();
1662
1663 // Create the machine instruction DAG
1664 SDValue Value = PlainStore ? PlainStore->getValue() : AtomicStore->getVal();
1665
1666 assert(isPowerOf2_32(ToTypeWidth) && ToTypeWidth >= 8 && ToTypeWidth <= 128 &&
1667 "Invalid width for store");
1668
1669 const auto [Base, Offset] = selectADDR(ST->getBasePtr(), CurDAG);
1670
1671 // Extract eviction/prefetch hint and cache policy register.
1672 const auto [EvictionAndPrefetchHint, PolicyReg] = getMemCacheHintOperands(
1673 ST,
1674 {NVPTXMemCacheHintInstruction::St, CodeAddrSpace,
1675 /*NumElts=*/1, /*EltWidth=*/ToTypeWidth, ST->isVolatile()},
1676 DL);
1677
1678 SDValue Ops[] = {selectPossiblyImm(Value),
1679 getI32Imm(Ordering, DL),
1680 getI32Imm(Scope, DL),
1681 getI32Imm(CodeAddrSpace, DL),
1682 getI32Imm(ToTypeWidth, DL),
1683 Base,
1684 Offset,
1685 EvictionAndPrefetchHint,
1686 PolicyReg,
1687 Chain};
1688
1689 const std::optional<unsigned> Opcode =
1690 pickOpcodeForVT(Value.getSimpleValueType().SimpleTy, NVPTX::ST_i16,
1691 NVPTX::ST_i32, NVPTX::ST_i64);
1692 if (!Opcode)
1693 return false;
1694
1695 SDNode *NVPTXST = CurDAG->getMachineNode(*Opcode, DL, MVT::Other, Ops);
1696
1697 if (!NVPTXST)
1698 return false;
1699
1700 MachineMemOperand *MemRef = ST->getMemOperand();
1701 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXST), {MemRef});
1702 ReplaceNode(ST, NVPTXST);
1703 return true;
1704}
1705
1706bool NVPTXDAGToDAGISel::tryStoreVector(SDNode *N) {
1708 const unsigned TotalWidth = ST->getMemoryVT().getSizeInBits();
1709
1710 // Address Space Setting
1711 const auto CodeAddrSpace = getAddrSpace(ST);
1712 if (CodeAddrSpace == NVPTX::AddressSpace::Const) {
1713 report_fatal_error("Cannot store to pointer that points to constant "
1714 "memory space");
1715 }
1716
1717 SDLoc DL(ST);
1718 SDValue Chain = ST->getChain();
1719 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, ST);
1720
1721 const unsigned NumElts = getStoreVectorNumElts(ST);
1722
1724 for (auto &V : ST->ops().slice(1, NumElts))
1725 Ops.push_back(selectPossiblyImm(V));
1726 SDValue Addr = N->getOperand(NumElts + 1);
1727 const unsigned ToTypeWidth = TotalWidth / NumElts;
1728
1729 assert(isPowerOf2_32(ToTypeWidth) && ToTypeWidth >= 8 && ToTypeWidth <= 128 &&
1730 TotalWidth <= 256 && "Invalid width for store");
1731
1732 // Extract eviction/prefetch hint and cache policy register.
1733 const auto [EvictionAndPrefetchHint, PolicyReg] = getMemCacheHintOperands(
1734 ST,
1735 {NVPTXMemCacheHintInstruction::St, CodeAddrSpace,
1736 /*NumElts=*/NumElts, /*EltWidth=*/ToTypeWidth, ST->isVolatile()},
1737 DL);
1738
1739 const auto [Base, Offset] = selectADDR(Addr, CurDAG);
1740 Ops.append({getI32Imm(Ordering, DL), getI32Imm(Scope, DL),
1741 getI32Imm(CodeAddrSpace, DL), getI32Imm(ToTypeWidth, DL), Base,
1742 Offset, EvictionAndPrefetchHint, PolicyReg, Chain});
1743
1744 const MVT::SimpleValueType EltVT =
1745 ST->getOperand(1).getSimpleValueType().SimpleTy;
1746 std::optional<unsigned> Opcode;
1747 switch (ST->getOpcode()) {
1748 default:
1749 return false;
1750 case NVPTXISD::StoreV2:
1751 Opcode = pickOpcodeForVT(EltVT, NVPTX::STV_i16_v2, NVPTX::STV_i32_v2,
1752 NVPTX::STV_i64_v2);
1753 break;
1754 case NVPTXISD::StoreV4:
1755 Opcode = pickOpcodeForVT(EltVT, NVPTX::STV_i16_v4, NVPTX::STV_i32_v4,
1756 NVPTX::STV_i64_v4);
1757 break;
1758 case NVPTXISD::StoreV8:
1759 Opcode = pickOpcodeForVT(EltVT, {/* no v8i16 */}, NVPTX::STV_i32_v8,
1760 {/* no v8i64 */});
1761 break;
1762 }
1763
1764 if (!Opcode)
1765 return false;
1766
1767 SDNode *NVPTXST = CurDAG->getMachineNode(*Opcode, DL, MVT::Other, Ops);
1768
1769 MachineMemOperand *MemRef = ST->getMemOperand();
1770 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXST), {MemRef});
1771
1772 ReplaceNode(ST, NVPTXST);
1773 return true;
1774}
1775
1776/// SelectBFE - Look for instruction sequences that can be made more efficient
1777/// by using the 'bfe' (bit-field extract) PTX instruction
1778bool NVPTXDAGToDAGISel::tryBFE(SDNode *N) {
1779 SDLoc DL(N);
1780 SDValue LHS = N->getOperand(0);
1781 SDValue RHS = N->getOperand(1);
1782 SDValue Len;
1783 SDValue Start;
1784 SDValue Val;
1785 bool IsSigned = false;
1786
1787 if (N->getOpcode() == ISD::AND) {
1788 // Canonicalize the operands
1789 // We want 'and %val, %mask'
1791 std::swap(LHS, RHS);
1792 }
1793
1795 if (!Mask) {
1796 // We need a constant mask on the RHS of the AND
1797 return false;
1798 }
1799
1800 // Extract the mask bits
1801 uint64_t MaskVal = Mask->getZExtValue();
1802 if (!isMask_64(MaskVal)) {
1803 // We *could* handle shifted masks here, but doing so would require an
1804 // 'and' operation to fix up the low-order bits so we would trade
1805 // shr+and for bfe+and, which has the same throughput
1806 return false;
1807 }
1808
1809 // How many bits are in our mask?
1810 int64_t NumBits = countr_one(MaskVal);
1811 Len = CurDAG->getTargetConstant(NumBits, DL, MVT::i32);
1812
1813 if (LHS.getOpcode() == ISD::SRL || LHS.getOpcode() == ISD::SRA) {
1814 // We have a 'srl/and' pair, extract the effective start bit and length
1815 Val = LHS.getNode()->getOperand(0);
1816 Start = LHS.getNode()->getOperand(1);
1817 ConstantSDNode *StartConst = dyn_cast<ConstantSDNode>(Start);
1818 if (StartConst) {
1819 uint64_t StartVal = StartConst->getZExtValue();
1820 // How many "good" bits do we have left? "good" is defined here as bits
1821 // that exist in the original value, not shifted in.
1822 int64_t GoodBits = Start.getValueSizeInBits() - StartVal;
1823 if (NumBits > GoodBits) {
1824 // Do not handle the case where bits have been shifted in. In theory
1825 // we could handle this, but the cost is likely higher than just
1826 // emitting the srl/and pair.
1827 return false;
1828 }
1829 Start = CurDAG->getTargetConstant(StartVal, DL, MVT::i32);
1830 } else {
1831 // Do not handle the case where the shift amount (can be zero if no srl
1832 // was found) is not constant. We could handle this case, but it would
1833 // require run-time logic that would be more expensive than just
1834 // emitting the srl/and pair.
1835 return false;
1836 }
1837 } else {
1838 // Do not handle the case where the LHS of the and is not a shift. While
1839 // it would be trivial to handle this case, it would just transform
1840 // 'and' -> 'bfe', but 'and' has higher-throughput.
1841 return false;
1842 }
1843 } else if (N->getOpcode() == ISD::SRL || N->getOpcode() == ISD::SRA) {
1844 if (LHS->getOpcode() == ISD::AND) {
1846 if (!ShiftCnst) {
1847 // Shift amount must be constant
1848 return false;
1849 }
1850
1851 uint64_t ShiftAmt = ShiftCnst->getZExtValue();
1852
1853 SDValue AndLHS = LHS->getOperand(0);
1854 SDValue AndRHS = LHS->getOperand(1);
1855
1856 // Canonicalize the AND to have the mask on the RHS
1857 if (isa<ConstantSDNode>(AndLHS)) {
1858 std::swap(AndLHS, AndRHS);
1859 }
1860
1861 ConstantSDNode *MaskCnst = dyn_cast<ConstantSDNode>(AndRHS);
1862 if (!MaskCnst) {
1863 // Mask must be constant
1864 return false;
1865 }
1866
1867 uint64_t MaskVal = MaskCnst->getZExtValue();
1868 uint64_t NumZeros;
1869 uint64_t NumBits;
1870 if (isMask_64(MaskVal)) {
1871 NumZeros = 0;
1872 // The number of bits in the result bitfield will be the number of
1873 // trailing ones (the AND) minus the number of bits we shift off
1874 NumBits = llvm::countr_one(MaskVal) - ShiftAmt;
1875 } else if (isShiftedMask_64(MaskVal)) {
1876 NumZeros = llvm::countr_zero(MaskVal);
1877 unsigned NumOnes = llvm::countr_one(MaskVal >> NumZeros);
1878 // The number of bits in the result bitfield will be the number of
1879 // trailing zeros plus the number of set bits in the mask minus the
1880 // number of bits we shift off
1881 NumBits = NumZeros + NumOnes - ShiftAmt;
1882 } else {
1883 // This is not a mask we can handle
1884 return false;
1885 }
1886
1887 if (ShiftAmt < NumZeros) {
1888 // Handling this case would require extra logic that would make this
1889 // transformation non-profitable
1890 return false;
1891 }
1892
1893 Val = AndLHS;
1894 Start = CurDAG->getTargetConstant(ShiftAmt, DL, MVT::i32);
1895 Len = CurDAG->getTargetConstant(NumBits, DL, MVT::i32);
1896
1897 // If pre-shift AND includes the sign bit in the bitfield, we must use
1898 // signed BFE to replicate that bit during bitfield extraction. If the
1899 // sign bit is not part of the mask, unsigned BFE will zero out upper bits
1900 // of the result
1901 if (N->getOpcode() == ISD::SRA)
1902 IsSigned = (ShiftAmt + NumBits) == Val.getValueSizeInBits();
1903 } else if (LHS->getOpcode() == ISD::SHL) {
1904 // Here, we have a pattern like:
1905 //
1906 // (sra (shl val, NN), MM)
1907 // or
1908 // (srl (shl val, NN), MM)
1909 //
1910 // If MM >= NN, we can efficiently optimize this with bfe
1911 Val = LHS->getOperand(0);
1912
1913 SDValue ShlRHS = LHS->getOperand(1);
1914 ConstantSDNode *ShlCnst = dyn_cast<ConstantSDNode>(ShlRHS);
1915 if (!ShlCnst) {
1916 // Shift amount must be constant
1917 return false;
1918 }
1919 uint64_t InnerShiftAmt = ShlCnst->getZExtValue();
1920
1921 SDValue ShrRHS = RHS;
1922 ConstantSDNode *ShrCnst = dyn_cast<ConstantSDNode>(ShrRHS);
1923 if (!ShrCnst) {
1924 // Shift amount must be constant
1925 return false;
1926 }
1927 uint64_t OuterShiftAmt = ShrCnst->getZExtValue();
1928
1929 // To avoid extra codegen and be profitable, we need Outer >= Inner
1930 if (OuterShiftAmt < InnerShiftAmt) {
1931 return false;
1932 }
1933
1934 // If the outer shift is more than the type size, we have no bitfield to
1935 // extract (since we also check that the inner shift is <= the outer shift
1936 // then this also implies that the inner shift is < the type size)
1937 if (OuterShiftAmt >= Val.getValueSizeInBits()) {
1938 return false;
1939 }
1940
1941 Start = CurDAG->getTargetConstant(OuterShiftAmt - InnerShiftAmt, DL,
1942 MVT::i32);
1943 Len = CurDAG->getTargetConstant(Val.getValueSizeInBits() - OuterShiftAmt,
1944 DL, MVT::i32);
1945
1946 if (N->getOpcode() == ISD::SRA) {
1947 // If we have a arithmetic right shift, we need to use the signed bfe
1948 // variant
1949 IsSigned = true;
1950 }
1951 } else {
1952 // No can do...
1953 return false;
1954 }
1955 } else {
1956 // No can do...
1957 return false;
1958 }
1959
1960
1961 unsigned Opc;
1962 // For the BFE operations we form here from "and" and "srl", always use the
1963 // unsigned variants.
1964 if (Val.getValueType() == MVT::i32) {
1965 if (IsSigned) {
1966 Opc = NVPTX::BFE_S32rii;
1967 } else {
1968 Opc = NVPTX::BFE_U32rii;
1969 }
1970 } else if (Val.getValueType() == MVT::i64) {
1971 if (IsSigned) {
1972 Opc = NVPTX::BFE_S64rii;
1973 } else {
1974 Opc = NVPTX::BFE_U64rii;
1975 }
1976 } else {
1977 // We cannot handle this type
1978 return false;
1979 }
1980
1981 SDValue Ops[] = {
1982 Val, Start, Len
1983 };
1984
1985 ReplaceNode(N, CurDAG->getMachineNode(Opc, DL, N->getVTList(), Ops));
1986 return true;
1987}
1988
1989// Select bf16/bf16v2 FADD, FSUB, FMUL as fma on targets with only fma
1990bool NVPTXDAGToDAGISel::tryBF16ArithToFMA(SDNode *N) {
1991 EVT VT = SDValue(N, 0).getValueType();
1992 if (VT.getScalarType() != MVT::bf16)
1993 return false;
1994
1995 const NVPTXSubtarget *STI = TM.getSubtargetImpl();
1996 if (STI->hasNativeBF16Support(N->getOpcode()))
1997 return false;
1998
1999 const bool IsVec = VT.isVector();
2000 assert(!IsVec || VT.getVectorNumElements() == 2);
2001 SDLoc DL(N);
2002 SDValue N0 = N->getOperand(0);
2003 SDValue N1 = N->getOperand(1);
2005 auto GetConstant = [&](float Value) -> SDValue {
2006 // BF16 immediates must be legalized to integer register values
2007 APFloat APF(Value);
2008 bool LosesInfo;
2009 APF.convert(APFloat::BFloat(), APFloat::rmNearestTiesToEven, &LosesInfo);
2010 assert(!LosesInfo);
2011 if (IsVec) {
2012 auto API = APF.bitcastToAPInt();
2013 API = API.concat(API);
2014 auto Const = CurDAG->getTargetConstant(API, DL, MVT::i32);
2015 return SDValue(CurDAG->getMachineNode(NVPTX::MOV_B32_i, DL, VT, Const),
2016 0);
2017 }
2018 auto Const = CurDAG->getTargetConstantFP(APF, DL, VT);
2019 return SDValue(CurDAG->getMachineNode(NVPTX::MOV_BF16_i, DL, VT, Const), 0);
2020 };
2021
2022 switch (N->getOpcode()) {
2023 case ISD::FADD:
2024 // add(a, b) -> fma(a, 1.0, b)
2025 Operands = {N0, GetConstant(1.0), N1};
2026 break;
2027 case ISD::FSUB:
2028 // sub(a, b) -> fma(b, -1.0, a)
2029 Operands = {N1, GetConstant(-1.0), N0};
2030 break;
2031 case ISD::FMUL:
2032 // mul(a, b) -> fma(a, b, -0.0)
2033 // NOTE: The identity is -0, not 0, because -0 + 0 == 0 for floats
2034 Operands = {N0, N1, GetConstant(-0.0)};
2035 break;
2036 default:
2037 llvm_unreachable("Unexpected opcode");
2038 };
2039
2040 int Opcode = IsVec ? NVPTX::FMA_BF16x2rrr : NVPTX::FMA_BF16rrr;
2041 MachineSDNode *FMA = CurDAG->getMachineNode(Opcode, DL, VT, Operands);
2042 ReplaceNode(N, FMA);
2043 return true;
2044}
2045
2046SDValue NVPTXDAGToDAGISel::selectPossiblyImm(SDValue V) {
2047 if (V.getOpcode() == ISD::BITCAST)
2048 V = V.getOperand(0);
2049
2050 if (auto *CN = dyn_cast<ConstantSDNode>(V))
2051 return CurDAG->getTargetConstant(CN->getAPIntValue(), SDLoc(V),
2052 V.getValueType());
2053 if (auto *CN = dyn_cast<ConstantFPSDNode>(V))
2054 return CurDAG->getTargetConstantFP(CN->getValueAPF(), SDLoc(V),
2055 V.getValueType());
2056 return V;
2057}
2058
2059/// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
2060/// inline asm expressions.
2061bool NVPTXDAGToDAGISel::SelectInlineAsmMemoryOperand(
2062 const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
2063 std::vector<SDValue> &OutOps) {
2064 switch (ConstraintID) {
2065 default:
2066 return true;
2067 case InlineAsm::ConstraintCode::m: { // memory
2068 const auto [Base, Offset] = selectADDR(Op, CurDAG);
2069 OutOps.push_back(Base);
2070 OutOps.push_back(Offset);
2071 return false;
2072 }
2073 }
2074 return true;
2075}
2076
2077void NVPTXDAGToDAGISel::SelectV2I64toI128(SDNode *N) {
2078 // Lower a CopyToReg with two 64-bit inputs
2079 // Dst:i128, lo:i64, hi:i64
2080 //
2081 // CopyToReg Dst, lo, hi;
2082 //
2083 // ==>
2084 //
2085 // tmp = V2I64toI128 {lo, hi};
2086 // CopyToReg Dst, tmp;
2087 SDValue Dst = N->getOperand(1);
2088 SDValue Lo = N->getOperand(2);
2089 SDValue Hi = N->getOperand(3);
2090
2091 SDLoc DL(N);
2092 SDNode *Mov =
2093 CurDAG->getMachineNode(NVPTX::V2I64toI128, DL, MVT::i128, {Lo, Hi});
2094
2095 SmallVector<SDValue, 4> NewOps(N->getNumOperands() - 1);
2096 NewOps[0] = N->getOperand(0);
2097 NewOps[1] = Dst;
2098 NewOps[2] = SDValue(Mov, 0);
2099 if (N->getNumOperands() == 5)
2100 NewOps[3] = N->getOperand(4);
2101 SDValue NewValue = CurDAG->getNode(ISD::CopyToReg, DL, SmallVector<EVT>(N->values()), NewOps);
2102
2103 ReplaceNode(N, NewValue.getNode());
2104}
2105
2106void NVPTXDAGToDAGISel::SelectI128toV2I64(SDNode *N) {
2107 // Lower CopyFromReg from a 128-bit regs to two 64-bit regs
2108 // Dst:i128, Src:i128
2109 //
2110 // {lo, hi} = CopyFromReg Src
2111 //
2112 // ==>
2113 //
2114 // {lo, hi} = I128toV2I64 Src
2115 //
2116 SDValue Ch = N->getOperand(0);
2117 SDValue Src = N->getOperand(1);
2118 SDValue Glue = N->getOperand(2);
2119 SDLoc DL(N);
2120
2121 // Add Glue and Ch to the operands and results to avoid break the execution
2122 // order
2123 SDNode *Mov = CurDAG->getMachineNode(
2124 NVPTX::I128toV2I64, DL,
2125 {MVT::i64, MVT::i64, Ch.getValueType(), Glue.getValueType()},
2126 {Src, Ch, Glue});
2127
2128 ReplaceNode(N, Mov);
2129}
2130
2131bool NVPTXDAGToDAGISel::tryFence(SDNode *N) {
2132 SDLoc DL(N);
2133 assert(N->getOpcode() == ISD::ATOMIC_FENCE);
2134 auto Scope = Scopes[N->getConstantOperandVal(2)];
2135
2136 // Singlethread fences have no inter-thread synchronization requirements.
2137 // Note: std::atomic_signal_fence lowers to singlethread LLVM IR fences;
2138 // this intentionally drops these before emitting PTX.
2139 if (Scope == NVPTX::Scope::Thread) {
2140 CurDAG->ReplaceAllUsesOfValueWith(SDValue(N, 0), N->getOperand(0));
2141 CurDAG->RemoveDeadNode(N);
2142 return true;
2143 }
2144
2145 unsigned int FenceOp = getFenceOp(
2146 NVPTX::Ordering(N->getConstantOperandVal(1)), Scope, Subtarget);
2147 SDValue Chain = N->getOperand(0);
2148 SDNode *FenceNode = CurDAG->getMachineNode(FenceOp, DL, MVT::Other, Chain);
2149 ReplaceNode(N, FenceNode);
2150 return true;
2151}
2152
2153NVPTXScopes::NVPTXScopes(LLVMContext &C, const Triple &T) : Context(&C) {
2154 auto ScopeID = [&](AtomicScope Scope) {
2155 return C.getOrInsertSyncScopeID(*getAtomicScopeIRString(T, Scope));
2156 };
2162}
2163
2164NVPTX::Scope NVPTXScopes::operator[](SyncScope::ID ID) const {
2165 if (Scopes.empty())
2166 llvm_unreachable("NVPTX Scopes must be initialized before calling "
2167 "NVPTXScopes::operator[]");
2168
2169 auto S = Scopes.find(ID);
2170 if (S == Scopes.end()) {
2171 auto scopeName = Context->getSyncScopeName(ID);
2172 assert(scopeName.has_value() && "Scope name must exist.");
2173
2174 // Build list of supported syncscopes programmatically
2175 SmallVector<StringRef> supportedScopes;
2176 for (const auto &Entry : Scopes) {
2177 if (auto name = Context->getSyncScopeName(Entry.first))
2178 supportedScopes.push_back(name->empty() ? "<empty string>" : *name);
2179 }
2180
2182 formatv("NVPTX backend does not support syncscope \"{0}\" (ID={1}).\n"
2183 "Supported syncscopes are: {2}.",
2184 scopeName.value(), int(ID),
2185 make_range(supportedScopes.begin(), supportedScopes.end())));
2186 }
2187 return S->second;
2188}
2189
2190bool NVPTXScopes::empty() const { return Scopes.size() == 0; }
2191
2192#define TCGEN05_ST_OPCODE(SHAPE, NUM) \
2193 (enableUnpack ? NVPTX::TCGEN05_ST_##SHAPE##_##NUM##_UNPACK \
2194 : NVPTX::TCGEN05_ST_##SHAPE##_##NUM)
2195
2196static unsigned getTcgen05StOpcode(unsigned IID, bool enableUnpack) {
2197 switch (IID) {
2198 case Intrinsic::nvvm_tcgen05_st_16x64b_x1:
2199 return TCGEN05_ST_OPCODE(16x64b, x1);
2200 case Intrinsic::nvvm_tcgen05_st_16x64b_x2:
2201 return TCGEN05_ST_OPCODE(16x64b, x2);
2202 case Intrinsic::nvvm_tcgen05_st_16x64b_x4:
2203 return TCGEN05_ST_OPCODE(16x64b, x4);
2204 case Intrinsic::nvvm_tcgen05_st_16x64b_x8:
2205 return TCGEN05_ST_OPCODE(16x64b, x8);
2206 case Intrinsic::nvvm_tcgen05_st_16x64b_x16:
2207 return TCGEN05_ST_OPCODE(16x64b, x16);
2208 case Intrinsic::nvvm_tcgen05_st_16x64b_x32:
2209 return TCGEN05_ST_OPCODE(16x64b, x32);
2210 case Intrinsic::nvvm_tcgen05_st_16x64b_x64:
2211 return TCGEN05_ST_OPCODE(16x64b, x64);
2212 case Intrinsic::nvvm_tcgen05_st_16x64b_x128:
2213 return TCGEN05_ST_OPCODE(16x64b, x128);
2214 case Intrinsic::nvvm_tcgen05_st_16x128b_x1:
2215 return TCGEN05_ST_OPCODE(16x128b, x1);
2216 case Intrinsic::nvvm_tcgen05_st_16x128b_x2:
2217 return TCGEN05_ST_OPCODE(16x128b, x2);
2218 case Intrinsic::nvvm_tcgen05_st_16x128b_x4:
2219 return TCGEN05_ST_OPCODE(16x128b, x4);
2220 case Intrinsic::nvvm_tcgen05_st_16x128b_x8:
2221 return TCGEN05_ST_OPCODE(16x128b, x8);
2222 case Intrinsic::nvvm_tcgen05_st_16x128b_x16:
2223 return TCGEN05_ST_OPCODE(16x128b, x16);
2224 case Intrinsic::nvvm_tcgen05_st_16x128b_x32:
2225 return TCGEN05_ST_OPCODE(16x128b, x32);
2226 case Intrinsic::nvvm_tcgen05_st_16x128b_x64:
2227 return TCGEN05_ST_OPCODE(16x128b, x64);
2228 case Intrinsic::nvvm_tcgen05_st_16x256b_x1:
2229 return TCGEN05_ST_OPCODE(16x256b, x1);
2230 case Intrinsic::nvvm_tcgen05_st_16x256b_x2:
2231 return TCGEN05_ST_OPCODE(16x256b, x2);
2232 case Intrinsic::nvvm_tcgen05_st_16x256b_x4:
2233 return TCGEN05_ST_OPCODE(16x256b, x4);
2234 case Intrinsic::nvvm_tcgen05_st_16x256b_x8:
2235 return TCGEN05_ST_OPCODE(16x256b, x8);
2236 case Intrinsic::nvvm_tcgen05_st_16x256b_x16:
2237 return TCGEN05_ST_OPCODE(16x256b, x16);
2238 case Intrinsic::nvvm_tcgen05_st_16x256b_x32:
2239 return TCGEN05_ST_OPCODE(16x256b, x32);
2240 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x1:
2241 return TCGEN05_ST_OPCODE(16x32bx2, x1);
2242 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x2:
2243 return TCGEN05_ST_OPCODE(16x32bx2, x2);
2244 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x4:
2245 return TCGEN05_ST_OPCODE(16x32bx2, x4);
2246 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x8:
2247 return TCGEN05_ST_OPCODE(16x32bx2, x8);
2248 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x16:
2249 return TCGEN05_ST_OPCODE(16x32bx2, x16);
2250 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x32:
2251 return TCGEN05_ST_OPCODE(16x32bx2, x32);
2252 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x64:
2253 return TCGEN05_ST_OPCODE(16x32bx2, x64);
2254 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x128:
2255 return TCGEN05_ST_OPCODE(16x32bx2, x128);
2256 case Intrinsic::nvvm_tcgen05_st_32x32b_x1:
2257 return TCGEN05_ST_OPCODE(32x32b, x1);
2258 case Intrinsic::nvvm_tcgen05_st_32x32b_x2:
2259 return TCGEN05_ST_OPCODE(32x32b, x2);
2260 case Intrinsic::nvvm_tcgen05_st_32x32b_x4:
2261 return TCGEN05_ST_OPCODE(32x32b, x4);
2262 case Intrinsic::nvvm_tcgen05_st_32x32b_x8:
2263 return TCGEN05_ST_OPCODE(32x32b, x8);
2264 case Intrinsic::nvvm_tcgen05_st_32x32b_x16:
2265 return TCGEN05_ST_OPCODE(32x32b, x16);
2266 case Intrinsic::nvvm_tcgen05_st_32x32b_x32:
2267 return TCGEN05_ST_OPCODE(32x32b, x32);
2268 case Intrinsic::nvvm_tcgen05_st_32x32b_x64:
2269 return TCGEN05_ST_OPCODE(32x32b, x64);
2270 case Intrinsic::nvvm_tcgen05_st_32x32b_x128:
2271 return TCGEN05_ST_OPCODE(32x32b, x128);
2272 }
2273 llvm_unreachable("unhandled tcgen05.st lowering");
2274}
2275
2276void NVPTXDAGToDAGISel::SelectTcgen05St(SDNode *N, bool hasOffset) {
2277 if (!Subtarget->hasTcgen05InstSupport())
2279 "tcgen05.st is not supported on this architecture variant");
2280
2281 SDLoc DL(N);
2282 unsigned IID = cast<ConstantSDNode>(N->getOperand(1))->getZExtValue();
2283
2285 N->getOperand(2) // taddr
2286 };
2287
2288 if (hasOffset)
2289 Operands.push_back(CurDAG->getTargetConstant(
2290 cast<ConstantSDNode>(N->getOperand(3))->getZExtValue(), DL,
2291 MVT::i32)); // Offset
2292
2293 for (unsigned I = hasOffset ? 4 : 3; I < (N->getNumOperands() - 1); I++)
2294 Operands.push_back(N->getOperand(I));
2295
2296 bool enableUnpack =
2297 cast<ConstantSDNode>(N->getOperand(N->getNumOperands() - 1))
2298 ->getZExtValue();
2299
2300 Operands.push_back(N->getOperand(0)); // Chain
2301 ReplaceNode(N, CurDAG->getMachineNode(getTcgen05StOpcode(IID, enableUnpack),
2302 DL, N->getVTList(), Operands));
2303}
2304
2305bool NVPTXDAGToDAGISel::tryIntrinsicVoid(SDNode *N) {
2306 unsigned IID = N->getConstantOperandVal(1);
2307 switch (IID) {
2308 default:
2309 return false;
2310 case Intrinsic::nvvm_tcgen05_st_16x64b_x1:
2311 case Intrinsic::nvvm_tcgen05_st_16x64b_x2:
2312 case Intrinsic::nvvm_tcgen05_st_16x64b_x4:
2313 case Intrinsic::nvvm_tcgen05_st_16x64b_x8:
2314 case Intrinsic::nvvm_tcgen05_st_16x64b_x16:
2315 case Intrinsic::nvvm_tcgen05_st_16x64b_x32:
2316 case Intrinsic::nvvm_tcgen05_st_16x64b_x64:
2317 case Intrinsic::nvvm_tcgen05_st_16x64b_x128:
2318 case Intrinsic::nvvm_tcgen05_st_32x32b_x1:
2319 case Intrinsic::nvvm_tcgen05_st_32x32b_x2:
2320 case Intrinsic::nvvm_tcgen05_st_32x32b_x4:
2321 case Intrinsic::nvvm_tcgen05_st_32x32b_x8:
2322 case Intrinsic::nvvm_tcgen05_st_32x32b_x16:
2323 case Intrinsic::nvvm_tcgen05_st_32x32b_x32:
2324 case Intrinsic::nvvm_tcgen05_st_32x32b_x64:
2325 case Intrinsic::nvvm_tcgen05_st_32x32b_x128:
2326 case Intrinsic::nvvm_tcgen05_st_16x128b_x1:
2327 case Intrinsic::nvvm_tcgen05_st_16x128b_x2:
2328 case Intrinsic::nvvm_tcgen05_st_16x128b_x4:
2329 case Intrinsic::nvvm_tcgen05_st_16x128b_x8:
2330 case Intrinsic::nvvm_tcgen05_st_16x128b_x16:
2331 case Intrinsic::nvvm_tcgen05_st_16x128b_x32:
2332 case Intrinsic::nvvm_tcgen05_st_16x128b_x64:
2333 case Intrinsic::nvvm_tcgen05_st_16x256b_x1:
2334 case Intrinsic::nvvm_tcgen05_st_16x256b_x2:
2335 case Intrinsic::nvvm_tcgen05_st_16x256b_x4:
2336 case Intrinsic::nvvm_tcgen05_st_16x256b_x8:
2337 case Intrinsic::nvvm_tcgen05_st_16x256b_x16:
2338 case Intrinsic::nvvm_tcgen05_st_16x256b_x32: {
2339 SelectTcgen05St(N);
2340 return true;
2341 }
2342
2343 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x1:
2344 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x2:
2345 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x4:
2346 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x8:
2347 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x16:
2348 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x32:
2349 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x64:
2350 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x128: {
2351 SelectTcgen05St(N, /* hasOffset */ true);
2352 return true;
2353 }
2354 }
2355}
2356
2357void NVPTXDAGToDAGISel::selectAtomicSwap128(SDNode *N) {
2358 MemSDNode *AN = cast<MemSDNode>(N);
2359 SDLoc dl(N);
2360
2361 const SDValue Chain = N->getOperand(0);
2362 const auto [Base, Offset] = selectADDR(N->getOperand(1), CurDAG);
2364 Ops.append(N->op_begin() + 2, N->op_end());
2365 Ops.append({getI32Imm(getMemOrder(AN), dl), getI32Imm(getAtomicScope(AN), dl),
2366 getI32Imm(getAddrSpace(AN), dl)});
2367
2368 if (N->getOpcode() == NVPTXISD::ATOMIC_SWAP_B128) {
2369 unsigned EltWidth = AN->getMemoryVT().getFixedSizeInBits();
2370 NVPTXMemCacheHintAccess Access{NVPTXMemCacheHintInstruction::Atom,
2371 getAddrSpace(AN),
2372 /*NumElts=*/1, EltWidth, AN->isVolatile()};
2373 const auto [EvictionAndPrefetchHint, CachePolicyReg] =
2374 getMemCacheHintOperands(AN, Access, dl);
2375 Ops.push_back(EvictionAndPrefetchHint);
2376 Ops.push_back(CachePolicyReg);
2377 }
2378
2379 Ops.push_back(Chain);
2380
2381 assert(N->getOpcode() == NVPTXISD::ATOMIC_CMP_SWAP_B128 ||
2382 N->getOpcode() == NVPTXISD::ATOMIC_SWAP_B128);
2383 unsigned Opcode = N->getOpcode() == NVPTXISD::ATOMIC_SWAP_B128
2384 ? NVPTX::ATOM_EXCH_B128
2385 : NVPTX::ATOM_CAS_B128;
2386
2387 auto *ATOM = CurDAG->getMachineNode(Opcode, dl, N->getVTList(), Ops);
2388 CurDAG->setNodeMemRefs(ATOM, AN->getMemOperand());
2389
2390 ReplaceNode(N, ATOM);
2391}
return SDValue()
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned Imm
unsigned uint64_t
AMDGPU Register Bank Select
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< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
DXIL Resource Access
#define DEBUG_TYPE
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
loop data Loop Data Prefetch
#define I(x, y, z)
Definition MD5.cpp:57
This file implements a map that provides insertion order iteration.
This file contains the declarations for metadata subclasses.
#define T
static NVPTX::Scope resolveScope(NVPTX::Scope S, const NVPTXSubtarget *T)
static unsigned getStoreVectorNumElts(SDNode *N)
static bool isAddLike(const SDValue V)
static std::optional< NVPTX::L2Eviction > parseL2Eviction(StringRef Str)
static SDValue selectBaseADDR(SDValue N, SelectionDAG *DAG)
static std::optional< NVPTX::L2Prefetch > parseL2Prefetch(StringRef Str)
static std::optional< NVPTX::L1Eviction > parseL1Eviction(StringRef Str)
static SDValue accumulateOffset(SDValue &Addr, SDLoc DL, SelectionDAG *DAG)
static bool isGlobalOrGeneric(NVPTX::AddressSpace AddrSpace)
static bool isL2PrefetchSupported(const NVPTXSubtarget &Subtarget, NVPTX::L2Prefetch Prefetch, NVPTXMemCacheHintAccess Access)
static bool isLdOrSt(NVPTXMemCacheHintAccess Access)
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"))
#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 void emitInvalidMemCacheHint(LLVMContext &Ctx, const Twine &Msg)
static unsigned int getFenceOp(NVPTX::Ordering O, NVPTX::Scope S, NVPTXSubtarget const *T)
static std::optional< T > parseMemCacheHintStringValue(LLVMContext &Ctx, StringRef Key, const Metadata *Value, std::optional< T >(*Parse)(StringRef), bool EmitDiagnostics)
static bool isL2EvictionSupported(const NVPTXSubtarget &Subtarget, NVPTX::L2Eviction Eviction, NVPTXMemCacheHintAccess Access)
#define TCGEN05_ST_OPCODE(SHAPE, NUM)
static bool isL1EvictionSupported(const NVPTXSubtarget &Subtarget, NVPTX::L1Eviction Eviction, NVPTXMemCacheHintAccess Access)
static bool isCachePolicySupported(const NVPTXSubtarget &Subtarget, NVPTXMemCacheHintAccess Access)
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)
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
SI Fold Operands
const char * Msg
static const char * name
This file implements the StringSwitch template, which mimics a switch() statement whose cases are str...
#define PASS_NAME
Value * RHS
Value * LHS
static const fltSemantics & BFloat()
Definition APFloat.h:303
static constexpr roundingMode rmNearestTiesToEven
Definition APFloat.h:361
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:1030
int64_t getSExtValue() const
Get sign extended value.
Definition APInt.h:1583
This is an SDNode representing atomic operations.
const SDValue & getVal() const
uint64_t getZExtValue() const
Diagnostic information for unsupported feature in backend.
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:356
Record instruction ordering so we can query their relative positions within a function.
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.
This class is used to represent ISD::LOAD nodes.
ISD::LoadExtType getExtensionType() const
Return whether this is a plain node, or one of the varieties of value-extending loads.
Metadata node.
Definition Metadata.h:1069
Machine Value Type.
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.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
Function & getFunction()
Return the LLVM function that this machine code represents.
const TargetMachine & getTarget() const
getTarget - Return the target machine this machine code is compiled with
A description of a memory reference used in the backend.
An SDNode that represents everything that will be needed to construct a MachineInstr.
This is an abstract virtual class for memory operations.
bool isVolatile() const
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.
Root of the metadata hierarchy.
Definition Metadata.h:64
NVPTXISelDAGToDAGPass(NVPTXTargetMachine &TM, CodeGenOptLevel OptLevel)
bool hasL2Prefetch256B() const
bool hasL2EvictionHint() const
bool hasTcgen05InstSupport() const
bool hasL2Prefetch64B() const
bool hasL2Prefetch128B() const
bool hasNativeBF16Support(unsigned Opcode) const
bool hasL1EvictionHint() const
bool hasRelaxedMMIO() const
bool hasL2CacheHint() const
bool hasMemoryOrdering() const
bool allowFMA(MachineFunction &MF, CodeGenOptLevel OptLevel) const
Wrapper class for IR location info (IR ordering and DebugLoc) to be passed into SDNode creation funct...
Represents one node in the SelectionDAG.
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
SelectionDAGISelPass(std::unique_ptr< SelectionDAGISel > Selector)
SelectionDAGISel - This is the common base class used for SelectionDAG-based pattern-matching instruc...
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)
SDValue getTargetFrameIndex(int FI, EVT VT)
SDValue getSignedTargetConstant(int64_t Val, const SDLoc &DL, EVT VT, bool isOpaque=false)
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.
This class is used to represent ISD::STORE nodes.
const SDValue & getValue() const
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
A switch()-like statement whose cases are string literals.
StringSwitch & Case(StringLiteral S, T Value)
const Triple & getTargetTriple() const
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
LLVM Value Representation.
Definition Value.h:75
#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.
@ Entry
Definition COFF.h:862
@ 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:334
@ SharedCluster
Definition NVPTX.h:327
@ EntryParam
Definition NVPTX.h:328
unsigned encodeEvictionAndPrefetchHint(L1Eviction L1, L2Eviction L2, L2Prefetch P)
Definition NVPTX.h:379
std::string OrderingToString(Ordering Order)
bool isPackedVectorTy(EVT VT)
DivPrecisionLevel
Definition NVPTX.h:465
@ DefaultDevice
Definition NVPTX.h:316
@ RelaxedMMIO
Definition NVPTX.h:306
@ AcquireRelease
Definition NVPTX.h:302
@ NotAtomic
Definition NVPTX.h:295
@ SequentiallyConsistent
Definition NVPTX.h:303
initializer< Ty > init(const Ty &Val)
std::enable_if_t< detail::IsValidPointer< X, Y >::value, X * > dyn_extract(Y &&MD)
Extract a Value from Metadata, if any.
Definition Metadata.h:696
NodeAddr< NodeBase * > Node
Definition RDFGraph.h:381
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:577
AtomicScope
Target-neutral memory synchronization scopes.
Definition AtomicScope.h:23
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,...
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:177
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
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
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
std::optional< StringRef > getAtomicScopeIRString(const Triple &T, AtomicScope S, bool IsSingleAddressSpace=false)
Returns the LLVM IR syncscope string that T uses to spell S.
Definition AtomicScope.h:34
unsigned getFromTypeWidthForLoad(const MemSDNode *Mem)
The bit-width of a single element loaded by Mem, i.e.
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
A record for a potential prefetch made during the initial scan of the loop.
static void set(StorageType &Packed, typename Bitfield::Type Value)
Sets the typed value in the provided Packed value.
Definition Bitfields.h:223
Extended Value Type.
Definition ValueTypes.h:35
uint64_t getFixedSizeInBits() const
Return the size of the specified fixed width value type in bits.
Definition ValueTypes.h:404
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
A MapVector that performs no allocations if smaller than a certain size.
Definition MapVector.h:342