• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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