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