Home | History | Annotate | Download | only in SPIRV
      1 //===- SPIRVToOCL20.cpp - Transform SPIR-V builtins to OCL20 builtins-------===//
      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 transform SPIR-V builtins to OCL 2.0 builtins.
     36 //
     37 //===----------------------------------------------------------------------===//
     38 #define DEBUG_TYPE "spvtocl20"
     39 
     40 #include "SPIRVInternal.h"
     41 #include "OCLUtil.h"
     42 #include "llvm/ADT/StringSwitch.h"
     43 #include "llvm/IR/InstVisitor.h"
     44 #include "llvm/IR/Instructions.h"
     45 #include "llvm/IR/IRBuilder.h"
     46 #include "llvm/IR/Verifier.h"
     47 #include "llvm/Pass.h"
     48 #include "llvm/PassSupport.h"
     49 #include "llvm/Support/CommandLine.h"
     50 #include "llvm/Support/Debug.h"
     51 #include "llvm/Support/raw_ostream.h"
     52 
     53 #include <cstring>
     54 
     55 using namespace llvm;
     56 using namespace SPIRV;
     57 using namespace OCLUtil;
     58 
     59 namespace SPIRV {
     60 
     61 static cl::opt<std::string>
     62 MangledAtomicTypeNamePrefix("spirv-atomic-prefix",
     63     cl::desc("Mangled atomic type name prefix"), cl::init("U7_Atomic"));
     64 
     65 class SPIRVToOCL20: public ModulePass,
     66   public InstVisitor<SPIRVToOCL20> {
     67 public:
     68   SPIRVToOCL20():ModulePass(ID), M(nullptr), Ctx(nullptr) {
     69     initializeSPIRVToOCL20Pass(*PassRegistry::getPassRegistry());
     70   }
     71   virtual bool runOnModule(Module &M);
     72 
     73   void visitCallInst(CallInst &CI);
     74 
     75   // SPIR-V reader should translate vector casts into OCL built-ins because
     76   // such conversions are not defined neither by OpenCL C/C++ nor
     77   // by SPIR 1.2/2.0 standards. So, it is safer to convert such casts into
     78   // appropriate calls to conversion built-ins defined by the standards.
     79   void visitCastInst(CastInst &CI);
     80 
     81   /// Transform __spirv_ImageQuerySize[Lod] into vector of the same lenght
     82   /// containing {[get_image_width | get_image_dim], get_image_array_size}
     83   /// for all images except image1d_t which is always converted into
     84   /// get_image_width returning scalar result.
     85   void visitCallSPRIVImageQuerySize(CallInst *CI);
     86 
     87   /// Transform __spirv_Atomic* to atomic_*.
     88   ///   __spirv_Atomic*(atomic_op, scope, sema, ops, ...) =>
     89   ///      atomic_*(atomic_op, ops, ..., order(sema), map(scope))
     90   void visitCallSPIRVAtomicBuiltin(CallInst *CI, Op OC);
     91 
     92   /// Transform __spirv_Group* to {work_group|sub_group}_*.
     93   ///
     94   /// Special handling of work_group_broadcast.
     95   ///   __spirv_GroupBroadcast(a, vec3(x, y, z))
     96   ///     =>
     97   ///   work_group_broadcast(a, x, y, z)
     98   ///
     99   /// Transform OpenCL group builtin function names from group_
    100   /// to workgroup_ and sub_group_.
    101   /// Insert group operation part: reduce_/inclusive_scan_/exclusive_scan_
    102   /// Transform the operation part:
    103   ///    fadd/iadd/sadd => add
    104   ///    fmax/smax => max
    105   ///    fmin/smin => min
    106   /// Keep umax/umin unchanged.
    107   void visitCallSPIRVGroupBuiltin(CallInst *CI, Op OC);
    108 
    109   /// Transform __spirv_MemoryBarrier to atomic_work_item_fence.
    110   ///   __spirv_MemoryBarrier(scope, sema) =>
    111   ///       atomic_work_item_fence(flag(sema), order(sema), map(scope))
    112   void visitCallSPIRVMemoryBarrier(CallInst *CI);
    113 
    114   /// Transform __spirv_{PipeOpName} to OCL pipe builtin functions.
    115   void visitCallSPIRVPipeBuiltin(CallInst *CI, Op OC);
    116 
    117   /// Transform __spirv_* builtins to OCL 2.0 builtins.
    118   /// No change with arguments.
    119   void visitCallSPIRVBuiltin(CallInst *CI, Op OC);
    120 
    121   /// Translate mangled atomic type name: "atomic_" =>
    122   ///   MangledAtomicTypeNamePrefix
    123   void translateMangledAtomicTypeName();
    124 
    125   /// Get prefix work_/sub_ for OCL group builtin functions.
    126   /// Assuming the first argument of \param CI is a constant integer for
    127   /// workgroup/subgroup scope enums.
    128   std::string getGroupBuiltinPrefix(CallInst *CI);
    129 
    130   static char ID;
    131 private:
    132   Module *M;
    133   LLVMContext *Ctx;
    134 };
    135 
    136 char SPIRVToOCL20::ID = 0;
    137 
    138 bool
    139 SPIRVToOCL20::runOnModule(Module& Module) {
    140   M = &Module;
    141   Ctx = &M->getContext();
    142   visit(*M);
    143 
    144   translateMangledAtomicTypeName();
    145 
    146   eraseUselessFunctions(&Module);
    147 
    148   DEBUG(dbgs() << "After SPIRVToOCL20:\n" << *M);
    149 
    150   std::string Err;
    151   raw_string_ostream ErrorOS(Err);
    152   if (verifyModule(*M, &ErrorOS)){
    153     DEBUG(errs() << "Fails to verify module: " << ErrorOS.str());
    154   }
    155   return true;
    156 }
    157 
    158 void
    159 SPIRVToOCL20::visitCallInst(CallInst& CI) {
    160   DEBUG(dbgs() << "[visistCallInst] " << CI << '\n');
    161   auto F = CI.getCalledFunction();
    162   if (!F)
    163     return;
    164 
    165   auto MangledName = F->getName();
    166   std::string DemangledName;
    167   Op OC = OpNop;
    168   if (!oclIsBuiltin(MangledName, &DemangledName) ||
    169       (OC = getSPIRVFuncOC(DemangledName)) == OpNop)
    170     return;
    171   DEBUG(dbgs() << "DemangledName = " << DemangledName.c_str() << '\n'
    172                << "OpCode = " << OC << '\n');
    173 
    174   if (OC == OpImageQuerySize || OC == OpImageQuerySizeLod) {
    175     visitCallSPRIVImageQuerySize(&CI);
    176     return;
    177   }
    178   if (OC == OpMemoryBarrier) {
    179     visitCallSPIRVMemoryBarrier(&CI);
    180     return;
    181   }
    182   if (isAtomicOpCode(OC)) {
    183     visitCallSPIRVAtomicBuiltin(&CI, OC);
    184     return;
    185   }
    186   if (isGroupOpCode(OC)) {
    187     visitCallSPIRVGroupBuiltin(&CI, OC);
    188     return;
    189   }
    190   if (isPipeOpCode(OC)) {
    191     visitCallSPIRVPipeBuiltin(&CI, OC);
    192     return;
    193   }
    194   if (OCLSPIRVBuiltinMap::rfind(OC))
    195     visitCallSPIRVBuiltin(&CI, OC);
    196 
    197 }
    198 
    199 void SPIRVToOCL20::visitCallSPIRVMemoryBarrier(CallInst* CI) {
    200   AttributeSet Attrs = CI->getCalledFunction()->getAttributes();
    201   mutateCallInstOCL(M, CI, [=](CallInst *, std::vector<Value *> &Args){
    202     auto getArg = [=](unsigned I){
    203       return cast<ConstantInt>(Args[I])->getZExtValue();
    204     };
    205     auto MScope = static_cast<Scope>(getArg(0));
    206     auto Sema = mapSPIRVMemSemanticToOCL(getArg(1));
    207     Args.resize(3);
    208     Args[0] = getInt32(M, Sema.first);
    209     Args[1] = getInt32(M, Sema.second);
    210     Args[2] = getInt32(M, rmap<OCLScopeKind>(MScope));
    211     return kOCLBuiltinName::AtomicWorkItemFence;
    212   }, &Attrs);
    213 }
    214 
    215 void SPIRVToOCL20::visitCallSPRIVImageQuerySize(CallInst *CI) {
    216   Function * func = CI->getCalledFunction();
    217   // Get image type
    218   Type * argTy = func->getFunctionType()->getParamType(0);
    219   assert(argTy->isPointerTy() && "argument must be a pointer to opaque structure");
    220   StructType * imgTy = cast<StructType>(argTy->getPointerElementType());
    221   assert(imgTy->isOpaque() && "image type must be an opaque structure");
    222   StringRef imgTyName = imgTy->getName();
    223   assert(imgTyName.startswith("opencl.image") && "not an OCL image type");
    224 
    225   unsigned imgDim = 0;
    226   bool imgArray = false;
    227 
    228   if (imgTyName.startswith("opencl.image1d")) {
    229     imgDim = 1;
    230   } else if (imgTyName.startswith("opencl.image2d")) {
    231     imgDim = 2;
    232   } else if (imgTyName.startswith("opencl.image3d")) {
    233     imgDim = 3;
    234   }
    235   assert(imgDim != 0 && "unexpected image dimensionality");
    236 
    237   if (imgTyName.count("_array_") != 0) {
    238     imgArray = true;
    239   }
    240 
    241   AttributeSet attributes = CI->getCalledFunction()->getAttributes();
    242   BuiltinFuncMangleInfo mangle;
    243   Type * int32Ty = Type::getInt32Ty(*Ctx);
    244   Instruction * getImageSize = nullptr;
    245 
    246   if (imgDim == 1) {
    247     // OpImageQuerySize from non-arrayed 1d image is always translated
    248     // into get_image_width returning scalar argument
    249     getImageSize =
    250       addCallInst(M, kOCLBuiltinName::GetImageWidth, int32Ty,
    251                   CI->getArgOperand(0), &attributes,
    252                   CI, &mangle, CI->getName(), false);
    253     // The width of integer type returning by OpImageQuerySize[Lod] may
    254     // differ from i32
    255     if (CI->getType()->getScalarType() != int32Ty) {
    256       getImageSize =
    257         CastInst::CreateIntegerCast(getImageSize, CI->getType()->getScalarType(), false,
    258                                     CI->getName(), CI);
    259     }
    260   } else {
    261     assert((imgDim == 2 || imgDim == 3) && "invalid image type");
    262     assert(CI->getType()->isVectorTy() && "this code can handle vector result type only");
    263     // get_image_dim returns int2 and int4 for 2d and 3d images respecitvely.
    264     const unsigned imgDimRetEls = imgDim == 2 ? 2 : 4;
    265     VectorType * retTy = VectorType::get(int32Ty, imgDimRetEls);
    266     getImageSize =
    267       addCallInst(M, kOCLBuiltinName::GetImageDim, retTy,
    268                   CI->getArgOperand(0), &attributes,
    269                   CI, &mangle, CI->getName(), false);
    270     // The width of integer type returning by OpImageQuerySize[Lod] may
    271     // differ from i32
    272     if (CI->getType()->getScalarType() != int32Ty) {
    273       getImageSize =
    274         CastInst::CreateIntegerCast(getImageSize,
    275                                     VectorType::get(CI->getType()->getScalarType(),
    276                                                     getImageSize->getType()->getVectorNumElements()),
    277                                     false, CI->getName(), CI);
    278     }
    279   }
    280 
    281   if (imgArray || imgDim == 3) {
    282     assert(CI->getType()->isVectorTy() &&
    283            "OpImageQuerySize[Lod] must return vector for arrayed and 3d images");
    284     const unsigned imgQuerySizeRetEls = CI->getType()->getVectorNumElements();
    285 
    286     if (imgDim == 1) {
    287       // get_image_width returns scalar result while OpImageQuerySize
    288       // for image1d_array_t returns <2 x i32> vector.
    289       assert(imgQuerySizeRetEls == 2 &&
    290              "OpImageQuerySize[Lod] must return <2 x iN> vector type");
    291       getImageSize =
    292         InsertElementInst::Create(UndefValue::get(CI->getType()), getImageSize,
    293                                   ConstantInt::get(int32Ty, 0), CI->getName(), CI);
    294     } else {
    295       // get_image_dim and OpImageQuerySize returns different vector
    296       // types for arrayed and 3d images.
    297       SmallVector<Constant*, 4> maskEls;
    298       for(unsigned idx = 0; idx < imgQuerySizeRetEls; ++idx)
    299         maskEls.push_back(ConstantInt::get(int32Ty, idx));
    300       Constant * mask = ConstantVector::get(maskEls);
    301 
    302       getImageSize =
    303         new ShuffleVectorInst(getImageSize, UndefValue::get(getImageSize->getType()),
    304                               mask, CI->getName(), CI);
    305     }
    306   }
    307 
    308   if (imgArray) {
    309     assert((imgDim == 1 || imgDim == 2) && "invalid image array type");
    310     // Insert get_image_array_size to the last position of the resulting vector.
    311     Type * sizeTy = Type::getIntNTy(*Ctx, M->getDataLayout().getPointerSizeInBits(0));
    312     Instruction * getImageArraySize =
    313       addCallInst(M, kOCLBuiltinName::GetImageArraySize, sizeTy,
    314                   CI->getArgOperand(0), &attributes,
    315                   CI, &mangle, CI->getName(), false);
    316     // The width of integer type returning by OpImageQuerySize[Lod] may
    317     // differ from size_t which is returned by get_image_array_size
    318     if (getImageArraySize->getType() != CI->getType()->getScalarType()) {
    319       getImageArraySize =
    320         CastInst::CreateIntegerCast(getImageArraySize, CI->getType()->getScalarType(),
    321                                     false, CI->getName(), CI);
    322     }
    323     getImageSize =
    324       InsertElementInst::Create(getImageSize, getImageArraySize,
    325                                 ConstantInt::get(int32Ty,
    326                                                  CI->getType()->getVectorNumElements() - 1),
    327                                 CI->getName(), CI);
    328   }
    329 
    330   assert(getImageSize && "must not be null");
    331   CI->replaceAllUsesWith(getImageSize);
    332   CI->eraseFromParent();
    333 }
    334 
    335 void SPIRVToOCL20::visitCallSPIRVAtomicBuiltin(CallInst* CI, Op OC) {
    336   AttributeSet Attrs = CI->getCalledFunction()->getAttributes();
    337   Instruction * pInsertBefore = CI;
    338 
    339   mutateCallInstOCL(M, CI, [=](CallInst *, std::vector<Value *> &Args, Type *& RetTy){
    340     auto Ptr = findFirstPtr(Args);
    341     auto Name = OCLSPIRVBuiltinMap::rmap(OC);
    342     auto NumOrder = getAtomicBuiltinNumMemoryOrderArgs(Name);
    343     auto ScopeIdx = Ptr + 1;
    344     auto OrderIdx = Ptr + 2;
    345     if (OC == OpAtomicIIncrement ||
    346         OC == OpAtomicIDecrement) {
    347       // Since OpenCL 1.2 atomic_inc and atomic_dec builtins don't have, memory
    348       // scope and memory order syntax, and OpenCL 2.0 doesn't have such
    349       // builtins, therefore we translate these instructions to
    350       // atomic_fetch_add_explicit and atomic_fetch_sub_explicit OpenCL 2.0
    351       // builtins with "operand" argument = 1.
    352       Name = OCLSPIRVBuiltinMap::rmap(OC == OpAtomicIIncrement ?
    353                                       OpAtomicIAdd: OpAtomicISub);
    354       Type* ValueTy = cast<PointerType>(Args[Ptr]->getType())->getElementType();
    355       assert(ValueTy->isIntegerTy());
    356       Args.push_back(llvm::ConstantInt::get(ValueTy, 1));
    357     }
    358     Args[ScopeIdx] = mapUInt(M, cast<ConstantInt>(Args[ScopeIdx]),
    359       [](unsigned I) { return rmap<OCLScopeKind>(static_cast<Scope>(I));});
    360     for (size_t I = 0; I < NumOrder; ++I)
    361       Args[OrderIdx + I] = mapUInt(M, cast<ConstantInt>(Args[OrderIdx + I]),
    362         [](unsigned Ord) { return mapSPIRVMemOrderToOCL(Ord); });
    363     std::swap(Args[ScopeIdx], Args.back());
    364     if(OC == OpAtomicCompareExchange ||
    365        OC == OpAtomicCompareExchangeWeak) {
    366       // OpAtomicCompareExchange[Weak] semantics is different from
    367       // atomic_compare_exchange_[strong|weak] semantics as well as
    368       // arguments order.
    369       // OCL built-ins returns boolean value and stores a new/original
    370       // value by pointer passed as 2nd argument (aka expected) while SPIR-V
    371       // instructions returns this new/original value as a resulting value.
    372       AllocaInst *pExpected = new AllocaInst(CI->getType(), "expected",
    373         static_cast<Instruction*>(pInsertBefore->getParent()->getParent()->getEntryBlock().getFirstInsertionPt()));
    374       pExpected->setAlignment(CI->getType()->getScalarSizeInBits() / 8);
    375       new StoreInst(Args[1], pExpected, pInsertBefore);
    376       Args[1] = pExpected;
    377       std::swap(Args[3], Args[4]);
    378       std::swap(Args[2], Args[3]);
    379       RetTy = Type::getInt1Ty(*Ctx);
    380     }
    381     return Name;
    382   },
    383   [=](CallInst * CI) -> Instruction * {
    384     if(OC == OpAtomicCompareExchange ||
    385        OC == OpAtomicCompareExchangeWeak) {
    386       // OCL built-ins atomic_compare_exchange_[strong|weak] return boolean value. So,
    387       // to obtain the same value as SPIR-V instruction is returning it has to be loaded
    388       // from the memory where 'expected' value is stored. This memory must contain the
    389       // needed value after a call to OCL built-in is completed.
    390       LoadInst * pOriginal = new LoadInst(CI->getArgOperand(1), "original", pInsertBefore);
    391       return pOriginal;
    392     }
    393     // For other built-ins the return values match.
    394     return CI;
    395   },
    396   &Attrs);
    397 }
    398 
    399 void SPIRVToOCL20::visitCallSPIRVBuiltin(CallInst* CI, Op OC) {
    400   AttributeSet Attrs = CI->getCalledFunction()->getAttributes();
    401   mutateCallInstOCL(M, CI, [=](CallInst *, std::vector<Value *> &Args){
    402     return OCLSPIRVBuiltinMap::rmap(OC);
    403   }, &Attrs);
    404 }
    405 
    406 void SPIRVToOCL20::visitCallSPIRVGroupBuiltin(CallInst* CI, Op OC) {
    407   auto DemangledName = OCLSPIRVBuiltinMap::rmap(OC);
    408   assert(DemangledName.find(kSPIRVName::GroupPrefix) == 0);
    409 
    410   std::string Prefix = getGroupBuiltinPrefix(CI);
    411 
    412   bool HasGroupOperation = hasGroupOperation(OC);
    413   if (!HasGroupOperation) {
    414     DemangledName = Prefix + DemangledName;
    415   } else {
    416     auto GO = getArgAs<spv::GroupOperation>(CI, 1);
    417     StringRef Op = DemangledName;
    418     Op = Op.drop_front(strlen(kSPIRVName::GroupPrefix));
    419     bool Unsigned = Op.front() == 'u';
    420     if (!Unsigned)
    421       Op = Op.drop_front(1);
    422     DemangledName = Prefix + kSPIRVName::GroupPrefix +
    423         SPIRSPIRVGroupOperationMap::rmap(GO) + '_' + Op.str();
    424   }
    425   AttributeSet Attrs = CI->getCalledFunction()->getAttributes();
    426   mutateCallInstOCL(M, CI, [=](CallInst *, std::vector<Value *> &Args){
    427     Args.erase(Args.begin(), Args.begin() + (HasGroupOperation ? 2 : 1));
    428     if (OC == OpGroupBroadcast)
    429       expandVector(CI, Args, 1);
    430     return DemangledName;
    431   }, &Attrs);
    432 }
    433 
    434 void SPIRVToOCL20::visitCallSPIRVPipeBuiltin(CallInst* CI, Op OC) {
    435   switch(OC) {
    436   case OpReservedReadPipe:
    437     OC = OpReadPipe;
    438     break;
    439   case OpReservedWritePipe:
    440     OC = OpWritePipe;
    441     break;
    442   default:
    443     // Do nothing.
    444     break;
    445   }
    446   auto DemangledName = OCLSPIRVBuiltinMap::rmap(OC);
    447 
    448   bool HasScope = DemangledName.find(kSPIRVName::GroupPrefix) == 0;
    449   if (HasScope)
    450     DemangledName = getGroupBuiltinPrefix(CI) + DemangledName;
    451 
    452   AttributeSet Attrs = CI->getCalledFunction()->getAttributes();
    453   mutateCallInstOCL(M, CI, [=](CallInst *, std::vector<Value *> &Args){
    454     if (HasScope)
    455       Args.erase(Args.begin(), Args.begin() + 1);
    456 
    457     if (!(OC == OpReadPipe ||
    458           OC == OpWritePipe ||
    459           OC == OpReservedReadPipe ||
    460           OC == OpReservedWritePipe))
    461       return DemangledName;
    462 
    463     auto &P = Args[Args.size() - 3];
    464     auto T = P->getType();
    465     assert(isa<PointerType>(T));
    466     auto ET = T->getPointerElementType();
    467     if (!ET->isIntegerTy(8) ||
    468         T->getPointerAddressSpace() != SPIRAS_Generic) {
    469       auto NewTy = PointerType::getInt8PtrTy(*Ctx, SPIRAS_Generic);
    470       P = CastInst::CreatePointerBitCastOrAddrSpaceCast(P, NewTy, "", CI);
    471     }
    472     return DemangledName;
    473   }, &Attrs);
    474 }
    475 
    476 void SPIRVToOCL20::translateMangledAtomicTypeName() {
    477   for (auto &I:M->functions()) {
    478     if (!I.hasName())
    479       continue;
    480     std::string MangledName = I.getName();
    481     std::string DemangledName;
    482     if (!oclIsBuiltin(MangledName, &DemangledName) ||
    483         DemangledName.find(kOCLBuiltinName::AtomPrefix) != 0)
    484       continue;
    485     auto Loc = MangledName.find(kOCLBuiltinName::AtomPrefix);
    486     Loc = MangledName.find(kMangledName::AtomicPrefixInternal, Loc);
    487     MangledName.replace(Loc, strlen(kMangledName::AtomicPrefixInternal),
    488         MangledAtomicTypeNamePrefix);
    489     I.setName(MangledName);
    490   }
    491 }
    492 
    493 std::string
    494 SPIRVToOCL20::getGroupBuiltinPrefix(CallInst* CI) {
    495   std::string Prefix;
    496   auto ES = getArgAsScope(CI, 0);
    497   switch(ES) {
    498   case ScopeWorkgroup:
    499     Prefix = kOCLBuiltinName::WorkPrefix;
    500     break;
    501   case ScopeSubgroup:
    502     Prefix = kOCLBuiltinName::SubPrefix;
    503     break;
    504   default:
    505     llvm_unreachable("Invalid execution scope");
    506   }
    507   return Prefix;
    508 }
    509 
    510 void SPIRVToOCL20::visitCastInst(CastInst &Cast) {
    511   if(!isa<ZExtInst>(Cast) && !isa<SExtInst>(Cast) &&
    512      !isa<TruncInst>(Cast) && !isa<FPTruncInst>(Cast) &&
    513      !isa<FPExtInst>(Cast) && !isa<FPToUIInst>(Cast) &&
    514      !isa<FPToSIInst>(Cast) && !isa<UIToFPInst>(Cast) &&
    515      !isa<SIToFPInst>(Cast))
    516     return;
    517 
    518   Type const* srcTy = Cast.getSrcTy();
    519   Type * dstVecTy = Cast.getDestTy();
    520   // Leave scalar casts as is. Skip boolean vector casts becase there
    521   // are no suitable OCL built-ins.
    522   if(!dstVecTy->isVectorTy() ||
    523      srcTy->getScalarSizeInBits() == 1 ||
    524      dstVecTy->getScalarSizeInBits() == 1)
    525     return;
    526 
    527   // Assemble built-in name -> convert_gentypeN
    528   std::string castBuiltInName(kOCLBuiltinName::ConvertPrefix);
    529   // Check if this is 'floating point -> unsigned integer' cast
    530   castBuiltInName +=
    531     mapLLVMTypeToOCLType(dstVecTy, !isa<FPToUIInst>(Cast));
    532 
    533   // Replace LLVM conversion instruction with call to conversion built-in
    534   BuiltinFuncMangleInfo mangle;
    535   // It does matter if the source is unsigned integer or not. SExt is for
    536   // signed source, ZExt and UIToFPInst are for unsigned source.
    537   if(isa<ZExtInst>(Cast) || isa<UIToFPInst>(Cast))
    538     mangle.addUnsignedArg(0);
    539 
    540   AttributeSet attributes;
    541   CallInst *call = addCallInst(M, castBuiltInName, dstVecTy, Cast.getOperand(0),
    542                               &attributes, &Cast, &mangle, Cast.getName(), false);
    543   Cast.replaceAllUsesWith(call);
    544   Cast.eraseFromParent();
    545 }
    546 
    547 } // namespace SPIRV
    548 
    549 INITIALIZE_PASS(SPIRVToOCL20, "spvtoocl20",
    550     "Translate SPIR-V builtins to OCL 2.0 builtins", false, false)
    551 
    552 ModulePass *llvm::createSPIRVToOCL20() {
    553   return new SPIRVToOCL20();
    554 }
    555