1 //===----- CGCUDANV.cpp - Interface to NVIDIA CUDA Runtime ----------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This provides a class for CUDA code generation targeting the NVIDIA CUDA 11 // runtime library. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "CGCUDARuntime.h" 16 #include "CodeGenFunction.h" 17 #include "CodeGenModule.h" 18 #include "clang/AST/Decl.h" 19 #include "clang/CodeGen/ConstantInitBuilder.h" 20 #include "llvm/IR/BasicBlock.h" 21 #include "llvm/IR/CallSite.h" 22 #include "llvm/IR/Constants.h" 23 #include "llvm/IR/DerivedTypes.h" 24 #include "llvm/Support/Format.h" 25 26 using namespace clang; 27 using namespace CodeGen; 28 29 namespace { 30 constexpr unsigned CudaFatMagic = 0x466243b1; 31 constexpr unsigned HIPFatMagic = 0x48495046; // "HIPF" 32 33 class CGNVCUDARuntime : public CGCUDARuntime { 34 35 private: 36 llvm::IntegerType *IntTy, *SizeTy; 37 llvm::Type *VoidTy; 38 llvm::PointerType *CharPtrTy, *VoidPtrTy, *VoidPtrPtrTy; 39 40 /// Convenience reference to LLVM Context 41 llvm::LLVMContext &Context; 42 /// Convenience reference to the current module 43 llvm::Module &TheModule; 44 /// Keeps track of kernel launch stubs emitted in this module 45 llvm::SmallVector<llvm::Function *, 16> EmittedKernels; 46 llvm::SmallVector<std::pair<llvm::GlobalVariable *, unsigned>, 16> DeviceVars; 47 /// Keeps track of variable containing handle of GPU binary. Populated by 48 /// ModuleCtorFunction() and used to create corresponding cleanup calls in 49 /// ModuleDtorFunction() 50 llvm::GlobalVariable *GpuBinaryHandle = nullptr; 51 /// Whether we generate relocatable device code. 52 bool RelocatableDeviceCode; 53 54 llvm::Constant *getSetupArgumentFn() const; 55 llvm::Constant *getLaunchFn() const; 56 57 llvm::FunctionType *getRegisterGlobalsFnTy() const; 58 llvm::FunctionType *getCallbackFnTy() const; 59 llvm::FunctionType *getRegisterLinkedBinaryFnTy() const; 60 std::string addPrefixToName(StringRef FuncName) const; 61 std::string addUnderscoredPrefixToName(StringRef FuncName) const; 62 63 /// Creates a function to register all kernel stubs generated in this module. 64 llvm::Function *makeRegisterGlobalsFn(); 65 66 /// Helper function that generates a constant string and returns a pointer to 67 /// the start of the string. The result of this function can be used anywhere 68 /// where the C code specifies const char*. 69 llvm::Constant *makeConstantString(const std::string &Str, 70 const std::string &Name = "", 71 const std::string &SectionName = "", 72 unsigned Alignment = 0) { 73 llvm::Constant *Zeros[] = {llvm::ConstantInt::get(SizeTy, 0), 74 llvm::ConstantInt::get(SizeTy, 0)}; 75 auto ConstStr = CGM.GetAddrOfConstantCString(Str, Name.c_str()); 76 llvm::GlobalVariable *GV = 77 cast<llvm::GlobalVariable>(ConstStr.getPointer()); 78 if (!SectionName.empty()) 79 GV->setSection(SectionName); 80 if (Alignment) 81 GV->setAlignment(Alignment); 82 83 return llvm::ConstantExpr::getGetElementPtr(ConstStr.getElementType(), 84 ConstStr.getPointer(), Zeros); 85 } 86 87 /// Helper function that generates an empty dummy function returning void. 88 llvm::Function *makeDummyFunction(llvm::FunctionType *FnTy) { 89 assert(FnTy->getReturnType()->isVoidTy() && 90 "Can only generate dummy functions returning void!"); 91 llvm::Function *DummyFunc = llvm::Function::Create( 92 FnTy, llvm::GlobalValue::InternalLinkage, "dummy", &TheModule); 93 94 llvm::BasicBlock *DummyBlock = 95 llvm::BasicBlock::Create(Context, "", DummyFunc); 96 CGBuilderTy FuncBuilder(CGM, Context); 97 FuncBuilder.SetInsertPoint(DummyBlock); 98 FuncBuilder.CreateRetVoid(); 99 100 return DummyFunc; 101 } 102 103 void emitDeviceStubBody(CodeGenFunction &CGF, FunctionArgList &Args); 104 105 public: 106 CGNVCUDARuntime(CodeGenModule &CGM); 107 108 void emitDeviceStub(CodeGenFunction &CGF, FunctionArgList &Args) override; 109 void registerDeviceVar(llvm::GlobalVariable &Var, unsigned Flags) override { 110 DeviceVars.push_back(std::make_pair(&Var, Flags)); 111 } 112 113 /// Creates module constructor function 114 llvm::Function *makeModuleCtorFunction() override; 115 /// Creates module destructor function 116 llvm::Function *makeModuleDtorFunction() override; 117 }; 118 119 } 120 121 std::string CGNVCUDARuntime::addPrefixToName(StringRef FuncName) const { 122 if (CGM.getLangOpts().HIP) 123 return ((Twine("hip") + Twine(FuncName)).str()); 124 return ((Twine("cuda") + Twine(FuncName)).str()); 125 } 126 std::string 127 CGNVCUDARuntime::addUnderscoredPrefixToName(StringRef FuncName) const { 128 if (CGM.getLangOpts().HIP) 129 return ((Twine("__hip") + Twine(FuncName)).str()); 130 return ((Twine("__cuda") + Twine(FuncName)).str()); 131 } 132 133 CGNVCUDARuntime::CGNVCUDARuntime(CodeGenModule &CGM) 134 : CGCUDARuntime(CGM), Context(CGM.getLLVMContext()), 135 TheModule(CGM.getModule()), 136 RelocatableDeviceCode(CGM.getLangOpts().CUDARelocatableDeviceCode) { 137 CodeGen::CodeGenTypes &Types = CGM.getTypes(); 138 ASTContext &Ctx = CGM.getContext(); 139 140 IntTy = CGM.IntTy; 141 SizeTy = CGM.SizeTy; 142 VoidTy = CGM.VoidTy; 143 144 CharPtrTy = llvm::PointerType::getUnqual(Types.ConvertType(Ctx.CharTy)); 145 VoidPtrTy = cast<llvm::PointerType>(Types.ConvertType(Ctx.VoidPtrTy)); 146 VoidPtrPtrTy = VoidPtrTy->getPointerTo(); 147 } 148 149 llvm::Constant *CGNVCUDARuntime::getSetupArgumentFn() const { 150 // cudaError_t cudaSetupArgument(void *, size_t, size_t) 151 llvm::Type *Params[] = {VoidPtrTy, SizeTy, SizeTy}; 152 return CGM.CreateRuntimeFunction( 153 llvm::FunctionType::get(IntTy, Params, false), 154 addPrefixToName("SetupArgument")); 155 } 156 157 llvm::Constant *CGNVCUDARuntime::getLaunchFn() const { 158 if (CGM.getLangOpts().HIP) { 159 // hipError_t hipLaunchByPtr(char *); 160 return CGM.CreateRuntimeFunction( 161 llvm::FunctionType::get(IntTy, CharPtrTy, false), "hipLaunchByPtr"); 162 } else { 163 // cudaError_t cudaLaunch(char *); 164 return CGM.CreateRuntimeFunction( 165 llvm::FunctionType::get(IntTy, CharPtrTy, false), "cudaLaunch"); 166 } 167 } 168 169 llvm::FunctionType *CGNVCUDARuntime::getRegisterGlobalsFnTy() const { 170 return llvm::FunctionType::get(VoidTy, VoidPtrPtrTy, false); 171 } 172 173 llvm::FunctionType *CGNVCUDARuntime::getCallbackFnTy() const { 174 return llvm::FunctionType::get(VoidTy, VoidPtrTy, false); 175 } 176 177 llvm::FunctionType *CGNVCUDARuntime::getRegisterLinkedBinaryFnTy() const { 178 auto CallbackFnTy = getCallbackFnTy(); 179 auto RegisterGlobalsFnTy = getRegisterGlobalsFnTy(); 180 llvm::Type *Params[] = {RegisterGlobalsFnTy->getPointerTo(), VoidPtrTy, 181 VoidPtrTy, CallbackFnTy->getPointerTo()}; 182 return llvm::FunctionType::get(VoidTy, Params, false); 183 } 184 185 void CGNVCUDARuntime::emitDeviceStub(CodeGenFunction &CGF, 186 FunctionArgList &Args) { 187 EmittedKernels.push_back(CGF.CurFn); 188 emitDeviceStubBody(CGF, Args); 189 } 190 191 void CGNVCUDARuntime::emitDeviceStubBody(CodeGenFunction &CGF, 192 FunctionArgList &Args) { 193 // Emit a call to cudaSetupArgument for each arg in Args. 194 llvm::Constant *cudaSetupArgFn = getSetupArgumentFn(); 195 llvm::BasicBlock *EndBlock = CGF.createBasicBlock("setup.end"); 196 CharUnits Offset = CharUnits::Zero(); 197 for (const VarDecl *A : Args) { 198 CharUnits TyWidth, TyAlign; 199 std::tie(TyWidth, TyAlign) = 200 CGM.getContext().getTypeInfoInChars(A->getType()); 201 Offset = Offset.alignTo(TyAlign); 202 llvm::Value *Args[] = { 203 CGF.Builder.CreatePointerCast(CGF.GetAddrOfLocalVar(A).getPointer(), 204 VoidPtrTy), 205 llvm::ConstantInt::get(SizeTy, TyWidth.getQuantity()), 206 llvm::ConstantInt::get(SizeTy, Offset.getQuantity()), 207 }; 208 llvm::CallSite CS = CGF.EmitRuntimeCallOrInvoke(cudaSetupArgFn, Args); 209 llvm::Constant *Zero = llvm::ConstantInt::get(IntTy, 0); 210 llvm::Value *CSZero = CGF.Builder.CreateICmpEQ(CS.getInstruction(), Zero); 211 llvm::BasicBlock *NextBlock = CGF.createBasicBlock("setup.next"); 212 CGF.Builder.CreateCondBr(CSZero, NextBlock, EndBlock); 213 CGF.EmitBlock(NextBlock); 214 Offset += TyWidth; 215 } 216 217 // Emit the call to cudaLaunch 218 llvm::Constant *cudaLaunchFn = getLaunchFn(); 219 llvm::Value *Arg = CGF.Builder.CreatePointerCast(CGF.CurFn, CharPtrTy); 220 CGF.EmitRuntimeCallOrInvoke(cudaLaunchFn, Arg); 221 CGF.EmitBranch(EndBlock); 222 223 CGF.EmitBlock(EndBlock); 224 } 225 226 /// Creates a function that sets up state on the host side for CUDA objects that 227 /// have a presence on both the host and device sides. Specifically, registers 228 /// the host side of kernel functions and device global variables with the CUDA 229 /// runtime. 230 /// \code 231 /// void __cuda_register_globals(void** GpuBinaryHandle) { 232 /// __cudaRegisterFunction(GpuBinaryHandle,Kernel0,...); 233 /// ... 234 /// __cudaRegisterFunction(GpuBinaryHandle,KernelM,...); 235 /// __cudaRegisterVar(GpuBinaryHandle, GlobalVar0, ...); 236 /// ... 237 /// __cudaRegisterVar(GpuBinaryHandle, GlobalVarN, ...); 238 /// } 239 /// \endcode 240 llvm::Function *CGNVCUDARuntime::makeRegisterGlobalsFn() { 241 // No need to register anything 242 if (EmittedKernels.empty() && DeviceVars.empty()) 243 return nullptr; 244 245 llvm::Function *RegisterKernelsFunc = llvm::Function::Create( 246 getRegisterGlobalsFnTy(), llvm::GlobalValue::InternalLinkage, 247 addUnderscoredPrefixToName("_register_globals"), &TheModule); 248 llvm::BasicBlock *EntryBB = 249 llvm::BasicBlock::Create(Context, "entry", RegisterKernelsFunc); 250 CGBuilderTy Builder(CGM, Context); 251 Builder.SetInsertPoint(EntryBB); 252 253 // void __cudaRegisterFunction(void **, const char *, char *, const char *, 254 // int, uint3*, uint3*, dim3*, dim3*, int*) 255 llvm::Type *RegisterFuncParams[] = { 256 VoidPtrPtrTy, CharPtrTy, CharPtrTy, CharPtrTy, IntTy, 257 VoidPtrTy, VoidPtrTy, VoidPtrTy, VoidPtrTy, IntTy->getPointerTo()}; 258 llvm::Constant *RegisterFunc = CGM.CreateRuntimeFunction( 259 llvm::FunctionType::get(IntTy, RegisterFuncParams, false), 260 addUnderscoredPrefixToName("RegisterFunction")); 261 262 // Extract GpuBinaryHandle passed as the first argument passed to 263 // __cuda_register_globals() and generate __cudaRegisterFunction() call for 264 // each emitted kernel. 265 llvm::Argument &GpuBinaryHandlePtr = *RegisterKernelsFunc->arg_begin(); 266 for (llvm::Function *Kernel : EmittedKernels) { 267 llvm::Constant *KernelName = makeConstantString(Kernel->getName()); 268 llvm::Constant *NullPtr = llvm::ConstantPointerNull::get(VoidPtrTy); 269 llvm::Value *Args[] = { 270 &GpuBinaryHandlePtr, Builder.CreateBitCast(Kernel, VoidPtrTy), 271 KernelName, KernelName, llvm::ConstantInt::get(IntTy, -1), NullPtr, 272 NullPtr, NullPtr, NullPtr, 273 llvm::ConstantPointerNull::get(IntTy->getPointerTo())}; 274 Builder.CreateCall(RegisterFunc, Args); 275 } 276 277 // void __cudaRegisterVar(void **, char *, char *, const char *, 278 // int, int, int, int) 279 llvm::Type *RegisterVarParams[] = {VoidPtrPtrTy, CharPtrTy, CharPtrTy, 280 CharPtrTy, IntTy, IntTy, 281 IntTy, IntTy}; 282 llvm::Constant *RegisterVar = CGM.CreateRuntimeFunction( 283 llvm::FunctionType::get(IntTy, RegisterVarParams, false), 284 addUnderscoredPrefixToName("RegisterVar")); 285 for (auto &Pair : DeviceVars) { 286 llvm::GlobalVariable *Var = Pair.first; 287 unsigned Flags = Pair.second; 288 llvm::Constant *VarName = makeConstantString(Var->getName()); 289 uint64_t VarSize = 290 CGM.getDataLayout().getTypeAllocSize(Var->getValueType()); 291 llvm::Value *Args[] = { 292 &GpuBinaryHandlePtr, 293 Builder.CreateBitCast(Var, VoidPtrTy), 294 VarName, 295 VarName, 296 llvm::ConstantInt::get(IntTy, (Flags & ExternDeviceVar) ? 1 : 0), 297 llvm::ConstantInt::get(IntTy, VarSize), 298 llvm::ConstantInt::get(IntTy, (Flags & ConstantDeviceVar) ? 1 : 0), 299 llvm::ConstantInt::get(IntTy, 0)}; 300 Builder.CreateCall(RegisterVar, Args); 301 } 302 303 Builder.CreateRetVoid(); 304 return RegisterKernelsFunc; 305 } 306 307 /// Creates a global constructor function for the module: 308 /// \code 309 /// void __cuda_module_ctor(void*) { 310 /// Handle = __cudaRegisterFatBinary(GpuBinaryBlob); 311 /// __cuda_register_globals(Handle); 312 /// } 313 /// \endcode 314 llvm::Function *CGNVCUDARuntime::makeModuleCtorFunction() { 315 bool IsHIP = CGM.getLangOpts().HIP; 316 // No need to generate ctors/dtors if there is no GPU binary. 317 StringRef CudaGpuBinaryFileName = CGM.getCodeGenOpts().CudaGpuBinaryFileName; 318 if (CudaGpuBinaryFileName.empty() && !IsHIP) 319 return nullptr; 320 321 // void __{cuda|hip}_register_globals(void* handle); 322 llvm::Function *RegisterGlobalsFunc = makeRegisterGlobalsFn(); 323 // We always need a function to pass in as callback. Create a dummy 324 // implementation if we don't need to register anything. 325 if (RelocatableDeviceCode && !RegisterGlobalsFunc) 326 RegisterGlobalsFunc = makeDummyFunction(getRegisterGlobalsFnTy()); 327 328 // void ** __{cuda|hip}RegisterFatBinary(void *); 329 llvm::Constant *RegisterFatbinFunc = CGM.CreateRuntimeFunction( 330 llvm::FunctionType::get(VoidPtrPtrTy, VoidPtrTy, false), 331 addUnderscoredPrefixToName("RegisterFatBinary")); 332 // struct { int magic, int version, void * gpu_binary, void * dont_care }; 333 llvm::StructType *FatbinWrapperTy = 334 llvm::StructType::get(IntTy, IntTy, VoidPtrTy, VoidPtrTy); 335 336 // Register GPU binary with the CUDA runtime, store returned handle in a 337 // global variable and save a reference in GpuBinaryHandle to be cleaned up 338 // in destructor on exit. Then associate all known kernels with the GPU binary 339 // handle so CUDA runtime can figure out what to call on the GPU side. 340 std::unique_ptr<llvm::MemoryBuffer> CudaGpuBinary; 341 if (!IsHIP) { 342 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> CudaGpuBinaryOrErr = 343 llvm::MemoryBuffer::getFileOrSTDIN(CudaGpuBinaryFileName); 344 if (std::error_code EC = CudaGpuBinaryOrErr.getError()) { 345 CGM.getDiags().Report(diag::err_cannot_open_file) 346 << CudaGpuBinaryFileName << EC.message(); 347 return nullptr; 348 } 349 CudaGpuBinary = std::move(CudaGpuBinaryOrErr.get()); 350 } 351 352 llvm::Function *ModuleCtorFunc = llvm::Function::Create( 353 llvm::FunctionType::get(VoidTy, VoidPtrTy, false), 354 llvm::GlobalValue::InternalLinkage, 355 addUnderscoredPrefixToName("_module_ctor"), &TheModule); 356 llvm::BasicBlock *CtorEntryBB = 357 llvm::BasicBlock::Create(Context, "entry", ModuleCtorFunc); 358 CGBuilderTy CtorBuilder(CGM, Context); 359 360 CtorBuilder.SetInsertPoint(CtorEntryBB); 361 362 const char *FatbinConstantName; 363 const char *FatbinSectionName; 364 const char *ModuleIDSectionName; 365 StringRef ModuleIDPrefix; 366 llvm::Constant *FatBinStr; 367 unsigned FatMagic; 368 if (IsHIP) { 369 FatbinConstantName = ".hip_fatbin"; 370 FatbinSectionName = ".hipFatBinSegment"; 371 372 ModuleIDSectionName = "__hip_module_id"; 373 ModuleIDPrefix = "__hip_"; 374 375 // For HIP, create an external symbol __hip_fatbin in section .hip_fatbin. 376 // The external symbol is supposed to contain the fat binary but will be 377 // populated somewhere else, e.g. by lld through link script. 378 FatBinStr = new llvm::GlobalVariable( 379 CGM.getModule(), CGM.Int8Ty, 380 /*isConstant=*/true, llvm::GlobalValue::ExternalLinkage, nullptr, 381 "__hip_fatbin", nullptr, 382 llvm::GlobalVariable::NotThreadLocal); 383 cast<llvm::GlobalVariable>(FatBinStr)->setSection(FatbinConstantName); 384 385 FatMagic = HIPFatMagic; 386 } else { 387 if (RelocatableDeviceCode) 388 // TODO: Figure out how this is called on mac OS! 389 FatbinConstantName = "__nv_relfatbin"; 390 else 391 FatbinConstantName = 392 CGM.getTriple().isMacOSX() ? "__NV_CUDA,__nv_fatbin" : ".nv_fatbin"; 393 // NVIDIA's cuobjdump looks for fatbins in this section. 394 FatbinSectionName = 395 CGM.getTriple().isMacOSX() ? "__NV_CUDA,__fatbin" : ".nvFatBinSegment"; 396 397 // TODO: Figure out how this is called on mac OS! 398 ModuleIDSectionName = "__nv_module_id"; 399 ModuleIDPrefix = "__nv_"; 400 401 // For CUDA, create a string literal containing the fat binary loaded from 402 // the given file. 403 FatBinStr = makeConstantString(CudaGpuBinary->getBuffer(), "", 404 FatbinConstantName, 8); 405 FatMagic = CudaFatMagic; 406 } 407 408 // Create initialized wrapper structure that points to the loaded GPU binary 409 ConstantInitBuilder Builder(CGM); 410 auto Values = Builder.beginStruct(FatbinWrapperTy); 411 // Fatbin wrapper magic. 412 Values.addInt(IntTy, FatMagic); 413 // Fatbin version. 414 Values.addInt(IntTy, 1); 415 // Data. 416 Values.add(FatBinStr); 417 // Unused in fatbin v1. 418 Values.add(llvm::ConstantPointerNull::get(VoidPtrTy)); 419 llvm::GlobalVariable *FatbinWrapper = Values.finishAndCreateGlobal( 420 addUnderscoredPrefixToName("_fatbin_wrapper"), CGM.getPointerAlign(), 421 /*constant*/ true); 422 FatbinWrapper->setSection(FatbinSectionName); 423 424 // Register binary with CUDA/HIP runtime. This is substantially different in 425 // default mode vs. separate compilation! 426 if (!RelocatableDeviceCode) { 427 // GpuBinaryHandle = __{cuda|hip}RegisterFatBinary(&FatbinWrapper); 428 llvm::CallInst *RegisterFatbinCall = CtorBuilder.CreateCall( 429 RegisterFatbinFunc, 430 CtorBuilder.CreateBitCast(FatbinWrapper, VoidPtrTy)); 431 GpuBinaryHandle = new llvm::GlobalVariable( 432 TheModule, VoidPtrPtrTy, false, llvm::GlobalValue::InternalLinkage, 433 llvm::ConstantPointerNull::get(VoidPtrPtrTy), 434 addUnderscoredPrefixToName("_gpubin_handle")); 435 436 CtorBuilder.CreateAlignedStore(RegisterFatbinCall, GpuBinaryHandle, 437 CGM.getPointerAlign()); 438 439 // Call __{cuda|hip}_register_globals(GpuBinaryHandle); 440 if (RegisterGlobalsFunc) 441 CtorBuilder.CreateCall(RegisterGlobalsFunc, RegisterFatbinCall); 442 } else { 443 // Generate a unique module ID. 444 SmallString<64> ModuleID; 445 llvm::raw_svector_ostream OS(ModuleID); 446 OS << ModuleIDPrefix << llvm::format("%x", FatbinWrapper->getGUID()); 447 llvm::Constant *ModuleIDConstant = 448 makeConstantString(ModuleID.str(), "", ModuleIDSectionName, 32); 449 450 // Create an alias for the FatbinWrapper that nvcc or hip backend will 451 // look for. 452 llvm::GlobalAlias::create(llvm::GlobalValue::ExternalLinkage, 453 Twine("__fatbinwrap") + ModuleID, FatbinWrapper); 454 455 // void __{cuda|hip}RegisterLinkedBinary%ModuleID%(void (*)(void *), void *, 456 // void *, void (*)(void **)) 457 SmallString<128> RegisterLinkedBinaryName( 458 addUnderscoredPrefixToName("RegisterLinkedBinary")); 459 RegisterLinkedBinaryName += ModuleID; 460 llvm::Constant *RegisterLinkedBinaryFunc = CGM.CreateRuntimeFunction( 461 getRegisterLinkedBinaryFnTy(), RegisterLinkedBinaryName); 462 463 assert(RegisterGlobalsFunc && "Expecting at least dummy function!"); 464 llvm::Value *Args[] = {RegisterGlobalsFunc, 465 CtorBuilder.CreateBitCast(FatbinWrapper, VoidPtrTy), 466 ModuleIDConstant, 467 makeDummyFunction(getCallbackFnTy())}; 468 CtorBuilder.CreateCall(RegisterLinkedBinaryFunc, Args); 469 } 470 471 CtorBuilder.CreateRetVoid(); 472 return ModuleCtorFunc; 473 } 474 475 /// Creates a global destructor function that unregisters the GPU code blob 476 /// registered by constructor. 477 /// \code 478 /// void __cuda_module_dtor(void*) { 479 /// __cudaUnregisterFatBinary(Handle); 480 /// } 481 /// \endcode 482 llvm::Function *CGNVCUDARuntime::makeModuleDtorFunction() { 483 // No need for destructor if we don't have a handle to unregister. 484 if (!GpuBinaryHandle) 485 return nullptr; 486 487 // void __cudaUnregisterFatBinary(void ** handle); 488 llvm::Constant *UnregisterFatbinFunc = CGM.CreateRuntimeFunction( 489 llvm::FunctionType::get(VoidTy, VoidPtrPtrTy, false), 490 addUnderscoredPrefixToName("UnregisterFatBinary")); 491 492 llvm::Function *ModuleDtorFunc = llvm::Function::Create( 493 llvm::FunctionType::get(VoidTy, VoidPtrTy, false), 494 llvm::GlobalValue::InternalLinkage, 495 addUnderscoredPrefixToName("_module_dtor"), &TheModule); 496 497 llvm::BasicBlock *DtorEntryBB = 498 llvm::BasicBlock::Create(Context, "entry", ModuleDtorFunc); 499 CGBuilderTy DtorBuilder(CGM, Context); 500 DtorBuilder.SetInsertPoint(DtorEntryBB); 501 502 auto HandleValue = 503 DtorBuilder.CreateAlignedLoad(GpuBinaryHandle, CGM.getPointerAlign()); 504 DtorBuilder.CreateCall(UnregisterFatbinFunc, HandleValue); 505 506 DtorBuilder.CreateRetVoid(); 507 return ModuleDtorFunc; 508 } 509 510 CGCUDARuntime *CodeGen::CreateNVCUDARuntime(CodeGenModule &CGM) { 511 return new CGNVCUDARuntime(CGM); 512 } 513