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