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