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