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