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 "llvm/IR/Constants.h"
16 #include "llvm/IR/Function.h"
17 #include "llvm/IR/GlobalVariable.h"
18 #include "llvm/IR/InstIterator.h"
19 #include "llvm/IR/Module.h"
20 #include "llvm/IR/Operator.h"
21 #include "llvm/Support/Mutex.h"
22 #include <algorithm>
23 #include <cstring>
24 #include <map>
25 #include <mutex>
26 #include <string>
27 #include <vector>
28
29 namespace llvm {
30
31 namespace {
32 typedef std::map<std::string, std::vector<unsigned> > key_val_pair_t;
33 typedef std::map<const GlobalValue *, key_val_pair_t> global_val_annot_t;
34
35 struct AnnotationCache {
36 sys::Mutex Lock;
37 std::map<const Module *, global_val_annot_t> Cache;
38 };
39
getAnnotationCache()40 AnnotationCache &getAnnotationCache() {
41 static AnnotationCache AC;
42 return AC;
43 }
44 } // anonymous namespace
45
clearAnnotationCache(const Module * Mod)46 void clearAnnotationCache(const Module *Mod) {
47 auto &AC = getAnnotationCache();
48 std::lock_guard<sys::Mutex> Guard(AC.Lock);
49 AC.Cache.erase(Mod);
50 }
51
cacheAnnotationFromMD(const MDNode * md,key_val_pair_t & retval)52 static void cacheAnnotationFromMD(const MDNode *md, key_val_pair_t &retval) {
53 auto &AC = getAnnotationCache();
54 std::lock_guard<sys::Mutex> Guard(AC.Lock);
55 assert(md && "Invalid mdnode for annotation");
56 assert((md->getNumOperands() % 2) == 1 && "Invalid number of operands");
57 // start index = 1, to skip the global variable key
58 // increment = 2, to skip the value for each property-value pairs
59 for (unsigned i = 1, e = md->getNumOperands(); i != e; i += 2) {
60 // property
61 const MDString *prop = dyn_cast<MDString>(md->getOperand(i));
62 assert(prop && "Annotation property not a string");
63
64 // value
65 ConstantInt *Val = mdconst::dyn_extract<ConstantInt>(md->getOperand(i + 1));
66 assert(Val && "Value operand not a constant int");
67
68 std::string keyname = prop->getString().str();
69 if (retval.find(keyname) != retval.end())
70 retval[keyname].push_back(Val->getZExtValue());
71 else {
72 std::vector<unsigned> tmp;
73 tmp.push_back(Val->getZExtValue());
74 retval[keyname] = tmp;
75 }
76 }
77 }
78
cacheAnnotationFromMD(const Module * m,const GlobalValue * gv)79 static void cacheAnnotationFromMD(const Module *m, const GlobalValue *gv) {
80 auto &AC = getAnnotationCache();
81 std::lock_guard<sys::Mutex> Guard(AC.Lock);
82 NamedMDNode *NMD = m->getNamedMetadata("nvvm.annotations");
83 if (!NMD)
84 return;
85 key_val_pair_t tmp;
86 for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) {
87 const MDNode *elem = NMD->getOperand(i);
88
89 GlobalValue *entity =
90 mdconst::dyn_extract_or_null<GlobalValue>(elem->getOperand(0));
91 // entity may be null due to DCE
92 if (!entity)
93 continue;
94 if (entity != gv)
95 continue;
96
97 // accumulate annotations for entity in tmp
98 cacheAnnotationFromMD(elem, tmp);
99 }
100
101 if (tmp.empty()) // no annotations for this gv
102 return;
103
104 if (AC.Cache.find(m) != AC.Cache.end())
105 AC.Cache[m][gv] = std::move(tmp);
106 else {
107 global_val_annot_t tmp1;
108 tmp1[gv] = std::move(tmp);
109 AC.Cache[m] = std::move(tmp1);
110 }
111 }
112
findOneNVVMAnnotation(const GlobalValue * gv,const std::string & prop,unsigned & retval)113 bool findOneNVVMAnnotation(const GlobalValue *gv, const std::string &prop,
114 unsigned &retval) {
115 auto &AC = getAnnotationCache();
116 std::lock_guard<sys::Mutex> Guard(AC.Lock);
117 const Module *m = gv->getParent();
118 if (AC.Cache.find(m) == AC.Cache.end())
119 cacheAnnotationFromMD(m, gv);
120 else if (AC.Cache[m].find(gv) == AC.Cache[m].end())
121 cacheAnnotationFromMD(m, gv);
122 if (AC.Cache[m][gv].find(prop) == AC.Cache[m][gv].end())
123 return false;
124 retval = AC.Cache[m][gv][prop][0];
125 return true;
126 }
127
findAllNVVMAnnotation(const GlobalValue * gv,const std::string & prop,std::vector<unsigned> & retval)128 bool findAllNVVMAnnotation(const GlobalValue *gv, const std::string &prop,
129 std::vector<unsigned> &retval) {
130 auto &AC = getAnnotationCache();
131 std::lock_guard<sys::Mutex> Guard(AC.Lock);
132 const Module *m = gv->getParent();
133 if (AC.Cache.find(m) == AC.Cache.end())
134 cacheAnnotationFromMD(m, gv);
135 else if (AC.Cache[m].find(gv) == AC.Cache[m].end())
136 cacheAnnotationFromMD(m, gv);
137 if (AC.Cache[m][gv].find(prop) == AC.Cache[m][gv].end())
138 return false;
139 retval = AC.Cache[m][gv][prop];
140 return true;
141 }
142
isTexture(const Value & val)143 bool isTexture(const Value &val) {
144 if (const GlobalValue *gv = dyn_cast<GlobalValue>(&val)) {
145 unsigned annot;
146 if (findOneNVVMAnnotation(gv, "texture", annot)) {
147 assert((annot == 1) && "Unexpected annotation on a texture symbol");
148 return true;
149 }
150 }
151 return false;
152 }
153
isSurface(const Value & val)154 bool isSurface(const Value &val) {
155 if (const GlobalValue *gv = dyn_cast<GlobalValue>(&val)) {
156 unsigned annot;
157 if (findOneNVVMAnnotation(gv, "surface", annot)) {
158 assert((annot == 1) && "Unexpected annotation on a surface symbol");
159 return true;
160 }
161 }
162 return false;
163 }
164
isSampler(const Value & val)165 bool isSampler(const Value &val) {
166 const char *AnnotationName = "sampler";
167
168 if (const GlobalValue *gv = dyn_cast<GlobalValue>(&val)) {
169 unsigned annot;
170 if (findOneNVVMAnnotation(gv, AnnotationName, annot)) {
171 assert((annot == 1) && "Unexpected annotation on a sampler symbol");
172 return true;
173 }
174 }
175 if (const Argument *arg = dyn_cast<Argument>(&val)) {
176 const Function *func = arg->getParent();
177 std::vector<unsigned> annot;
178 if (findAllNVVMAnnotation(func, AnnotationName, annot)) {
179 if (is_contained(annot, arg->getArgNo()))
180 return true;
181 }
182 }
183 return false;
184 }
185
isImageReadOnly(const Value & val)186 bool isImageReadOnly(const Value &val) {
187 if (const Argument *arg = dyn_cast<Argument>(&val)) {
188 const Function *func = arg->getParent();
189 std::vector<unsigned> annot;
190 if (findAllNVVMAnnotation(func, "rdoimage", annot)) {
191 if (is_contained(annot, arg->getArgNo()))
192 return true;
193 }
194 }
195 return false;
196 }
197
isImageWriteOnly(const Value & val)198 bool isImageWriteOnly(const Value &val) {
199 if (const Argument *arg = dyn_cast<Argument>(&val)) {
200 const Function *func = arg->getParent();
201 std::vector<unsigned> annot;
202 if (findAllNVVMAnnotation(func, "wroimage", annot)) {
203 if (is_contained(annot, arg->getArgNo()))
204 return true;
205 }
206 }
207 return false;
208 }
209
isImageReadWrite(const Value & val)210 bool isImageReadWrite(const Value &val) {
211 if (const Argument *arg = dyn_cast<Argument>(&val)) {
212 const Function *func = arg->getParent();
213 std::vector<unsigned> annot;
214 if (findAllNVVMAnnotation(func, "rdwrimage", annot)) {
215 if (is_contained(annot, arg->getArgNo()))
216 return true;
217 }
218 }
219 return false;
220 }
221
isImage(const Value & val)222 bool isImage(const Value &val) {
223 return isImageReadOnly(val) || isImageWriteOnly(val) || isImageReadWrite(val);
224 }
225
isManaged(const Value & val)226 bool isManaged(const Value &val) {
227 if(const GlobalValue *gv = dyn_cast<GlobalValue>(&val)) {
228 unsigned annot;
229 if (findOneNVVMAnnotation(gv, "managed", annot)) {
230 assert((annot == 1) && "Unexpected annotation on a managed symbol");
231 return true;
232 }
233 }
234 return false;
235 }
236
getTextureName(const Value & val)237 std::string getTextureName(const Value &val) {
238 assert(val.hasName() && "Found texture variable with no name");
239 return std::string(val.getName());
240 }
241
getSurfaceName(const Value & val)242 std::string getSurfaceName(const Value &val) {
243 assert(val.hasName() && "Found surface variable with no name");
244 return std::string(val.getName());
245 }
246
getSamplerName(const Value & val)247 std::string getSamplerName(const Value &val) {
248 assert(val.hasName() && "Found sampler variable with no name");
249 return std::string(val.getName());
250 }
251
getMaxNTIDx(const Function & F,unsigned & x)252 bool getMaxNTIDx(const Function &F, unsigned &x) {
253 return findOneNVVMAnnotation(&F, "maxntidx", x);
254 }
255
getMaxNTIDy(const Function & F,unsigned & y)256 bool getMaxNTIDy(const Function &F, unsigned &y) {
257 return findOneNVVMAnnotation(&F, "maxntidy", y);
258 }
259
getMaxNTIDz(const Function & F,unsigned & z)260 bool getMaxNTIDz(const Function &F, unsigned &z) {
261 return findOneNVVMAnnotation(&F, "maxntidz", z);
262 }
263
getReqNTIDx(const Function & F,unsigned & x)264 bool getReqNTIDx(const Function &F, unsigned &x) {
265 return findOneNVVMAnnotation(&F, "reqntidx", x);
266 }
267
getReqNTIDy(const Function & F,unsigned & y)268 bool getReqNTIDy(const Function &F, unsigned &y) {
269 return findOneNVVMAnnotation(&F, "reqntidy", y);
270 }
271
getReqNTIDz(const Function & F,unsigned & z)272 bool getReqNTIDz(const Function &F, unsigned &z) {
273 return findOneNVVMAnnotation(&F, "reqntidz", z);
274 }
275
getMinCTASm(const Function & F,unsigned & x)276 bool getMinCTASm(const Function &F, unsigned &x) {
277 return findOneNVVMAnnotation(&F, "minctasm", x);
278 }
279
getMaxNReg(const Function & F,unsigned & x)280 bool getMaxNReg(const Function &F, unsigned &x) {
281 return findOneNVVMAnnotation(&F, "maxnreg", x);
282 }
283
isKernelFunction(const Function & F)284 bool isKernelFunction(const Function &F) {
285 unsigned x = 0;
286 bool retval = findOneNVVMAnnotation(&F, "kernel", x);
287 if (!retval) {
288 // There is no NVVM metadata, check the calling convention
289 return F.getCallingConv() == CallingConv::PTX_Kernel;
290 }
291 return (x == 1);
292 }
293
getAlign(const Function & F,unsigned index,unsigned & align)294 bool getAlign(const Function &F, unsigned index, unsigned &align) {
295 std::vector<unsigned> Vs;
296 bool retval = findAllNVVMAnnotation(&F, "align", Vs);
297 if (!retval)
298 return false;
299 for (unsigned v : Vs) {
300 if ((v >> 16) == index) {
301 align = v & 0xFFFF;
302 return true;
303 }
304 }
305 return false;
306 }
307
getAlign(const CallInst & I,unsigned index,unsigned & align)308 bool getAlign(const CallInst &I, unsigned index, unsigned &align) {
309 if (MDNode *alignNode = I.getMetadata("callalign")) {
310 for (int i = 0, n = alignNode->getNumOperands(); i < n; i++) {
311 if (const ConstantInt *CI =
312 mdconst::dyn_extract<ConstantInt>(alignNode->getOperand(i))) {
313 unsigned v = CI->getZExtValue();
314 if ((v >> 16) == index) {
315 align = v & 0xFFFF;
316 return true;
317 }
318 if ((v >> 16) > index) {
319 return false;
320 }
321 }
322 }
323 }
324 return false;
325 }
326
327 } // namespace llvm
328