1 //===--- cuda/dynamic_cuda/cuda.pp ------------------------------- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Implement subset of cuda api by calling into cuda library via dlopen 10 // Does the dlopen/dlsym calls as part of the call to cuInit 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "cuda.h" 15 #include "Debug.h" 16 #include "dlwrap.h" 17 18 #include <string> 19 #include <unordered_map> 20 21 #include <dlfcn.h> 22 23 DLWRAP_INTERNAL(cuInit, 1); 24 25 DLWRAP(cuCtxGetDevice, 1); 26 DLWRAP(cuDeviceGet, 2); 27 DLWRAP(cuDeviceGetAttribute, 3); 28 DLWRAP(cuDeviceGetCount, 1); 29 DLWRAP(cuFuncGetAttribute, 3); 30 31 DLWRAP(cuGetErrorString, 2); 32 DLWRAP(cuLaunchKernel, 11); 33 34 DLWRAP(cuMemAlloc, 2); 35 DLWRAP(cuMemAllocHost, 2); 36 DLWRAP(cuMemAllocManaged, 3); 37 38 DLWRAP(cuMemcpyDtoDAsync, 4); 39 DLWRAP(cuMemcpyDtoH, 3); 40 DLWRAP(cuMemcpyDtoHAsync, 4); 41 DLWRAP(cuMemcpyHtoD, 3); 42 DLWRAP(cuMemcpyHtoDAsync, 4); 43 44 DLWRAP(cuMemFree, 1); 45 DLWRAP(cuMemFreeHost, 1); 46 DLWRAP(cuModuleGetFunction, 3); 47 DLWRAP(cuModuleGetGlobal, 4); 48 49 DLWRAP(cuModuleUnload, 1); 50 DLWRAP(cuStreamCreate, 2); 51 DLWRAP(cuStreamDestroy, 1); 52 DLWRAP(cuStreamSynchronize, 1); 53 DLWRAP(cuCtxSetCurrent, 1); 54 DLWRAP(cuDevicePrimaryCtxRelease, 1); 55 DLWRAP(cuDevicePrimaryCtxGetState, 3); 56 DLWRAP(cuDevicePrimaryCtxSetFlags, 2); 57 DLWRAP(cuDevicePrimaryCtxRetain, 2); 58 DLWRAP(cuModuleLoadDataEx, 5); 59 60 DLWRAP(cuDeviceCanAccessPeer, 3); 61 DLWRAP(cuCtxEnablePeerAccess, 2); 62 DLWRAP(cuMemcpyPeerAsync, 6); 63 64 DLWRAP_FINALIZE(); 65 66 #ifndef DYNAMIC_CUDA_PATH 67 #define DYNAMIC_CUDA_PATH "libcuda.so" 68 #endif 69 70 #define TARGET_NAME CUDA 71 #define DEBUG_PREFIX "Target " GETNAME(TARGET_NAME) " RTL" 72 73 static bool checkForCUDA() { 74 // return true if dlopen succeeded and all functions found 75 76 // Prefer _v2 versions of functions if found in the library 77 std::unordered_map<std::string, const char *> TryFirst = { 78 {"cuMemAlloc", "cuMemAlloc_v2"}, 79 {"cuMemFree", "cuMemFree_v2"}, 80 {"cuMemcpyDtoH", "cuMemcpyDtoH_v2"}, 81 {"cuMemcpyHtoD", "cuMemcpyHtoD_v2"}, 82 {"cuStreamDestroy", "cuStreamDestroy_v2"}, 83 {"cuModuleGetGlobal", "cuModuleGetGlobal_v2"}, 84 {"cuMemcpyDtoHAsync", "cuMemcpyDtoHAsync_v2"}, 85 {"cuMemcpyDtoDAsync", "cuMemcpyDtoDAsync_v2"}, 86 {"cuMemcpyHtoDAsync", "cuMemcpyHtoDAsync_v2"}, 87 {"cuDevicePrimaryCtxRelease", "cuDevicePrimaryCtxRelease_v2"}, 88 {"cuDevicePrimaryCtxSetFlags", "cuDevicePrimaryCtxSetFlags_v2"}, 89 }; 90 91 const char *CudaLib = DYNAMIC_CUDA_PATH; 92 void *DynlibHandle = dlopen(CudaLib, RTLD_NOW); 93 if (!DynlibHandle) { 94 DP("Unable to load library '%s': %s!\n", CudaLib, dlerror()); 95 return false; 96 } 97 98 for (size_t I = 0; I < dlwrap::size(); I++) { 99 const char *Sym = dlwrap::symbol(I); 100 101 auto It = TryFirst.find(Sym); 102 if (It != TryFirst.end()) { 103 const char *First = It->second; 104 void *P = dlsym(DynlibHandle, First); 105 if (P) { 106 DP("Implementing %s with dlsym(%s) -> %p\n", Sym, First, P); 107 *dlwrap::pointer(I) = P; 108 continue; 109 } 110 } 111 112 void *P = dlsym(DynlibHandle, Sym); 113 if (P == nullptr) { 114 DP("Unable to find '%s' in '%s'!\n", Sym, CudaLib); 115 return false; 116 } 117 DP("Implementing %s with dlsym(%s) -> %p\n", Sym, Sym, P); 118 119 *dlwrap::pointer(I) = P; 120 } 121 122 return true; 123 } 124 125 CUresult cuInit(unsigned X) { 126 // Note: Called exactly once from cuda rtl.cpp in a global constructor so 127 // does not need to handle being called repeatedly or concurrently 128 if (!checkForCUDA()) { 129 return CUDA_ERROR_INVALID_HANDLE; 130 } 131 return dlwrap_cuInit(X); 132 } 133