13de4cd12SGeorge Rokos //===----RTLs/cuda/src/rtl.cpp - Target RTLs Implementation ------- C++ -*-===//
23de4cd12SGeorge Rokos //
357b08b09SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
457b08b09SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
557b08b09SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63de4cd12SGeorge Rokos //
73de4cd12SGeorge Rokos //===----------------------------------------------------------------------===//
83de4cd12SGeorge Rokos //
93de4cd12SGeorge Rokos // RTL for CUDA machine
103de4cd12SGeorge Rokos //
113de4cd12SGeorge Rokos //===----------------------------------------------------------------------===//
123de4cd12SGeorge Rokos 
13a5153dbcSJohannes Doerfert #include <atomic>
143de4cd12SGeorge Rokos #include <cassert>
153de4cd12SGeorge Rokos #include <cstddef>
163de4cd12SGeorge Rokos #include <cuda.h>
173de4cd12SGeorge Rokos #include <list>
18a5153dbcSJohannes Doerfert #include <memory>
19*feed674dSShilei Tian #include <mutex>
203de4cd12SGeorge Rokos #include <string>
213de4cd12SGeorge Rokos #include <vector>
223de4cd12SGeorge Rokos 
231546d319SGeorge Rokos #include "omptargetplugin.h"
243de4cd12SGeorge Rokos 
253de4cd12SGeorge Rokos #ifndef TARGET_NAME
263de4cd12SGeorge Rokos #define TARGET_NAME CUDA
273de4cd12SGeorge Rokos #endif
283de4cd12SGeorge Rokos 
29b305d26bSSergey Dmitriev #ifdef OMPTARGET_DEBUG
30b305d26bSSergey Dmitriev static int DebugLevel = 0;
31b305d26bSSergey Dmitriev 
323de4cd12SGeorge Rokos #define GETNAME2(name) #name
333de4cd12SGeorge Rokos #define GETNAME(name) GETNAME2(name)
34b305d26bSSergey Dmitriev #define DP(...) \
35b305d26bSSergey Dmitriev   do { \
36b305d26bSSergey Dmitriev     if (DebugLevel > 0) { \
37b305d26bSSergey Dmitriev       DEBUGP("Target " GETNAME(TARGET_NAME) " RTL", __VA_ARGS__); \
38b305d26bSSergey Dmitriev     } \
39b305d26bSSergey Dmitriev   } while (false)
4078769ec4SMichael Kruse 
4178769ec4SMichael Kruse // Utility for retrieving and printing CUDA error string.
4278769ec4SMichael Kruse #define CUDA_ERR_STRING(err) \
4378769ec4SMichael Kruse   do { \
4478769ec4SMichael Kruse     if (DebugLevel > 0) { \
4578769ec4SMichael Kruse       const char *errStr; \
4678769ec4SMichael Kruse       cuGetErrorString(err, &errStr); \
4778769ec4SMichael Kruse       DEBUGP("Target " GETNAME(TARGET_NAME) " RTL", "CUDA error is: %s\n", errStr); \
4878769ec4SMichael Kruse     } \
4978769ec4SMichael Kruse   } while (false)
50b305d26bSSergey Dmitriev #else // OMPTARGET_DEBUG
51b305d26bSSergey Dmitriev #define DP(...) {}
5278769ec4SMichael Kruse #define CUDA_ERR_STRING(err) {}
53b305d26bSSergey Dmitriev #endif // OMPTARGET_DEBUG
543de4cd12SGeorge Rokos 
553de4cd12SGeorge Rokos #include "../../common/elf_common.c"
563de4cd12SGeorge Rokos 
573de4cd12SGeorge Rokos /// Keep entries table per device.
583de4cd12SGeorge Rokos struct FuncOrGblEntryTy {
593de4cd12SGeorge Rokos   __tgt_target_table Table;
603de4cd12SGeorge Rokos   std::vector<__tgt_offload_entry> Entries;
613de4cd12SGeorge Rokos };
623de4cd12SGeorge Rokos 
633de4cd12SGeorge Rokos enum ExecutionModeType {
64c13df8e5SGeorge Rokos   SPMD, // constructors, destructors,
65c13df8e5SGeorge Rokos         // combined constructs (`teams distribute parallel for [simd]`)
66c13df8e5SGeorge Rokos   GENERIC, // everything else
673de4cd12SGeorge Rokos   NONE
683de4cd12SGeorge Rokos };
693de4cd12SGeorge Rokos 
70dc34b1c9SRon Lieberman /// Use a single entity to encode a kernel and a set of flags.
713de4cd12SGeorge Rokos struct KernelTy {
723de4cd12SGeorge Rokos   CUfunction Func;
733de4cd12SGeorge Rokos 
743de4cd12SGeorge Rokos   // execution mode of kernel
753de4cd12SGeorge Rokos   // 0 - SPMD mode (without master warp)
763de4cd12SGeorge Rokos   // 1 - Generic mode (with master warp)
773de4cd12SGeorge Rokos   int8_t ExecutionMode;
783de4cd12SGeorge Rokos 
793de4cd12SGeorge Rokos   KernelTy(CUfunction _Func, int8_t _ExecutionMode)
803de4cd12SGeorge Rokos       : Func(_Func), ExecutionMode(_ExecutionMode) {}
813de4cd12SGeorge Rokos };
823de4cd12SGeorge Rokos 
834c6a098aSKazuaki Ishizaki /// Device environment data
84*feed674dSShilei Tian /// Manually sync with the deviceRTL side for now, move to a dedicated header
85*feed674dSShilei Tian /// file later.
86e1c7a46dSGuansong Zhang struct omptarget_device_environmentTy {
87e1c7a46dSGuansong Zhang   int32_t debug_level;
88e1c7a46dSGuansong Zhang };
89e1c7a46dSGuansong Zhang 
903de4cd12SGeorge Rokos /// List that contains all the kernels.
913de4cd12SGeorge Rokos /// FIXME: we may need this to be per device and per library.
923de4cd12SGeorge Rokos std::list<KernelTy> KernelsList;
933de4cd12SGeorge Rokos 
94*feed674dSShilei Tian namespace {
95*feed674dSShilei Tian bool checkResult(CUresult Err, const char *ErrMsg) {
96*feed674dSShilei Tian   if (Err == CUDA_SUCCESS)
97*feed674dSShilei Tian     return true;
98*feed674dSShilei Tian 
99*feed674dSShilei Tian   DP(ErrMsg);
100*feed674dSShilei Tian   CUDA_ERR_STRING(Err);
101*feed674dSShilei Tian   return false;
102*feed674dSShilei Tian }
103*feed674dSShilei Tian } // namespace
104*feed674dSShilei Tian 
105*feed674dSShilei Tian class StreamManagerTy {
106*feed674dSShilei Tian   int NumberOfDevices;
107*feed674dSShilei Tian   // Per-device stream mutex
108*feed674dSShilei Tian   std::vector<std::unique_ptr<std::mutex>> StreamMtx;
109*feed674dSShilei Tian   // Per-device stream Id indicates the next available stream in the pool
110*feed674dSShilei Tian   std::vector<int> NextStreamId;
111*feed674dSShilei Tian   // Per-device stream pool
112*feed674dSShilei Tian   std::vector<std::vector<CUstream>> StreamPool;
113*feed674dSShilei Tian   // Pointer to per-device context
114*feed674dSShilei Tian   std::vector<CUcontext> &ContextsPtr;
115*feed674dSShilei Tian 
116*feed674dSShilei Tian   // If there is no CUstream left in the pool, we will resize the pool to
117*feed674dSShilei Tian   // allocate more CUstream. This function should be called with device mutex,
118*feed674dSShilei Tian   // and we do not resize to smaller one.
119*feed674dSShilei Tian   void resizeStreamPool(const int DeviceId, const size_t NewSize) {
120*feed674dSShilei Tian     std::vector<CUstream> &Pool = StreamPool[DeviceId];
121*feed674dSShilei Tian     const size_t CurrentSize = Pool.size();
122*feed674dSShilei Tian     assert(NewSize > CurrentSize && "new size is not larger than current size");
123*feed674dSShilei Tian 
124*feed674dSShilei Tian     Pool.resize(NewSize, nullptr);
125*feed674dSShilei Tian 
126*feed674dSShilei Tian     CUresult err = cuCtxSetCurrent(ContextsPtr[DeviceId]);
127*feed674dSShilei Tian     if (!checkResult(err, "Error when setting current CUDA context\n")) {
128*feed674dSShilei Tian       // We will return if cannot switch to the right context in case of
129*feed674dSShilei Tian       // creating bunch of streams that are not corresponding to the right
130*feed674dSShilei Tian       // device. The offloading will fail later because selected CUstream is
131*feed674dSShilei Tian       // nullptr.
132*feed674dSShilei Tian       return;
133*feed674dSShilei Tian     }
134*feed674dSShilei Tian 
135*feed674dSShilei Tian     for (size_t I = CurrentSize; I < NewSize; ++I) {
136*feed674dSShilei Tian       err = cuStreamCreate(&Pool[I], CU_STREAM_NON_BLOCKING);
137*feed674dSShilei Tian       checkResult(err,
138*feed674dSShilei Tian                   "Error when creating CUDA stream to resize stream pool\n");
139*feed674dSShilei Tian     }
140*feed674dSShilei Tian   }
141*feed674dSShilei Tian 
142*feed674dSShilei Tian public:
143*feed674dSShilei Tian   StreamManagerTy(const int NumberOfDevices, std::vector<CUcontext> &CtxPtr)
144*feed674dSShilei Tian       : NumberOfDevices(NumberOfDevices), ContextsPtr(CtxPtr) {
145*feed674dSShilei Tian     StreamPool.resize(NumberOfDevices);
146*feed674dSShilei Tian     NextStreamId.resize(NumberOfDevices);
147*feed674dSShilei Tian     StreamMtx.resize(NumberOfDevices);
148*feed674dSShilei Tian 
149*feed674dSShilei Tian     // Initially let's create 32 streams for each device
150*feed674dSShilei Tian     int EnvNumInitialStreams = 32;
151*feed674dSShilei Tian     char *envStr = getenv("LIBOMPTARGET_NUM_INITIAL_STREAMS");
152*feed674dSShilei Tian     if (envStr)
153*feed674dSShilei Tian       EnvNumInitialStreams = std::stoi(envStr);
154*feed674dSShilei Tian 
155*feed674dSShilei Tian     // Initialize the stream pool for each device
156*feed674dSShilei Tian     for (std::vector<CUstream> &S : StreamPool)
157*feed674dSShilei Tian       S.resize(EnvNumInitialStreams);
158*feed674dSShilei Tian 
159*feed674dSShilei Tian     // Initialize the next stream id
160*feed674dSShilei Tian     std::fill(NextStreamId.begin(), NextStreamId.end(), 0);
161*feed674dSShilei Tian 
162*feed674dSShilei Tian     // Initialize stream mutex
163*feed674dSShilei Tian     for (std::unique_ptr<std::mutex> &Ptr : StreamMtx)
164*feed674dSShilei Tian       Ptr = std::make_unique<std::mutex>();
165*feed674dSShilei Tian   }
166*feed674dSShilei Tian 
167*feed674dSShilei Tian   ~StreamManagerTy() {
168*feed674dSShilei Tian     // Destroy streams
169*feed674dSShilei Tian     for (int I = 0; I < NumberOfDevices; ++I) {
170*feed674dSShilei Tian       CUresult err = cuCtxSetCurrent(ContextsPtr[I]);
171*feed674dSShilei Tian       checkResult(err, "Error when setting current CUDA context\n");
172*feed674dSShilei Tian 
173*feed674dSShilei Tian       for (CUstream &S : StreamPool[I]) {
174*feed674dSShilei Tian         if (!S)
175*feed674dSShilei Tian           continue;
176*feed674dSShilei Tian         err = cuStreamDestroy(S);
177*feed674dSShilei Tian         checkResult(err, "Error when destroying CUDA stream\n");
178*feed674dSShilei Tian       }
179*feed674dSShilei Tian     }
180*feed674dSShilei Tian   }
181*feed674dSShilei Tian 
182*feed674dSShilei Tian   // Get a CUstream from pool. Per-device next stream id always points to the
183*feed674dSShilei Tian   // next available CUstream. That means, CUstreams [0, id-1] have been
184*feed674dSShilei Tian   // assigned, and [id,] are still available. If there is no CUstream left, we
185*feed674dSShilei Tian   // will ask more CUstreams from CUDA RT. Each time a CUstream is assigned,
186*feed674dSShilei Tian   // the id will increase one.
187*feed674dSShilei Tian   // xxxxxs+++++++++
188*feed674dSShilei Tian   //      ^
189*feed674dSShilei Tian   //      id
190*feed674dSShilei Tian   // After assignment, the pool becomes the following and s is assigned.
191*feed674dSShilei Tian   // xxxxxs+++++++++
192*feed674dSShilei Tian   //       ^
193*feed674dSShilei Tian   //       id
194*feed674dSShilei Tian   CUstream getStream(const int DeviceId) {
195*feed674dSShilei Tian     assert(DeviceId >= 0 &&
196*feed674dSShilei Tian            static_cast<size_t>(DeviceId) < NextStreamId.size() &&
197*feed674dSShilei Tian            "Unexpected device id");
198*feed674dSShilei Tian 
199*feed674dSShilei Tian     const std::lock_guard<std::mutex> Lock(*StreamMtx[DeviceId]);
200*feed674dSShilei Tian     int &Id = NextStreamId[DeviceId];
201*feed674dSShilei Tian     // No CUstream left in the pool, we need to request from CUDA RT
202*feed674dSShilei Tian     if (Id == StreamPool[DeviceId].size()) {
203*feed674dSShilei Tian       // By default we double the stream pool every time
204*feed674dSShilei Tian       resizeStreamPool(DeviceId, Id * 2);
205*feed674dSShilei Tian     }
206*feed674dSShilei Tian     return StreamPool[DeviceId][Id++];
207*feed674dSShilei Tian   }
208*feed674dSShilei Tian 
209*feed674dSShilei Tian   // Return a CUstream back to pool. As mentioned above, per-device next
210*feed674dSShilei Tian   // stream is always points to the next available CUstream, so when we return
211*feed674dSShilei Tian   // a CUstream, we need to first decrease the id, and then copy the CUstream
212*feed674dSShilei Tian   // back.
213*feed674dSShilei Tian   // It is worth noting that, the order of streams return might be different
214*feed674dSShilei Tian   // from that they're assigned, that saying, at some point, there might be
215*feed674dSShilei Tian   // two identical CUstreams.
216*feed674dSShilei Tian   // xxax+a+++++
217*feed674dSShilei Tian   //     ^
218*feed674dSShilei Tian   //     id
219*feed674dSShilei Tian   // However, it doesn't matter, because they're always on the two sides of
220*feed674dSShilei Tian   // id. The left one will in the end be overwritten by another CUstream.
221*feed674dSShilei Tian   // Therefore, after several execution, the order of pool might be different
222*feed674dSShilei Tian   // from its initial state.
223*feed674dSShilei Tian   void returnStream(const int DeviceId, CUstream Stream) {
224*feed674dSShilei Tian     assert(DeviceId >= 0 &&
225*feed674dSShilei Tian            static_cast<size_t>(DeviceId) < NextStreamId.size() &&
226*feed674dSShilei Tian            "Unexpected device id");
227*feed674dSShilei Tian 
228*feed674dSShilei Tian     const std::lock_guard<std::mutex> Lock(*StreamMtx[DeviceId]);
229*feed674dSShilei Tian     int &Id = NextStreamId[DeviceId];
230*feed674dSShilei Tian     assert(Id > 0 && "Wrong stream ID");
231*feed674dSShilei Tian     StreamPool[DeviceId][--Id] = Stream;
232*feed674dSShilei Tian   }
233*feed674dSShilei Tian 
234*feed674dSShilei Tian   void initializeDevice(int DeviceId) {
235*feed674dSShilei Tian     // This function should be called after setting right context
236*feed674dSShilei Tian     for (CUstream &Stream : StreamPool[DeviceId]) {
237*feed674dSShilei Tian       CUresult Err = cuStreamCreate(&Stream, CU_STREAM_NON_BLOCKING);
238*feed674dSShilei Tian       checkResult(Err, "Error when creating CUDA stream\n");
239*feed674dSShilei Tian     }
240*feed674dSShilei Tian   }
241*feed674dSShilei Tian };
242*feed674dSShilei Tian 
2433de4cd12SGeorge Rokos /// Class containing all the device information.
2443de4cd12SGeorge Rokos class RTLDeviceInfoTy {
2452622e9e5SAlexey Bataev   std::vector<std::list<FuncOrGblEntryTy>> FuncGblEntries;
246*feed674dSShilei Tian   std::shared_ptr<StreamManagerTy> StreamManager;
2473de4cd12SGeorge Rokos 
2483de4cd12SGeorge Rokos public:
2493de4cd12SGeorge Rokos   int NumberOfDevices;
2503de4cd12SGeorge Rokos   std::vector<CUmodule> Modules;
2513de4cd12SGeorge Rokos   std::vector<CUcontext> Contexts;
2523de4cd12SGeorge Rokos 
2533de4cd12SGeorge Rokos   // Device properties
2543de4cd12SGeorge Rokos   std::vector<int> ThreadsPerBlock;
2553de4cd12SGeorge Rokos   std::vector<int> BlocksPerGrid;
2563de4cd12SGeorge Rokos   std::vector<int> WarpSize;
2573de4cd12SGeorge Rokos 
2583de4cd12SGeorge Rokos   // OpenMP properties
2593de4cd12SGeorge Rokos   std::vector<int> NumTeams;
2603de4cd12SGeorge Rokos   std::vector<int> NumThreads;
2613de4cd12SGeorge Rokos 
2623de4cd12SGeorge Rokos   // OpenMP Environment properties
2633de4cd12SGeorge Rokos   int EnvNumTeams;
2643de4cd12SGeorge Rokos   int EnvTeamLimit;
2653de4cd12SGeorge Rokos 
266c5fe030cSGheorghe-Teodor Bercea   // OpenMP Requires Flags
267c5fe030cSGheorghe-Teodor Bercea   int64_t RequiresFlags;
268c5fe030cSGheorghe-Teodor Bercea 
2693de4cd12SGeorge Rokos   // static int EnvNumThreads;
2703de4cd12SGeorge Rokos   static const int HardTeamLimit = 1 << 16; // 64k
2713de4cd12SGeorge Rokos   static const int HardThreadLimit = 1024;
2723de4cd12SGeorge Rokos   static const int DefaultNumTeams = 128;
273c13df8e5SGeorge Rokos   static const int DefaultNumThreads = 128;
2743de4cd12SGeorge Rokos 
275*feed674dSShilei Tian   std::shared_ptr<StreamManagerTy> getStreamManager() { return StreamManager; }
276*feed674dSShilei Tian 
277*feed674dSShilei Tian   CUstream getStream(const int DeviceId) {
278*feed674dSShilei Tian     return StreamManager->getStream(DeviceId);
279*feed674dSShilei Tian   }
280*feed674dSShilei Tian 
281*feed674dSShilei Tian   void returnStream(const int DeviceId, __tgt_async_info *AsyncInfoPtr) {
282*feed674dSShilei Tian     assert(AsyncInfoPtr && "AsyncInfoPtr is nullptr");
283*feed674dSShilei Tian     assert(AsyncInfoPtr->Queue && "AsyncInfoPtr->Queue is nullptr");
284*feed674dSShilei Tian 
285*feed674dSShilei Tian     StreamManager->returnStream(
286*feed674dSShilei Tian         DeviceId, reinterpret_cast<CUstream>(AsyncInfoPtr->Queue));
287*feed674dSShilei Tian     AsyncInfoPtr->Queue = nullptr;
288*feed674dSShilei Tian   }
289*feed674dSShilei Tian 
2903de4cd12SGeorge Rokos   // Record entry point associated with device
2913de4cd12SGeorge Rokos   void addOffloadEntry(int32_t device_id, __tgt_offload_entry entry) {
2923de4cd12SGeorge Rokos     assert(device_id < (int32_t)FuncGblEntries.size() &&
2933de4cd12SGeorge Rokos            "Unexpected device id!");
2942622e9e5SAlexey Bataev     FuncOrGblEntryTy &E = FuncGblEntries[device_id].back();
2953de4cd12SGeorge Rokos 
2963de4cd12SGeorge Rokos     E.Entries.push_back(entry);
2973de4cd12SGeorge Rokos   }
2983de4cd12SGeorge Rokos 
2993de4cd12SGeorge Rokos   // Return true if the entry is associated with device
3003de4cd12SGeorge Rokos   bool findOffloadEntry(int32_t device_id, void *addr) {
3013de4cd12SGeorge Rokos     assert(device_id < (int32_t)FuncGblEntries.size() &&
3023de4cd12SGeorge Rokos            "Unexpected device id!");
3032622e9e5SAlexey Bataev     FuncOrGblEntryTy &E = FuncGblEntries[device_id].back();
3043de4cd12SGeorge Rokos 
3053de4cd12SGeorge Rokos     for (auto &it : E.Entries) {
3063de4cd12SGeorge Rokos       if (it.addr == addr)
3073de4cd12SGeorge Rokos         return true;
3083de4cd12SGeorge Rokos     }
3093de4cd12SGeorge Rokos 
3103de4cd12SGeorge Rokos     return false;
3113de4cd12SGeorge Rokos   }
3123de4cd12SGeorge Rokos 
3133de4cd12SGeorge Rokos   // Return the pointer to the target entries table
3143de4cd12SGeorge Rokos   __tgt_target_table *getOffloadEntriesTable(int32_t device_id) {
3153de4cd12SGeorge Rokos     assert(device_id < (int32_t)FuncGblEntries.size() &&
3163de4cd12SGeorge Rokos            "Unexpected device id!");
3172622e9e5SAlexey Bataev     FuncOrGblEntryTy &E = FuncGblEntries[device_id].back();
3183de4cd12SGeorge Rokos 
3193de4cd12SGeorge Rokos     int32_t size = E.Entries.size();
3203de4cd12SGeorge Rokos 
3213de4cd12SGeorge Rokos     // Table is empty
3223de4cd12SGeorge Rokos     if (!size)
3233de4cd12SGeorge Rokos       return 0;
3243de4cd12SGeorge Rokos 
3253de4cd12SGeorge Rokos     __tgt_offload_entry *begin = &E.Entries[0];
3263de4cd12SGeorge Rokos     __tgt_offload_entry *end = &E.Entries[size - 1];
3273de4cd12SGeorge Rokos 
3283de4cd12SGeorge Rokos     // Update table info according to the entries and return the pointer
3293de4cd12SGeorge Rokos     E.Table.EntriesBegin = begin;
3303de4cd12SGeorge Rokos     E.Table.EntriesEnd = ++end;
3313de4cd12SGeorge Rokos 
3323de4cd12SGeorge Rokos     return &E.Table;
3333de4cd12SGeorge Rokos   }
3343de4cd12SGeorge Rokos 
3353de4cd12SGeorge Rokos   // Clear entries table for a device
3363de4cd12SGeorge Rokos   void clearOffloadEntriesTable(int32_t device_id) {
3373de4cd12SGeorge Rokos     assert(device_id < (int32_t)FuncGblEntries.size() &&
3383de4cd12SGeorge Rokos            "Unexpected device id!");
3392622e9e5SAlexey Bataev     FuncGblEntries[device_id].emplace_back();
3402622e9e5SAlexey Bataev     FuncOrGblEntryTy &E = FuncGblEntries[device_id].back();
3413de4cd12SGeorge Rokos     E.Entries.clear();
3423de4cd12SGeorge Rokos     E.Table.EntriesBegin = E.Table.EntriesEnd = 0;
3433de4cd12SGeorge Rokos   }
3443de4cd12SGeorge Rokos 
3453de4cd12SGeorge Rokos   RTLDeviceInfoTy() {
346b305d26bSSergey Dmitriev #ifdef OMPTARGET_DEBUG
347b305d26bSSergey Dmitriev     if (char *envStr = getenv("LIBOMPTARGET_DEBUG")) {
348b305d26bSSergey Dmitriev       DebugLevel = std::stoi(envStr);
349b305d26bSSergey Dmitriev     }
350b305d26bSSergey Dmitriev #endif // OMPTARGET_DEBUG
351b305d26bSSergey Dmitriev 
3523de4cd12SGeorge Rokos     DP("Start initializing CUDA\n");
3533de4cd12SGeorge Rokos 
3543de4cd12SGeorge Rokos     CUresult err = cuInit(0);
3553de4cd12SGeorge Rokos     if (err != CUDA_SUCCESS) {
3563de4cd12SGeorge Rokos       DP("Error when initializing CUDA\n");
3573de4cd12SGeorge Rokos       CUDA_ERR_STRING(err);
3583de4cd12SGeorge Rokos       return;
3593de4cd12SGeorge Rokos     }
3603de4cd12SGeorge Rokos 
3613de4cd12SGeorge Rokos     NumberOfDevices = 0;
3623de4cd12SGeorge Rokos 
3633de4cd12SGeorge Rokos     err = cuDeviceGetCount(&NumberOfDevices);
3643de4cd12SGeorge Rokos     if (err != CUDA_SUCCESS) {
3653de4cd12SGeorge Rokos       DP("Error when getting CUDA device count\n");
3663de4cd12SGeorge Rokos       CUDA_ERR_STRING(err);
3673de4cd12SGeorge Rokos       return;
3683de4cd12SGeorge Rokos     }
3693de4cd12SGeorge Rokos 
3703de4cd12SGeorge Rokos     if (NumberOfDevices == 0) {
3713de4cd12SGeorge Rokos       DP("There are no devices supporting CUDA.\n");
3723de4cd12SGeorge Rokos       return;
3733de4cd12SGeorge Rokos     }
3743de4cd12SGeorge Rokos 
3753de4cd12SGeorge Rokos     FuncGblEntries.resize(NumberOfDevices);
3763de4cd12SGeorge Rokos     Contexts.resize(NumberOfDevices);
3773de4cd12SGeorge Rokos     ThreadsPerBlock.resize(NumberOfDevices);
3783de4cd12SGeorge Rokos     BlocksPerGrid.resize(NumberOfDevices);
3793de4cd12SGeorge Rokos     WarpSize.resize(NumberOfDevices);
3803de4cd12SGeorge Rokos     NumTeams.resize(NumberOfDevices);
3813de4cd12SGeorge Rokos     NumThreads.resize(NumberOfDevices);
3823de4cd12SGeorge Rokos 
3833de4cd12SGeorge Rokos     // Get environment variables regarding teams
3843de4cd12SGeorge Rokos     char *envStr = getenv("OMP_TEAM_LIMIT");
3853de4cd12SGeorge Rokos     if (envStr) {
3863de4cd12SGeorge Rokos       // OMP_TEAM_LIMIT has been set
3873de4cd12SGeorge Rokos       EnvTeamLimit = std::stoi(envStr);
3883de4cd12SGeorge Rokos       DP("Parsed OMP_TEAM_LIMIT=%d\n", EnvTeamLimit);
3893de4cd12SGeorge Rokos     } else {
3903de4cd12SGeorge Rokos       EnvTeamLimit = -1;
3913de4cd12SGeorge Rokos     }
3923de4cd12SGeorge Rokos     envStr = getenv("OMP_NUM_TEAMS");
3933de4cd12SGeorge Rokos     if (envStr) {
3943de4cd12SGeorge Rokos       // OMP_NUM_TEAMS has been set
3953de4cd12SGeorge Rokos       EnvNumTeams = std::stoi(envStr);
3963de4cd12SGeorge Rokos       DP("Parsed OMP_NUM_TEAMS=%d\n", EnvNumTeams);
3973de4cd12SGeorge Rokos     } else {
3983de4cd12SGeorge Rokos       EnvNumTeams = -1;
3993de4cd12SGeorge Rokos     }
400c5fe030cSGheorghe-Teodor Bercea 
401*feed674dSShilei Tian     StreamManager =
402*feed674dSShilei Tian         std::make_shared<StreamManagerTy>(NumberOfDevices, Contexts);
403a5153dbcSJohannes Doerfert 
404c5fe030cSGheorghe-Teodor Bercea     // Default state.
405c5fe030cSGheorghe-Teodor Bercea     RequiresFlags = OMP_REQ_UNDEFINED;
4063de4cd12SGeorge Rokos   }
4073de4cd12SGeorge Rokos 
4083de4cd12SGeorge Rokos   ~RTLDeviceInfoTy() {
409*feed674dSShilei Tian     // First destruct stream manager in case of Contexts is destructed before it
410*feed674dSShilei Tian     StreamManager = nullptr;
411*feed674dSShilei Tian 
4123de4cd12SGeorge Rokos     // Close modules
4133de4cd12SGeorge Rokos     for (auto &module : Modules)
4143de4cd12SGeorge Rokos       if (module) {
4153de4cd12SGeorge Rokos         CUresult err = cuModuleUnload(module);
4163de4cd12SGeorge Rokos         if (err != CUDA_SUCCESS) {
4173de4cd12SGeorge Rokos           DP("Error when unloading CUDA module\n");
4183de4cd12SGeorge Rokos           CUDA_ERR_STRING(err);
4193de4cd12SGeorge Rokos         }
4203de4cd12SGeorge Rokos       }
4213de4cd12SGeorge Rokos 
4223de4cd12SGeorge Rokos     // Destroy contexts
4233de4cd12SGeorge Rokos     for (auto &ctx : Contexts)
4243de4cd12SGeorge Rokos       if (ctx) {
4253de4cd12SGeorge Rokos         CUresult err = cuCtxDestroy(ctx);
4263de4cd12SGeorge Rokos         if (err != CUDA_SUCCESS) {
4273de4cd12SGeorge Rokos           DP("Error when destroying CUDA context\n");
4283de4cd12SGeorge Rokos           CUDA_ERR_STRING(err);
4293de4cd12SGeorge Rokos         }
4303de4cd12SGeorge Rokos       }
4313de4cd12SGeorge Rokos   }
4323de4cd12SGeorge Rokos };
4333de4cd12SGeorge Rokos 
4343de4cd12SGeorge Rokos static RTLDeviceInfoTy DeviceInfo;
4353de4cd12SGeorge Rokos 
43632ed2927SShilei Tian namespace {
437*feed674dSShilei Tian CUstream getStream(int32_t DeviceId, __tgt_async_info *AsyncInfoPtr) {
438*feed674dSShilei Tian   assert(AsyncInfoPtr && "AsyncInfoPtr is nullptr");
43932ed2927SShilei Tian 
440*feed674dSShilei Tian   if (!AsyncInfoPtr->Queue)
441*feed674dSShilei Tian     AsyncInfoPtr->Queue = DeviceInfo.getStream(DeviceId);
44232ed2927SShilei Tian 
443*feed674dSShilei Tian   return reinterpret_cast<CUstream>(AsyncInfoPtr->Queue);
44432ed2927SShilei Tian }
44532ed2927SShilei Tian 
44632ed2927SShilei Tian int32_t dataRetrieve(int32_t DeviceId, void *HstPtr, void *TgtPtr, int64_t Size,
44732ed2927SShilei Tian                      __tgt_async_info *AsyncInfoPtr) {
44832ed2927SShilei Tian   assert(AsyncInfoPtr && "AsyncInfoPtr is nullptr");
44932ed2927SShilei Tian   // Set the context we are using.
45032ed2927SShilei Tian   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[DeviceId]);
45132ed2927SShilei Tian   if (err != CUDA_SUCCESS) {
45232ed2927SShilei Tian     DP("Error when setting CUDA context\n");
45332ed2927SShilei Tian     CUDA_ERR_STRING(err);
45432ed2927SShilei Tian     return OFFLOAD_FAIL;
45532ed2927SShilei Tian   }
45632ed2927SShilei Tian 
457*feed674dSShilei Tian   CUstream Stream = getStream(DeviceId, AsyncInfoPtr);
45832ed2927SShilei Tian 
45932ed2927SShilei Tian   err = cuMemcpyDtoHAsync(HstPtr, (CUdeviceptr)TgtPtr, Size, Stream);
46032ed2927SShilei Tian   if (err != CUDA_SUCCESS) {
46132ed2927SShilei Tian     DP("Error when copying data from device to host. Pointers: host = " DPxMOD
46232ed2927SShilei Tian        ", device = " DPxMOD ", size = %" PRId64 "\n",
46332ed2927SShilei Tian        DPxPTR(HstPtr), DPxPTR(TgtPtr), Size);
46432ed2927SShilei Tian     CUDA_ERR_STRING(err);
46532ed2927SShilei Tian     return OFFLOAD_FAIL;
46632ed2927SShilei Tian   }
46732ed2927SShilei Tian 
46832ed2927SShilei Tian   return OFFLOAD_SUCCESS;
46932ed2927SShilei Tian }
47032ed2927SShilei Tian 
47132ed2927SShilei Tian int32_t dataSubmit(int32_t DeviceId, void *TgtPtr, void *HstPtr, int64_t Size,
47232ed2927SShilei Tian                    __tgt_async_info *AsyncInfoPtr) {
47332ed2927SShilei Tian   assert(AsyncInfoPtr && "AsyncInfoPtr is nullptr");
47432ed2927SShilei Tian   // Set the context we are using.
47532ed2927SShilei Tian   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[DeviceId]);
47632ed2927SShilei Tian   if (err != CUDA_SUCCESS) {
47732ed2927SShilei Tian     DP("Error when setting CUDA context\n");
47832ed2927SShilei Tian     CUDA_ERR_STRING(err);
47932ed2927SShilei Tian     return OFFLOAD_FAIL;
48032ed2927SShilei Tian   }
48132ed2927SShilei Tian 
482*feed674dSShilei Tian   CUstream Stream = getStream(DeviceId, AsyncInfoPtr);
48332ed2927SShilei Tian 
48432ed2927SShilei Tian   err = cuMemcpyHtoDAsync((CUdeviceptr)TgtPtr, HstPtr, Size, Stream);
48532ed2927SShilei Tian   if (err != CUDA_SUCCESS) {
48632ed2927SShilei Tian     DP("Error when copying data from host to device. Pointers: host = " DPxMOD
48732ed2927SShilei Tian        ", device = " DPxMOD ", size = %" PRId64 "\n",
48832ed2927SShilei Tian        DPxPTR(HstPtr), DPxPTR(TgtPtr), Size);
48932ed2927SShilei Tian     CUDA_ERR_STRING(err);
49032ed2927SShilei Tian     return OFFLOAD_FAIL;
49132ed2927SShilei Tian   }
49232ed2927SShilei Tian 
49332ed2927SShilei Tian   return OFFLOAD_SUCCESS;
49432ed2927SShilei Tian }
49532ed2927SShilei Tian } // namespace
49632ed2927SShilei Tian 
4973de4cd12SGeorge Rokos #ifdef __cplusplus
4983de4cd12SGeorge Rokos extern "C" {
4993de4cd12SGeorge Rokos #endif
5003de4cd12SGeorge Rokos 
5013de4cd12SGeorge Rokos int32_t __tgt_rtl_is_valid_binary(__tgt_device_image *image) {
5023de4cd12SGeorge Rokos   return elf_check_machine(image, 190); // EM_CUDA = 190.
5033de4cd12SGeorge Rokos }
5043de4cd12SGeorge Rokos 
5053de4cd12SGeorge Rokos int32_t __tgt_rtl_number_of_devices() { return DeviceInfo.NumberOfDevices; }
5063de4cd12SGeorge Rokos 
507c5fe030cSGheorghe-Teodor Bercea int64_t __tgt_rtl_init_requires(int64_t RequiresFlags) {
508c5fe030cSGheorghe-Teodor Bercea   DP("Init requires flags to %ld\n", RequiresFlags);
509c5fe030cSGheorghe-Teodor Bercea   DeviceInfo.RequiresFlags = RequiresFlags;
510c5fe030cSGheorghe-Teodor Bercea   return RequiresFlags;
511c5fe030cSGheorghe-Teodor Bercea }
512c5fe030cSGheorghe-Teodor Bercea 
5133de4cd12SGeorge Rokos int32_t __tgt_rtl_init_device(int32_t device_id) {
5143de4cd12SGeorge Rokos 
5153de4cd12SGeorge Rokos   CUdevice cuDevice;
5163de4cd12SGeorge Rokos   DP("Getting device %d\n", device_id);
5173de4cd12SGeorge Rokos   CUresult err = cuDeviceGet(&cuDevice, device_id);
5183de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
5193de4cd12SGeorge Rokos     DP("Error when getting CUDA device with id = %d\n", device_id);
5203de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
5213de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
5223de4cd12SGeorge Rokos   }
5233de4cd12SGeorge Rokos 
5243de4cd12SGeorge Rokos   // Create the context and save it to use whenever this device is selected.
5253de4cd12SGeorge Rokos   err = cuCtxCreate(&DeviceInfo.Contexts[device_id], CU_CTX_SCHED_BLOCKING_SYNC,
5263de4cd12SGeorge Rokos                     cuDevice);
5273de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
5283de4cd12SGeorge Rokos     DP("Error when creating a CUDA context\n");
5293de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
5303de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
5313de4cd12SGeorge Rokos   }
5323de4cd12SGeorge Rokos 
533a5153dbcSJohannes Doerfert   err = cuCtxSetCurrent(DeviceInfo.Contexts[device_id]);
534a5153dbcSJohannes Doerfert   if (err != CUDA_SUCCESS) {
535a5153dbcSJohannes Doerfert     DP("Error when setting current CUDA context\n");
536a5153dbcSJohannes Doerfert     CUDA_ERR_STRING(err);
537a5153dbcSJohannes Doerfert   }
538a5153dbcSJohannes Doerfert 
539*feed674dSShilei Tian   // Initialize stream pool
540*feed674dSShilei Tian   DeviceInfo.getStreamManager()->initializeDevice(device_id);
541a5153dbcSJohannes Doerfert 
542bb51d398SJonas Hahnfeld   // Query attributes to determine number of threads/block and blocks/grid.
543bb51d398SJonas Hahnfeld   int maxGridDimX;
544bb51d398SJonas Hahnfeld   err = cuDeviceGetAttribute(&maxGridDimX, CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_X,
545bb51d398SJonas Hahnfeld                              cuDevice);
546c1895235SJonas Hahnfeld   if (err != CUDA_SUCCESS) {
547bb51d398SJonas Hahnfeld     DP("Error getting max grid dimension, use default\n");
5483de4cd12SGeorge Rokos     DeviceInfo.BlocksPerGrid[device_id] = RTLDeviceInfoTy::DefaultNumTeams;
549bb51d398SJonas Hahnfeld   } else if (maxGridDimX <= RTLDeviceInfoTy::HardTeamLimit) {
550bb51d398SJonas Hahnfeld     DeviceInfo.BlocksPerGrid[device_id] = maxGridDimX;
551bb51d398SJonas Hahnfeld     DP("Using %d CUDA blocks per grid\n", maxGridDimX);
5523de4cd12SGeorge Rokos   } else {
5533de4cd12SGeorge Rokos     DeviceInfo.BlocksPerGrid[device_id] = RTLDeviceInfoTy::HardTeamLimit;
5543de4cd12SGeorge Rokos     DP("Max CUDA blocks per grid %d exceeds the hard team limit %d, capping "
555bb51d398SJonas Hahnfeld        "at the hard limit\n",
556bb51d398SJonas Hahnfeld        maxGridDimX, RTLDeviceInfoTy::HardTeamLimit);
5573de4cd12SGeorge Rokos   }
5583de4cd12SGeorge Rokos 
559bb51d398SJonas Hahnfeld   // We are only exploiting threads along the x axis.
560bb51d398SJonas Hahnfeld   int maxBlockDimX;
561bb51d398SJonas Hahnfeld   err = cuDeviceGetAttribute(&maxBlockDimX, CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_X,
562bb51d398SJonas Hahnfeld                              cuDevice);
563bb51d398SJonas Hahnfeld   if (err != CUDA_SUCCESS) {
564bb51d398SJonas Hahnfeld     DP("Error getting max block dimension, use default\n");
565bb51d398SJonas Hahnfeld     DeviceInfo.ThreadsPerBlock[device_id] = RTLDeviceInfoTy::DefaultNumThreads;
566bb51d398SJonas Hahnfeld   } else if (maxBlockDimX <= RTLDeviceInfoTy::HardThreadLimit) {
567bb51d398SJonas Hahnfeld     DeviceInfo.ThreadsPerBlock[device_id] = maxBlockDimX;
568bb51d398SJonas Hahnfeld     DP("Using %d CUDA threads per block\n", maxBlockDimX);
5693de4cd12SGeorge Rokos   } else {
5703de4cd12SGeorge Rokos     DeviceInfo.ThreadsPerBlock[device_id] = RTLDeviceInfoTy::HardThreadLimit;
571bb51d398SJonas Hahnfeld     DP("Max CUDA threads per block %d exceeds the hard thread limit %d, capping"
572bb51d398SJonas Hahnfeld        "at the hard limit\n",
573bb51d398SJonas Hahnfeld        maxBlockDimX, RTLDeviceInfoTy::HardThreadLimit);
5743de4cd12SGeorge Rokos   }
5753de4cd12SGeorge Rokos 
576bb51d398SJonas Hahnfeld   int warpSize;
577bb51d398SJonas Hahnfeld   err =
578bb51d398SJonas Hahnfeld       cuDeviceGetAttribute(&warpSize, CU_DEVICE_ATTRIBUTE_WARP_SIZE, cuDevice);
579bb51d398SJonas Hahnfeld   if (err != CUDA_SUCCESS) {
580bb51d398SJonas Hahnfeld     DP("Error getting warp size, assume default\n");
581bb51d398SJonas Hahnfeld     DeviceInfo.WarpSize[device_id] = 32;
582bb51d398SJonas Hahnfeld   } else {
583bb51d398SJonas Hahnfeld     DeviceInfo.WarpSize[device_id] = warpSize;
5843de4cd12SGeorge Rokos   }
5853de4cd12SGeorge Rokos 
5863de4cd12SGeorge Rokos   // Adjust teams to the env variables
5873de4cd12SGeorge Rokos   if (DeviceInfo.EnvTeamLimit > 0 &&
5883de4cd12SGeorge Rokos       DeviceInfo.BlocksPerGrid[device_id] > DeviceInfo.EnvTeamLimit) {
5893de4cd12SGeorge Rokos     DeviceInfo.BlocksPerGrid[device_id] = DeviceInfo.EnvTeamLimit;
5903de4cd12SGeorge Rokos     DP("Capping max CUDA blocks per grid to OMP_TEAM_LIMIT=%d\n",
5913de4cd12SGeorge Rokos         DeviceInfo.EnvTeamLimit);
5923de4cd12SGeorge Rokos   }
5933de4cd12SGeorge Rokos 
5943de4cd12SGeorge Rokos   DP("Max number of CUDA blocks %d, threads %d & warp size %d\n",
5953de4cd12SGeorge Rokos      DeviceInfo.BlocksPerGrid[device_id], DeviceInfo.ThreadsPerBlock[device_id],
5963de4cd12SGeorge Rokos      DeviceInfo.WarpSize[device_id]);
5973de4cd12SGeorge Rokos 
5983de4cd12SGeorge Rokos   // Set default number of teams
5993de4cd12SGeorge Rokos   if (DeviceInfo.EnvNumTeams > 0) {
6003de4cd12SGeorge Rokos     DeviceInfo.NumTeams[device_id] = DeviceInfo.EnvNumTeams;
6013de4cd12SGeorge Rokos     DP("Default number of teams set according to environment %d\n",
6023de4cd12SGeorge Rokos         DeviceInfo.EnvNumTeams);
6033de4cd12SGeorge Rokos   } else {
6043de4cd12SGeorge Rokos     DeviceInfo.NumTeams[device_id] = RTLDeviceInfoTy::DefaultNumTeams;
6053de4cd12SGeorge Rokos     DP("Default number of teams set according to library's default %d\n",
6063de4cd12SGeorge Rokos         RTLDeviceInfoTy::DefaultNumTeams);
6073de4cd12SGeorge Rokos   }
6083de4cd12SGeorge Rokos   if (DeviceInfo.NumTeams[device_id] > DeviceInfo.BlocksPerGrid[device_id]) {
6093de4cd12SGeorge Rokos     DeviceInfo.NumTeams[device_id] = DeviceInfo.BlocksPerGrid[device_id];
6103de4cd12SGeorge Rokos     DP("Default number of teams exceeds device limit, capping at %d\n",
6113de4cd12SGeorge Rokos         DeviceInfo.BlocksPerGrid[device_id]);
6123de4cd12SGeorge Rokos   }
6133de4cd12SGeorge Rokos 
6143de4cd12SGeorge Rokos   // Set default number of threads
6153de4cd12SGeorge Rokos   DeviceInfo.NumThreads[device_id] = RTLDeviceInfoTy::DefaultNumThreads;
6163de4cd12SGeorge Rokos   DP("Default number of threads set according to library's default %d\n",
6173de4cd12SGeorge Rokos           RTLDeviceInfoTy::DefaultNumThreads);
6183de4cd12SGeorge Rokos   if (DeviceInfo.NumThreads[device_id] >
6193de4cd12SGeorge Rokos       DeviceInfo.ThreadsPerBlock[device_id]) {
6203de4cd12SGeorge Rokos     DeviceInfo.NumTeams[device_id] = DeviceInfo.ThreadsPerBlock[device_id];
6213de4cd12SGeorge Rokos     DP("Default number of threads exceeds device limit, capping at %d\n",
6223de4cd12SGeorge Rokos         DeviceInfo.ThreadsPerBlock[device_id]);
6233de4cd12SGeorge Rokos   }
6243de4cd12SGeorge Rokos 
6253de4cd12SGeorge Rokos   return OFFLOAD_SUCCESS;
6263de4cd12SGeorge Rokos }
6273de4cd12SGeorge Rokos 
6283de4cd12SGeorge Rokos __tgt_target_table *__tgt_rtl_load_binary(int32_t device_id,
6293de4cd12SGeorge Rokos     __tgt_device_image *image) {
6303de4cd12SGeorge Rokos 
6313de4cd12SGeorge Rokos   // Set the context we are using.
6323de4cd12SGeorge Rokos   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[device_id]);
6333de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
6343de4cd12SGeorge Rokos     DP("Error when setting a CUDA context for device %d\n", device_id);
6353de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
6363de4cd12SGeorge Rokos     return NULL;
6373de4cd12SGeorge Rokos   }
6383de4cd12SGeorge Rokos 
6393de4cd12SGeorge Rokos   // Clear the offload table as we are going to create a new one.
6403de4cd12SGeorge Rokos   DeviceInfo.clearOffloadEntriesTable(device_id);
6413de4cd12SGeorge Rokos 
6423de4cd12SGeorge Rokos   // Create the module and extract the function pointers.
6433de4cd12SGeorge Rokos 
6443de4cd12SGeorge Rokos   CUmodule cumod;
6453de4cd12SGeorge Rokos   DP("Load data from image " DPxMOD "\n", DPxPTR(image->ImageStart));
6463de4cd12SGeorge Rokos   err = cuModuleLoadDataEx(&cumod, image->ImageStart, 0, NULL, NULL);
6473de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
6483de4cd12SGeorge Rokos     DP("Error when loading CUDA module\n");
6493de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
6503de4cd12SGeorge Rokos     return NULL;
6513de4cd12SGeorge Rokos   }
6523de4cd12SGeorge Rokos 
6533de4cd12SGeorge Rokos   DP("CUDA module successfully loaded!\n");
6543de4cd12SGeorge Rokos   DeviceInfo.Modules.push_back(cumod);
6553de4cd12SGeorge Rokos 
6563de4cd12SGeorge Rokos   // Find the symbols in the module by name.
6573de4cd12SGeorge Rokos   __tgt_offload_entry *HostBegin = image->EntriesBegin;
6583de4cd12SGeorge Rokos   __tgt_offload_entry *HostEnd = image->EntriesEnd;
6593de4cd12SGeorge Rokos 
6603de4cd12SGeorge Rokos   for (__tgt_offload_entry *e = HostBegin; e != HostEnd; ++e) {
6613de4cd12SGeorge Rokos 
6623de4cd12SGeorge Rokos     if (!e->addr) {
6633de4cd12SGeorge Rokos       // We return NULL when something like this happens, the host should have
6643de4cd12SGeorge Rokos       // always something in the address to uniquely identify the target region.
6653de4cd12SGeorge Rokos       DP("Invalid binary: host entry '<null>' (size = %zd)...\n", e->size);
6663de4cd12SGeorge Rokos 
6673de4cd12SGeorge Rokos       return NULL;
6683de4cd12SGeorge Rokos     }
6693de4cd12SGeorge Rokos 
6703de4cd12SGeorge Rokos     if (e->size) {
6713de4cd12SGeorge Rokos       __tgt_offload_entry entry = *e;
6723de4cd12SGeorge Rokos 
6733de4cd12SGeorge Rokos       CUdeviceptr cuptr;
6743de4cd12SGeorge Rokos       size_t cusize;
6753de4cd12SGeorge Rokos       err = cuModuleGetGlobal(&cuptr, &cusize, cumod, e->name);
6763de4cd12SGeorge Rokos 
6773de4cd12SGeorge Rokos       if (err != CUDA_SUCCESS) {
6783de4cd12SGeorge Rokos         DP("Loading global '%s' (Failed)\n", e->name);
6793de4cd12SGeorge Rokos         CUDA_ERR_STRING(err);
6803de4cd12SGeorge Rokos         return NULL;
6813de4cd12SGeorge Rokos       }
6823de4cd12SGeorge Rokos 
6833de4cd12SGeorge Rokos       if (cusize != e->size) {
6843de4cd12SGeorge Rokos         DP("Loading global '%s' - size mismatch (%zd != %zd)\n", e->name,
6853de4cd12SGeorge Rokos             cusize, e->size);
6863de4cd12SGeorge Rokos         CUDA_ERR_STRING(err);
6873de4cd12SGeorge Rokos         return NULL;
6883de4cd12SGeorge Rokos       }
6893de4cd12SGeorge Rokos 
6903de4cd12SGeorge Rokos       DP("Entry point " DPxMOD " maps to global %s (" DPxMOD ")\n",
6913de4cd12SGeorge Rokos           DPxPTR(e - HostBegin), e->name, DPxPTR(cuptr));
6923de4cd12SGeorge Rokos       entry.addr = (void *)cuptr;
6933de4cd12SGeorge Rokos 
694aace6d28SGheorghe-Teodor Bercea       // Note: In the current implementation declare target variables
695aace6d28SGheorghe-Teodor Bercea       // can either be link or to. This means that once unified
696aace6d28SGheorghe-Teodor Bercea       // memory is activated via the requires directive, the variable
697aace6d28SGheorghe-Teodor Bercea       // can be used directly from the host in both cases.
698aace6d28SGheorghe-Teodor Bercea       // TODO: when variables types other than to or link are added,
6994c6a098aSKazuaki Ishizaki       // the below condition should be changed to explicitly
700aace6d28SGheorghe-Teodor Bercea       // check for to and link variables types:
701aace6d28SGheorghe-Teodor Bercea       //  (DeviceInfo.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY &&
702aace6d28SGheorghe-Teodor Bercea       //   (e->flags & OMP_DECLARE_TARGET_LINK ||
703aace6d28SGheorghe-Teodor Bercea       //    e->flags == OMP_DECLARE_TARGET_TO))
704aace6d28SGheorghe-Teodor Bercea       if (DeviceInfo.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY) {
705aace6d28SGheorghe-Teodor Bercea         // If unified memory is present any target link or to variables
706c5fe030cSGheorghe-Teodor Bercea         // can access host addresses directly. There is no longer a
707c5fe030cSGheorghe-Teodor Bercea         // need for device copies.
708c5fe030cSGheorghe-Teodor Bercea         cuMemcpyHtoD(cuptr, e->addr, sizeof(void *));
709c5fe030cSGheorghe-Teodor Bercea         DP("Copy linked variable host address (" DPxMOD ")"
710c5fe030cSGheorghe-Teodor Bercea            "to device address (" DPxMOD ")\n",
711c5fe030cSGheorghe-Teodor Bercea           DPxPTR(*((void**)e->addr)), DPxPTR(cuptr));
712c5fe030cSGheorghe-Teodor Bercea       }
713c5fe030cSGheorghe-Teodor Bercea 
7143de4cd12SGeorge Rokos       DeviceInfo.addOffloadEntry(device_id, entry);
7153de4cd12SGeorge Rokos 
7163de4cd12SGeorge Rokos       continue;
7173de4cd12SGeorge Rokos     }
7183de4cd12SGeorge Rokos 
7193de4cd12SGeorge Rokos     CUfunction fun;
7203de4cd12SGeorge Rokos     err = cuModuleGetFunction(&fun, cumod, e->name);
7213de4cd12SGeorge Rokos 
7223de4cd12SGeorge Rokos     if (err != CUDA_SUCCESS) {
7233de4cd12SGeorge Rokos       DP("Loading '%s' (Failed)\n", e->name);
7243de4cd12SGeorge Rokos       CUDA_ERR_STRING(err);
7253de4cd12SGeorge Rokos       return NULL;
7263de4cd12SGeorge Rokos     }
7273de4cd12SGeorge Rokos 
7283de4cd12SGeorge Rokos     DP("Entry point " DPxMOD " maps to %s (" DPxMOD ")\n",
7293de4cd12SGeorge Rokos         DPxPTR(e - HostBegin), e->name, DPxPTR(fun));
7303de4cd12SGeorge Rokos 
7313de4cd12SGeorge Rokos     // default value GENERIC (in case symbol is missing from cubin file)
7323de4cd12SGeorge Rokos     int8_t ExecModeVal = ExecutionModeType::GENERIC;
7333de4cd12SGeorge Rokos     std::string ExecModeNameStr (e->name);
7343de4cd12SGeorge Rokos     ExecModeNameStr += "_exec_mode";
7353de4cd12SGeorge Rokos     const char *ExecModeName = ExecModeNameStr.c_str();
7363de4cd12SGeorge Rokos 
7373de4cd12SGeorge Rokos     CUdeviceptr ExecModePtr;
7383de4cd12SGeorge Rokos     size_t cusize;
7393de4cd12SGeorge Rokos     err = cuModuleGetGlobal(&ExecModePtr, &cusize, cumod, ExecModeName);
7403de4cd12SGeorge Rokos     if (err == CUDA_SUCCESS) {
7413de4cd12SGeorge Rokos       if ((size_t)cusize != sizeof(int8_t)) {
7423de4cd12SGeorge Rokos         DP("Loading global exec_mode '%s' - size mismatch (%zd != %zd)\n",
7433de4cd12SGeorge Rokos            ExecModeName, cusize, sizeof(int8_t));
7443de4cd12SGeorge Rokos         CUDA_ERR_STRING(err);
7453de4cd12SGeorge Rokos         return NULL;
7463de4cd12SGeorge Rokos       }
7473de4cd12SGeorge Rokos 
7483de4cd12SGeorge Rokos       err = cuMemcpyDtoH(&ExecModeVal, ExecModePtr, cusize);
7493de4cd12SGeorge Rokos       if (err != CUDA_SUCCESS) {
7503de4cd12SGeorge Rokos         DP("Error when copying data from device to host. Pointers: "
7513de4cd12SGeorge Rokos            "host = " DPxMOD ", device = " DPxMOD ", size = %zd\n",
7523de4cd12SGeorge Rokos            DPxPTR(&ExecModeVal), DPxPTR(ExecModePtr), cusize);
7533de4cd12SGeorge Rokos         CUDA_ERR_STRING(err);
7543de4cd12SGeorge Rokos         return NULL;
7553de4cd12SGeorge Rokos       }
7563de4cd12SGeorge Rokos 
7573de4cd12SGeorge Rokos       if (ExecModeVal < 0 || ExecModeVal > 1) {
7583de4cd12SGeorge Rokos         DP("Error wrong exec_mode value specified in cubin file: %d\n",
7593de4cd12SGeorge Rokos            ExecModeVal);
7603de4cd12SGeorge Rokos         return NULL;
7613de4cd12SGeorge Rokos       }
7623de4cd12SGeorge Rokos     } else {
7633de4cd12SGeorge Rokos       DP("Loading global exec_mode '%s' - symbol missing, using default value "
7643de4cd12SGeorge Rokos           "GENERIC (1)\n", ExecModeName);
7653de4cd12SGeorge Rokos       CUDA_ERR_STRING(err);
7663de4cd12SGeorge Rokos     }
7673de4cd12SGeorge Rokos 
7683de4cd12SGeorge Rokos     KernelsList.push_back(KernelTy(fun, ExecModeVal));
7693de4cd12SGeorge Rokos 
7703de4cd12SGeorge Rokos     __tgt_offload_entry entry = *e;
7713de4cd12SGeorge Rokos     entry.addr = (void *)&KernelsList.back();
7723de4cd12SGeorge Rokos     DeviceInfo.addOffloadEntry(device_id, entry);
7733de4cd12SGeorge Rokos   }
7743de4cd12SGeorge Rokos 
775e1c7a46dSGuansong Zhang   // send device environment data to the device
776e1c7a46dSGuansong Zhang   {
777e1c7a46dSGuansong Zhang     omptarget_device_environmentTy device_env;
778e1c7a46dSGuansong Zhang 
779e1c7a46dSGuansong Zhang     device_env.debug_level = 0;
780e1c7a46dSGuansong Zhang 
781e1c7a46dSGuansong Zhang #ifdef OMPTARGET_DEBUG
782e1c7a46dSGuansong Zhang     if (char *envStr = getenv("LIBOMPTARGET_DEVICE_RTL_DEBUG")) {
783e1c7a46dSGuansong Zhang       device_env.debug_level = std::stoi(envStr);
784e1c7a46dSGuansong Zhang     }
785e1c7a46dSGuansong Zhang #endif
786e1c7a46dSGuansong Zhang 
787e1c7a46dSGuansong Zhang     const char * device_env_Name="omptarget_device_environment";
788e1c7a46dSGuansong Zhang     CUdeviceptr device_env_Ptr;
789e1c7a46dSGuansong Zhang     size_t cusize;
790e1c7a46dSGuansong Zhang 
791e1c7a46dSGuansong Zhang     err = cuModuleGetGlobal(&device_env_Ptr, &cusize, cumod, device_env_Name);
792e1c7a46dSGuansong Zhang 
793e1c7a46dSGuansong Zhang     if (err == CUDA_SUCCESS) {
794e1c7a46dSGuansong Zhang       if ((size_t)cusize != sizeof(device_env)) {
795e1c7a46dSGuansong Zhang         DP("Global device_environment '%s' - size mismatch (%zu != %zu)\n",
796e1c7a46dSGuansong Zhang             device_env_Name, cusize, sizeof(int32_t));
797e1c7a46dSGuansong Zhang         CUDA_ERR_STRING(err);
798e1c7a46dSGuansong Zhang         return NULL;
799e1c7a46dSGuansong Zhang       }
800e1c7a46dSGuansong Zhang 
801e1c7a46dSGuansong Zhang       err = cuMemcpyHtoD(device_env_Ptr, &device_env, cusize);
802e1c7a46dSGuansong Zhang       if (err != CUDA_SUCCESS) {
803e1c7a46dSGuansong Zhang         DP("Error when copying data from host to device. Pointers: "
804e1c7a46dSGuansong Zhang             "host = " DPxMOD ", device = " DPxMOD ", size = %zu\n",
805e1c7a46dSGuansong Zhang             DPxPTR(&device_env), DPxPTR(device_env_Ptr), cusize);
806e1c7a46dSGuansong Zhang         CUDA_ERR_STRING(err);
807e1c7a46dSGuansong Zhang         return NULL;
808e1c7a46dSGuansong Zhang       }
809e1c7a46dSGuansong Zhang 
810e1c7a46dSGuansong Zhang       DP("Sending global device environment data %zu bytes\n", (size_t)cusize);
811e1c7a46dSGuansong Zhang     } else {
812e1c7a46dSGuansong Zhang       DP("Finding global device environment '%s' - symbol missing.\n", device_env_Name);
8134c6a098aSKazuaki Ishizaki       DP("Continue, considering this is a device RTL which does not accept environment setting.\n");
814e1c7a46dSGuansong Zhang     }
815e1c7a46dSGuansong Zhang   }
816e1c7a46dSGuansong Zhang 
8173de4cd12SGeorge Rokos   return DeviceInfo.getOffloadEntriesTable(device_id);
8183de4cd12SGeorge Rokos }
8193de4cd12SGeorge Rokos 
8201546d319SGeorge Rokos void *__tgt_rtl_data_alloc(int32_t device_id, int64_t size, void *hst_ptr) {
8213de4cd12SGeorge Rokos   if (size == 0) {
8223de4cd12SGeorge Rokos     return NULL;
8233de4cd12SGeorge Rokos   }
8243de4cd12SGeorge Rokos 
8253de4cd12SGeorge Rokos   // Set the context we are using.
8263de4cd12SGeorge Rokos   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[device_id]);
8273de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
8283de4cd12SGeorge Rokos     DP("Error while trying to set CUDA current context\n");
8293de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
8303de4cd12SGeorge Rokos     return NULL;
8313de4cd12SGeorge Rokos   }
8323de4cd12SGeorge Rokos 
8333de4cd12SGeorge Rokos   CUdeviceptr ptr;
8343de4cd12SGeorge Rokos   err = cuMemAlloc(&ptr, size);
8353de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
8363de4cd12SGeorge Rokos     DP("Error while trying to allocate %d\n", err);
8373de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
8383de4cd12SGeorge Rokos     return NULL;
8393de4cd12SGeorge Rokos   }
8403de4cd12SGeorge Rokos 
8413de4cd12SGeorge Rokos   void *vptr = (void *)ptr;
8423de4cd12SGeorge Rokos   return vptr;
8433de4cd12SGeorge Rokos }
8443de4cd12SGeorge Rokos 
8453de4cd12SGeorge Rokos int32_t __tgt_rtl_data_submit(int32_t device_id, void *tgt_ptr, void *hst_ptr,
84603ff643dSShilei Tian                               int64_t size) {
84732ed2927SShilei Tian   __tgt_async_info async_info;
84803ff643dSShilei Tian   int32_t rc = __tgt_rtl_data_submit_async(device_id, tgt_ptr, hst_ptr, size,
84903ff643dSShilei Tian                                            &async_info);
85032ed2927SShilei Tian   if (rc != OFFLOAD_SUCCESS)
8513de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
8523de4cd12SGeorge Rokos 
85332ed2927SShilei Tian   return __tgt_rtl_synchronize(device_id, &async_info);
8543de4cd12SGeorge Rokos }
8553de4cd12SGeorge Rokos 
85603ff643dSShilei Tian int32_t __tgt_rtl_data_submit_async(int32_t device_id, void *tgt_ptr,
85703ff643dSShilei Tian                                     void *hst_ptr, int64_t size,
85832ed2927SShilei Tian                                     __tgt_async_info *async_info_ptr) {
85903ff643dSShilei Tian   assert(async_info_ptr && "async_info_ptr is nullptr");
86003ff643dSShilei Tian   return dataSubmit(device_id, tgt_ptr, hst_ptr, size, async_info_ptr);
86103ff643dSShilei Tian }
86232ed2927SShilei Tian 
86303ff643dSShilei Tian int32_t __tgt_rtl_data_retrieve(int32_t device_id, void *hst_ptr, void *tgt_ptr,
86403ff643dSShilei Tian                                 int64_t size) {
86532ed2927SShilei Tian   __tgt_async_info async_info;
86603ff643dSShilei Tian   int32_t rc = __tgt_rtl_data_retrieve_async(device_id, hst_ptr, tgt_ptr, size,
86703ff643dSShilei Tian                                              &async_info);
86832ed2927SShilei Tian   if (rc != OFFLOAD_SUCCESS)
8693de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
8703de4cd12SGeorge Rokos 
87132ed2927SShilei Tian   return __tgt_rtl_synchronize(device_id, &async_info);
8723de4cd12SGeorge Rokos }
8733de4cd12SGeorge Rokos 
87403ff643dSShilei Tian int32_t __tgt_rtl_data_retrieve_async(int32_t device_id, void *hst_ptr,
87503ff643dSShilei Tian                                       void *tgt_ptr, int64_t size,
87603ff643dSShilei Tian                                       __tgt_async_info *async_info_ptr) {
87703ff643dSShilei Tian   assert(async_info_ptr && "async_info_ptr is nullptr");
87803ff643dSShilei Tian   return dataRetrieve(device_id, hst_ptr, tgt_ptr, size, async_info_ptr);
87903ff643dSShilei Tian }
88003ff643dSShilei Tian 
8813de4cd12SGeorge Rokos int32_t __tgt_rtl_data_delete(int32_t device_id, void *tgt_ptr) {
8823de4cd12SGeorge Rokos   // Set the context we are using.
8833de4cd12SGeorge Rokos   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[device_id]);
8843de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
8853de4cd12SGeorge Rokos     DP("Error when setting CUDA context\n");
8863de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
8873de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
8883de4cd12SGeorge Rokos   }
8893de4cd12SGeorge Rokos 
8903de4cd12SGeorge Rokos   err = cuMemFree((CUdeviceptr)tgt_ptr);
8913de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
8923de4cd12SGeorge Rokos     DP("Error when freeing CUDA memory\n");
8933de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
8943de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
8953de4cd12SGeorge Rokos   }
8963de4cd12SGeorge Rokos   return OFFLOAD_SUCCESS;
8973de4cd12SGeorge Rokos }
8983de4cd12SGeorge Rokos 
8993de4cd12SGeorge Rokos int32_t __tgt_rtl_run_target_team_region(int32_t device_id, void *tgt_entry_ptr,
90032ed2927SShilei Tian                                          void **tgt_args,
90132ed2927SShilei Tian                                          ptrdiff_t *tgt_offsets,
90232ed2927SShilei Tian                                          int32_t arg_num, int32_t team_num,
90332ed2927SShilei Tian                                          int32_t thread_limit,
90403ff643dSShilei Tian                                          uint64_t loop_tripcount) {
90503ff643dSShilei Tian   __tgt_async_info async_info;
90603ff643dSShilei Tian   int32_t rc = __tgt_rtl_run_target_team_region_async(
90703ff643dSShilei Tian       device_id, tgt_entry_ptr, tgt_args, tgt_offsets, arg_num, team_num,
90803ff643dSShilei Tian       thread_limit, loop_tripcount, &async_info);
90903ff643dSShilei Tian   if (rc != OFFLOAD_SUCCESS)
91003ff643dSShilei Tian     return OFFLOAD_FAIL;
91103ff643dSShilei Tian 
91203ff643dSShilei Tian   return __tgt_rtl_synchronize(device_id, &async_info);
91303ff643dSShilei Tian }
91403ff643dSShilei Tian 
91503ff643dSShilei Tian int32_t __tgt_rtl_run_target_team_region_async(
91603ff643dSShilei Tian     int32_t device_id, void *tgt_entry_ptr, void **tgt_args,
91703ff643dSShilei Tian     ptrdiff_t *tgt_offsets, int32_t arg_num, int32_t team_num,
91803ff643dSShilei Tian     int32_t thread_limit, uint64_t loop_tripcount,
91932ed2927SShilei Tian     __tgt_async_info *async_info) {
9203de4cd12SGeorge Rokos   // Set the context we are using.
9213de4cd12SGeorge Rokos   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[device_id]);
9223de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
9233de4cd12SGeorge Rokos     DP("Error when setting CUDA context\n");
9243de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
9253de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
9263de4cd12SGeorge Rokos   }
9273de4cd12SGeorge Rokos 
9283de4cd12SGeorge Rokos   // All args are references.
9293de4cd12SGeorge Rokos   std::vector<void *> args(arg_num);
9301546d319SGeorge Rokos   std::vector<void *> ptrs(arg_num);
9313de4cd12SGeorge Rokos 
9321546d319SGeorge Rokos   for (int32_t i = 0; i < arg_num; ++i) {
9331546d319SGeorge Rokos     ptrs[i] = (void *)((intptr_t)tgt_args[i] + tgt_offsets[i]);
9341546d319SGeorge Rokos     args[i] = &ptrs[i];
9351546d319SGeorge Rokos   }
9363de4cd12SGeorge Rokos 
9373de4cd12SGeorge Rokos   KernelTy *KernelInfo = (KernelTy *)tgt_entry_ptr;
9383de4cd12SGeorge Rokos 
9393de4cd12SGeorge Rokos   int cudaThreadsPerBlock;
9403de4cd12SGeorge Rokos 
9413de4cd12SGeorge Rokos   if (thread_limit > 0) {
9423de4cd12SGeorge Rokos     cudaThreadsPerBlock = thread_limit;
9433de4cd12SGeorge Rokos     DP("Setting CUDA threads per block to requested %d\n", thread_limit);
9443de4cd12SGeorge Rokos     // Add master warp if necessary
9453de4cd12SGeorge Rokos     if (KernelInfo->ExecutionMode == GENERIC) {
9463de4cd12SGeorge Rokos       cudaThreadsPerBlock += DeviceInfo.WarpSize[device_id];
9473de4cd12SGeorge Rokos       DP("Adding master warp: +%d threads\n", DeviceInfo.WarpSize[device_id]);
9483de4cd12SGeorge Rokos     }
949c13df8e5SGeorge Rokos   } else {
950c13df8e5SGeorge Rokos     cudaThreadsPerBlock = DeviceInfo.NumThreads[device_id];
951c13df8e5SGeorge Rokos     DP("Setting CUDA threads per block to default %d\n",
952c13df8e5SGeorge Rokos         DeviceInfo.NumThreads[device_id]);
953c13df8e5SGeorge Rokos   }
9543de4cd12SGeorge Rokos 
9553de4cd12SGeorge Rokos   if (cudaThreadsPerBlock > DeviceInfo.ThreadsPerBlock[device_id]) {
9563de4cd12SGeorge Rokos     cudaThreadsPerBlock = DeviceInfo.ThreadsPerBlock[device_id];
9573de4cd12SGeorge Rokos     DP("Threads per block capped at device limit %d\n",
9583de4cd12SGeorge Rokos         DeviceInfo.ThreadsPerBlock[device_id]);
9593de4cd12SGeorge Rokos   }
9603de4cd12SGeorge Rokos 
9613de4cd12SGeorge Rokos   int kernel_limit;
9623de4cd12SGeorge Rokos   err = cuFuncGetAttribute(&kernel_limit,
9633de4cd12SGeorge Rokos       CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, KernelInfo->Func);
9643de4cd12SGeorge Rokos   if (err == CUDA_SUCCESS) {
9653de4cd12SGeorge Rokos     if (kernel_limit < cudaThreadsPerBlock) {
9663de4cd12SGeorge Rokos       cudaThreadsPerBlock = kernel_limit;
9673de4cd12SGeorge Rokos       DP("Threads per block capped at kernel limit %d\n", kernel_limit);
9683de4cd12SGeorge Rokos     }
9693de4cd12SGeorge Rokos   }
9703de4cd12SGeorge Rokos 
9713de4cd12SGeorge Rokos   int cudaBlocksPerGrid;
9723de4cd12SGeorge Rokos   if (team_num <= 0) {
9733de4cd12SGeorge Rokos     if (loop_tripcount > 0 && DeviceInfo.EnvNumTeams < 0) {
974c13df8e5SGeorge Rokos       if (KernelInfo->ExecutionMode == SPMD) {
975c13df8e5SGeorge Rokos         // We have a combined construct, i.e. `target teams distribute parallel
976c13df8e5SGeorge Rokos         // for [simd]`. We launch so many teams so that each thread will
977c13df8e5SGeorge Rokos         // execute one iteration of the loop.
9783de4cd12SGeorge Rokos         // round up to the nearest integer
9793de4cd12SGeorge Rokos         cudaBlocksPerGrid = ((loop_tripcount - 1) / cudaThreadsPerBlock) + 1;
980c13df8e5SGeorge Rokos       } else {
981c13df8e5SGeorge Rokos         // If we reach this point, then we have a non-combined construct, i.e.
982c13df8e5SGeorge Rokos         // `teams distribute` with a nested `parallel for` and each team is
983c13df8e5SGeorge Rokos         // assigned one iteration of the `distribute` loop. E.g.:
984c13df8e5SGeorge Rokos         //
985c13df8e5SGeorge Rokos         // #pragma omp target teams distribute
986c13df8e5SGeorge Rokos         // for(...loop_tripcount...) {
987c13df8e5SGeorge Rokos         //   #pragma omp parallel for
988c13df8e5SGeorge Rokos         //   for(...) {}
989c13df8e5SGeorge Rokos         // }
990c13df8e5SGeorge Rokos         //
991c13df8e5SGeorge Rokos         // Threads within a team will execute the iterations of the `parallel`
992c13df8e5SGeorge Rokos         // loop.
993c13df8e5SGeorge Rokos         cudaBlocksPerGrid = loop_tripcount;
994c13df8e5SGeorge Rokos       }
9953de4cd12SGeorge Rokos       DP("Using %d teams due to loop trip count %" PRIu64 " and number of "
9963de4cd12SGeorge Rokos           "threads per block %d\n", cudaBlocksPerGrid, loop_tripcount,
9973de4cd12SGeorge Rokos           cudaThreadsPerBlock);
9983de4cd12SGeorge Rokos     } else {
9993de4cd12SGeorge Rokos       cudaBlocksPerGrid = DeviceInfo.NumTeams[device_id];
10003de4cd12SGeorge Rokos       DP("Using default number of teams %d\n", DeviceInfo.NumTeams[device_id]);
10013de4cd12SGeorge Rokos     }
10023de4cd12SGeorge Rokos   } else if (team_num > DeviceInfo.BlocksPerGrid[device_id]) {
10033de4cd12SGeorge Rokos     cudaBlocksPerGrid = DeviceInfo.BlocksPerGrid[device_id];
10043de4cd12SGeorge Rokos     DP("Capping number of teams to team limit %d\n",
10053de4cd12SGeorge Rokos         DeviceInfo.BlocksPerGrid[device_id]);
10063de4cd12SGeorge Rokos   } else {
10073de4cd12SGeorge Rokos     cudaBlocksPerGrid = team_num;
10083de4cd12SGeorge Rokos     DP("Using requested number of teams %d\n", team_num);
10093de4cd12SGeorge Rokos   }
10103de4cd12SGeorge Rokos 
10113de4cd12SGeorge Rokos   // Run on the device.
10123de4cd12SGeorge Rokos   DP("Launch kernel with %d blocks and %d threads\n", cudaBlocksPerGrid,
10133de4cd12SGeorge Rokos      cudaThreadsPerBlock);
10143de4cd12SGeorge Rokos 
1015*feed674dSShilei Tian   CUstream Stream = getStream(device_id, async_info);
10163de4cd12SGeorge Rokos   err = cuLaunchKernel(KernelInfo->Func, cudaBlocksPerGrid, 1, 1,
1017a5153dbcSJohannes Doerfert                        cudaThreadsPerBlock, 1, 1, 0 /*bytes of shared memory*/,
1018a5153dbcSJohannes Doerfert                        Stream, &args[0], 0);
10193de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
10203de4cd12SGeorge Rokos     DP("Device kernel launch failed!\n");
10213de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
10223de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
10233de4cd12SGeorge Rokos   }
10243de4cd12SGeorge Rokos 
10253de4cd12SGeorge Rokos   DP("Launch of entry point at " DPxMOD " successful!\n",
10263de4cd12SGeorge Rokos      DPxPTR(tgt_entry_ptr));
10273de4cd12SGeorge Rokos 
10283de4cd12SGeorge Rokos   return OFFLOAD_SUCCESS;
10293de4cd12SGeorge Rokos }
10303de4cd12SGeorge Rokos 
10313de4cd12SGeorge Rokos int32_t __tgt_rtl_run_target_region(int32_t device_id, void *tgt_entry_ptr,
103232ed2927SShilei Tian                                     void **tgt_args, ptrdiff_t *tgt_offsets,
103303ff643dSShilei Tian                                     int32_t arg_num) {
103403ff643dSShilei Tian   __tgt_async_info async_info;
103503ff643dSShilei Tian   int32_t rc = __tgt_rtl_run_target_region_async(
103603ff643dSShilei Tian       device_id, tgt_entry_ptr, tgt_args, tgt_offsets, arg_num, &async_info);
103703ff643dSShilei Tian   if (rc != OFFLOAD_SUCCESS)
103803ff643dSShilei Tian     return OFFLOAD_FAIL;
103903ff643dSShilei Tian 
104003ff643dSShilei Tian   return __tgt_rtl_synchronize(device_id, &async_info);
104103ff643dSShilei Tian }
104203ff643dSShilei Tian 
104303ff643dSShilei Tian int32_t __tgt_rtl_run_target_region_async(int32_t device_id,
104403ff643dSShilei Tian                                           void *tgt_entry_ptr, void **tgt_args,
104503ff643dSShilei Tian                                           ptrdiff_t *tgt_offsets,
104632ed2927SShilei Tian                                           int32_t arg_num,
104732ed2927SShilei Tian                                           __tgt_async_info *async_info) {
10483de4cd12SGeorge Rokos   // use one team and the default number of threads.
10493de4cd12SGeorge Rokos   const int32_t team_num = 1;
10503de4cd12SGeorge Rokos   const int32_t thread_limit = 0;
105103ff643dSShilei Tian   return __tgt_rtl_run_target_team_region_async(
105203ff643dSShilei Tian       device_id, tgt_entry_ptr, tgt_args, tgt_offsets, arg_num, team_num,
105332ed2927SShilei Tian       thread_limit, 0, async_info);
105432ed2927SShilei Tian }
105532ed2927SShilei Tian 
105632ed2927SShilei Tian int32_t __tgt_rtl_synchronize(int32_t device_id, __tgt_async_info *async_info) {
105732ed2927SShilei Tian   assert(async_info && "async_info is nullptr");
105832ed2927SShilei Tian   assert(async_info->Queue && "async_info->Queue is nullptr");
105932ed2927SShilei Tian 
106032ed2927SShilei Tian   CUstream Stream = reinterpret_cast<CUstream>(async_info->Queue);
106132ed2927SShilei Tian   CUresult Err = cuStreamSynchronize(Stream);
106232ed2927SShilei Tian   if (Err != CUDA_SUCCESS) {
106332ed2927SShilei Tian     DP("Error when synchronizing stream. stream = " DPxMOD
106432ed2927SShilei Tian        ", async info ptr = " DPxMOD "\n",
106532ed2927SShilei Tian        DPxPTR(Stream), DPxPTR(async_info));
106632ed2927SShilei Tian     CUDA_ERR_STRING(Err);
106732ed2927SShilei Tian     return OFFLOAD_FAIL;
106832ed2927SShilei Tian   }
1069*feed674dSShilei Tian 
1070*feed674dSShilei Tian   // Once the stream is synchronized, return it to stream pool and reset
1071*feed674dSShilei Tian   // async_info. This is to make sure the synchronization only works for its own
1072*feed674dSShilei Tian   // tasks.
1073*feed674dSShilei Tian   DeviceInfo.returnStream(device_id, async_info);
1074*feed674dSShilei Tian 
107532ed2927SShilei Tian   return OFFLOAD_SUCCESS;
10763de4cd12SGeorge Rokos }
10773de4cd12SGeorge Rokos 
10783de4cd12SGeorge Rokos #ifdef __cplusplus
10793de4cd12SGeorge Rokos }
10803de4cd12SGeorge Rokos #endif
1081