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 63 OCLTypeToSPIRV::OCLTypeToSPIRV() 64 :ModulePass(ID), M(nullptr), Ctx(nullptr) { 65 initializeOCLTypeToSPIRVPass(*PassRegistry::getPassRegistry()); 66 } 67 68 void 69 OCLTypeToSPIRV::getAnalysisUsage(AnalysisUsage& AU) const { 70 AU.setPreservesAll(); 71 } 72 73 bool 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 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 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 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 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* 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 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 * 182 OCLTypeToSPIRV::getArgAccessQualifierMetadata(Function *F) { 183 return getArgMetadata(F, SPIR_MD_KERNEL_ARG_ACCESS_QUAL); 184 } 185 186 MDNode * 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 * 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 * 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. 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 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 * 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 358 ModulePass *llvm::createOCLTypeToSPIRV() { 359 return new OCLTypeToSPIRV(); 360 } 361