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
55using namespace llvm;
56using namespace SPIRV;
57using namespace OCLUtil;
58
59namespace SPIRV {
60
61static cl::opt<std::string>
62MangledAtomicTypeNamePrefix("spirv-atomic-prefix",
63    cl::desc("Mangled atomic type name prefix"), cl::init("U7_Atomic"));
64
65class SPIRVToOCL20: public ModulePass,
66  public InstVisitor<SPIRVToOCL20> {
67public:
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;
131private:
132  Module *M;
133  LLVMContext *Ctx;
134};
135
136char SPIRVToOCL20::ID = 0;
137
138bool
139SPIRVToOCL20::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
158void
159SPIRVToOCL20::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
199void 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
215void 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
335void 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
399void 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
406void 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
434void 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
476void 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
493std::string
494SPIRVToOCL20::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
510void 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
549INITIALIZE_PASS(SPIRVToOCL20, "spvtoocl20",
550    "Translate SPIR-V builtins to OCL 2.0 builtins", false, false)
551
552ModulePass *llvm::createSPIRVToOCL20() {
553  return new SPIRVToOCL20();
554}
555