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 SPIR-V.
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 "OCLUtil.h"
45 #include "SPIRVInternal.h"
46 
47 #include "llvm/Pass.h"
48 #include "llvm/Support/Debug.h"
49 
50 #include <iterator>
51 #include <set>
52 
53 using namespace llvm;
54 using namespace SPIRV;
55 using namespace OCLUtil;
56 
57 namespace SPIRV {
58 
59 char OCLTypeToSPIRVLegacy::ID = 0;
60 
OCLTypeToSPIRVLegacy()61 OCLTypeToSPIRVLegacy::OCLTypeToSPIRVLegacy() : ModulePass(ID) {
62   initializeOCLTypeToSPIRVLegacyPass(*PassRegistry::getPassRegistry());
63 }
64 
getAnalysisUsage(AnalysisUsage & AU) const65 void OCLTypeToSPIRVLegacy::getAnalysisUsage(AnalysisUsage &AU) const {
66   AU.setPreservesAll();
67 }
68 
runOnModule(Module & M)69 bool OCLTypeToSPIRVLegacy::runOnModule(Module &M) {
70   return runOCLTypeToSPIRV(M);
71 }
72 
run(llvm::Module & M,llvm::ModuleAnalysisManager & MAM)73 OCLTypeToSPIRVBase OCLTypeToSPIRVPass::run(llvm::Module &M,
74                                            llvm::ModuleAnalysisManager &MAM) {
75   runOCLTypeToSPIRV(M);
76   return *this;
77 }
78 
OCLTypeToSPIRVBase()79 OCLTypeToSPIRVBase::OCLTypeToSPIRVBase() : M(nullptr), Ctx(nullptr) {}
80 
runOCLTypeToSPIRV(Module & Module)81 bool OCLTypeToSPIRVBase::runOCLTypeToSPIRV(Module &Module) {
82   LLVM_DEBUG(dbgs() << "Enter OCLTypeToSPIRV:\n");
83   M = &Module;
84   Ctx = &M->getContext();
85   auto Src = getSPIRVSource(&Module);
86   if (std::get<0>(Src) != spv::SourceLanguageOpenCL_C)
87     return false;
88 
89   for (auto &F : Module.functions())
90     adaptArgumentsByMetadata(&F);
91 
92   for (auto &F : Module.functions())
93     adaptFunctionArguments(&F);
94 
95   adaptArgumentsBySamplerUse(Module);
96 
97   while (!WorkSet.empty()) {
98     Function *F = *WorkSet.begin();
99     WorkSet.erase(WorkSet.begin());
100 
101     adaptFunction(F);
102   }
103 
104   return false;
105 }
106 
addAdaptedType(Value * V,Type * T)107 void OCLTypeToSPIRVBase::addAdaptedType(Value *V, Type *T) {
108   LLVM_DEBUG(dbgs() << "[add adapted type] ";
109              V->printAsOperand(dbgs(), true, M);
110              dbgs() << " => " << *T << '\n');
111   AdaptedTy[V] = T;
112 }
113 
addWork(Function * F)114 void OCLTypeToSPIRVBase::addWork(Function *F) {
115   LLVM_DEBUG(dbgs() << "[add work] "; F->printAsOperand(dbgs(), true, M);
116              dbgs() << '\n');
117   WorkSet.insert(F);
118 }
119 
120 /// Find index of \param V as argument of function call \param CI.
getArgIndex(CallInst * CI,Value * V)121 static unsigned getArgIndex(CallInst *CI, Value *V) {
122   for (unsigned AI = 0, AE = CI->getNumArgOperands(); AI != AE; ++AI) {
123     if (CI->getArgOperand(AI) == V)
124       return AI;
125   }
126   llvm_unreachable("Not argument of function call");
127   return ~0U;
128 }
129 
130 /// Find index of \param V as argument of function call \param CI.
getArgIndex(Function * F,Value * V)131 static unsigned getArgIndex(Function *F, Value *V) {
132   auto A = F->arg_begin(), E = F->arg_end();
133   for (unsigned I = 0; A != E; ++I, ++A) {
134     if (&(*A) == V)
135       return I;
136   }
137   llvm_unreachable("Not argument of function");
138   return ~0U;
139 }
140 
141 /// Get i-th argument of a function.
getArg(Function * F,unsigned I)142 static Argument *getArg(Function *F, unsigned I) {
143   auto AI = F->arg_begin();
144   std::advance(AI, I);
145   return &(*AI);
146 }
147 
148 /// Create a new function type if \param F has arguments in AdaptedTy, and
149 /// propagates the adapted arguments to functions called by \param F.
adaptFunction(Function * F)150 void OCLTypeToSPIRVBase::adaptFunction(Function *F) {
151   LLVM_DEBUG(dbgs() << "\n[work on function] ";
152              F->printAsOperand(dbgs(), true, M); dbgs() << '\n');
153   assert(AdaptedTy.count(F) == 0);
154 
155   std::vector<Type *> ArgTys;
156   bool Changed = false;
157   for (auto &I : F->args()) {
158     auto Loc = AdaptedTy.find(&I);
159     auto Found = (Loc != AdaptedTy.end());
160     Changed |= Found;
161     ArgTys.push_back(Found ? Loc->second : I.getType());
162 
163     if (Found) {
164       for (auto U : I.users()) {
165         if (auto CI = dyn_cast<CallInst>(U)) {
166           auto ArgIndex = getArgIndex(CI, &I);
167           auto CF = CI->getCalledFunction();
168           if (AdaptedTy.count(CF) == 0) {
169             addAdaptedType(getArg(CF, ArgIndex), Loc->second);
170             addWork(CF);
171           }
172         }
173       }
174     }
175   }
176 
177   if (!Changed)
178     return;
179 
180   auto FT = F->getFunctionType();
181   FT = FunctionType::get(FT->getReturnType(), ArgTys, FT->isVarArg());
182   addAdaptedType(F, FT);
183 }
184 
185 // Handle functions with sampler arguments that don't get called by
186 // a kernel function.
adaptArgumentsBySamplerUse(Module & M)187 void OCLTypeToSPIRVBase::adaptArgumentsBySamplerUse(Module &M) {
188   SmallPtrSet<Function *, 5> Processed;
189 
190   std::function<void(Function *, unsigned)> TraceArg = [&](Function *F,
191                                                            unsigned Idx) {
192     // If we have cycles in the call graph in the future, bail out
193     // if we've already processed this function.
194     if (Processed.insert(F).second == false)
195       return;
196 
197     for (auto U : F->users()) {
198       auto *CI = dyn_cast<CallInst>(U);
199       if (!CI)
200         continue;
201 
202       auto SamplerArg = CI->getArgOperand(Idx);
203       if (!isa<Argument>(SamplerArg) ||
204           AdaptedTy.count(SamplerArg) != 0) // Already traced this, move on.
205         continue;
206 
207       if (isSPIRVType(SamplerArg->getType(), kSPIRVTypeName::Sampler))
208         return;
209 
210       addAdaptedType(SamplerArg, getSamplerType(&M));
211       auto Caller = cast<Argument>(SamplerArg)->getParent();
212       addWork(Caller);
213       TraceArg(Caller, getArgIndex(Caller, SamplerArg));
214     }
215   };
216 
217   for (auto &F : M) {
218     if (!F.empty()) // not decl
219       continue;
220     auto MangledName = F.getName();
221     StringRef DemangledName;
222     if (!oclIsBuiltin(MangledName, DemangledName, false))
223       continue;
224     if (DemangledName.find(kSPIRVName::SampledImage) == std::string::npos)
225       continue;
226 
227     TraceArg(&F, 1);
228   }
229 }
230 
adaptFunctionArguments(Function * F)231 void OCLTypeToSPIRVBase::adaptFunctionArguments(Function *F) {
232   auto TypeMD = F->getMetadata(SPIR_MD_KERNEL_ARG_BASE_TYPE);
233   if (TypeMD)
234     return;
235   bool Changed = false;
236   auto FT = F->getFunctionType();
237   auto PI = FT->param_begin();
238   auto Arg = F->arg_begin();
239   for (unsigned I = 0; I < F->arg_size(); ++I, ++PI, ++Arg) {
240     auto NewTy = *PI;
241     if (isPointerToOpaqueStructType(NewTy)) {
242       auto STName = NewTy->getPointerElementType()->getStructName();
243       if (!hasAccessQualifiedName(STName))
244         continue;
245       if (STName.startswith(kSPR2TypeName::ImagePrefix)) {
246         auto Ty = STName.str();
247         auto AccStr = getAccessQualifierFullName(Ty);
248         addAdaptedType(&*Arg, getOrCreateOpaquePtrType(
249                                   M, mapOCLTypeNameToSPIRV(Ty, AccStr)));
250         Changed = true;
251       }
252     }
253   }
254   if (Changed)
255     addWork(F);
256 }
257 
258 /// Go through all kernel functions, get access qualifier for image and pipe
259 /// types and use them to map the function arguments to the SPIR-V type.
260 /// ToDo: Map other OpenCL opaque types to SPIR-V types.
adaptArgumentsByMetadata(Function * F)261 void OCLTypeToSPIRVBase::adaptArgumentsByMetadata(Function *F) {
262   auto TypeMD = F->getMetadata(SPIR_MD_KERNEL_ARG_BASE_TYPE);
263   if (!TypeMD)
264     return;
265   bool Changed = false;
266   auto FT = F->getFunctionType();
267   auto PI = FT->param_begin();
268   auto Arg = F->arg_begin();
269   for (unsigned I = 0, E = TypeMD->getNumOperands(); I != E; ++I, ++PI, ++Arg) {
270     auto OCLTyStr = getMDOperandAsString(TypeMD, I);
271     auto NewTy = *PI;
272     if (OCLTyStr == OCL_TYPE_NAME_SAMPLER_T && !NewTy->isStructTy()) {
273       addAdaptedType(&(*Arg), getSamplerType(M));
274       Changed = true;
275     } else if (isPointerToOpaqueStructType(NewTy)) {
276       auto STName = NewTy->getPointerElementType()->getStructName();
277       if (STName.startswith(kSPR2TypeName::ImagePrefix)) {
278         auto Ty = STName.str();
279         auto AccMD = F->getMetadata(SPIR_MD_KERNEL_ARG_ACCESS_QUAL);
280         assert(AccMD && "Invalid access qualifier metadata");
281         auto AccStr = getMDOperandAsString(AccMD, I);
282         addAdaptedType(&(*Arg), getOrCreateOpaquePtrType(
283                                     M, mapOCLTypeNameToSPIRV(Ty, AccStr)));
284         Changed = true;
285       }
286     }
287   }
288   if (Changed)
289     addWork(F);
290 }
291 
292 // OCL sampler, image and pipe type need to be regularized before converting
293 // to SPIRV types.
294 //
295 // OCL sampler type is represented as i32 in LLVM, however in SPIRV it is
296 // represented as OpTypeSampler. Also LLVM uses the same pipe type to
297 // represent pipe types with different underlying data types, however
298 // in SPIRV they are different types. OCL image and pipe types do not
299 // encode access qualifier, which is part of SPIRV types for image and pipe.
300 //
301 // The function types in LLVM need to be regularized before translating
302 // to SPIRV function types:
303 //
304 // sampler type as i32 -> opencl.sampler_t opaque type
305 // opencl.pipe_t opaque type with underlying opencl type x and access
306 //   qualifier y -> opencl.pipe_t.x.y opaque type
307 // opencl.image_x opaque type with access qualifier y ->
308 //   opencl.image_x.y opaque type
309 //
310 // The converter relies on kernel_arg_base_type to identify the sampler
311 // type, the underlying data type of pipe type, and access qualifier for
312 // image and pipe types. The FE is responsible to generate the correct
313 // kernel_arg_base_type metadata.
314 //
315 // Alternatively,the FE may choose to use opencl.sampler_t to represent
316 // sampler type, use opencl.pipe_t.x.y to represent pipe type with underlying
317 // opencl data type x and access qualifier y, and use opencl.image_x.y to
318 // represent image_x type with access qualifier y.
319 //
getAdaptedType(Value * V)320 Type *OCLTypeToSPIRVBase::getAdaptedType(Value *V) {
321   auto Loc = AdaptedTy.find(V);
322   if (Loc != AdaptedTy.end())
323     return Loc->second;
324 
325   if (auto F = dyn_cast<Function>(V))
326     return F->getFunctionType();
327   return V->getType();
328 }
329 
330 } // namespace SPIRV
331 
332 INITIALIZE_PASS(OCLTypeToSPIRVLegacy, "cltytospv", "Adapt OCL types for SPIR-V",
333                 false, true)
334 
createOCLTypeToSPIRVLegacy()335 ModulePass *llvm::createOCLTypeToSPIRVLegacy() {
336   return new OCLTypeToSPIRVLegacy();
337 }
338