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 if (!Subtarget->hasAtomScope())
681 return resolveScope(Scopes[N->getSyncScopeID()], Subtarget);
682}
683
684namespace {
685
686struct OperationOrderings {
687 NVPTX::Ordering InstructionOrdering, FenceOrdering;
688 OperationOrderings(NVPTX::Ordering IO = NVPTX::Ordering::NotAtomic,
689 NVPTX::Ordering FO = NVPTX::Ordering::NotAtomic)
690 : InstructionOrdering(IO), FenceOrdering(FO) {}
691};
692
693static OperationOrderings
694getOperationOrderings(MemSDNode *N, const NVPTXSubtarget *Subtarget) {
695 AtomicOrdering Ordering = N->getSuccessOrdering();
696 auto CodeAddrSpace = NVPTXDAGToDAGISel::getAddrSpace(N);
697
698 bool HasMemoryOrdering = Subtarget->hasMemoryOrdering();
699 bool HasRelaxedMMIO = Subtarget->hasRelaxedMMIO();
700 bool IsSupportedLocalVolatile = CodeAddrSpace == NVPTX::AddressSpace::Local &&
701 Subtarget->hasFeature(NVPTX::PTX91) &&
702 N->isVolatile() &&
706
707 // clang-format off
708
709 // Lowering for Load/Store Operations (note: AcquireRelease Loads or Stores error).
710 // Note: uses of Relaxed in the Atomic column of this table refer
711 // to LLVM AtomicOrdering::Monotonic.
712 //
713 // | Atomic | Volatile | Statespace | PTX sm_60- | PTX sm_70+ |
714 // |---------|----------|--------------------|------------|------------------------------|
715 // | No | No | All | plain | .weak |
716 // | No | Yes | Generic,Shared, | .volatile | .volatile |
717 // | | | Global [0] | | |
718 // | No | Yes | Local (PTX 9.0-) | plain [1] | .weak [1] |
719 // | No | Yes | Local (PTX 9.1+) | .volatile | .volatile |
720 // | No | Yes | Const,Param | plain [1] | .weak [1] |
721 // | Unorder | Yes/No | All | == Relaxed | == Relaxed |
722 // | Relaxed | No | Generic,Shared, | .volatile | <atomic sem> |
723 // | | | Global [0] | | |
724 // | Other | No | Generic,Shared, | Error [2] | <atomic sem> |
725 // | | | Global [0] | | |
726 // | Yes | No | Local,Const,Param | plain [1] | .weak [1] |
727 // | Relaxed | Yes | Generic,Shared [0] | .volatile | .volatile |
728 // | Relaxed | Yes | Global [0] | .volatile | .mmio.relaxed.sys (PTX 8.2+) |
729 // | | | | | or .volatile (PTX 8.1-) |
730 // | Relaxed | Yes | Local (PTX 9.0-) | plain [1] | .weak [1] |
731 // | Relaxed | Yes | Local (PTX 9.1+) | .volatile | .volatile |
732 // | Relaxed | Yes | Const,Param | plain [1] | .weak [1] |
733 // | Other | Yes | Generic, Shared, | Error [2] | <atomic sem> [3] |
734 // | | | / Global [0] | | |
735
736 // Lowering of CUDA C++ SequentiallyConsistent Operations and Fences to PTX
737 // by following the ABI proven sound in:
738 // Lustig et al, A Formal Analysis of the NVIDIA PTX Memory Consistency Model, ASPLOS’19.
739 // https://dl.acm.org/doi/pdf/10.1145/3297858.3304043
740 //
741 // | CUDA C++ Atomic Operation or Atomic Fence | PTX Atomic Operation or Fence |
742 // |------------------------------------------------------|-------------------------------|
743 // | cuda::atomic_thread_fence | fence.sc.<scope>; |
744 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | |
745 // |------------------------------------------------------|-------------------------------|
746 // | cuda::atomic_load | fence.sc.<scope>; |
747 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | ld.acquire.<scope>; |
748 // |------------------------------------------------------|-------------------------------|
749 // | cuda::atomic_store | fence.sc.<scope>; |
750 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | st.release.<scope>; |
751 // |------------------------------------------------------|-------------------------------|
752 // | cuda::atomic_fetch_<op> | fence.sc.<scope>; |
753 // | (memory_order_seq_cst, cuda::thread_scope_<scope>) | atom.acq_rel.<scope>; |
754
755 // clang-format on
756
757 // [0]: volatile and atomics are only supported on global or shared
758 // memory locations, accessed via generic/shared/global pointers.
759 // PTX 9.1 adds volatile support on local ld/st.
760 // MMIO is only supported on global memory locations,
761 // accessed via generic/global pointers.
762 // TODO: Implement MMIO access via generic pointer to global.
763 // Currently implemented for global pointers only.
764
765 // [1]: Lowering volatile/atomic operations to non-volatile/non-atomic
766 // PTX instructions fails to preserve their C++ side-effects.
767 //
768 // Example (https://github.com/llvm/llvm-project/issues/62057):
769 //
770 // void example() {
771 // std::atomic<bool> True = true;
772 // while (True.load(std::memory_order_relaxed));
773 // }
774 //
775 // A C++ program that calls "example" is well-defined: the infinite loop
776 // performs an atomic operation. By lowering volatile/atomics to
777 // "weak" memory operations, we are transforming the above into:
778 //
779 // void undefined_behavior() {
780 // bool True = true;
781 // while (True);
782 // }
783 //
784 // which exhibits undefined behavior in both C++ and PTX.
785 //
786 // Calling "example" in CUDA C++ compiled for sm_60- exhibits undefined
787 // behavior due to lack of Independent Forward Progress. Lowering these
788 // to weak memory operations in sm_60- is therefore fine.
789 //
790 // TODO: Where direct volatile or atomic operations are unsupported,
791 // preserve the side-effect using the weak memory instruction and
792 // another instruction, such as a dead dummy volatile load.
793
794 if ((CodeAddrSpace == NVPTX::AddressSpace::Local &&
795 !IsSupportedLocalVolatile) ||
796 CodeAddrSpace == NVPTX::AddressSpace::Const ||
797 CodeAddrSpace == NVPTX::AddressSpace::EntryParam ||
798 CodeAddrSpace == NVPTX::AddressSpace::DeviceParam) {
800 }
801
802 // [2]: Atomics with Ordering different than Unordered or Relaxed are not
803 // supported on sm_60 and older; this includes volatile atomics.
804 if (!(Ordering == AtomicOrdering::NotAtomic ||
805 Ordering == AtomicOrdering::Unordered ||
806 Ordering == AtomicOrdering::Monotonic) &&
807 !HasMemoryOrdering) {
809 formatv("PTX does not support \"atomic\" for orderings different than"
810 "\"NotAtomic\" or \"Monotonic\" for sm_60 or older, but order "
811 "is: \"{}\".",
812 toIRString(Ordering)));
813 }
814
815 // [3]: TODO: these should eventually use .mmio<.atomic sem>; for now we drop
816 // the volatile semantics and preserve the atomic ones.
817
818 // PTX atomics are not available outside generic, global, or shared memory.
819 // PTX volatile operations additionally support local memory in PTX 9.1+.
820 bool AddrSupportsVolatileOrAtomic =
821 (IsSupportedLocalVolatile ||
822 CodeAddrSpace == NVPTX::AddressSpace::Generic ||
823 CodeAddrSpace == NVPTX::AddressSpace::Global ||
824 CodeAddrSpace == NVPTX::AddressSpace::Shared ||
825 CodeAddrSpace == NVPTX::AddressSpace::SharedCluster);
826 if (!AddrSupportsVolatileOrAtomic)
828
829 bool UseRelaxedMMIO =
830 HasRelaxedMMIO && CodeAddrSpace == NVPTX::AddressSpace::Global;
831
832 switch (Ordering) {
834 return N->isVolatile() ? NVPTX::Ordering::Volatile
837 // We lower unordered in the exact same way as 'monotonic' to respect
838 // LLVM IR atomicity requirements.
840 if (N->isVolatile())
841 return UseRelaxedMMIO ? NVPTX::Ordering::RelaxedMMIO
843 else
844 return HasMemoryOrdering ? NVPTX::Ordering::Relaxed
846 // case AtomicOrdering::Consume: // If LLVM ever provides this, lower it to
847 // Acquire.
849 if (!N->readMem())
851 formatv("PTX only supports Acquire Ordering on reads: {}",
852 N->getOperationName()));
855 if (!N->writeMem())
857 formatv("PTX only supports Release Ordering on writes: {}",
858 N->getOperationName()));
862 formatv("NVPTX does not support AcquireRelease Ordering on "
863 "read-modify-write "
864 "yet and PTX does not support it on loads or stores: {}",
865 N->getOperationName()));
866 }
868 // LLVM-IR SequentiallyConsistent atomics map to a two-instruction PTX
869 // sequence including a "fence.sc.sco" and the memory instruction with an
870 // Ordering that differs from "sc": acq, rel, or acq_rel, depending on
871 // whether the memory operation is a read, write, or read-modify-write.
872 //
873 // This sets the ordering of the fence to SequentiallyConsistent, and
874 // sets the corresponding ordering for the instruction.
875 NVPTX::Ordering InstrOrder;
876 if (N->readMem())
877 InstrOrder = NVPTX::Ordering::Acquire;
878 else if (N->writeMem())
879 InstrOrder = NVPTX::Ordering::Release;
880 else
882 formatv("NVPTX does not support SequentiallyConsistent Ordering on "
883 "read-modify-writes yet: {}",
884 N->getOperationName()));
885 return OperationOrderings(InstrOrder,
887 }
888 }
890 formatv("NVPTX backend does not support AtomicOrdering \"{}\" yet.",
891 toIRString(Ordering)));
892}
893
894} // namespace
895
896NVPTX::Scope NVPTXDAGToDAGISel::getOperationScope(MemSDNode *N,
897 NVPTX::Ordering O) const {
898 switch (O) {
900 case NVPTX::Ordering::Volatile: // Non-atomic volatile operations
901 // NVPTX uses Thread scope as the scope of non-atomic operations.
904 // RelaxedMMIO operations are always system scope.
905 // If a RelaxedMMIO order was generated from an atomic volatile operation
906 // with a smaller thread scope, we bump it here to system scope.
913 auto S = Scopes[N->getSyncScopeID()];
914
915 S = resolveScope(S, Subtarget);
916
917 // If operation is volatile, then its scope is system.
918 return N->isVolatile() ? NVPTX::Scope::System : S;
919 }
920 llvm_unreachable("unhandled ordering");
921}
922
923static bool canLowerToLDG(const MemSDNode &N, const NVPTXSubtarget &Subtarget,
924 NVPTX::AddressSpace CodeAddrSpace) {
925 // We use ldg (i.e. ld.global.nc) for invariant loads from the global address
926 // space.
927 return Subtarget.hasLDG() && CodeAddrSpace == NVPTX::AddressSpace::Global &&
928 N.isInvariant();
929}
930
931static unsigned int getFenceOp(NVPTX::Ordering O, NVPTX::Scope S,
932 NVPTXSubtarget const *T) {
933 S = resolveScope(S, T);
934
935 // Fall back to .acq_rel if .acquire, .release is not supported.
936 if (!T->hasSplitAcquireAndReleaseFences() &&
939
940 switch (O) {
942 switch (S) {
944 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acquire_sys
945 : NVPTX::INT_MEMBAR_SYS;
947 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acquire_cta
948 : NVPTX::INT_MEMBAR_CTA;
950 return NVPTX::atomic_thread_fence_acquire_cluster;
952 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acquire_gpu
953 : NVPTX::INT_MEMBAR_GL;
957 formatv("Unsupported scope \"{}\" for acquire/release/acq_rel fence.",
958 ScopeToString(S)));
959 }
960 break;
962 switch (S) {
964 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_release_sys
965 : NVPTX::INT_MEMBAR_SYS;
967 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_release_cta
968 : NVPTX::INT_MEMBAR_CTA;
970 return NVPTX::atomic_thread_fence_release_cluster;
972 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_release_gpu
973 : NVPTX::INT_MEMBAR_GL;
977 formatv("Unsupported scope \"{}\" for acquire/release/acq_rel fence.",
978 ScopeToString(S)));
979 }
980 break;
982 switch (S) {
984 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acq_rel_sys
985 : NVPTX::INT_MEMBAR_SYS;
987 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acq_rel_cta
988 : NVPTX::INT_MEMBAR_CTA;
990 return NVPTX::atomic_thread_fence_acq_rel_cluster;
992 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_acq_rel_gpu
993 : NVPTX::INT_MEMBAR_GL;
997 formatv("Unsupported scope \"{}\" for acquire/release/acq_rel fence.",
998 ScopeToString(S)));
999 }
1000 break;
1001 }
1003 switch (S) {
1005 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_seq_cst_sys
1006 : NVPTX::INT_MEMBAR_SYS;
1008 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_seq_cst_cta
1009 : NVPTX::INT_MEMBAR_CTA;
1011 return NVPTX::atomic_thread_fence_seq_cst_cluster;
1013 return T->hasMemoryOrdering() ? NVPTX::atomic_thread_fence_seq_cst_gpu
1014 : NVPTX::INT_MEMBAR_GL;
1017 report_fatal_error(formatv("Unsupported scope \"{}\" for seq_cst fence.",
1018 ScopeToString(S)));
1019 }
1020 break;
1021 }
1027 formatv("Unsupported \"{}\" ordering and \"{}\" scope for fence.",
1028 OrderingToString(O), ScopeToString(S)));
1029 }
1030 llvm_unreachable("unhandled ordering");
1031}
1032
1033// Returns Memory Order and Scope of a memory instruction, and
1034// inserts any fence before the instruction that's required to
1035// implement its memory ordering.
1036std::pair<NVPTX::Ordering, NVPTX::Scope>
1037NVPTXDAGToDAGISel::insertMemoryInstructionFence(SDLoc DL, SDValue &Chain,
1038 MemSDNode *N) {
1039 auto [InstructionOrdering, FenceOrdering] =
1040 getOperationOrderings(N, Subtarget);
1041 auto Scope = getOperationScope(N, InstructionOrdering);
1042
1043 // Singlethread scope has no inter-thread synchronization requirements, so
1044 // the atomic operation is lowered as plain and the fence is skipped.
1045 // NotAtomic and Volatile operations naturally have Thread scope and must
1046 // preserve their ordering.
1047 if (Scope == NVPTX::Scope::Thread &&
1051
1052 // If a fence is required before the operation, insert it:
1053 switch (NVPTX::Ordering(FenceOrdering)) {
1055 break;
1057 auto Op = getFenceOp(FenceOrdering, Scope, Subtarget);
1058 Chain = SDValue(CurDAG->getMachineNode(Op, DL, MVT::Other, Chain), 0);
1059 break;
1060 }
1061 default:
1063 formatv("Unexpected fence ordering: \"{}\".",
1064 OrderingToString(NVPTX::Ordering(FenceOrdering))));
1065 }
1066 return {InstructionOrdering, Scope};
1067}
1068
1069// Helper function template to reduce amount of boilerplate code for
1070// opcode selection.
1071static std::optional<unsigned>
1072pickOpcodeForVT(MVT::SimpleValueType VT, std::optional<unsigned> Opcode_i16,
1073 std::optional<unsigned> Opcode_i32,
1074 std::optional<unsigned> Opcode_i64) {
1075 switch (VT) {
1076 case MVT::f16:
1077 case MVT::i16:
1078 case MVT::bf16:
1079 return Opcode_i16;
1080 case MVT::v2f16:
1081 case MVT::v2bf16:
1082 case MVT::v2i16:
1083 case MVT::v4i8:
1084 case MVT::i32:
1085 case MVT::f32:
1086 return Opcode_i32;
1087 case MVT::v2f32:
1088 case MVT::v2i32:
1089 case MVT::i64:
1090 case MVT::f64:
1091 return Opcode_i64;
1092 default:
1093 return std::nullopt;
1094 }
1095}
1096
1097static inline bool isAddLike(const SDValue V) {
1098 return V.getOpcode() == ISD::ADD ||
1099 (V->getOpcode() == ISD::OR && V->getFlags().hasDisjoint());
1100}
1101
1103 if (N.getOpcode() == ISD::AssertAlign)
1104 N = N.getOperand(0);
1105 return N;
1106}
1107
1108// selectBaseADDR - Match a dag node which will serve as the base address for an
1109// ADDR operand pair.
1111 N = stripAssertAlign(N);
1112 if (const auto *GA = dyn_cast<GlobalAddressSDNode>(N))
1113 return DAG->getTargetGlobalAddress(GA->getGlobal(), SDLoc(N),
1114 GA->getValueType(0), GA->getOffset(),
1115 GA->getTargetFlags());
1116 if (const auto *ES = dyn_cast<ExternalSymbolSDNode>(N))
1117 return DAG->getTargetExternalSymbol(ES->getSymbol(), ES->getValueType(0),
1118 ES->getTargetFlags());
1119 if (const auto *FIN = dyn_cast<FrameIndexSDNode>(N))
1120 return DAG->getTargetFrameIndex(FIN->getIndex(), FIN->getValueType(0));
1121 if (N.getOpcode() == NVPTXISD::Symbol)
1122 return N.getOperand(0);
1123
1124 return N;
1125}
1126
1128 Addr = stripAssertAlign(Addr);
1129 APInt AccumulatedOffset(64u, 0);
1130 while (isAddLike(Addr)) {
1131 const auto *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1));
1132 if (!CN)
1133 break;
1134
1135 const APInt CI = CN->getAPIntValue().sext(64);
1136 if (!(CI + AccumulatedOffset).isSignedIntN(32))
1137 break;
1138
1139 AccumulatedOffset += CI;
1140 Addr = stripAssertAlign(Addr->getOperand(0));
1141 }
1142 return DAG->getSignedTargetConstant(AccumulatedOffset.getSExtValue(), DL,
1143 MVT::i32);
1144}
1145
1146static std::pair<SDValue, SDValue> selectADDR(SDValue Addr, SelectionDAG *DAG) {
1147 SDValue Offset = accumulateOffset(Addr, SDLoc(Addr), DAG);
1148 SDValue Base = selectBaseADDR(Addr, DAG);
1149 return {Base, Offset};
1150}
1151
1152// Select a pair of operands which represent a valid PTX address, this could be
1153// one of the following things:
1154// - [var] - Offset is simply set to 0
1155// - [reg] - Offset is simply set to 0
1156// - [reg+immOff]
1157// - [var+immOff]
1158// Note that immOff must fit into a 32-bit signed integer.
1159bool NVPTXDAGToDAGISel::SelectADDR(SDValue Addr, SDValue &Base,
1160 SDValue &Offset) {
1161 std::tie(Base, Offset) = selectADDR(Addr, CurDAG);
1162 return true;
1163}
1164
1166 Ctx.diagnose(DiagnosticInfoGeneric(
1167 Twine("invalid NVPTX !mem.cache_hint metadata: ") + Msg, DS_Warning));
1168}
1169
1170static std::optional<NVPTX::L1Eviction> parseL1Eviction(StringRef Str) {
1172 .Case("normal", NVPTX::L1Eviction::Normal)
1173 .Case("unchanged", NVPTX::L1Eviction::Unchanged)
1176 .Case("no_allocate", NVPTX::L1Eviction::NoAllocate)
1177 .Default(std::nullopt);
1178}
1179
1180static std::optional<NVPTX::L2Eviction> parseL2Eviction(StringRef Str) {
1182 .Case("normal", NVPTX::L2Eviction::Normal)
1185 .Default(std::nullopt);
1186}
1187
1188static std::optional<NVPTX::L2Prefetch> parseL2Prefetch(StringRef Str) {
1190 .Case("64B", NVPTX::L2Prefetch::Bytes64)
1193 .Default(std::nullopt);
1194}
1195
1196template <typename T>
1197static std::optional<T> parseMemCacheHintStringValue(
1198 LLVMContext &Ctx, StringRef Key, const Metadata *Value,
1199 std::optional<T> (*Parse)(StringRef), bool EmitDiagnostics) {
1200 const auto *Val = dyn_cast<MDString>(Value);
1201 if (!Val) {
1202 if (EmitDiagnostics)
1204 Twine("'") + Key + "' expects a string value");
1205 return std::nullopt;
1206 }
1207
1208 StringRef ValStr = Val->getString();
1209 auto Parsed = Parse(ValStr);
1210 if (!Parsed && EmitDiagnostics)
1211 emitInvalidMemCacheHint(Ctx, Twine("unknown value '") + ValStr + "' for '" +
1212 Key + "'");
1213 return Parsed;
1214}
1215
1217 return AddrSpace == NVPTX::AddressSpace::Global ||
1218 AddrSpace == NVPTX::AddressSpace::Generic;
1219}
1220
1221static bool isLdOrSt(NVPTXMemCacheHintAccess Access) {
1222 return Access.Instruction == NVPTXMemCacheHintInstruction::Ld ||
1223 Access.Instruction == NVPTXMemCacheHintInstruction::St;
1224}
1225
1226static bool isL1EvictionSupported(const NVPTXSubtarget &Subtarget,
1227 NVPTX::L1Eviction Eviction,
1228 NVPTXMemCacheHintAccess Access) {
1229 if (Eviction == NVPTX::L1Eviction::Normal)
1230 return true;
1231
1232 return isLdOrSt(Access) && !Access.IsVolatile &&
1233 Subtarget.hasL1EvictionHint();
1234}
1235
1236static bool isL2PrefetchSupported(const NVPTXSubtarget &Subtarget,
1238 NVPTXMemCacheHintAccess Access) {
1239 switch (Prefetch) {
1241 return true;
1243 return Access.Instruction == NVPTXMemCacheHintInstruction::Ld &&
1244 isGlobalOrGeneric(Access.AddrSpace) && Subtarget.hasL2Prefetch64B();
1246 return Access.Instruction == NVPTXMemCacheHintInstruction::Ld &&
1247 isGlobalOrGeneric(Access.AddrSpace) && Subtarget.hasL2Prefetch128B();
1249 return Access.Instruction == NVPTXMemCacheHintInstruction::Ld &&
1250 isGlobalOrGeneric(Access.AddrSpace) && Subtarget.hasL2Prefetch256B();
1251 }
1252 llvm_unreachable("Unexpected L2 prefetch hint");
1253}
1254
1255static bool isL2EvictionSupported(const NVPTXSubtarget &Subtarget,
1256 NVPTX::L2Eviction Eviction,
1257 NVPTXMemCacheHintAccess Access) {
1258 if (Eviction == NVPTX::L2Eviction::Normal)
1259 return true;
1260
1261 return isLdOrSt(Access) && !Access.IsVolatile &&
1262 Subtarget.hasL2EvictionHint() && isGlobalOrGeneric(Access.AddrSpace) &&
1263 ((Access.NumElts == 8 && Access.EltWidth == 32) ||
1264 (Access.NumElts == 4 && Access.EltWidth == 64));
1265}
1266
1267static bool isCachePolicySupported(const NVPTXSubtarget &Subtarget,
1268 NVPTXMemCacheHintAccess Access) {
1269 return !Access.IsVolatile && isGlobalOrGeneric(Access.AddrSpace) &&
1270 Subtarget.hasL2CacheHint();
1271}
1272
1273NVPTXMemCacheHintOperands NVPTXDAGToDAGISel::getMemCacheHintOperands(
1274 const MemSDNode *N, NVPTXMemCacheHintAccess Access, const SDLoc &DL,
1275 bool EmitDiagnostics) {
1276 LLVMContext &Ctx = *CurDAG->getContext();
1277 const MDNode *Node = N->getMemCacheHint();
1278 SDValue PolicyReg = CurDAG->getRegister(NVPTX::NoRegister, MVT::i64);
1279 if (!Node)
1280 return {getI32Imm(0, DL), PolicyReg};
1281 if (Node->getNumOperands() == 0) {
1282 if (EmitDiagnostics)
1283 emitInvalidMemCacheHint(Ctx, "empty hint node");
1284 return {getI32Imm(0, DL), PolicyReg};
1285 }
1286
1290 std::optional<uint64_t> CachePolicy;
1291
1292 for (unsigned I = 0; I + 1 < Node->getNumOperands(); I += 2) {
1293 const auto *Key = cast<MDString>(Node->getOperand(I));
1294 StringRef KeyStr = Key->getString();
1295 const Metadata *Value = Node->getOperand(I + 1).get();
1296
1297 if (KeyStr == "nvvm.l1_eviction") {
1298 auto ParsedL1 = parseMemCacheHintStringValue(
1299 Ctx, KeyStr, Value, parseL1Eviction, EmitDiagnostics);
1300 if (ParsedL1 && isL1EvictionSupported(*Subtarget, *ParsedL1, Access))
1301 L1 = *ParsedL1;
1302 continue;
1303 }
1304
1305 if (KeyStr == "nvvm.l2_eviction") {
1306 auto ParsedL2 = parseMemCacheHintStringValue(
1307 Ctx, KeyStr, Value, parseL2Eviction, EmitDiagnostics);
1308 if (ParsedL2 && isL2EvictionSupported(*Subtarget, *ParsedL2, Access))
1309 L2 = *ParsedL2;
1310 continue;
1311 }
1312
1313 if (KeyStr == "nvvm.l2_prefetch_size") {
1314 auto ParsedPrefetch = parseMemCacheHintStringValue(
1315 Ctx, KeyStr, Value, parseL2Prefetch, EmitDiagnostics);
1316 if (ParsedPrefetch &&
1317 isL2PrefetchSupported(*Subtarget, *ParsedPrefetch, Access))
1318 Prefetch = *ParsedPrefetch;
1319 continue;
1320 }
1321
1322 if (KeyStr == "nvvm.l2_cache_hint") {
1323 const auto *ValCI = mdconst::dyn_extract<ConstantInt>(Value);
1324 if (!ValCI) {
1325 if (EmitDiagnostics)
1327 Ctx, "'nvvm.l2_cache_hint' expects an integer value");
1328 } else if (isCachePolicySupported(*Subtarget, Access)) {
1329 CachePolicy = ValCI->getZExtValue();
1330 }
1331 continue;
1332 }
1333
1334 if (EmitDiagnostics)
1335 emitInvalidMemCacheHint(Ctx, Twine("unknown key '") + KeyStr + "'");
1336 }
1337
1338 unsigned EvictionAndPrefetchHint =
1340 if (CachePolicy) {
1341 SDValue PolicyConst = CurDAG->getTargetConstant(*CachePolicy, DL, MVT::i64);
1342 PolicyReg = SDValue(
1343 CurDAG->getMachineNode(NVPTX::MOV_B64_i, DL, MVT::i64, PolicyConst), 0);
1344 Bitfield::set<NVPTX::L2CacheHintBit>(EvictionAndPrefetchHint, true);
1345 }
1346
1347 return {getI32Imm(EvictionAndPrefetchHint, DL), PolicyReg};
1348}
1349
1350bool NVPTXDAGToDAGISel::tryLoad(SDNode *N) {
1352 assert(LD->readMem() && "Expected load");
1353
1354 // do not support pre/post inc/dec
1355 const LoadSDNode *PlainLoad = dyn_cast<LoadSDNode>(LD);
1356 if (PlainLoad && PlainLoad->isIndexed())
1357 return false;
1358
1359 // Address Space Setting
1360 const auto CodeAddrSpace = getAddrSpace(LD);
1361 if (canLowerToLDG(*LD, *Subtarget, CodeAddrSpace))
1362 return tryLDG(LD);
1363
1364 SDLoc DL(LD);
1365 SDValue Chain = N->getOperand(0);
1366 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, LD);
1367
1368 const unsigned FromTypeWidth = LD->getMemoryVT().getSizeInBits();
1369
1370 // Vector Setting
1371 const unsigned FromType =
1372 (PlainLoad && (PlainLoad->getExtensionType() == ISD::SEXTLOAD))
1375
1376 uint32_t UsedBytesMask;
1377 switch (N->getOpcode()) {
1378 case ISD::LOAD:
1379 case ISD::ATOMIC_LOAD:
1380 UsedBytesMask = UINT32_MAX;
1381 break;
1382 case NVPTXISD::MLoad:
1383 UsedBytesMask = N->getConstantOperandVal(3);
1384 break;
1385 default:
1386 llvm_unreachable("Unexpected opcode");
1387 }
1388
1389 assert(isPowerOf2_32(FromTypeWidth) && FromTypeWidth >= 8 &&
1390 FromTypeWidth <= 128 && "Invalid width for load");
1391
1392 const auto [Base, Offset] = selectADDR(N->getOperand(1), CurDAG);
1393 const auto [EvictionAndPrefetchHint, PolicyReg] = getMemCacheHintOperands(
1394 LD,
1395 {NVPTXMemCacheHintInstruction::Ld, CodeAddrSpace,
1396 /*NumElts=*/1, /*EltWidth=*/FromTypeWidth, LD->isVolatile()},
1397 DL);
1398
1399 // Create the machine instruction DAG
1400 SDValue Ops[] = {getI32Imm(Ordering, DL),
1401 getI32Imm(Scope, DL),
1402 getI32Imm(CodeAddrSpace, DL),
1403 getI32Imm(FromType, DL),
1404 getI32Imm(FromTypeWidth, DL),
1405 getI32Imm(UsedBytesMask, DL),
1406 Base,
1407 Offset,
1408 EvictionAndPrefetchHint,
1409 PolicyReg,
1410 Chain};
1411
1412 const MVT::SimpleValueType TargetVT = LD->getSimpleValueType(0).SimpleTy;
1413 const std::optional<unsigned> Opcode =
1414 pickOpcodeForVT(TargetVT, NVPTX::LD_i16, NVPTX::LD_i32, NVPTX::LD_i64);
1415 if (!Opcode)
1416 return false;
1417
1418 SDNode *NVPTXLD = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1419 if (!NVPTXLD)
1420 return false;
1421
1422 MachineMemOperand *MemRef = LD->getMemOperand();
1423 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXLD), {MemRef});
1424
1425 ReplaceNode(LD, NVPTXLD);
1426 return true;
1427}
1428
1429static unsigned getStoreVectorNumElts(SDNode *N) {
1430 switch (N->getOpcode()) {
1431 case NVPTXISD::StoreV2:
1432 return 2;
1433 case NVPTXISD::StoreV4:
1434 return 4;
1435 case NVPTXISD::StoreV8:
1436 return 8;
1437 default:
1438 llvm_unreachable("Unexpected opcode");
1439 }
1440}
1441
1442bool NVPTXDAGToDAGISel::tryLoadVector(SDNode *N) {
1444
1445 // Address Space Setting
1446 const auto CodeAddrSpace = getAddrSpace(LD);
1447 if (canLowerToLDG(*LD, *Subtarget, CodeAddrSpace))
1448 return tryLDG(LD);
1449
1450 const MVT EltVT = LD->getSimpleValueType(0);
1451 SDLoc DL(LD);
1452 SDValue Chain = LD->getChain();
1453 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, LD);
1454
1455 // Type Setting: fromType + fromTypeWidth
1456 //
1457 // Sign : ISD::SEXTLOAD
1458 // Unsign : ISD::ZEXTLOAD, ISD::NON_EXTLOAD or ISD::EXTLOAD and the
1459 // type is integer
1460 // Float : ISD::NON_EXTLOAD or ISD::EXTLOAD and the type is float
1461 // Read at least 8 bits (predicates are stored as 8-bit values)
1462 // Get the original LoadSDNode::getExtensionType() value
1463 const unsigned ExtensionType = N->getConstantOperandVal(4);
1464 const unsigned FromType = (ExtensionType == ISD::SEXTLOAD)
1466 : NVPTX::PTXLdStInstCode::Untyped;
1467
1468 const unsigned FromTypeWidth = getFromTypeWidthForLoad(LD);
1469 const uint32_t UsedBytesMask = N->getConstantOperandVal(3);
1470
1471 assert(!(EltVT.isVector() && ExtensionType != ISD::NON_EXTLOAD));
1472
1473 const auto [EvictionAndPrefetchHint, PolicyReg] =
1474 getMemCacheHintOperands(LD,
1475 {NVPTXMemCacheHintInstruction::Ld, CodeAddrSpace,
1476 /*NumElts=*/LD->getNumValues() - 1,
1477 /*EltWidth=*/FromTypeWidth, LD->isVolatile()},
1478 DL);
1479 const auto [Base, Offset] = selectADDR(N->getOperand(1), CurDAG);
1480 SDValue Ops[] = {getI32Imm(Ordering, DL),
1481 getI32Imm(Scope, DL),
1482 getI32Imm(CodeAddrSpace, DL),
1483 getI32Imm(FromType, DL),
1484 getI32Imm(FromTypeWidth, DL),
1485 getI32Imm(UsedBytesMask, DL),
1486 Base,
1487 Offset,
1488 EvictionAndPrefetchHint,
1489 PolicyReg,
1490 Chain};
1491
1492 std::optional<unsigned> Opcode;
1493 switch (N->getOpcode()) {
1494 default:
1495 llvm_unreachable("Unexpected opcode");
1496 case NVPTXISD::LoadV2:
1497 Opcode = pickOpcodeForVT(EltVT.SimpleTy, NVPTX::LDV_i16_v2,
1498 NVPTX::LDV_i32_v2, NVPTX::LDV_i64_v2);
1499 break;
1500 case NVPTXISD::LoadV4:
1501 Opcode = pickOpcodeForVT(EltVT.SimpleTy, NVPTX::LDV_i16_v4,
1502 NVPTX::LDV_i32_v4, NVPTX::LDV_i64_v4);
1503 break;
1504 case NVPTXISD::LoadV8:
1505 Opcode = pickOpcodeForVT(EltVT.SimpleTy, {/* no v8i16 */},
1506 NVPTX::LDV_i32_v8, {/* no v8i64 */});
1507 break;
1508 }
1509 if (!Opcode)
1510 return false;
1511
1512 SDNode *NVPTXLD = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1513
1514 MachineMemOperand *MemRef = LD->getMemOperand();
1515 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXLD), {MemRef});
1516
1517 ReplaceNode(LD, NVPTXLD);
1518 return true;
1519}
1520
1521bool NVPTXDAGToDAGISel::tryLDG(MemSDNode *LD) {
1522 SDLoc DL(LD);
1523
1524 unsigned ExtensionType;
1525 uint32_t UsedBytesMask;
1526 if (const auto *Load = dyn_cast<LoadSDNode>(LD)) {
1527 ExtensionType = Load->getExtensionType();
1528 UsedBytesMask = UINT32_MAX;
1529 } else {
1530 ExtensionType = LD->getConstantOperandVal(4);
1531 UsedBytesMask = LD->getConstantOperandVal(3);
1532 }
1533 const unsigned FromType = (ExtensionType == ISD::SEXTLOAD)
1535 : NVPTX::PTXLdStInstCode::Untyped;
1536
1537 const unsigned FromTypeWidth = getFromTypeWidthForLoad(LD);
1538
1539 assert(!(LD->getSimpleValueType(0).isVector() &&
1540 ExtensionType != ISD::NON_EXTLOAD));
1541
1542 const auto [Base, Offset] = selectADDR(LD->getOperand(1), CurDAG);
1543 const auto [EvictionAndPrefetchHint, PolicyReg] = getMemCacheHintOperands(
1544 LD,
1545 {NVPTXMemCacheHintInstruction::Ld, NVPTX::AddressSpace::Global,
1546 LD->getNumValues() - 1, FromTypeWidth, LD->isVolatile()},
1547 DL);
1548 SDValue Ops[] = {getI32Imm(FromType, DL),
1549 getI32Imm(FromTypeWidth, DL),
1550 getI32Imm(UsedBytesMask, DL),
1551 Base,
1552 Offset,
1553 EvictionAndPrefetchHint,
1554 PolicyReg,
1555 LD->getChain()};
1556
1557 const MVT::SimpleValueType TargetVT = LD->getSimpleValueType(0).SimpleTy;
1558 std::optional<unsigned> Opcode;
1559 switch (LD->getOpcode()) {
1560 default:
1561 llvm_unreachable("Unexpected opcode");
1562 case ISD::LOAD:
1563 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LD_GLOBAL_NC_i16,
1564 NVPTX::LD_GLOBAL_NC_i32, NVPTX::LD_GLOBAL_NC_i64);
1565 break;
1566 case NVPTXISD::MLoad:
1567 Opcode = pickOpcodeForVT(TargetVT, std::nullopt, NVPTX::LD_GLOBAL_NC_i32,
1568 NVPTX::LD_GLOBAL_NC_i64);
1569 break;
1570 case NVPTXISD::LoadV2:
1571 Opcode =
1572 pickOpcodeForVT(TargetVT, NVPTX::LD_GLOBAL_NC_v2i16,
1573 NVPTX::LD_GLOBAL_NC_v2i32, NVPTX::LD_GLOBAL_NC_v2i64);
1574 break;
1575 case NVPTXISD::LoadV4:
1576 Opcode =
1577 pickOpcodeForVT(TargetVT, NVPTX::LD_GLOBAL_NC_v4i16,
1578 NVPTX::LD_GLOBAL_NC_v4i32, NVPTX::LD_GLOBAL_NC_v4i64);
1579 break;
1580 case NVPTXISD::LoadV8:
1581 Opcode = pickOpcodeForVT(TargetVT, {/* no v8i16 */},
1582 NVPTX::LD_GLOBAL_NC_v8i32, {/* no v8i64 */});
1583 break;
1584 }
1585 if (!Opcode)
1586 return false;
1587
1588 SDNode *NVPTXLDG = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1589
1590 ReplaceNode(LD, NVPTXLDG);
1591 return true;
1592}
1593
1594bool NVPTXDAGToDAGISel::tryLDU(SDNode *N) {
1595 auto *LD = cast<MemSDNode>(N);
1596
1597 SDLoc DL(N);
1598 const unsigned FromTypeWidth = getFromTypeWidthForLoad(LD);
1599 const MVT::SimpleValueType TargetVT = LD->getSimpleValueType(0).SimpleTy;
1600
1601 // If this is an LDU intrinsic, the address is the third operand. If its an
1602 // LDU SD node (from custom vector handling), then its the second operand
1603 SDValue Addr =
1604 LD->getOperand(LD->getOpcode() == ISD::INTRINSIC_W_CHAIN ? 2 : 1);
1605
1606 const auto [Base, Offset] = selectADDR(Addr, CurDAG);
1607 SDValue Ops[] = {getI32Imm(FromTypeWidth, DL), Base, Offset, LD->getChain()};
1608
1609 std::optional<unsigned> Opcode;
1610 switch (N->getOpcode()) {
1611 default:
1612 llvm_unreachable("Unexpected opcode");
1614 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LDU_GLOBAL_i16,
1615 NVPTX::LDU_GLOBAL_i32, NVPTX::LDU_GLOBAL_i64);
1616 break;
1617 case NVPTXISD::LDUV2:
1618 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LDU_GLOBAL_v2i16,
1619 NVPTX::LDU_GLOBAL_v2i32, NVPTX::LDU_GLOBAL_v2i64);
1620 break;
1621 case NVPTXISD::LDUV4:
1622 Opcode = pickOpcodeForVT(TargetVT, NVPTX::LDU_GLOBAL_v4i16,
1623 NVPTX::LDU_GLOBAL_v4i32, {/* no v4i64 */});
1624 break;
1625 }
1626 if (!Opcode)
1627 return false;
1628
1629 SDNode *NVPTXLDU = CurDAG->getMachineNode(*Opcode, DL, LD->getVTList(), Ops);
1630
1631 ReplaceNode(LD, NVPTXLDU);
1632 return true;
1633}
1634
1635bool NVPTXDAGToDAGISel::tryStore(SDNode *N) {
1637 assert(ST->writeMem() && "Expected store");
1638 StoreSDNode *PlainStore = dyn_cast<StoreSDNode>(ST);
1639 AtomicSDNode *AtomicStore = dyn_cast<AtomicSDNode>(ST);
1640 assert((PlainStore || AtomicStore) && "Expected store");
1641
1642 // do not support pre/post inc/dec
1643 if (PlainStore && PlainStore->isIndexed())
1644 return false;
1645
1646 // Address Space Setting
1647 const auto CodeAddrSpace = getAddrSpace(ST);
1648
1649 SDLoc DL(ST);
1650 SDValue Chain = ST->getChain();
1651 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, ST);
1652
1653 // Vector Setting
1654 const unsigned ToTypeWidth = ST->getMemoryVT().getSizeInBits();
1655
1656 // Create the machine instruction DAG
1657 SDValue Value = PlainStore ? PlainStore->getValue() : AtomicStore->getVal();
1658
1659 assert(isPowerOf2_32(ToTypeWidth) && ToTypeWidth >= 8 && ToTypeWidth <= 128 &&
1660 "Invalid width for store");
1661
1662 const auto [Base, Offset] = selectADDR(ST->getBasePtr(), CurDAG);
1663
1664 // Extract eviction/prefetch hint and cache policy register.
1665 const auto [EvictionAndPrefetchHint, PolicyReg] = getMemCacheHintOperands(
1666 ST,
1667 {NVPTXMemCacheHintInstruction::St, CodeAddrSpace,
1668 /*NumElts=*/1, /*EltWidth=*/ToTypeWidth, ST->isVolatile()},
1669 DL);
1670
1671 SDValue Ops[] = {selectPossiblyImm(Value),
1672 getI32Imm(Ordering, DL),
1673 getI32Imm(Scope, DL),
1674 getI32Imm(CodeAddrSpace, DL),
1675 getI32Imm(ToTypeWidth, DL),
1676 Base,
1677 Offset,
1678 EvictionAndPrefetchHint,
1679 PolicyReg,
1680 Chain};
1681
1682 const std::optional<unsigned> Opcode =
1683 pickOpcodeForVT(Value.getSimpleValueType().SimpleTy, NVPTX::ST_i16,
1684 NVPTX::ST_i32, NVPTX::ST_i64);
1685 if (!Opcode)
1686 return false;
1687
1688 SDNode *NVPTXST = CurDAG->getMachineNode(*Opcode, DL, MVT::Other, Ops);
1689
1690 if (!NVPTXST)
1691 return false;
1692
1693 MachineMemOperand *MemRef = ST->getMemOperand();
1694 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXST), {MemRef});
1695 ReplaceNode(ST, NVPTXST);
1696 return true;
1697}
1698
1699bool NVPTXDAGToDAGISel::tryStoreVector(SDNode *N) {
1701 const unsigned TotalWidth = ST->getMemoryVT().getSizeInBits();
1702
1703 // Address Space Setting
1704 const auto CodeAddrSpace = getAddrSpace(ST);
1705 if (CodeAddrSpace == NVPTX::AddressSpace::Const) {
1706 report_fatal_error("Cannot store to pointer that points to constant "
1707 "memory space");
1708 }
1709
1710 SDLoc DL(ST);
1711 SDValue Chain = ST->getChain();
1712 const auto [Ordering, Scope] = insertMemoryInstructionFence(DL, Chain, ST);
1713
1714 const unsigned NumElts = getStoreVectorNumElts(ST);
1715
1717 for (auto &V : ST->ops().slice(1, NumElts))
1718 Ops.push_back(selectPossiblyImm(V));
1719 SDValue Addr = N->getOperand(NumElts + 1);
1720 const unsigned ToTypeWidth = TotalWidth / NumElts;
1721
1722 assert(isPowerOf2_32(ToTypeWidth) && ToTypeWidth >= 8 && ToTypeWidth <= 128 &&
1723 TotalWidth <= 256 && "Invalid width for store");
1724
1725 // Extract eviction/prefetch hint and cache policy register.
1726 const auto [EvictionAndPrefetchHint, PolicyReg] = getMemCacheHintOperands(
1727 ST,
1728 {NVPTXMemCacheHintInstruction::St, CodeAddrSpace,
1729 /*NumElts=*/NumElts, /*EltWidth=*/ToTypeWidth, ST->isVolatile()},
1730 DL);
1731
1732 const auto [Base, Offset] = selectADDR(Addr, CurDAG);
1733 Ops.append({getI32Imm(Ordering, DL), getI32Imm(Scope, DL),
1734 getI32Imm(CodeAddrSpace, DL), getI32Imm(ToTypeWidth, DL), Base,
1735 Offset, EvictionAndPrefetchHint, PolicyReg, Chain});
1736
1737 const MVT::SimpleValueType EltVT =
1738 ST->getOperand(1).getSimpleValueType().SimpleTy;
1739 std::optional<unsigned> Opcode;
1740 switch (ST->getOpcode()) {
1741 default:
1742 return false;
1743 case NVPTXISD::StoreV2:
1744 Opcode = pickOpcodeForVT(EltVT, NVPTX::STV_i16_v2, NVPTX::STV_i32_v2,
1745 NVPTX::STV_i64_v2);
1746 break;
1747 case NVPTXISD::StoreV4:
1748 Opcode = pickOpcodeForVT(EltVT, NVPTX::STV_i16_v4, NVPTX::STV_i32_v4,
1749 NVPTX::STV_i64_v4);
1750 break;
1751 case NVPTXISD::StoreV8:
1752 Opcode = pickOpcodeForVT(EltVT, {/* no v8i16 */}, NVPTX::STV_i32_v8,
1753 {/* no v8i64 */});
1754 break;
1755 }
1756
1757 if (!Opcode)
1758 return false;
1759
1760 SDNode *NVPTXST = CurDAG->getMachineNode(*Opcode, DL, MVT::Other, Ops);
1761
1762 MachineMemOperand *MemRef = ST->getMemOperand();
1763 CurDAG->setNodeMemRefs(cast<MachineSDNode>(NVPTXST), {MemRef});
1764
1765 ReplaceNode(ST, NVPTXST);
1766 return true;
1767}
1768
1769/// SelectBFE - Look for instruction sequences that can be made more efficient
1770/// by using the 'bfe' (bit-field extract) PTX instruction
1771bool NVPTXDAGToDAGISel::tryBFE(SDNode *N) {
1772 SDLoc DL(N);
1773 SDValue LHS = N->getOperand(0);
1774 SDValue RHS = N->getOperand(1);
1775 SDValue Len;
1776 SDValue Start;
1777 SDValue Val;
1778 bool IsSigned = false;
1779
1780 if (N->getOpcode() == ISD::AND) {
1781 // Canonicalize the operands
1782 // We want 'and %val, %mask'
1784 std::swap(LHS, RHS);
1785 }
1786
1788 if (!Mask) {
1789 // We need a constant mask on the RHS of the AND
1790 return false;
1791 }
1792
1793 // Extract the mask bits
1794 uint64_t MaskVal = Mask->getZExtValue();
1795 if (!isMask_64(MaskVal)) {
1796 // We *could* handle shifted masks here, but doing so would require an
1797 // 'and' operation to fix up the low-order bits so we would trade
1798 // shr+and for bfe+and, which has the same throughput
1799 return false;
1800 }
1801
1802 // How many bits are in our mask?
1803 int64_t NumBits = countr_one(MaskVal);
1804 Len = CurDAG->getTargetConstant(NumBits, DL, MVT::i32);
1805
1806 if (LHS.getOpcode() == ISD::SRL || LHS.getOpcode() == ISD::SRA) {
1807 // We have a 'srl/and' pair, extract the effective start bit and length
1808 Val = LHS.getNode()->getOperand(0);
1809 Start = LHS.getNode()->getOperand(1);
1810 ConstantSDNode *StartConst = dyn_cast<ConstantSDNode>(Start);
1811 if (StartConst) {
1812 uint64_t StartVal = StartConst->getZExtValue();
1813 // How many "good" bits do we have left? "good" is defined here as bits
1814 // that exist in the original value, not shifted in.
1815 int64_t GoodBits = Start.getValueSizeInBits() - StartVal;
1816 if (NumBits > GoodBits) {
1817 // Do not handle the case where bits have been shifted in. In theory
1818 // we could handle this, but the cost is likely higher than just
1819 // emitting the srl/and pair.
1820 return false;
1821 }
1822 Start = CurDAG->getTargetConstant(StartVal, DL, MVT::i32);
1823 } else {
1824 // Do not handle the case where the shift amount (can be zero if no srl
1825 // was found) is not constant. We could handle this case, but it would
1826 // require run-time logic that would be more expensive than just
1827 // emitting the srl/and pair.
1828 return false;
1829 }
1830 } else {
1831 // Do not handle the case where the LHS of the and is not a shift. While
1832 // it would be trivial to handle this case, it would just transform
1833 // 'and' -> 'bfe', but 'and' has higher-throughput.
1834 return false;
1835 }
1836 } else if (N->getOpcode() == ISD::SRL || N->getOpcode() == ISD::SRA) {
1837 if (LHS->getOpcode() == ISD::AND) {
1839 if (!ShiftCnst) {
1840 // Shift amount must be constant
1841 return false;
1842 }
1843
1844 uint64_t ShiftAmt = ShiftCnst->getZExtValue();
1845
1846 SDValue AndLHS = LHS->getOperand(0);
1847 SDValue AndRHS = LHS->getOperand(1);
1848
1849 // Canonicalize the AND to have the mask on the RHS
1850 if (isa<ConstantSDNode>(AndLHS)) {
1851 std::swap(AndLHS, AndRHS);
1852 }
1853
1854 ConstantSDNode *MaskCnst = dyn_cast<ConstantSDNode>(AndRHS);
1855 if (!MaskCnst) {
1856 // Mask must be constant
1857 return false;
1858 }
1859
1860 uint64_t MaskVal = MaskCnst->getZExtValue();
1861 uint64_t NumZeros;
1862 uint64_t NumBits;
1863 if (isMask_64(MaskVal)) {
1864 NumZeros = 0;
1865 // The number of bits in the result bitfield will be the number of
1866 // trailing ones (the AND) minus the number of bits we shift off
1867 NumBits = llvm::countr_one(MaskVal) - ShiftAmt;
1868 } else if (isShiftedMask_64(MaskVal)) {
1869 NumZeros = llvm::countr_zero(MaskVal);
1870 unsigned NumOnes = llvm::countr_one(MaskVal >> NumZeros);
1871 // The number of bits in the result bitfield will be the number of
1872 // trailing zeros plus the number of set bits in the mask minus the
1873 // number of bits we shift off
1874 NumBits = NumZeros + NumOnes - ShiftAmt;
1875 } else {
1876 // This is not a mask we can handle
1877 return false;
1878 }
1879
1880 if (ShiftAmt < NumZeros) {
1881 // Handling this case would require extra logic that would make this
1882 // transformation non-profitable
1883 return false;
1884 }
1885
1886 Val = AndLHS;
1887 Start = CurDAG->getTargetConstant(ShiftAmt, DL, MVT::i32);
1888 Len = CurDAG->getTargetConstant(NumBits, DL, MVT::i32);
1889
1890 // If pre-shift AND includes the sign bit in the bitfield, we must use
1891 // signed BFE to replicate that bit during bitfield extraction. If the
1892 // sign bit is not part of the mask, unsigned BFE will zero out upper bits
1893 // of the result
1894 if (N->getOpcode() == ISD::SRA)
1895 IsSigned = (ShiftAmt + NumBits) == Val.getValueSizeInBits();
1896 } else if (LHS->getOpcode() == ISD::SHL) {
1897 // Here, we have a pattern like:
1898 //
1899 // (sra (shl val, NN), MM)
1900 // or
1901 // (srl (shl val, NN), MM)
1902 //
1903 // If MM >= NN, we can efficiently optimize this with bfe
1904 Val = LHS->getOperand(0);
1905
1906 SDValue ShlRHS = LHS->getOperand(1);
1907 ConstantSDNode *ShlCnst = dyn_cast<ConstantSDNode>(ShlRHS);
1908 if (!ShlCnst) {
1909 // Shift amount must be constant
1910 return false;
1911 }
1912 uint64_t InnerShiftAmt = ShlCnst->getZExtValue();
1913
1914 SDValue ShrRHS = RHS;
1915 ConstantSDNode *ShrCnst = dyn_cast<ConstantSDNode>(ShrRHS);
1916 if (!ShrCnst) {
1917 // Shift amount must be constant
1918 return false;
1919 }
1920 uint64_t OuterShiftAmt = ShrCnst->getZExtValue();
1921
1922 // To avoid extra codegen and be profitable, we need Outer >= Inner
1923 if (OuterShiftAmt < InnerShiftAmt) {
1924 return false;
1925 }
1926
1927 // If the outer shift is more than the type size, we have no bitfield to
1928 // extract (since we also check that the inner shift is <= the outer shift
1929 // then this also implies that the inner shift is < the type size)
1930 if (OuterShiftAmt >= Val.getValueSizeInBits()) {
1931 return false;
1932 }
1933
1934 Start = CurDAG->getTargetConstant(OuterShiftAmt - InnerShiftAmt, DL,
1935 MVT::i32);
1936 Len = CurDAG->getTargetConstant(Val.getValueSizeInBits() - OuterShiftAmt,
1937 DL, MVT::i32);
1938
1939 if (N->getOpcode() == ISD::SRA) {
1940 // If we have a arithmetic right shift, we need to use the signed bfe
1941 // variant
1942 IsSigned = true;
1943 }
1944 } else {
1945 // No can do...
1946 return false;
1947 }
1948 } else {
1949 // No can do...
1950 return false;
1951 }
1952
1953
1954 unsigned Opc;
1955 // For the BFE operations we form here from "and" and "srl", always use the
1956 // unsigned variants.
1957 if (Val.getValueType() == MVT::i32) {
1958 if (IsSigned) {
1959 Opc = NVPTX::BFE_S32rii;
1960 } else {
1961 Opc = NVPTX::BFE_U32rii;
1962 }
1963 } else if (Val.getValueType() == MVT::i64) {
1964 if (IsSigned) {
1965 Opc = NVPTX::BFE_S64rii;
1966 } else {
1967 Opc = NVPTX::BFE_U64rii;
1968 }
1969 } else {
1970 // We cannot handle this type
1971 return false;
1972 }
1973
1974 SDValue Ops[] = {
1975 Val, Start, Len
1976 };
1977
1978 ReplaceNode(N, CurDAG->getMachineNode(Opc, DL, N->getVTList(), Ops));
1979 return true;
1980}
1981
1982// Select bf16/bf16v2 FADD, FSUB, FMUL as fma on targets with only fma
1983bool NVPTXDAGToDAGISel::tryBF16ArithToFMA(SDNode *N) {
1984 EVT VT = SDValue(N, 0).getValueType();
1985 if (VT.getScalarType() != MVT::bf16)
1986 return false;
1987
1988 const NVPTXSubtarget *STI = TM.getSubtargetImpl();
1989 if (STI->hasNativeBF16Support(N->getOpcode()))
1990 return false;
1991
1992 const bool IsVec = VT.isVector();
1993 assert(!IsVec || VT.getVectorNumElements() == 2);
1994 SDLoc DL(N);
1995 SDValue N0 = N->getOperand(0);
1996 SDValue N1 = N->getOperand(1);
1998 auto GetConstant = [&](float Value) -> SDValue {
1999 // BF16 immediates must be legalized to integer register values
2000 APFloat APF(Value);
2001 bool LosesInfo;
2002 APF.convert(APFloat::BFloat(), APFloat::rmNearestTiesToEven, &LosesInfo);
2003 assert(!LosesInfo);
2004 if (IsVec) {
2005 auto API = APF.bitcastToAPInt();
2006 API = API.concat(API);
2007 auto Const = CurDAG->getTargetConstant(API, DL, MVT::i32);
2008 return SDValue(CurDAG->getMachineNode(NVPTX::MOV_B32_i, DL, VT, Const),
2009 0);
2010 }
2011 auto Const = CurDAG->getTargetConstantFP(APF, DL, VT);
2012 return SDValue(CurDAG->getMachineNode(NVPTX::MOV_BF16_i, DL, VT, Const), 0);
2013 };
2014
2015 switch (N->getOpcode()) {
2016 case ISD::FADD:
2017 // add(a, b) -> fma(a, 1.0, b)
2018 Operands = {N0, GetConstant(1.0), N1};
2019 break;
2020 case ISD::FSUB:
2021 // sub(a, b) -> fma(b, -1.0, a)
2022 Operands = {N1, GetConstant(-1.0), N0};
2023 break;
2024 case ISD::FMUL:
2025 // mul(a, b) -> fma(a, b, -0.0)
2026 // NOTE: The identity is -0, not 0, because -0 + 0 == 0 for floats
2027 Operands = {N0, N1, GetConstant(-0.0)};
2028 break;
2029 default:
2030 llvm_unreachable("Unexpected opcode");
2031 };
2032
2033 int Opcode = IsVec ? NVPTX::FMA_BF16x2rrr : NVPTX::FMA_BF16rrr;
2034 MachineSDNode *FMA = CurDAG->getMachineNode(Opcode, DL, VT, Operands);
2035 ReplaceNode(N, FMA);
2036 return true;
2037}
2038
2039SDValue NVPTXDAGToDAGISel::selectPossiblyImm(SDValue V) {
2040 if (V.getOpcode() == ISD::BITCAST)
2041 V = V.getOperand(0);
2042
2043 if (auto *CN = dyn_cast<ConstantSDNode>(V))
2044 return CurDAG->getTargetConstant(CN->getAPIntValue(), SDLoc(V),
2045 V.getValueType());
2046 if (auto *CN = dyn_cast<ConstantFPSDNode>(V))
2047 return CurDAG->getTargetConstantFP(CN->getValueAPF(), SDLoc(V),
2048 V.getValueType());
2049 return V;
2050}
2051
2052/// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
2053/// inline asm expressions.
2054bool NVPTXDAGToDAGISel::SelectInlineAsmMemoryOperand(
2055 const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
2056 std::vector<SDValue> &OutOps) {
2057 switch (ConstraintID) {
2058 default:
2059 return true;
2060 case InlineAsm::ConstraintCode::m: { // memory
2061 const auto [Base, Offset] = selectADDR(Op, CurDAG);
2062 OutOps.push_back(Base);
2063 OutOps.push_back(Offset);
2064 return false;
2065 }
2066 }
2067 return true;
2068}
2069
2070void NVPTXDAGToDAGISel::SelectV2I64toI128(SDNode *N) {
2071 // Lower a CopyToReg with two 64-bit inputs
2072 // Dst:i128, lo:i64, hi:i64
2073 //
2074 // CopyToReg Dst, lo, hi;
2075 //
2076 // ==>
2077 //
2078 // tmp = V2I64toI128 {lo, hi};
2079 // CopyToReg Dst, tmp;
2080 SDValue Dst = N->getOperand(1);
2081 SDValue Lo = N->getOperand(2);
2082 SDValue Hi = N->getOperand(3);
2083
2084 SDLoc DL(N);
2085 SDNode *Mov =
2086 CurDAG->getMachineNode(NVPTX::V2I64toI128, DL, MVT::i128, {Lo, Hi});
2087
2088 SmallVector<SDValue, 4> NewOps(N->getNumOperands() - 1);
2089 NewOps[0] = N->getOperand(0);
2090 NewOps[1] = Dst;
2091 NewOps[2] = SDValue(Mov, 0);
2092 if (N->getNumOperands() == 5)
2093 NewOps[3] = N->getOperand(4);
2094 SDValue NewValue = CurDAG->getNode(ISD::CopyToReg, DL, SmallVector<EVT>(N->values()), NewOps);
2095
2096 ReplaceNode(N, NewValue.getNode());
2097}
2098
2099void NVPTXDAGToDAGISel::SelectI128toV2I64(SDNode *N) {
2100 // Lower CopyFromReg from a 128-bit regs to two 64-bit regs
2101 // Dst:i128, Src:i128
2102 //
2103 // {lo, hi} = CopyFromReg Src
2104 //
2105 // ==>
2106 //
2107 // {lo, hi} = I128toV2I64 Src
2108 //
2109 SDValue Ch = N->getOperand(0);
2110 SDValue Src = N->getOperand(1);
2111 SDValue Glue = N->getOperand(2);
2112 SDLoc DL(N);
2113
2114 // Add Glue and Ch to the operands and results to avoid break the execution
2115 // order
2116 SDNode *Mov = CurDAG->getMachineNode(
2117 NVPTX::I128toV2I64, DL,
2118 {MVT::i64, MVT::i64, Ch.getValueType(), Glue.getValueType()},
2119 {Src, Ch, Glue});
2120
2121 ReplaceNode(N, Mov);
2122}
2123
2124bool NVPTXDAGToDAGISel::tryFence(SDNode *N) {
2125 SDLoc DL(N);
2126 assert(N->getOpcode() == ISD::ATOMIC_FENCE);
2127 auto Scope = Scopes[N->getConstantOperandVal(2)];
2128
2129 // Singlethread fences have no inter-thread synchronization requirements.
2130 // Note: std::atomic_signal_fence lowers to singlethread LLVM IR fences;
2131 // this intentionally drops these before emitting PTX.
2132 if (Scope == NVPTX::Scope::Thread) {
2133 CurDAG->ReplaceAllUsesOfValueWith(SDValue(N, 0), N->getOperand(0));
2134 CurDAG->RemoveDeadNode(N);
2135 return true;
2136 }
2137
2138 unsigned int FenceOp = getFenceOp(
2139 NVPTX::Ordering(N->getConstantOperandVal(1)), Scope, Subtarget);
2140 SDValue Chain = N->getOperand(0);
2141 SDNode *FenceNode = CurDAG->getMachineNode(FenceOp, DL, MVT::Other, Chain);
2142 ReplaceNode(N, FenceNode);
2143 return true;
2144}
2145
2146NVPTXScopes::NVPTXScopes(LLVMContext &C, const Triple &T) : Context(&C) {
2147 auto ScopeID = [&](AtomicScope Scope) {
2148 return C.getOrInsertSyncScopeID(*getAtomicScopeIRString(T, Scope));
2149 };
2155}
2156
2157NVPTX::Scope NVPTXScopes::operator[](SyncScope::ID ID) const {
2158 if (Scopes.empty())
2159 llvm_unreachable("NVPTX Scopes must be initialized before calling "
2160 "NVPTXScopes::operator[]");
2161
2162 auto S = Scopes.find(ID);
2163 if (S == Scopes.end()) {
2164 auto scopeName = Context->getSyncScopeName(ID);
2165 assert(scopeName.has_value() && "Scope name must exist.");
2166
2167 // Build list of supported syncscopes programmatically
2168 SmallVector<StringRef> supportedScopes;
2169 for (const auto &Entry : Scopes) {
2170 if (auto name = Context->getSyncScopeName(Entry.first))
2171 supportedScopes.push_back(name->empty() ? "<empty string>" : *name);
2172 }
2173
2175 formatv("NVPTX backend does not support syncscope \"{0}\" (ID={1}).\n"
2176 "Supported syncscopes are: {2}.",
2177 scopeName.value(), int(ID),
2178 make_range(supportedScopes.begin(), supportedScopes.end())));
2179 }
2180 return S->second;
2181}
2182
2183bool NVPTXScopes::empty() const { return Scopes.size() == 0; }
2184
2185#define TCGEN05_ST_OPCODE(SHAPE, NUM) \
2186 (enableUnpack ? NVPTX::TCGEN05_ST_##SHAPE##_##NUM##_UNPACK \
2187 : NVPTX::TCGEN05_ST_##SHAPE##_##NUM)
2188
2189static unsigned getTcgen05StOpcode(unsigned IID, bool enableUnpack) {
2190 switch (IID) {
2191 case Intrinsic::nvvm_tcgen05_st_16x64b_x1:
2192 return TCGEN05_ST_OPCODE(16x64b, x1);
2193 case Intrinsic::nvvm_tcgen05_st_16x64b_x2:
2194 return TCGEN05_ST_OPCODE(16x64b, x2);
2195 case Intrinsic::nvvm_tcgen05_st_16x64b_x4:
2196 return TCGEN05_ST_OPCODE(16x64b, x4);
2197 case Intrinsic::nvvm_tcgen05_st_16x64b_x8:
2198 return TCGEN05_ST_OPCODE(16x64b, x8);
2199 case Intrinsic::nvvm_tcgen05_st_16x64b_x16:
2200 return TCGEN05_ST_OPCODE(16x64b, x16);
2201 case Intrinsic::nvvm_tcgen05_st_16x64b_x32:
2202 return TCGEN05_ST_OPCODE(16x64b, x32);
2203 case Intrinsic::nvvm_tcgen05_st_16x64b_x64:
2204 return TCGEN05_ST_OPCODE(16x64b, x64);
2205 case Intrinsic::nvvm_tcgen05_st_16x64b_x128:
2206 return TCGEN05_ST_OPCODE(16x64b, x128);
2207 case Intrinsic::nvvm_tcgen05_st_16x128b_x1:
2208 return TCGEN05_ST_OPCODE(16x128b, x1);
2209 case Intrinsic::nvvm_tcgen05_st_16x128b_x2:
2210 return TCGEN05_ST_OPCODE(16x128b, x2);
2211 case Intrinsic::nvvm_tcgen05_st_16x128b_x4:
2212 return TCGEN05_ST_OPCODE(16x128b, x4);
2213 case Intrinsic::nvvm_tcgen05_st_16x128b_x8:
2214 return TCGEN05_ST_OPCODE(16x128b, x8);
2215 case Intrinsic::nvvm_tcgen05_st_16x128b_x16:
2216 return TCGEN05_ST_OPCODE(16x128b, x16);
2217 case Intrinsic::nvvm_tcgen05_st_16x128b_x32:
2218 return TCGEN05_ST_OPCODE(16x128b, x32);
2219 case Intrinsic::nvvm_tcgen05_st_16x128b_x64:
2220 return TCGEN05_ST_OPCODE(16x128b, x64);
2221 case Intrinsic::nvvm_tcgen05_st_16x256b_x1:
2222 return TCGEN05_ST_OPCODE(16x256b, x1);
2223 case Intrinsic::nvvm_tcgen05_st_16x256b_x2:
2224 return TCGEN05_ST_OPCODE(16x256b, x2);
2225 case Intrinsic::nvvm_tcgen05_st_16x256b_x4:
2226 return TCGEN05_ST_OPCODE(16x256b, x4);
2227 case Intrinsic::nvvm_tcgen05_st_16x256b_x8:
2228 return TCGEN05_ST_OPCODE(16x256b, x8);
2229 case Intrinsic::nvvm_tcgen05_st_16x256b_x16:
2230 return TCGEN05_ST_OPCODE(16x256b, x16);
2231 case Intrinsic::nvvm_tcgen05_st_16x256b_x32:
2232 return TCGEN05_ST_OPCODE(16x256b, x32);
2233 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x1:
2234 return TCGEN05_ST_OPCODE(16x32bx2, x1);
2235 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x2:
2236 return TCGEN05_ST_OPCODE(16x32bx2, x2);
2237 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x4:
2238 return TCGEN05_ST_OPCODE(16x32bx2, x4);
2239 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x8:
2240 return TCGEN05_ST_OPCODE(16x32bx2, x8);
2241 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x16:
2242 return TCGEN05_ST_OPCODE(16x32bx2, x16);
2243 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x32:
2244 return TCGEN05_ST_OPCODE(16x32bx2, x32);
2245 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x64:
2246 return TCGEN05_ST_OPCODE(16x32bx2, x64);
2247 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x128:
2248 return TCGEN05_ST_OPCODE(16x32bx2, x128);
2249 case Intrinsic::nvvm_tcgen05_st_32x32b_x1:
2250 return TCGEN05_ST_OPCODE(32x32b, x1);
2251 case Intrinsic::nvvm_tcgen05_st_32x32b_x2:
2252 return TCGEN05_ST_OPCODE(32x32b, x2);
2253 case Intrinsic::nvvm_tcgen05_st_32x32b_x4:
2254 return TCGEN05_ST_OPCODE(32x32b, x4);
2255 case Intrinsic::nvvm_tcgen05_st_32x32b_x8:
2256 return TCGEN05_ST_OPCODE(32x32b, x8);
2257 case Intrinsic::nvvm_tcgen05_st_32x32b_x16:
2258 return TCGEN05_ST_OPCODE(32x32b, x16);
2259 case Intrinsic::nvvm_tcgen05_st_32x32b_x32:
2260 return TCGEN05_ST_OPCODE(32x32b, x32);
2261 case Intrinsic::nvvm_tcgen05_st_32x32b_x64:
2262 return TCGEN05_ST_OPCODE(32x32b, x64);
2263 case Intrinsic::nvvm_tcgen05_st_32x32b_x128:
2264 return TCGEN05_ST_OPCODE(32x32b, x128);
2265 }
2266 llvm_unreachable("unhandled tcgen05.st lowering");
2267}
2268
2269void NVPTXDAGToDAGISel::SelectTcgen05St(SDNode *N, bool hasOffset) {
2270 if (!Subtarget->hasTcgen05InstSupport())
2272 "tcgen05.st is not supported on this architecture variant");
2273
2274 SDLoc DL(N);
2275 unsigned IID = cast<ConstantSDNode>(N->getOperand(1))->getZExtValue();
2276
2278 N->getOperand(2) // taddr
2279 };
2280
2281 if (hasOffset)
2282 Operands.push_back(CurDAG->getTargetConstant(
2283 cast<ConstantSDNode>(N->getOperand(3))->getZExtValue(), DL,
2284 MVT::i32)); // Offset
2285
2286 for (unsigned I = hasOffset ? 4 : 3; I < (N->getNumOperands() - 1); I++)
2287 Operands.push_back(N->getOperand(I));
2288
2289 bool enableUnpack =
2290 cast<ConstantSDNode>(N->getOperand(N->getNumOperands() - 1))
2291 ->getZExtValue();
2292
2293 Operands.push_back(N->getOperand(0)); // Chain
2294 ReplaceNode(N, CurDAG->getMachineNode(getTcgen05StOpcode(IID, enableUnpack),
2295 DL, N->getVTList(), Operands));
2296}
2297
2298bool NVPTXDAGToDAGISel::tryIntrinsicVoid(SDNode *N) {
2299 unsigned IID = N->getConstantOperandVal(1);
2300 switch (IID) {
2301 default:
2302 return false;
2303 case Intrinsic::nvvm_tcgen05_st_16x64b_x1:
2304 case Intrinsic::nvvm_tcgen05_st_16x64b_x2:
2305 case Intrinsic::nvvm_tcgen05_st_16x64b_x4:
2306 case Intrinsic::nvvm_tcgen05_st_16x64b_x8:
2307 case Intrinsic::nvvm_tcgen05_st_16x64b_x16:
2308 case Intrinsic::nvvm_tcgen05_st_16x64b_x32:
2309 case Intrinsic::nvvm_tcgen05_st_16x64b_x64:
2310 case Intrinsic::nvvm_tcgen05_st_16x64b_x128:
2311 case Intrinsic::nvvm_tcgen05_st_32x32b_x1:
2312 case Intrinsic::nvvm_tcgen05_st_32x32b_x2:
2313 case Intrinsic::nvvm_tcgen05_st_32x32b_x4:
2314 case Intrinsic::nvvm_tcgen05_st_32x32b_x8:
2315 case Intrinsic::nvvm_tcgen05_st_32x32b_x16:
2316 case Intrinsic::nvvm_tcgen05_st_32x32b_x32:
2317 case Intrinsic::nvvm_tcgen05_st_32x32b_x64:
2318 case Intrinsic::nvvm_tcgen05_st_32x32b_x128:
2319 case Intrinsic::nvvm_tcgen05_st_16x128b_x1:
2320 case Intrinsic::nvvm_tcgen05_st_16x128b_x2:
2321 case Intrinsic::nvvm_tcgen05_st_16x128b_x4:
2322 case Intrinsic::nvvm_tcgen05_st_16x128b_x8:
2323 case Intrinsic::nvvm_tcgen05_st_16x128b_x16:
2324 case Intrinsic::nvvm_tcgen05_st_16x128b_x32:
2325 case Intrinsic::nvvm_tcgen05_st_16x128b_x64:
2326 case Intrinsic::nvvm_tcgen05_st_16x256b_x1:
2327 case Intrinsic::nvvm_tcgen05_st_16x256b_x2:
2328 case Intrinsic::nvvm_tcgen05_st_16x256b_x4:
2329 case Intrinsic::nvvm_tcgen05_st_16x256b_x8:
2330 case Intrinsic::nvvm_tcgen05_st_16x256b_x16:
2331 case Intrinsic::nvvm_tcgen05_st_16x256b_x32: {
2332 SelectTcgen05St(N);
2333 return true;
2334 }
2335
2336 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x1:
2337 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x2:
2338 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x4:
2339 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x8:
2340 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x16:
2341 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x32:
2342 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x64:
2343 case Intrinsic::nvvm_tcgen05_st_16x32bx2_x128: {
2344 SelectTcgen05St(N, /* hasOffset */ true);
2345 return true;
2346 }
2347 }
2348}
2349
2350void NVPTXDAGToDAGISel::selectAtomicSwap128(SDNode *N) {
2351 MemSDNode *AN = cast<MemSDNode>(N);
2352 SDLoc dl(N);
2353
2354 const SDValue Chain = N->getOperand(0);
2355 const auto [Base, Offset] = selectADDR(N->getOperand(1), CurDAG);
2357 Ops.append(N->op_begin() + 2, N->op_end());
2358 Ops.append({getI32Imm(getMemOrder(AN), dl), getI32Imm(getAtomicScope(AN), dl),
2359 getI32Imm(getAddrSpace(AN), dl)});
2360
2361 if (N->getOpcode() == NVPTXISD::ATOMIC_SWAP_B128) {
2362 unsigned EltWidth = AN->getMemoryVT().getFixedSizeInBits();
2363 NVPTXMemCacheHintAccess Access{NVPTXMemCacheHintInstruction::Atom,
2364 getAddrSpace(AN),
2365 /*NumElts=*/1, EltWidth, AN->isVolatile()};
2366 const auto [EvictionAndPrefetchHint, CachePolicyReg] =
2367 getMemCacheHintOperands(AN, Access, dl);
2368 Ops.push_back(EvictionAndPrefetchHint);
2369 Ops.push_back(CachePolicyReg);
2370 }
2371
2372 Ops.push_back(Chain);
2373
2374 assert(N->getOpcode() == NVPTXISD::ATOMIC_CMP_SWAP_B128 ||
2375 N->getOpcode() == NVPTXISD::ATOMIC_SWAP_B128);
2376 unsigned Opcode = N->getOpcode() == NVPTXISD::ATOMIC_SWAP_B128
2377 ? NVPTX::ATOM_EXCH_B128
2378 : NVPTX::ATOM_CAS_B128;
2379
2380 auto *ATOM = CurDAG->getMachineNode(Opcode, dl, N->getVTList(), Ops);
2381 CurDAG->setNodeMemRefs(ATOM, AN->getMemOperand());
2382
2383 ReplaceNode(N, ATOM);
2384}
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
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:149
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