1 //===- OCLTypeToSPIRV.cpp - Adapt types from OCL for SPIRV ------*- C++ -*-===//
2 //
3 // The LLVM/SPIRV Translator
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 // Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a
11 // copy of this software and associated documentation files (the "Software"),
12 // to deal with the Software without restriction, including without limitation
13 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 // and/or sell copies of the Software, and to permit persons to whom the
15 // Software is furnished to do so, subject to the following conditions:
16 //
17 // Redistributions of source code must retain the above copyright notice,
18 // this list of conditions and the following disclaimers.
19 // Redistributions in binary form must reproduce the above copyright notice,
20 // this list of conditions and the following disclaimers in the documentation
21 // and/or other materials provided with the distribution.
22 // Neither the names of Advanced Micro Devices, Inc., nor the names of its
23 // contributors may be used to endorse or promote products derived from this
24 // Software without specific prior written permission.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
31 // THE SOFTWARE.
32 //
33 //===----------------------------------------------------------------------===//
34 //
35 // This file implements adaptation of OCL types for SPIRV.
36 //
37 // It first maps kernel arguments of OCL opaque types to SPIR-V type, then
38 // propagates the mapping to the uses of the kernel arguments.
39 //
40 //===----------------------------------------------------------------------===//
41 #define DEBUG_TYPE "cltytospv"
42
43 #include "OCLTypeToSPIRV.h"
44 #include "SPIRVInternal.h"
45 #include "OCLUtil.h"
46
47 #include "llvm/Pass.h"
48 #include "llvm/PassSupport.h"
49 #include "llvm/Support/Debug.h"
50 #include "llvm/Support/raw_ostream.h"
51
52 #include <set>
53 #include <iterator>
54
55 using namespace llvm;
56 using namespace SPIRV;
57 using namespace OCLUtil;
58
59 namespace SPIRV {
60
61 char OCLTypeToSPIRV::ID = 0;
62
OCLTypeToSPIRV()63 OCLTypeToSPIRV::OCLTypeToSPIRV()
64 :ModulePass(ID), M(nullptr), Ctx(nullptr) {
65 initializeOCLTypeToSPIRVPass(*PassRegistry::getPassRegistry());
66 }
67
68 void
getAnalysisUsage(AnalysisUsage & AU) const69 OCLTypeToSPIRV::getAnalysisUsage(AnalysisUsage& AU) const {
70 AU.setPreservesAll();
71 }
72
73 bool
runOnModule(Module & Module)74 OCLTypeToSPIRV::runOnModule(Module& Module) {
75 DEBUG(dbgs() << "Enter OCLTypeToSPIRV:\n");
76 M = &Module;
77 Ctx = &M->getContext();
78 auto Src = getSPIRVSource(&Module);
79 if (std::get<0>(Src) != spv::SourceLanguageOpenCL_C)
80 return false;
81
82 for (auto &F:Module.functions())
83 adaptArgumentsByMetadata(&F);
84
85 adaptArgumentsBySamplerUse(Module);
86
87 while (!WorkSet.empty()) {
88 Function *F = *WorkSet.begin();
89 WorkSet.erase(WorkSet.begin());
90
91 adaptFunction(F);
92 }
93
94 return false;
95 }
96
97 void
addAdaptedType(Value * V,Type * T)98 OCLTypeToSPIRV::addAdaptedType(Value *V, Type *T) {
99 DEBUG(dbgs() << "[add adapted type] ";
100 V->printAsOperand(dbgs(), true, M);
101 dbgs() << " => " << *T << '\n');
102 AdaptedTy[V] = T;
103 }
104
105 void
addWork(Function * F)106 OCLTypeToSPIRV::addWork(Function *F) {
107 DEBUG(dbgs() << "[add work] ";
108 F->printAsOperand(dbgs(), true, M);
109 dbgs() << '\n');
110 WorkSet.insert(F);
111 }
112
113 /// Find index of \param V as argument of function call \param CI.
114 static unsigned
getArgIndex(CallInst * CI,Value * V)115 getArgIndex(CallInst *CI, Value *V) {
116 for (unsigned AI = 0, AE = CI->getNumArgOperands(); AI != AE; ++AI) {
117 if (CI->getArgOperand(AI) == V)
118 return AI;
119 }
120 llvm_unreachable("Not argument of function call");
121 }
122
123 /// Find index of \param V as argument of function call \param CI.
124 static unsigned
getArgIndex(Function * F,Value * V)125 getArgIndex(Function *F, Value *V) {
126 auto A = F->arg_begin(), E = F->arg_end();
127 for (unsigned I = 0; A != E; ++I, ++A) {
128 if (static_cast<Argument*>(A) == V)
129 return I;
130 }
131 llvm_unreachable("Not argument of function");
132 }
133
134 /// Get i-th argument of a function.
135 static Argument*
getArg(Function * F,unsigned I)136 getArg(Function *F, unsigned I) {
137 auto AI = F->arg_begin();
138 std::advance(AI, I);
139 return static_cast<Argument*>(AI);
140 }
141
142 /// Create a new function type if \param F has arguments in AdaptedTy, and
143 /// propagates the adapted arguments to functions called by \param F.
144 void
adaptFunction(Function * F)145 OCLTypeToSPIRV::adaptFunction(Function *F) {
146 DEBUG(dbgs() << "\n[work on function] ";
147 F->printAsOperand(dbgs(), true, M);
148 dbgs() << '\n');
149 assert (AdaptedTy.count(F) == 0);
150
151 std::vector<Type*> ArgTys;
152 bool Changed = false;
153 for (auto &I:F->args()) {
154 auto Loc = AdaptedTy.find(&I);
155 auto Found = (Loc != AdaptedTy.end());
156 Changed |= Found;
157 ArgTys.push_back (Found ? Loc->second : I.getType());
158
159 if (Found) {
160 for (auto U:I.users()) {
161 if (auto CI = dyn_cast<CallInst>(U)) {
162 auto ArgIndex = getArgIndex(CI, &I);
163 auto CF = CI->getCalledFunction();
164 if (AdaptedTy.count(CF) == 0) {
165 addAdaptedType(getArg(CF, ArgIndex), Loc->second);
166 addWork(CF);
167 }
168 }
169 }
170 }
171 }
172
173 if (!Changed)
174 return;
175
176 auto FT = F->getFunctionType();
177 FT = FunctionType::get(FT->getReturnType(), ArgTys, FT->isVarArg());
178 addAdaptedType(F, FT);
179 }
180
181 MDNode *
getArgAccessQualifierMetadata(Function * F)182 OCLTypeToSPIRV::getArgAccessQualifierMetadata(Function *F) {
183 return getArgMetadata(F, SPIR_MD_KERNEL_ARG_ACCESS_QUAL);
184 }
185
186 MDNode *
getKernelMetadata(Function * F)187 OCLTypeToSPIRV::getKernelMetadata(Function *F) {
188 NamedMDNode *KernelMDs = M->getNamedMetadata(SPIR_MD_KERNELS);
189 if (!KernelMDs)
190 return nullptr;
191
192 for (unsigned I = 0, E = KernelMDs->getNumOperands(); I < E; ++I) {
193 MDNode *KernelMD = KernelMDs->getOperand(I);
194 if (KernelMD->getNumOperands() == 0)
195 continue;
196 Function *Kernel = mdconst::dyn_extract<Function>(KernelMD->getOperand(0));
197
198 if (Kernel == F)
199 return KernelMD;
200 }
201 return nullptr;
202 }
203
204 MDNode *
getArgMetadata(Function * F,const std::string & MDName)205 OCLTypeToSPIRV::getArgMetadata(Function *F, const std::string &MDName) {
206 auto KernelMD = getKernelMetadata(F);
207 if (!KernelMD)
208 return nullptr;
209
210 for (unsigned MI = 1, ME = KernelMD->getNumOperands(); MI < ME; ++MI) {
211 MDNode *MD = dyn_cast<MDNode>(KernelMD->getOperand(MI));
212 if (!MD)
213 continue;
214 MDString *NameMD = dyn_cast<MDString>(MD->getOperand(0));
215 if (!NameMD)
216 continue;
217 StringRef Name = NameMD->getString();
218 if (Name == MDName) {
219 return MD;
220 }
221 }
222 return nullptr;
223 }
224
225
226 MDNode *
getArgBaseTypeMetadata(Function * F)227 OCLTypeToSPIRV::getArgBaseTypeMetadata(Function *F) {
228 return getArgMetadata(F, SPIR_MD_KERNEL_ARG_BASE_TYPE);
229 }
230
231 // Handle functions with sampler arguments that don't get called by
232 // a kernel function.
adaptArgumentsBySamplerUse(Module & M)233 void OCLTypeToSPIRV::adaptArgumentsBySamplerUse(Module &M) {
234 SmallPtrSet<Function *, 5> Processed;
235
236 std::function<void(Function *, unsigned)> TraceArg = [&](Function *F,
237 unsigned Idx) {
238 // If we have cycles in the call graph in the future, bail out
239 // if we've already processed this function.
240 if (Processed.insert(F).second == false)
241 return;
242
243 for (auto U : F->users()) {
244 auto *CI = dyn_cast<CallInst>(U);
245 if (!CI)
246 continue;
247
248 auto SamplerArg = CI->getArgOperand(Idx);
249 if (!isa<Argument>(SamplerArg) ||
250 AdaptedTy.count(SamplerArg) != 0) // Already traced this, move on.
251 continue;
252
253 if (isSPIRVType(SamplerArg->getType(), kSPIRVTypeName::Sampler))
254 return;
255
256 addAdaptedType(SamplerArg, getSamplerType(&M));
257 auto Caller = cast<Argument>(SamplerArg)->getParent();
258 addWork(Caller);
259 TraceArg(Caller, getArgIndex(Caller, SamplerArg));
260 }
261 };
262
263 for (auto &F : M) {
264 if (!F.empty()) // not decl
265 continue;
266 auto MangledName = F.getName();
267 std::string DemangledName;
268 if (!oclIsBuiltin(MangledName, &DemangledName, false))
269 continue;
270 if (DemangledName.find(kSPIRVName::SampledImage) == std::string::npos)
271 continue;
272
273 TraceArg(&F, 1);
274 }
275 }
276
277 /// Go through all kernel functions, get access qualifier for image and pipe
278 /// types and use them to map the function arguments to the SPIR-V type.
279 /// ToDo: Map other OpenCL opaque types to SPIR-V types.
280 void
adaptArgumentsByMetadata(Function * F)281 OCLTypeToSPIRV::adaptArgumentsByMetadata(Function* F) {
282 auto TypeMD = getArgBaseTypeMetadata(F);
283 if (!TypeMD)
284 return;
285 bool Changed = false;
286 auto FT = F->getFunctionType();
287 auto PI = FT->param_begin();
288 auto Arg = F->arg_begin();
289 for (unsigned I = 1, E = TypeMD->getNumOperands(); I != E;
290 ++I, ++PI, ++ Arg) {
291 auto OCLTyStr = getMDOperandAsString(TypeMD, I);
292 auto NewTy = *PI;
293 if (OCLTyStr == OCL_TYPE_NAME_SAMPLER_T && !NewTy->isStructTy()) {
294 addAdaptedType(static_cast<Argument*>(Arg), getSamplerType(M));
295 Changed = true;
296 } else if (isPointerToOpaqueStructType(NewTy)) {
297 auto STName = NewTy->getPointerElementType()->getStructName();
298 if (STName.startswith(kSPR2TypeName::ImagePrefix) ||
299 STName == kSPR2TypeName::Pipe) {
300 auto Ty = STName.str();
301 auto AccMD = getArgAccessQualifierMetadata(F);
302 assert(AccMD && "Invalid access qualifier metadata");
303 auto AccStr = getMDOperandAsString(AccMD, I);
304 addAdaptedType(static_cast<Argument*>(Arg), getOrCreateOpaquePtrType(M,
305 mapOCLTypeNameToSPIRV(Ty, AccStr)));
306 Changed = true;
307 }
308 }
309 }
310 if (Changed)
311 addWork(F);
312 }
313
314 // OCL sampler, image and pipe type need to be regularized before converting
315 // to SPIRV types.
316 //
317 // OCL sampler type is represented as i32 in LLVM, however in SPIRV it is
318 // represented as OpTypeSampler. Also LLVM uses the same pipe type to
319 // represent pipe types with different underlying data types, however
320 // in SPIRV they are different types. OCL image and pipie types do not
321 // encode access qualifier, which is part of SPIRV types for image and pipe.
322 //
323 // The function types in LLVM need to be regularized before translating
324 // to SPIRV function types:
325 //
326 // sampler type as i32 -> opencl.sampler_t opaque type
327 // opencl.pipe_t opaque type with underlying opencl type x and access
328 // qualifier y -> opencl.pipe_t.x.y opaque type
329 // opencl.image_x opaque type with access qualifier y ->
330 // opencl.image_x.y opaque type
331 //
332 // The converter relies on kernel_arg_base_type to identify the sampler
333 // type, the underlying data type of pipe type, and access qualifier for
334 // image and pipe types. The FE is responsible to generate the correct
335 // kernel_arg_base_type metadata.
336 //
337 // Alternatively,the FE may choose to use opencl.sampler_t to represent
338 // sampler type, use opencl.pipe_t.x.y to represent pipe type with underlying
339 // opencl data type x and access qualifier y, and use opencl.image_x.y to
340 // represent image_x type with access qualifier y.
341 //
342 Type *
getAdaptedType(Value * V)343 OCLTypeToSPIRV::getAdaptedType(Value *V) {
344 auto Loc = AdaptedTy.find(V);
345 if (Loc != AdaptedTy.end())
346 return Loc->second;
347
348 if(auto F = dyn_cast<Function>(V))
349 return F->getFunctionType();
350 return V->getType();
351 }
352
353 }
354
355 INITIALIZE_PASS(OCLTypeToSPIRV, "cltytospv", "Adapt OCL types for SPIR-V",
356 false, true)
357
createOCLTypeToSPIRV()358 ModulePass *llvm::createOCLTypeToSPIRV() {
359 return new OCLTypeToSPIRV();
360 }
361