LLVM 19.0.0git
NVPTXUtilities.cpp
Go to the documentation of this file.
1//===- NVPTXUtilities.cpp - Utility Functions -----------------------------===//
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 contains miscellaneous utility functions
10//
11//===----------------------------------------------------------------------===//
12
13#include "NVPTXUtilities.h"
14#include "NVPTX.h"
15#include "NVPTXTargetMachine.h"
16#include "llvm/IR/Constants.h"
17#include "llvm/IR/Function.h"
20#include "llvm/IR/Module.h"
21#include "llvm/IR/Operator.h"
23#include "llvm/Support/Mutex.h"
24#include <algorithm>
25#include <cstring>
26#include <map>
27#include <mutex>
28#include <optional>
29#include <string>
30#include <vector>
31
32namespace llvm {
33
34namespace {
35typedef std::map<std::string, std::vector<unsigned> > key_val_pair_t;
36typedef std::map<const GlobalValue *, key_val_pair_t> global_val_annot_t;
37
38struct AnnotationCache {
39 sys::Mutex Lock;
40 std::map<const Module *, global_val_annot_t> Cache;
41};
42
43AnnotationCache &getAnnotationCache() {
44 static AnnotationCache AC;
45 return AC;
46}
47} // anonymous namespace
48
50 auto &AC = getAnnotationCache();
51 std::lock_guard<sys::Mutex> Guard(AC.Lock);
52 AC.Cache.erase(Mod);
53}
54
55static void readIntVecFromMDNode(const MDNode *MetadataNode,
56 std::vector<unsigned> &Vec) {
57 for (unsigned i = 0, e = MetadataNode->getNumOperands(); i != e; ++i) {
58 ConstantInt *Val =
59 mdconst::extract<ConstantInt>(MetadataNode->getOperand(i));
60 Vec.push_back(Val->getZExtValue());
61 }
62}
63
64static void cacheAnnotationFromMD(const MDNode *MetadataNode,
65 key_val_pair_t &retval) {
66 auto &AC = getAnnotationCache();
67 std::lock_guard<sys::Mutex> Guard(AC.Lock);
68 assert(MetadataNode && "Invalid mdnode for annotation");
69 assert((MetadataNode->getNumOperands() % 2) == 1 &&
70 "Invalid number of operands");
71 // start index = 1, to skip the global variable key
72 // increment = 2, to skip the value for each property-value pairs
73 for (unsigned i = 1, e = MetadataNode->getNumOperands(); i != e; i += 2) {
74 // property
75 const MDString *prop = dyn_cast<MDString>(MetadataNode->getOperand(i));
76 assert(prop && "Annotation property not a string");
77 std::string Key = prop->getString().str();
78
79 // value
80 if (ConstantInt *Val = mdconst::dyn_extract<ConstantInt>(
81 MetadataNode->getOperand(i + 1))) {
82 retval[Key].push_back(Val->getZExtValue());
83 } else if (MDNode *VecMd =
84 dyn_cast<MDNode>(MetadataNode->getOperand(i + 1))) {
85 // note: only "grid_constant" annotations support vector MDNodes.
86 // assert: there can only exist one unique key value pair of
87 // the form (string key, MDNode node). Operands of such a node
88 // shall always be unsigned ints.
89 if (retval.find(Key) == retval.end()) {
90 readIntVecFromMDNode(VecMd, retval[Key]);
91 continue;
92 }
93 } else {
94 llvm_unreachable("Value operand not a constant int or an mdnode");
95 }
96 }
97}
98
99static void cacheAnnotationFromMD(const Module *m, const GlobalValue *gv) {
100 auto &AC = getAnnotationCache();
101 std::lock_guard<sys::Mutex> Guard(AC.Lock);
102 NamedMDNode *NMD = m->getNamedMetadata("nvvm.annotations");
103 if (!NMD)
104 return;
105 key_val_pair_t tmp;
106 for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) {
107 const MDNode *elem = NMD->getOperand(i);
108
109 GlobalValue *entity =
110 mdconst::dyn_extract_or_null<GlobalValue>(elem->getOperand(0));
111 // entity may be null due to DCE
112 if (!entity)
113 continue;
114 if (entity != gv)
115 continue;
116
117 // accumulate annotations for entity in tmp
118 cacheAnnotationFromMD(elem, tmp);
119 }
120
121 if (tmp.empty()) // no annotations for this gv
122 return;
123
124 if (AC.Cache.find(m) != AC.Cache.end())
125 AC.Cache[m][gv] = std::move(tmp);
126 else {
127 global_val_annot_t tmp1;
128 tmp1[gv] = std::move(tmp);
129 AC.Cache[m] = std::move(tmp1);
130 }
131}
132
133bool findOneNVVMAnnotation(const GlobalValue *gv, const std::string &prop,
134 unsigned &retval) {
135 auto &AC = getAnnotationCache();
136 std::lock_guard<sys::Mutex> Guard(AC.Lock);
137 const Module *m = gv->getParent();
138 if (AC.Cache.find(m) == AC.Cache.end())
140 else if (AC.Cache[m].find(gv) == AC.Cache[m].end())
142 if (AC.Cache[m][gv].find(prop) == AC.Cache[m][gv].end())
143 return false;
144 retval = AC.Cache[m][gv][prop][0];
145 return true;
146}
147
148static std::optional<unsigned>
149findOneNVVMAnnotation(const GlobalValue &GV, const std::string &PropName) {
150 unsigned RetVal;
151 if (findOneNVVMAnnotation(&GV, PropName, RetVal))
152 return RetVal;
153 return std::nullopt;
154}
155
156bool findAllNVVMAnnotation(const GlobalValue *gv, const std::string &prop,
157 std::vector<unsigned> &retval) {
158 auto &AC = getAnnotationCache();
159 std::lock_guard<sys::Mutex> Guard(AC.Lock);
160 const Module *m = gv->getParent();
161 if (AC.Cache.find(m) == AC.Cache.end())
163 else if (AC.Cache[m].find(gv) == AC.Cache[m].end())
165 if (AC.Cache[m][gv].find(prop) == AC.Cache[m][gv].end())
166 return false;
167 retval = AC.Cache[m][gv][prop];
168 return true;
169}
170
171bool isTexture(const Value &val) {
172 if (const GlobalValue *gv = dyn_cast<GlobalValue>(&val)) {
173 unsigned Annot;
174 if (findOneNVVMAnnotation(gv, "texture", Annot)) {
175 assert((Annot == 1) && "Unexpected annotation on a texture symbol");
176 return true;
177 }
178 }
179 return false;
180}
181
182bool isSurface(const Value &val) {
183 if (const GlobalValue *gv = dyn_cast<GlobalValue>(&val)) {
184 unsigned Annot;
185 if (findOneNVVMAnnotation(gv, "surface", Annot)) {
186 assert((Annot == 1) && "Unexpected annotation on a surface symbol");
187 return true;
188 }
189 }
190 return false;
191}
192
193static bool argHasNVVMAnnotation(const Value &Val,
194 const std::string &Annotation,
195 const bool StartArgIndexAtOne = false) {
196 if (const Argument *Arg = dyn_cast<Argument>(&Val)) {
197 const Function *Func = Arg->getParent();
198 std::vector<unsigned> Annot;
199 if (findAllNVVMAnnotation(Func, Annotation, Annot)) {
200 const unsigned BaseOffset = StartArgIndexAtOne ? 1 : 0;
201 if (is_contained(Annot, BaseOffset + Arg->getArgNo())) {
202 return true;
203 }
204 }
205 }
206 return false;
207}
208
210 if (const Argument *Arg = dyn_cast<Argument>(&V)) {
211 // "grid_constant" counts argument indices starting from 1
212 if (Arg->hasByValAttr() &&
213 argHasNVVMAnnotation(*Arg, "grid_constant", /*StartArgIndexAtOne*/true)) {
214 assert(isKernelFunction(*Arg->getParent()) &&
215 "only kernel arguments can be grid_constant");
216 return true;
217 }
218 }
219 return false;
220}
221
222bool isSampler(const Value &val) {
223 const char *AnnotationName = "sampler";
224
225 if (const GlobalValue *gv = dyn_cast<GlobalValue>(&val)) {
226 unsigned Annot;
227 if (findOneNVVMAnnotation(gv, AnnotationName, Annot)) {
228 assert((Annot == 1) && "Unexpected annotation on a sampler symbol");
229 return true;
230 }
231 }
232 return argHasNVVMAnnotation(val, AnnotationName);
233}
234
235bool isImageReadOnly(const Value &val) {
236 return argHasNVVMAnnotation(val, "rdoimage");
237}
238
239bool isImageWriteOnly(const Value &val) {
240 return argHasNVVMAnnotation(val, "wroimage");
241}
242
243bool isImageReadWrite(const Value &val) {
244 return argHasNVVMAnnotation(val, "rdwrimage");
245}
246
247bool isImage(const Value &val) {
248 return isImageReadOnly(val) || isImageWriteOnly(val) || isImageReadWrite(val);
249}
250
251bool isManaged(const Value &val) {
252 if(const GlobalValue *gv = dyn_cast<GlobalValue>(&val)) {
253 unsigned Annot;
254 if (findOneNVVMAnnotation(gv, "managed", Annot)) {
255 assert((Annot == 1) && "Unexpected annotation on a managed symbol");
256 return true;
257 }
258 }
259 return false;
260}
261
262std::string getTextureName(const Value &val) {
263 assert(val.hasName() && "Found texture variable with no name");
264 return std::string(val.getName());
265}
266
267std::string getSurfaceName(const Value &val) {
268 assert(val.hasName() && "Found surface variable with no name");
269 return std::string(val.getName());
270}
271
272std::string getSamplerName(const Value &val) {
273 assert(val.hasName() && "Found sampler variable with no name");
274 return std::string(val.getName());
275}
276
277std::optional<unsigned> getMaxNTIDx(const Function &F) {
278 return findOneNVVMAnnotation(F, "maxntidx");
279}
280
281std::optional<unsigned> getMaxNTIDy(const Function &F) {
282 return findOneNVVMAnnotation(F, "maxntidy");
283}
284
285std::optional<unsigned> getMaxNTIDz(const Function &F) {
286 return findOneNVVMAnnotation(F, "maxntidz");
287}
288
289std::optional<unsigned> getMaxNTID(const Function &F) {
290 // Note: The semantics here are a bit strange. The PTX ISA states the
291 // following (11.4.2. Performance-Tuning Directives: .maxntid):
292 //
293 // Note that this directive guarantees that the total number of threads does
294 // not exceed the maximum, but does not guarantee that the limit in any
295 // particular dimension is not exceeded.
296 std::optional<unsigned> MaxNTIDx = getMaxNTIDx(F);
297 std::optional<unsigned> MaxNTIDy = getMaxNTIDy(F);
298 std::optional<unsigned> MaxNTIDz = getMaxNTIDz(F);
299 if (MaxNTIDx || MaxNTIDy || MaxNTIDz)
300 return MaxNTIDx.value_or(1) * MaxNTIDy.value_or(1) * MaxNTIDz.value_or(1);
301 return std::nullopt;
302}
303
304bool getMaxClusterRank(const Function &F, unsigned &x) {
305 return findOneNVVMAnnotation(&F, "maxclusterrank", x);
306}
307
308std::optional<unsigned> getReqNTIDx(const Function &F) {
309 return findOneNVVMAnnotation(F, "reqntidx");
310}
311
312std::optional<unsigned> getReqNTIDy(const Function &F) {
313 return findOneNVVMAnnotation(F, "reqntidy");
314}
315
316std::optional<unsigned> getReqNTIDz(const Function &F) {
317 return findOneNVVMAnnotation(F, "reqntidz");
318}
319
320std::optional<unsigned> getReqNTID(const Function &F) {
321 // Note: The semantics here are a bit strange. See getMaxNTID.
322 std::optional<unsigned> ReqNTIDx = getReqNTIDx(F);
323 std::optional<unsigned> ReqNTIDy = getReqNTIDy(F);
324 std::optional<unsigned> ReqNTIDz = getReqNTIDz(F);
325 if (ReqNTIDx || ReqNTIDy || ReqNTIDz)
326 return ReqNTIDx.value_or(1) * ReqNTIDy.value_or(1) * ReqNTIDz.value_or(1);
327 return std::nullopt;
328}
329
330bool getMinCTASm(const Function &F, unsigned &x) {
331 return findOneNVVMAnnotation(&F, "minctasm", x);
332}
333
334bool getMaxNReg(const Function &F, unsigned &x) {
335 return findOneNVVMAnnotation(&F, "maxnreg", x);
336}
337
339 unsigned x = 0;
340 if (!findOneNVVMAnnotation(&F, "kernel", x)) {
341 // There is no NVVM metadata, check the calling convention
342 return F.getCallingConv() == CallingConv::PTX_Kernel;
343 }
344 return (x == 1);
345}
346
347MaybeAlign getAlign(const Function &F, unsigned Index) {
348 // First check the alignstack metadata
349 if (MaybeAlign StackAlign =
350 F.getAttributes().getAttributes(Index).getStackAlignment())
351 return StackAlign;
352
353 // If that is missing, check the legacy nvvm metadata
354 std::vector<unsigned> Vs;
355 bool retval = findAllNVVMAnnotation(&F, "align", Vs);
356 if (!retval)
357 return std::nullopt;
358 for (unsigned V : Vs)
359 if ((V >> 16) == Index)
360 return Align(V & 0xFFFF);
361
362 return std::nullopt;
363}
364
365MaybeAlign getAlign(const CallInst &I, unsigned Index) {
366 // First check the alignstack metadata
367 if (MaybeAlign StackAlign =
368 I.getAttributes().getAttributes(Index).getStackAlignment())
369 return StackAlign;
370
371 // If that is missing, check the legacy nvvm metadata
372 if (MDNode *alignNode = I.getMetadata("callalign")) {
373 for (int i = 0, n = alignNode->getNumOperands(); i < n; i++) {
374 if (const ConstantInt *CI =
375 mdconst::dyn_extract<ConstantInt>(alignNode->getOperand(i))) {
376 unsigned V = CI->getZExtValue();
377 if ((V >> 16) == Index)
378 return Align(V & 0xFFFF);
379 if ((V >> 16) > Index)
380 return std::nullopt;
381 }
382 }
383 }
384 return std::nullopt;
385}
386
388 return dyn_cast<Function>(CB->getCalledOperand()->stripPointerCasts());
389}
390
392 const auto &ST =
393 *static_cast<const NVPTXTargetMachine &>(TM).getSubtargetImpl();
394 if (!ST.hasNoReturn())
395 return false;
396
397 assert((isa<Function>(V) || isa<CallInst>(V)) &&
398 "Expect either a call instruction or a function");
399
400 if (const CallInst *CallI = dyn_cast<CallInst>(V))
401 return CallI->doesNotReturn() &&
402 CallI->getFunctionType()->getReturnType()->isVoidTy();
403
404 const Function *F = cast<Function>(V);
405 return F->doesNotReturn() &&
406 F->getFunctionType()->getReturnType()->isVoidTy() &&
408}
409
410bool Isv2x16VT(EVT VT) {
411 return (VT == MVT::v2f16 || VT == MVT::v2bf16 || VT == MVT::v2i16);
412}
413
414} // namespace llvm
This file contains the declarations for the subclasses of Constant, which represent the different fla...
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
Module.h This file contains the declarations for the Module class.
const char LLVMTargetMachineRef TM
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This class represents an incoming formal argument to a Function.
Definition: Argument.h:31
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
Definition: InstrTypes.h:1236
Value * getCalledOperand() const
Definition: InstrTypes.h:1458
This class represents a function call, abstracting a target machine's calling convention.
This is the shared class of boolean and integer constants.
Definition: Constants.h:81
uint64_t getZExtValue() const
Return the constant as a 64-bit unsigned integer value after it has been zero extended as appropriate...
Definition: Constants.h:155
Module * getParent()
Get the module that this global value is contained inside of...
Definition: GlobalValue.h:656
Metadata node.
Definition: Metadata.h:1067
const MDOperand & getOperand(unsigned I) const
Definition: Metadata.h:1428
unsigned getNumOperands() const
Return number of MDNode operands.
Definition: Metadata.h:1434
A single uniqued string.
Definition: Metadata.h:720
StringRef getString() const
Definition: Metadata.cpp:610
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:65
NamedMDNode * getNamedMetadata(const Twine &Name) const
Return the first NamedMDNode in the module with the specified name.
Definition: Module.cpp:262
A tuple of MDNodes.
Definition: Metadata.h:1729
MDNode * getOperand(unsigned i) const
Definition: Metadata.cpp:1381
unsigned getNumOperands() const
Definition: Metadata.cpp:1377
std::string str() const
str - Get the contents as an std::string.
Definition: StringRef.h:223
Primary interface to the complete machine description for the target machine.
Definition: TargetMachine.h:77
LLVM Value Representation.
Definition: Value.h:74
const Value * stripPointerCasts() const
Strip off pointer casts, all-zero GEPs and address space casts.
Definition: Value.cpp:693
bool hasName() const
Definition: Value.h:261
StringRef getName() const
Return a constant reference to the value's name.
Definition: Value.cpp:309
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ PTX_Kernel
Call to a PTX kernel. Passes all arguments in parameter space.
Definition: CallingConv.h:125
SmartMutex< false > Mutex
Mutex - A standard, always enforced mutex.
Definition: Mutex.h:66
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
bool shouldEmitPTXNoReturn(const Value *V, const TargetMachine &TM)
bool Isv2x16VT(EVT VT)
std::optional< unsigned > getMaxNTIDy(const Function &F)
static void readIntVecFromMDNode(const MDNode *MetadataNode, std::vector< unsigned > &Vec)
std::string getSamplerName(const Value &val)
bool isParamGridConstant(const Value &V)
bool getMinCTASm(const Function &F, unsigned &x)
bool isImage(const Value &val)
std::optional< unsigned > getMaxNTIDz(const Function &F)
MaybeAlign getAlign(const Function &F, unsigned Index)
std::optional< unsigned > getMaxNTIDx(const Function &F)
bool isImageReadOnly(const Value &val)
bool isManaged(const Value &val)
bool isSurface(const Value &val)
bool findAllNVVMAnnotation(const GlobalValue *gv, const std::string &prop, std::vector< unsigned > &retval)
static void cacheAnnotationFromMD(const MDNode *MetadataNode, key_val_pair_t &retval)
void clearAnnotationCache(const Module *Mod)
std::string getSurfaceName(const Value &val)
std::optional< unsigned > getReqNTIDy(const Function &F)
@ Mod
The access may modify the value stored in memory.
bool getMaxNReg(const Function &F, unsigned &x)
bool isTexture(const Value &val)
bool isImageWriteOnly(const Value &val)
static bool argHasNVVMAnnotation(const Value &Val, const std::string &Annotation, const bool StartArgIndexAtOne=false)
bool isImageReadWrite(const Value &val)
std::string getTextureName(const Value &val)
bool isKernelFunction(const Function &F)
Function * getMaybeBitcastedCallee(const CallBase *CB)
bool findOneNVVMAnnotation(const GlobalValue *gv, const std::string &prop, unsigned &retval)
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition: STLExtras.h:1879
std::optional< unsigned > getReqNTIDz(const Function &F)
std::optional< unsigned > getReqNTIDx(const Function &F)
bool isSampler(const Value &val)
bool getMaxClusterRank(const Function &F, unsigned &x)
std::optional< unsigned > getReqNTID(const Function &F)
std::optional< unsigned > getMaxNTID(const Function &F)
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39
Extended Value Type.
Definition: ValueTypes.h:34
This struct is a compact representation of a valid (power of two) or undefined (0) alignment.
Definition: Alignment.h:117