Home | History | Annotate | Download | only in SPIRV
      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