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