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