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(cuMemcpyDtoDAsync, 4);
36 
37 DLWRAP(cuMemcpyDtoH, 3);
38 DLWRAP(cuMemcpyDtoHAsync, 4);
39 DLWRAP(cuMemcpyHtoD, 3);
40 DLWRAP(cuMemcpyHtoDAsync, 4);
41 
42 DLWRAP(cuMemFree, 1);
43 DLWRAP(cuModuleGetFunction, 3);
44 DLWRAP(cuModuleGetGlobal, 4);
45 
46 DLWRAP(cuModuleUnload, 1);
47 DLWRAP(cuStreamCreate, 2);
48 DLWRAP(cuStreamDestroy, 1);
49 DLWRAP(cuStreamSynchronize, 1);
50 DLWRAP(cuCtxSetCurrent, 1);
51 DLWRAP(cuDevicePrimaryCtxRelease, 1);
52 DLWRAP(cuDevicePrimaryCtxGetState, 3);
53 DLWRAP(cuDevicePrimaryCtxSetFlags, 2);
54 DLWRAP(cuDevicePrimaryCtxRetain, 2);
55 DLWRAP(cuModuleLoadDataEx, 5);
56 
57 DLWRAP(cuDeviceCanAccessPeer, 3);
58 DLWRAP(cuCtxEnablePeerAccess, 2);
59 DLWRAP(cuMemcpyPeerAsync, 6);
60 
61 DLWRAP_FINALIZE();
62 
63 #ifndef DYNAMIC_CUDA_PATH
64 #define DYNAMIC_CUDA_PATH "libcuda.so"
65 #endif
66 
67 #define TARGET_NAME CUDA
68 #define DEBUG_PREFIX "Target " GETNAME(TARGET_NAME) " RTL"
69 
70 static bool checkForCUDA() {
71   // return true if dlopen succeeded and all functions found
72 
73   // Prefer _v2 versions of functions if found in the library
74   std::unordered_map<std::string, const char *> TryFirst = {
75       {"cuMemAlloc", "cuMemAlloc_v2"},
76       {"cuMemFree", "cuMemFree_v2"},
77       {"cuMemcpyDtoH", "cuMemcpyDtoH_v2"},
78       {"cuMemcpyHtoD", "cuMemcpyHtoD_v2"},
79       {"cuStreamDestroy", "cuStreamDestroy_v2"},
80       {"cuModuleGetGlobal", "cuModuleGetGlobal_v2"},
81       {"cuMemcpyDtoHAsync", "cuMemcpyDtoHAsync_v2"},
82       {"cuMemcpyDtoDAsync", "cuMemcpyDtoDAsync_v2"},
83       {"cuMemcpyHtoDAsync", "cuMemcpyHtoDAsync_v2"},
84       {"cuDevicePrimaryCtxRelease", "cuDevicePrimaryCtxRelease_v2"},
85       {"cuDevicePrimaryCtxSetFlags", "cuDevicePrimaryCtxSetFlags_v2"},
86   };
87 
88   const char *CudaLib = DYNAMIC_CUDA_PATH;
89   void *DynlibHandle = dlopen(CudaLib, RTLD_NOW);
90   if (!DynlibHandle) {
91     DP("Unable to load library '%s': %s!\n", CudaLib, dlerror());
92     return false;
93   }
94 
95   for (size_t I = 0; I < dlwrap::size(); I++) {
96     const char *Sym = dlwrap::symbol(I);
97 
98     auto It = TryFirst.find(Sym);
99     if (It != TryFirst.end()) {
100       const char *First = It->second;
101       void *P = dlsym(DynlibHandle, First);
102       if (P) {
103         DP("Implementing %s with dlsym(%s) -> %p\n", Sym, First, P);
104         *dlwrap::pointer(I) = P;
105         continue;
106       }
107     }
108 
109     void *P = dlsym(DynlibHandle, Sym);
110     if (P == nullptr) {
111       DP("Unable to find '%s' in '%s'!\n", Sym, CudaLib);
112       return false;
113     }
114     DP("Implementing %s with dlsym(%s) -> %p\n", Sym, Sym, P);
115 
116     *dlwrap::pointer(I) = P;
117   }
118 
119   return true;
120 }
121 
122 CUresult cuInit(unsigned X) {
123   // Note: Called exactly once from cuda rtl.cpp in a global constructor so
124   // does not need to handle being called repeatedly or concurrently
125   if (!checkForCUDA()) {
126     return CUDA_ERROR_INVALID_HANDLE;
127   }
128   return dlwrap_cuInit(X);
129 }
130