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:
SPIRVToOCL20()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
runOnModule(Module & Module)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
visitCallInst(CallInst & CI)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 
visitCallSPIRVMemoryBarrier(CallInst * CI)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 
visitCallSPRIVImageQuerySize(CallInst * CI)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 
visitCallSPIRVAtomicBuiltin(CallInst * CI,Op OC)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 
visitCallSPIRVBuiltin(CallInst * CI,Op OC)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 
visitCallSPIRVGroupBuiltin(CallInst * CI,Op OC)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 
visitCallSPIRVPipeBuiltin(CallInst * CI,Op OC)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 
translateMangledAtomicTypeName()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
getGroupBuiltinPrefix(CallInst * CI)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 
visitCastInst(CastInst & Cast)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 
createSPIRVToOCL20()552 ModulePass *llvm::createSPIRVToOCL20() {
553   return new SPIRVToOCL20();
554 }
555