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>
193de4cd12SGeorge Rokos #include <string>
203de4cd12SGeorge Rokos #include <vector>
213de4cd12SGeorge Rokos 
221546d319SGeorge Rokos #include "omptargetplugin.h"
233de4cd12SGeorge Rokos 
243de4cd12SGeorge Rokos #ifndef TARGET_NAME
253de4cd12SGeorge Rokos #define TARGET_NAME CUDA
263de4cd12SGeorge Rokos #endif
273de4cd12SGeorge Rokos 
28b305d26bSSergey Dmitriev #ifdef OMPTARGET_DEBUG
29b305d26bSSergey Dmitriev static int DebugLevel = 0;
30b305d26bSSergey Dmitriev 
313de4cd12SGeorge Rokos #define GETNAME2(name) #name
323de4cd12SGeorge Rokos #define GETNAME(name) GETNAME2(name)
33b305d26bSSergey Dmitriev #define DP(...) \
34b305d26bSSergey Dmitriev   do { \
35b305d26bSSergey Dmitriev     if (DebugLevel > 0) { \
36b305d26bSSergey Dmitriev       DEBUGP("Target " GETNAME(TARGET_NAME) " RTL", __VA_ARGS__); \
37b305d26bSSergey Dmitriev     } \
38b305d26bSSergey Dmitriev   } while (false)
3978769ec4SMichael Kruse 
4078769ec4SMichael Kruse // Utility for retrieving and printing CUDA error string.
4178769ec4SMichael Kruse #define CUDA_ERR_STRING(err) \
4278769ec4SMichael Kruse   do { \
4378769ec4SMichael Kruse     if (DebugLevel > 0) { \
4478769ec4SMichael Kruse       const char *errStr; \
4578769ec4SMichael Kruse       cuGetErrorString(err, &errStr); \
4678769ec4SMichael Kruse       DEBUGP("Target " GETNAME(TARGET_NAME) " RTL", "CUDA error is: %s\n", errStr); \
4778769ec4SMichael Kruse     } \
4878769ec4SMichael Kruse   } while (false)
49b305d26bSSergey Dmitriev #else // OMPTARGET_DEBUG
50b305d26bSSergey Dmitriev #define DP(...) {}
5178769ec4SMichael Kruse #define CUDA_ERR_STRING(err) {}
52b305d26bSSergey Dmitriev #endif // OMPTARGET_DEBUG
533de4cd12SGeorge Rokos 
543de4cd12SGeorge Rokos #include "../../common/elf_common.c"
553de4cd12SGeorge Rokos 
563de4cd12SGeorge Rokos /// Keep entries table per device.
573de4cd12SGeorge Rokos struct FuncOrGblEntryTy {
583de4cd12SGeorge Rokos   __tgt_target_table Table;
593de4cd12SGeorge Rokos   std::vector<__tgt_offload_entry> Entries;
603de4cd12SGeorge Rokos };
613de4cd12SGeorge Rokos 
623de4cd12SGeorge Rokos enum ExecutionModeType {
63c13df8e5SGeorge Rokos   SPMD, // constructors, destructors,
64c13df8e5SGeorge Rokos         // combined constructs (`teams distribute parallel for [simd]`)
65c13df8e5SGeorge Rokos   GENERIC, // everything else
663de4cd12SGeorge Rokos   NONE
673de4cd12SGeorge Rokos };
683de4cd12SGeorge Rokos 
69dc34b1c9SRon Lieberman /// Use a single entity to encode a kernel and a set of flags.
703de4cd12SGeorge Rokos struct KernelTy {
713de4cd12SGeorge Rokos   CUfunction Func;
723de4cd12SGeorge Rokos 
733de4cd12SGeorge Rokos   // execution mode of kernel
743de4cd12SGeorge Rokos   // 0 - SPMD mode (without master warp)
753de4cd12SGeorge Rokos   // 1 - Generic mode (with master warp)
763de4cd12SGeorge Rokos   int8_t ExecutionMode;
773de4cd12SGeorge Rokos 
783de4cd12SGeorge Rokos   KernelTy(CUfunction _Func, int8_t _ExecutionMode)
793de4cd12SGeorge Rokos       : Func(_Func), ExecutionMode(_ExecutionMode) {}
803de4cd12SGeorge Rokos };
813de4cd12SGeorge Rokos 
824c6a098aSKazuaki Ishizaki /// Device environment data
83e1c7a46dSGuansong Zhang /// Manually sync with the deviceRTL side for now, move to a dedicated header file later.
84e1c7a46dSGuansong Zhang struct omptarget_device_environmentTy {
85e1c7a46dSGuansong Zhang   int32_t debug_level;
86e1c7a46dSGuansong Zhang };
87e1c7a46dSGuansong Zhang 
883de4cd12SGeorge Rokos /// List that contains all the kernels.
893de4cd12SGeorge Rokos /// FIXME: we may need this to be per device and per library.
903de4cd12SGeorge Rokos std::list<KernelTy> KernelsList;
913de4cd12SGeorge Rokos 
923de4cd12SGeorge Rokos /// Class containing all the device information.
933de4cd12SGeorge Rokos class RTLDeviceInfoTy {
942622e9e5SAlexey Bataev   std::vector<std::list<FuncOrGblEntryTy>> FuncGblEntries;
95a5153dbcSJohannes Doerfert   std::vector<std::unique_ptr<std::atomic_uint>> NextStreamId;
963de4cd12SGeorge Rokos 
973de4cd12SGeorge Rokos public:
983de4cd12SGeorge Rokos   int NumberOfDevices;
993de4cd12SGeorge Rokos   std::vector<CUmodule> Modules;
1003de4cd12SGeorge Rokos   std::vector<CUcontext> Contexts;
101a5153dbcSJohannes Doerfert   std::vector<std::vector<CUstream>> Streams;
1023de4cd12SGeorge Rokos 
1033de4cd12SGeorge Rokos   // Device properties
1043de4cd12SGeorge Rokos   std::vector<int> ThreadsPerBlock;
1053de4cd12SGeorge Rokos   std::vector<int> BlocksPerGrid;
1063de4cd12SGeorge Rokos   std::vector<int> WarpSize;
1073de4cd12SGeorge Rokos 
1083de4cd12SGeorge Rokos   // OpenMP properties
1093de4cd12SGeorge Rokos   std::vector<int> NumTeams;
1103de4cd12SGeorge Rokos   std::vector<int> NumThreads;
1113de4cd12SGeorge Rokos 
1123de4cd12SGeorge Rokos   // OpenMP Environment properties
1133de4cd12SGeorge Rokos   int EnvNumTeams;
1143de4cd12SGeorge Rokos   int EnvTeamLimit;
115a5153dbcSJohannes Doerfert   int EnvNumStreams;
1163de4cd12SGeorge Rokos 
117c5fe030cSGheorghe-Teodor Bercea   // OpenMP Requires Flags
118c5fe030cSGheorghe-Teodor Bercea   int64_t RequiresFlags;
119c5fe030cSGheorghe-Teodor Bercea 
1203de4cd12SGeorge Rokos   //static int EnvNumThreads;
1213de4cd12SGeorge Rokos   static const int HardTeamLimit = 1<<16; // 64k
1223de4cd12SGeorge Rokos   static const int HardThreadLimit = 1024;
1233de4cd12SGeorge Rokos   static const int DefaultNumTeams = 128;
124c13df8e5SGeorge Rokos   static const int DefaultNumThreads = 128;
1253de4cd12SGeorge Rokos 
1263de4cd12SGeorge Rokos   // Record entry point associated with device
1273de4cd12SGeorge Rokos   void addOffloadEntry(int32_t device_id, __tgt_offload_entry entry) {
1283de4cd12SGeorge Rokos     assert(device_id < (int32_t)FuncGblEntries.size() &&
1293de4cd12SGeorge Rokos            "Unexpected device id!");
1302622e9e5SAlexey Bataev     FuncOrGblEntryTy &E = FuncGblEntries[device_id].back();
1313de4cd12SGeorge Rokos 
1323de4cd12SGeorge Rokos     E.Entries.push_back(entry);
1333de4cd12SGeorge Rokos   }
1343de4cd12SGeorge Rokos 
1353de4cd12SGeorge Rokos   // Return true if the entry is associated with device
1363de4cd12SGeorge Rokos   bool findOffloadEntry(int32_t device_id, void *addr) {
1373de4cd12SGeorge Rokos     assert(device_id < (int32_t)FuncGblEntries.size() &&
1383de4cd12SGeorge Rokos            "Unexpected device id!");
1392622e9e5SAlexey Bataev     FuncOrGblEntryTy &E = FuncGblEntries[device_id].back();
1403de4cd12SGeorge Rokos 
1413de4cd12SGeorge Rokos     for (auto &it : E.Entries) {
1423de4cd12SGeorge Rokos       if (it.addr == addr)
1433de4cd12SGeorge Rokos         return true;
1443de4cd12SGeorge Rokos     }
1453de4cd12SGeorge Rokos 
1463de4cd12SGeorge Rokos     return false;
1473de4cd12SGeorge Rokos   }
1483de4cd12SGeorge Rokos 
1493de4cd12SGeorge Rokos   // Return the pointer to the target entries table
1503de4cd12SGeorge Rokos   __tgt_target_table *getOffloadEntriesTable(int32_t device_id) {
1513de4cd12SGeorge Rokos     assert(device_id < (int32_t)FuncGblEntries.size() &&
1523de4cd12SGeorge Rokos            "Unexpected device id!");
1532622e9e5SAlexey Bataev     FuncOrGblEntryTy &E = FuncGblEntries[device_id].back();
1543de4cd12SGeorge Rokos 
1553de4cd12SGeorge Rokos     int32_t size = E.Entries.size();
1563de4cd12SGeorge Rokos 
1573de4cd12SGeorge Rokos     // Table is empty
1583de4cd12SGeorge Rokos     if (!size)
1593de4cd12SGeorge Rokos       return 0;
1603de4cd12SGeorge Rokos 
1613de4cd12SGeorge Rokos     __tgt_offload_entry *begin = &E.Entries[0];
1623de4cd12SGeorge Rokos     __tgt_offload_entry *end = &E.Entries[size - 1];
1633de4cd12SGeorge Rokos 
1643de4cd12SGeorge Rokos     // Update table info according to the entries and return the pointer
1653de4cd12SGeorge Rokos     E.Table.EntriesBegin = begin;
1663de4cd12SGeorge Rokos     E.Table.EntriesEnd = ++end;
1673de4cd12SGeorge Rokos 
1683de4cd12SGeorge Rokos     return &E.Table;
1693de4cd12SGeorge Rokos   }
1703de4cd12SGeorge Rokos 
1713de4cd12SGeorge Rokos   // Clear entries table for a device
1723de4cd12SGeorge Rokos   void clearOffloadEntriesTable(int32_t device_id) {
1733de4cd12SGeorge Rokos     assert(device_id < (int32_t)FuncGblEntries.size() &&
1743de4cd12SGeorge Rokos            "Unexpected device id!");
1752622e9e5SAlexey Bataev     FuncGblEntries[device_id].emplace_back();
1762622e9e5SAlexey Bataev     FuncOrGblEntryTy &E = FuncGblEntries[device_id].back();
1773de4cd12SGeorge Rokos     E.Entries.clear();
1783de4cd12SGeorge Rokos     E.Table.EntriesBegin = E.Table.EntriesEnd = 0;
1793de4cd12SGeorge Rokos   }
1803de4cd12SGeorge Rokos 
181a5153dbcSJohannes Doerfert   // Get the next stream on a given device in a round robin manner
182a5153dbcSJohannes Doerfert   CUstream &getNextStream(const int DeviceId) {
183a5153dbcSJohannes Doerfert     assert(DeviceId >= 0 &&
184a5153dbcSJohannes Doerfert            static_cast<size_t>(DeviceId) < NextStreamId.size() &&
185a5153dbcSJohannes Doerfert            "Unexpected device id!");
186a5153dbcSJohannes Doerfert     const unsigned int Id = NextStreamId[DeviceId]->fetch_add(1);
187a5153dbcSJohannes Doerfert     return Streams[DeviceId][Id % EnvNumStreams];
188a5153dbcSJohannes Doerfert   }
189a5153dbcSJohannes Doerfert 
1903de4cd12SGeorge Rokos   RTLDeviceInfoTy() {
191b305d26bSSergey Dmitriev #ifdef OMPTARGET_DEBUG
192b305d26bSSergey Dmitriev     if (char *envStr = getenv("LIBOMPTARGET_DEBUG")) {
193b305d26bSSergey Dmitriev       DebugLevel = std::stoi(envStr);
194b305d26bSSergey Dmitriev     }
195b305d26bSSergey Dmitriev #endif // OMPTARGET_DEBUG
196b305d26bSSergey Dmitriev 
1973de4cd12SGeorge Rokos     DP("Start initializing CUDA\n");
1983de4cd12SGeorge Rokos 
1993de4cd12SGeorge Rokos     CUresult err = cuInit(0);
2003de4cd12SGeorge Rokos     if (err != CUDA_SUCCESS) {
2013de4cd12SGeorge Rokos       DP("Error when initializing CUDA\n");
2023de4cd12SGeorge Rokos       CUDA_ERR_STRING(err);
2033de4cd12SGeorge Rokos       return;
2043de4cd12SGeorge Rokos     }
2053de4cd12SGeorge Rokos 
2063de4cd12SGeorge Rokos     NumberOfDevices = 0;
2073de4cd12SGeorge Rokos 
2083de4cd12SGeorge Rokos     err = cuDeviceGetCount(&NumberOfDevices);
2093de4cd12SGeorge Rokos     if (err != CUDA_SUCCESS) {
2103de4cd12SGeorge Rokos       DP("Error when getting CUDA device count\n");
2113de4cd12SGeorge Rokos       CUDA_ERR_STRING(err);
2123de4cd12SGeorge Rokos       return;
2133de4cd12SGeorge Rokos     }
2143de4cd12SGeorge Rokos 
2153de4cd12SGeorge Rokos     if (NumberOfDevices == 0) {
2163de4cd12SGeorge Rokos       DP("There are no devices supporting CUDA.\n");
2173de4cd12SGeorge Rokos       return;
2183de4cd12SGeorge Rokos     }
2193de4cd12SGeorge Rokos 
2203de4cd12SGeorge Rokos     FuncGblEntries.resize(NumberOfDevices);
2213de4cd12SGeorge Rokos     Contexts.resize(NumberOfDevices);
222a5153dbcSJohannes Doerfert     Streams.resize(NumberOfDevices);
223a5153dbcSJohannes Doerfert     NextStreamId.resize(NumberOfDevices);
2243de4cd12SGeorge Rokos     ThreadsPerBlock.resize(NumberOfDevices);
2253de4cd12SGeorge Rokos     BlocksPerGrid.resize(NumberOfDevices);
2263de4cd12SGeorge Rokos     WarpSize.resize(NumberOfDevices);
2273de4cd12SGeorge Rokos     NumTeams.resize(NumberOfDevices);
2283de4cd12SGeorge Rokos     NumThreads.resize(NumberOfDevices);
2293de4cd12SGeorge Rokos 
2303de4cd12SGeorge Rokos     // Get environment variables regarding teams
2313de4cd12SGeorge Rokos     char *envStr = getenv("OMP_TEAM_LIMIT");
2323de4cd12SGeorge Rokos     if (envStr) {
2333de4cd12SGeorge Rokos       // OMP_TEAM_LIMIT has been set
2343de4cd12SGeorge Rokos       EnvTeamLimit = std::stoi(envStr);
2353de4cd12SGeorge Rokos       DP("Parsed OMP_TEAM_LIMIT=%d\n", EnvTeamLimit);
2363de4cd12SGeorge Rokos     } else {
2373de4cd12SGeorge Rokos       EnvTeamLimit = -1;
2383de4cd12SGeorge Rokos     }
2393de4cd12SGeorge Rokos     envStr = getenv("OMP_NUM_TEAMS");
2403de4cd12SGeorge Rokos     if (envStr) {
2413de4cd12SGeorge Rokos       // OMP_NUM_TEAMS has been set
2423de4cd12SGeorge Rokos       EnvNumTeams = std::stoi(envStr);
2433de4cd12SGeorge Rokos       DP("Parsed OMP_NUM_TEAMS=%d\n", EnvNumTeams);
2443de4cd12SGeorge Rokos     } else {
2453de4cd12SGeorge Rokos       EnvNumTeams = -1;
2463de4cd12SGeorge Rokos     }
247c5fe030cSGheorghe-Teodor Bercea 
248a5153dbcSJohannes Doerfert     // By default let's create 256 streams per device
249a5153dbcSJohannes Doerfert     EnvNumStreams = 256;
250a5153dbcSJohannes Doerfert     envStr = getenv("LIBOMPTARGET_NUM_STREAMS");
251a5153dbcSJohannes Doerfert     if (envStr) {
252a5153dbcSJohannes Doerfert       EnvNumStreams = std::stoi(envStr);
253a5153dbcSJohannes Doerfert     }
254a5153dbcSJohannes Doerfert 
255a5153dbcSJohannes Doerfert     // Initialize streams for each device
256a5153dbcSJohannes Doerfert     for (std::vector<CUstream> &S : Streams) {
257a5153dbcSJohannes Doerfert       S.resize(EnvNumStreams);
258a5153dbcSJohannes Doerfert     }
259a5153dbcSJohannes Doerfert 
260a5153dbcSJohannes Doerfert     // Initialize the next stream id
261a5153dbcSJohannes Doerfert     for (std::unique_ptr<std::atomic_uint> &Ptr : NextStreamId) {
262a5153dbcSJohannes Doerfert       Ptr = std::make_unique<std::atomic_uint>(0);
263a5153dbcSJohannes Doerfert     }
264a5153dbcSJohannes Doerfert 
265c5fe030cSGheorghe-Teodor Bercea     // Default state.
266c5fe030cSGheorghe-Teodor Bercea     RequiresFlags = OMP_REQ_UNDEFINED;
2673de4cd12SGeorge Rokos   }
2683de4cd12SGeorge Rokos 
2693de4cd12SGeorge Rokos   ~RTLDeviceInfoTy() {
2703de4cd12SGeorge Rokos     // Close modules
2713de4cd12SGeorge Rokos     for (auto &module : Modules)
2723de4cd12SGeorge Rokos       if (module) {
2733de4cd12SGeorge Rokos         CUresult err = cuModuleUnload(module);
2743de4cd12SGeorge Rokos         if (err != CUDA_SUCCESS) {
2753de4cd12SGeorge Rokos           DP("Error when unloading CUDA module\n");
2763de4cd12SGeorge Rokos           CUDA_ERR_STRING(err);
2773de4cd12SGeorge Rokos         }
2783de4cd12SGeorge Rokos       }
2793de4cd12SGeorge Rokos 
280a5153dbcSJohannes Doerfert     // Destroy streams before contexts
281a5153dbcSJohannes Doerfert     for (int I = 0; I < NumberOfDevices; ++I) {
282a5153dbcSJohannes Doerfert       CUresult err = cuCtxSetCurrent(Contexts[I]);
283a5153dbcSJohannes Doerfert       if (err != CUDA_SUCCESS) {
284a5153dbcSJohannes Doerfert         DP("Error when setting current CUDA context\n");
285a5153dbcSJohannes Doerfert         CUDA_ERR_STRING(err);
286a5153dbcSJohannes Doerfert       }
287a5153dbcSJohannes Doerfert 
288a5153dbcSJohannes Doerfert       for (auto &S : Streams[I])
289a5153dbcSJohannes Doerfert         if (S) {
290a5153dbcSJohannes Doerfert           err = cuStreamDestroy(S);
291a5153dbcSJohannes Doerfert           if (err != CUDA_SUCCESS) {
292a5153dbcSJohannes Doerfert             DP("Error when destroying CUDA stream\n");
293a5153dbcSJohannes Doerfert             CUDA_ERR_STRING(err);
294a5153dbcSJohannes Doerfert           }
295a5153dbcSJohannes Doerfert         }
296a5153dbcSJohannes Doerfert     }
297a5153dbcSJohannes Doerfert 
2983de4cd12SGeorge Rokos     // Destroy contexts
2993de4cd12SGeorge Rokos     for (auto &ctx : Contexts)
3003de4cd12SGeorge Rokos       if (ctx) {
3013de4cd12SGeorge Rokos         CUresult err = cuCtxDestroy(ctx);
3023de4cd12SGeorge Rokos         if (err != CUDA_SUCCESS) {
3033de4cd12SGeorge Rokos           DP("Error when destroying CUDA context\n");
3043de4cd12SGeorge Rokos           CUDA_ERR_STRING(err);
3053de4cd12SGeorge Rokos         }
3063de4cd12SGeorge Rokos       }
3073de4cd12SGeorge Rokos   }
3083de4cd12SGeorge Rokos };
3093de4cd12SGeorge Rokos 
3103de4cd12SGeorge Rokos static RTLDeviceInfoTy DeviceInfo;
3113de4cd12SGeorge Rokos 
31232ed2927SShilei Tian namespace {
31332ed2927SShilei Tian CUstream selectStream(int32_t Id, __tgt_async_info *AsyncInfo) {
31432ed2927SShilei Tian   if (!AsyncInfo)
31532ed2927SShilei Tian     return DeviceInfo.getNextStream(Id);
31632ed2927SShilei Tian 
31732ed2927SShilei Tian   if (!AsyncInfo->Queue)
31832ed2927SShilei Tian     AsyncInfo->Queue = DeviceInfo.getNextStream(Id);
31932ed2927SShilei Tian 
32032ed2927SShilei Tian   return reinterpret_cast<CUstream>(AsyncInfo->Queue);
32132ed2927SShilei Tian }
32232ed2927SShilei Tian 
32332ed2927SShilei Tian int32_t dataRetrieve(int32_t DeviceId, void *HstPtr, void *TgtPtr, int64_t Size,
32432ed2927SShilei Tian                      __tgt_async_info *AsyncInfoPtr) {
32532ed2927SShilei Tian   assert(AsyncInfoPtr && "AsyncInfoPtr is nullptr");
32632ed2927SShilei Tian   // Set the context we are using.
32732ed2927SShilei Tian   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[DeviceId]);
32832ed2927SShilei Tian   if (err != CUDA_SUCCESS) {
32932ed2927SShilei Tian     DP("Error when setting CUDA context\n");
33032ed2927SShilei Tian     CUDA_ERR_STRING(err);
33132ed2927SShilei Tian     return OFFLOAD_FAIL;
33232ed2927SShilei Tian   }
33332ed2927SShilei Tian 
33432ed2927SShilei Tian   CUstream Stream = selectStream(DeviceId, AsyncInfoPtr);
33532ed2927SShilei Tian 
33632ed2927SShilei Tian   err = cuMemcpyDtoHAsync(HstPtr, (CUdeviceptr)TgtPtr, Size, Stream);
33732ed2927SShilei Tian   if (err != CUDA_SUCCESS) {
33832ed2927SShilei Tian     DP("Error when copying data from device to host. Pointers: host = " DPxMOD
33932ed2927SShilei Tian        ", device = " DPxMOD ", size = %" PRId64 "\n",
34032ed2927SShilei Tian        DPxPTR(HstPtr), DPxPTR(TgtPtr), Size);
34132ed2927SShilei Tian     CUDA_ERR_STRING(err);
34232ed2927SShilei Tian     return OFFLOAD_FAIL;
34332ed2927SShilei Tian   }
34432ed2927SShilei Tian 
34532ed2927SShilei Tian   return OFFLOAD_SUCCESS;
34632ed2927SShilei Tian }
34732ed2927SShilei Tian 
34832ed2927SShilei Tian int32_t dataSubmit(int32_t DeviceId, void *TgtPtr, void *HstPtr, int64_t Size,
34932ed2927SShilei Tian                    __tgt_async_info *AsyncInfoPtr) {
35032ed2927SShilei Tian   assert(AsyncInfoPtr && "AsyncInfoPtr is nullptr");
35132ed2927SShilei Tian   // Set the context we are using.
35232ed2927SShilei Tian   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[DeviceId]);
35332ed2927SShilei Tian   if (err != CUDA_SUCCESS) {
35432ed2927SShilei Tian     DP("Error when setting CUDA context\n");
35532ed2927SShilei Tian     CUDA_ERR_STRING(err);
35632ed2927SShilei Tian     return OFFLOAD_FAIL;
35732ed2927SShilei Tian   }
35832ed2927SShilei Tian 
35932ed2927SShilei Tian   CUstream Stream = selectStream(DeviceId, AsyncInfoPtr);
36032ed2927SShilei Tian 
36132ed2927SShilei Tian   err = cuMemcpyHtoDAsync((CUdeviceptr)TgtPtr, HstPtr, Size, Stream);
36232ed2927SShilei Tian   if (err != CUDA_SUCCESS) {
36332ed2927SShilei Tian     DP("Error when copying data from host to device. Pointers: host = " DPxMOD
36432ed2927SShilei Tian        ", device = " DPxMOD ", size = %" PRId64 "\n",
36532ed2927SShilei Tian        DPxPTR(HstPtr), DPxPTR(TgtPtr), Size);
36632ed2927SShilei Tian     CUDA_ERR_STRING(err);
36732ed2927SShilei Tian     return OFFLOAD_FAIL;
36832ed2927SShilei Tian   }
36932ed2927SShilei Tian 
37032ed2927SShilei Tian   return OFFLOAD_SUCCESS;
37132ed2927SShilei Tian }
37232ed2927SShilei Tian } // namespace
37332ed2927SShilei Tian 
3743de4cd12SGeorge Rokos #ifdef __cplusplus
3753de4cd12SGeorge Rokos extern "C" {
3763de4cd12SGeorge Rokos #endif
3773de4cd12SGeorge Rokos 
3783de4cd12SGeorge Rokos int32_t __tgt_rtl_is_valid_binary(__tgt_device_image *image) {
3793de4cd12SGeorge Rokos   return elf_check_machine(image, 190); // EM_CUDA = 190.
3803de4cd12SGeorge Rokos }
3813de4cd12SGeorge Rokos 
3823de4cd12SGeorge Rokos int32_t __tgt_rtl_number_of_devices() { return DeviceInfo.NumberOfDevices; }
3833de4cd12SGeorge Rokos 
384c5fe030cSGheorghe-Teodor Bercea int64_t __tgt_rtl_init_requires(int64_t RequiresFlags) {
385c5fe030cSGheorghe-Teodor Bercea   DP("Init requires flags to %ld\n", RequiresFlags);
386c5fe030cSGheorghe-Teodor Bercea   DeviceInfo.RequiresFlags = RequiresFlags;
387c5fe030cSGheorghe-Teodor Bercea   return RequiresFlags;
388c5fe030cSGheorghe-Teodor Bercea }
389c5fe030cSGheorghe-Teodor Bercea 
3903de4cd12SGeorge Rokos int32_t __tgt_rtl_init_device(int32_t device_id) {
3913de4cd12SGeorge Rokos 
3923de4cd12SGeorge Rokos   CUdevice cuDevice;
3933de4cd12SGeorge Rokos   DP("Getting device %d\n", device_id);
3943de4cd12SGeorge Rokos   CUresult err = cuDeviceGet(&cuDevice, device_id);
3953de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
3963de4cd12SGeorge Rokos     DP("Error when getting CUDA device with id = %d\n", device_id);
3973de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
3983de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
3993de4cd12SGeorge Rokos   }
4003de4cd12SGeorge Rokos 
4013de4cd12SGeorge Rokos   // Create the context and save it to use whenever this device is selected.
4023de4cd12SGeorge Rokos   err = cuCtxCreate(&DeviceInfo.Contexts[device_id], CU_CTX_SCHED_BLOCKING_SYNC,
4033de4cd12SGeorge Rokos                     cuDevice);
4043de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
4053de4cd12SGeorge Rokos     DP("Error when creating a CUDA context\n");
4063de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
4073de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
4083de4cd12SGeorge Rokos   }
4093de4cd12SGeorge Rokos 
410a5153dbcSJohannes Doerfert   err = cuCtxSetCurrent(DeviceInfo.Contexts[device_id]);
411a5153dbcSJohannes Doerfert   if (err != CUDA_SUCCESS) {
412a5153dbcSJohannes Doerfert     DP("Error when setting current CUDA context\n");
413a5153dbcSJohannes Doerfert     CUDA_ERR_STRING(err);
414a5153dbcSJohannes Doerfert   }
415a5153dbcSJohannes Doerfert 
416a5153dbcSJohannes Doerfert   for (CUstream &Stream : DeviceInfo.Streams[device_id]) {
417a5153dbcSJohannes Doerfert     err = cuStreamCreate(&Stream, CU_STREAM_NON_BLOCKING);
418a5153dbcSJohannes Doerfert     if (err != CUDA_SUCCESS) {
419a5153dbcSJohannes Doerfert       DP("Error when creating CUDA stream\n");
420a5153dbcSJohannes Doerfert       CUDA_ERR_STRING(err);
421a5153dbcSJohannes Doerfert     }
422a5153dbcSJohannes Doerfert   }
423a5153dbcSJohannes Doerfert 
424bb51d398SJonas Hahnfeld   // Query attributes to determine number of threads/block and blocks/grid.
425bb51d398SJonas Hahnfeld   int maxGridDimX;
426bb51d398SJonas Hahnfeld   err = cuDeviceGetAttribute(&maxGridDimX, CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_X,
427bb51d398SJonas Hahnfeld                              cuDevice);
428c1895235SJonas Hahnfeld   if (err != CUDA_SUCCESS) {
429bb51d398SJonas Hahnfeld     DP("Error getting max grid dimension, use default\n");
4303de4cd12SGeorge Rokos     DeviceInfo.BlocksPerGrid[device_id] = RTLDeviceInfoTy::DefaultNumTeams;
431bb51d398SJonas Hahnfeld   } else if (maxGridDimX <= RTLDeviceInfoTy::HardTeamLimit) {
432bb51d398SJonas Hahnfeld     DeviceInfo.BlocksPerGrid[device_id] = maxGridDimX;
433bb51d398SJonas Hahnfeld     DP("Using %d CUDA blocks per grid\n", maxGridDimX);
4343de4cd12SGeorge Rokos   } else {
4353de4cd12SGeorge Rokos     DeviceInfo.BlocksPerGrid[device_id] = RTLDeviceInfoTy::HardTeamLimit;
4363de4cd12SGeorge Rokos     DP("Max CUDA blocks per grid %d exceeds the hard team limit %d, capping "
437bb51d398SJonas Hahnfeld        "at the hard limit\n",
438bb51d398SJonas Hahnfeld        maxGridDimX, RTLDeviceInfoTy::HardTeamLimit);
4393de4cd12SGeorge Rokos   }
4403de4cd12SGeorge Rokos 
441bb51d398SJonas Hahnfeld   // We are only exploiting threads along the x axis.
442bb51d398SJonas Hahnfeld   int maxBlockDimX;
443bb51d398SJonas Hahnfeld   err = cuDeviceGetAttribute(&maxBlockDimX, CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_X,
444bb51d398SJonas Hahnfeld                              cuDevice);
445bb51d398SJonas Hahnfeld   if (err != CUDA_SUCCESS) {
446bb51d398SJonas Hahnfeld     DP("Error getting max block dimension, use default\n");
447bb51d398SJonas Hahnfeld     DeviceInfo.ThreadsPerBlock[device_id] = RTLDeviceInfoTy::DefaultNumThreads;
448bb51d398SJonas Hahnfeld   } else if (maxBlockDimX <= RTLDeviceInfoTy::HardThreadLimit) {
449bb51d398SJonas Hahnfeld     DeviceInfo.ThreadsPerBlock[device_id] = maxBlockDimX;
450bb51d398SJonas Hahnfeld     DP("Using %d CUDA threads per block\n", maxBlockDimX);
4513de4cd12SGeorge Rokos   } else {
4523de4cd12SGeorge Rokos     DeviceInfo.ThreadsPerBlock[device_id] = RTLDeviceInfoTy::HardThreadLimit;
453bb51d398SJonas Hahnfeld     DP("Max CUDA threads per block %d exceeds the hard thread limit %d, capping"
454bb51d398SJonas Hahnfeld        "at the hard limit\n",
455bb51d398SJonas Hahnfeld        maxBlockDimX, RTLDeviceInfoTy::HardThreadLimit);
4563de4cd12SGeorge Rokos   }
4573de4cd12SGeorge Rokos 
458bb51d398SJonas Hahnfeld   int warpSize;
459bb51d398SJonas Hahnfeld   err =
460bb51d398SJonas Hahnfeld       cuDeviceGetAttribute(&warpSize, CU_DEVICE_ATTRIBUTE_WARP_SIZE, cuDevice);
461bb51d398SJonas Hahnfeld   if (err != CUDA_SUCCESS) {
462bb51d398SJonas Hahnfeld     DP("Error getting warp size, assume default\n");
463bb51d398SJonas Hahnfeld     DeviceInfo.WarpSize[device_id] = 32;
464bb51d398SJonas Hahnfeld   } else {
465bb51d398SJonas Hahnfeld     DeviceInfo.WarpSize[device_id] = warpSize;
4663de4cd12SGeorge Rokos   }
4673de4cd12SGeorge Rokos 
4683de4cd12SGeorge Rokos   // Adjust teams to the env variables
4693de4cd12SGeorge Rokos   if (DeviceInfo.EnvTeamLimit > 0 &&
4703de4cd12SGeorge Rokos       DeviceInfo.BlocksPerGrid[device_id] > DeviceInfo.EnvTeamLimit) {
4713de4cd12SGeorge Rokos     DeviceInfo.BlocksPerGrid[device_id] = DeviceInfo.EnvTeamLimit;
4723de4cd12SGeorge Rokos     DP("Capping max CUDA blocks per grid to OMP_TEAM_LIMIT=%d\n",
4733de4cd12SGeorge Rokos         DeviceInfo.EnvTeamLimit);
4743de4cd12SGeorge Rokos   }
4753de4cd12SGeorge Rokos 
4763de4cd12SGeorge Rokos   DP("Max number of CUDA blocks %d, threads %d & warp size %d\n",
4773de4cd12SGeorge Rokos      DeviceInfo.BlocksPerGrid[device_id], DeviceInfo.ThreadsPerBlock[device_id],
4783de4cd12SGeorge Rokos      DeviceInfo.WarpSize[device_id]);
4793de4cd12SGeorge Rokos 
4803de4cd12SGeorge Rokos   // Set default number of teams
4813de4cd12SGeorge Rokos   if (DeviceInfo.EnvNumTeams > 0) {
4823de4cd12SGeorge Rokos     DeviceInfo.NumTeams[device_id] = DeviceInfo.EnvNumTeams;
4833de4cd12SGeorge Rokos     DP("Default number of teams set according to environment %d\n",
4843de4cd12SGeorge Rokos         DeviceInfo.EnvNumTeams);
4853de4cd12SGeorge Rokos   } else {
4863de4cd12SGeorge Rokos     DeviceInfo.NumTeams[device_id] = RTLDeviceInfoTy::DefaultNumTeams;
4873de4cd12SGeorge Rokos     DP("Default number of teams set according to library's default %d\n",
4883de4cd12SGeorge Rokos         RTLDeviceInfoTy::DefaultNumTeams);
4893de4cd12SGeorge Rokos   }
4903de4cd12SGeorge Rokos   if (DeviceInfo.NumTeams[device_id] > DeviceInfo.BlocksPerGrid[device_id]) {
4913de4cd12SGeorge Rokos     DeviceInfo.NumTeams[device_id] = DeviceInfo.BlocksPerGrid[device_id];
4923de4cd12SGeorge Rokos     DP("Default number of teams exceeds device limit, capping at %d\n",
4933de4cd12SGeorge Rokos         DeviceInfo.BlocksPerGrid[device_id]);
4943de4cd12SGeorge Rokos   }
4953de4cd12SGeorge Rokos 
4963de4cd12SGeorge Rokos   // Set default number of threads
4973de4cd12SGeorge Rokos   DeviceInfo.NumThreads[device_id] = RTLDeviceInfoTy::DefaultNumThreads;
4983de4cd12SGeorge Rokos   DP("Default number of threads set according to library's default %d\n",
4993de4cd12SGeorge Rokos           RTLDeviceInfoTy::DefaultNumThreads);
5003de4cd12SGeorge Rokos   if (DeviceInfo.NumThreads[device_id] >
5013de4cd12SGeorge Rokos       DeviceInfo.ThreadsPerBlock[device_id]) {
5023de4cd12SGeorge Rokos     DeviceInfo.NumTeams[device_id] = DeviceInfo.ThreadsPerBlock[device_id];
5033de4cd12SGeorge Rokos     DP("Default number of threads exceeds device limit, capping at %d\n",
5043de4cd12SGeorge Rokos         DeviceInfo.ThreadsPerBlock[device_id]);
5053de4cd12SGeorge Rokos   }
5063de4cd12SGeorge Rokos 
5073de4cd12SGeorge Rokos   return OFFLOAD_SUCCESS;
5083de4cd12SGeorge Rokos }
5093de4cd12SGeorge Rokos 
5103de4cd12SGeorge Rokos __tgt_target_table *__tgt_rtl_load_binary(int32_t device_id,
5113de4cd12SGeorge Rokos     __tgt_device_image *image) {
5123de4cd12SGeorge Rokos 
5133de4cd12SGeorge Rokos   // Set the context we are using.
5143de4cd12SGeorge Rokos   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[device_id]);
5153de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
5163de4cd12SGeorge Rokos     DP("Error when setting a CUDA context for device %d\n", device_id);
5173de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
5183de4cd12SGeorge Rokos     return NULL;
5193de4cd12SGeorge Rokos   }
5203de4cd12SGeorge Rokos 
5213de4cd12SGeorge Rokos   // Clear the offload table as we are going to create a new one.
5223de4cd12SGeorge Rokos   DeviceInfo.clearOffloadEntriesTable(device_id);
5233de4cd12SGeorge Rokos 
5243de4cd12SGeorge Rokos   // Create the module and extract the function pointers.
5253de4cd12SGeorge Rokos 
5263de4cd12SGeorge Rokos   CUmodule cumod;
5273de4cd12SGeorge Rokos   DP("Load data from image " DPxMOD "\n", DPxPTR(image->ImageStart));
5283de4cd12SGeorge Rokos   err = cuModuleLoadDataEx(&cumod, image->ImageStart, 0, NULL, NULL);
5293de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
5303de4cd12SGeorge Rokos     DP("Error when loading CUDA module\n");
5313de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
5323de4cd12SGeorge Rokos     return NULL;
5333de4cd12SGeorge Rokos   }
5343de4cd12SGeorge Rokos 
5353de4cd12SGeorge Rokos   DP("CUDA module successfully loaded!\n");
5363de4cd12SGeorge Rokos   DeviceInfo.Modules.push_back(cumod);
5373de4cd12SGeorge Rokos 
5383de4cd12SGeorge Rokos   // Find the symbols in the module by name.
5393de4cd12SGeorge Rokos   __tgt_offload_entry *HostBegin = image->EntriesBegin;
5403de4cd12SGeorge Rokos   __tgt_offload_entry *HostEnd = image->EntriesEnd;
5413de4cd12SGeorge Rokos 
5423de4cd12SGeorge Rokos   for (__tgt_offload_entry *e = HostBegin; e != HostEnd; ++e) {
5433de4cd12SGeorge Rokos 
5443de4cd12SGeorge Rokos     if (!e->addr) {
5453de4cd12SGeorge Rokos       // We return NULL when something like this happens, the host should have
5463de4cd12SGeorge Rokos       // always something in the address to uniquely identify the target region.
5473de4cd12SGeorge Rokos       DP("Invalid binary: host entry '<null>' (size = %zd)...\n", e->size);
5483de4cd12SGeorge Rokos 
5493de4cd12SGeorge Rokos       return NULL;
5503de4cd12SGeorge Rokos     }
5513de4cd12SGeorge Rokos 
5523de4cd12SGeorge Rokos     if (e->size) {
5533de4cd12SGeorge Rokos       __tgt_offload_entry entry = *e;
5543de4cd12SGeorge Rokos 
5553de4cd12SGeorge Rokos       CUdeviceptr cuptr;
5563de4cd12SGeorge Rokos       size_t cusize;
5573de4cd12SGeorge Rokos       err = cuModuleGetGlobal(&cuptr, &cusize, cumod, e->name);
5583de4cd12SGeorge Rokos 
5593de4cd12SGeorge Rokos       if (err != CUDA_SUCCESS) {
5603de4cd12SGeorge Rokos         DP("Loading global '%s' (Failed)\n", e->name);
5613de4cd12SGeorge Rokos         CUDA_ERR_STRING(err);
5623de4cd12SGeorge Rokos         return NULL;
5633de4cd12SGeorge Rokos       }
5643de4cd12SGeorge Rokos 
5653de4cd12SGeorge Rokos       if (cusize != e->size) {
5663de4cd12SGeorge Rokos         DP("Loading global '%s' - size mismatch (%zd != %zd)\n", e->name,
5673de4cd12SGeorge Rokos             cusize, e->size);
5683de4cd12SGeorge Rokos         CUDA_ERR_STRING(err);
5693de4cd12SGeorge Rokos         return NULL;
5703de4cd12SGeorge Rokos       }
5713de4cd12SGeorge Rokos 
5723de4cd12SGeorge Rokos       DP("Entry point " DPxMOD " maps to global %s (" DPxMOD ")\n",
5733de4cd12SGeorge Rokos           DPxPTR(e - HostBegin), e->name, DPxPTR(cuptr));
5743de4cd12SGeorge Rokos       entry.addr = (void *)cuptr;
5753de4cd12SGeorge Rokos 
576aace6d28SGheorghe-Teodor Bercea       // Note: In the current implementation declare target variables
577aace6d28SGheorghe-Teodor Bercea       // can either be link or to. This means that once unified
578aace6d28SGheorghe-Teodor Bercea       // memory is activated via the requires directive, the variable
579aace6d28SGheorghe-Teodor Bercea       // can be used directly from the host in both cases.
580aace6d28SGheorghe-Teodor Bercea       // TODO: when variables types other than to or link are added,
5814c6a098aSKazuaki Ishizaki       // the below condition should be changed to explicitly
582aace6d28SGheorghe-Teodor Bercea       // check for to and link variables types:
583aace6d28SGheorghe-Teodor Bercea       //  (DeviceInfo.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY &&
584aace6d28SGheorghe-Teodor Bercea       //   (e->flags & OMP_DECLARE_TARGET_LINK ||
585aace6d28SGheorghe-Teodor Bercea       //    e->flags == OMP_DECLARE_TARGET_TO))
586aace6d28SGheorghe-Teodor Bercea       if (DeviceInfo.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY) {
587aace6d28SGheorghe-Teodor Bercea         // If unified memory is present any target link or to variables
588c5fe030cSGheorghe-Teodor Bercea         // can access host addresses directly. There is no longer a
589c5fe030cSGheorghe-Teodor Bercea         // need for device copies.
590c5fe030cSGheorghe-Teodor Bercea         cuMemcpyHtoD(cuptr, e->addr, sizeof(void *));
591c5fe030cSGheorghe-Teodor Bercea         DP("Copy linked variable host address (" DPxMOD ")"
592c5fe030cSGheorghe-Teodor Bercea            "to device address (" DPxMOD ")\n",
593c5fe030cSGheorghe-Teodor Bercea           DPxPTR(*((void**)e->addr)), DPxPTR(cuptr));
594c5fe030cSGheorghe-Teodor Bercea       }
595c5fe030cSGheorghe-Teodor Bercea 
5963de4cd12SGeorge Rokos       DeviceInfo.addOffloadEntry(device_id, entry);
5973de4cd12SGeorge Rokos 
5983de4cd12SGeorge Rokos       continue;
5993de4cd12SGeorge Rokos     }
6003de4cd12SGeorge Rokos 
6013de4cd12SGeorge Rokos     CUfunction fun;
6023de4cd12SGeorge Rokos     err = cuModuleGetFunction(&fun, cumod, e->name);
6033de4cd12SGeorge Rokos 
6043de4cd12SGeorge Rokos     if (err != CUDA_SUCCESS) {
6053de4cd12SGeorge Rokos       DP("Loading '%s' (Failed)\n", e->name);
6063de4cd12SGeorge Rokos       CUDA_ERR_STRING(err);
6073de4cd12SGeorge Rokos       return NULL;
6083de4cd12SGeorge Rokos     }
6093de4cd12SGeorge Rokos 
6103de4cd12SGeorge Rokos     DP("Entry point " DPxMOD " maps to %s (" DPxMOD ")\n",
6113de4cd12SGeorge Rokos         DPxPTR(e - HostBegin), e->name, DPxPTR(fun));
6123de4cd12SGeorge Rokos 
6133de4cd12SGeorge Rokos     // default value GENERIC (in case symbol is missing from cubin file)
6143de4cd12SGeorge Rokos     int8_t ExecModeVal = ExecutionModeType::GENERIC;
6153de4cd12SGeorge Rokos     std::string ExecModeNameStr (e->name);
6163de4cd12SGeorge Rokos     ExecModeNameStr += "_exec_mode";
6173de4cd12SGeorge Rokos     const char *ExecModeName = ExecModeNameStr.c_str();
6183de4cd12SGeorge Rokos 
6193de4cd12SGeorge Rokos     CUdeviceptr ExecModePtr;
6203de4cd12SGeorge Rokos     size_t cusize;
6213de4cd12SGeorge Rokos     err = cuModuleGetGlobal(&ExecModePtr, &cusize, cumod, ExecModeName);
6223de4cd12SGeorge Rokos     if (err == CUDA_SUCCESS) {
6233de4cd12SGeorge Rokos       if ((size_t)cusize != sizeof(int8_t)) {
6243de4cd12SGeorge Rokos         DP("Loading global exec_mode '%s' - size mismatch (%zd != %zd)\n",
6253de4cd12SGeorge Rokos            ExecModeName, cusize, sizeof(int8_t));
6263de4cd12SGeorge Rokos         CUDA_ERR_STRING(err);
6273de4cd12SGeorge Rokos         return NULL;
6283de4cd12SGeorge Rokos       }
6293de4cd12SGeorge Rokos 
6303de4cd12SGeorge Rokos       err = cuMemcpyDtoH(&ExecModeVal, ExecModePtr, cusize);
6313de4cd12SGeorge Rokos       if (err != CUDA_SUCCESS) {
6323de4cd12SGeorge Rokos         DP("Error when copying data from device to host. Pointers: "
6333de4cd12SGeorge Rokos            "host = " DPxMOD ", device = " DPxMOD ", size = %zd\n",
6343de4cd12SGeorge Rokos            DPxPTR(&ExecModeVal), DPxPTR(ExecModePtr), cusize);
6353de4cd12SGeorge Rokos         CUDA_ERR_STRING(err);
6363de4cd12SGeorge Rokos         return NULL;
6373de4cd12SGeorge Rokos       }
6383de4cd12SGeorge Rokos 
6393de4cd12SGeorge Rokos       if (ExecModeVal < 0 || ExecModeVal > 1) {
6403de4cd12SGeorge Rokos         DP("Error wrong exec_mode value specified in cubin file: %d\n",
6413de4cd12SGeorge Rokos            ExecModeVal);
6423de4cd12SGeorge Rokos         return NULL;
6433de4cd12SGeorge Rokos       }
6443de4cd12SGeorge Rokos     } else {
6453de4cd12SGeorge Rokos       DP("Loading global exec_mode '%s' - symbol missing, using default value "
6463de4cd12SGeorge Rokos           "GENERIC (1)\n", ExecModeName);
6473de4cd12SGeorge Rokos       CUDA_ERR_STRING(err);
6483de4cd12SGeorge Rokos     }
6493de4cd12SGeorge Rokos 
6503de4cd12SGeorge Rokos     KernelsList.push_back(KernelTy(fun, ExecModeVal));
6513de4cd12SGeorge Rokos 
6523de4cd12SGeorge Rokos     __tgt_offload_entry entry = *e;
6533de4cd12SGeorge Rokos     entry.addr = (void *)&KernelsList.back();
6543de4cd12SGeorge Rokos     DeviceInfo.addOffloadEntry(device_id, entry);
6553de4cd12SGeorge Rokos   }
6563de4cd12SGeorge Rokos 
657e1c7a46dSGuansong Zhang   // send device environment data to the device
658e1c7a46dSGuansong Zhang   {
659e1c7a46dSGuansong Zhang     omptarget_device_environmentTy device_env;
660e1c7a46dSGuansong Zhang 
661e1c7a46dSGuansong Zhang     device_env.debug_level = 0;
662e1c7a46dSGuansong Zhang 
663e1c7a46dSGuansong Zhang #ifdef OMPTARGET_DEBUG
664e1c7a46dSGuansong Zhang     if (char *envStr = getenv("LIBOMPTARGET_DEVICE_RTL_DEBUG")) {
665e1c7a46dSGuansong Zhang       device_env.debug_level = std::stoi(envStr);
666e1c7a46dSGuansong Zhang     }
667e1c7a46dSGuansong Zhang #endif
668e1c7a46dSGuansong Zhang 
669e1c7a46dSGuansong Zhang     const char * device_env_Name="omptarget_device_environment";
670e1c7a46dSGuansong Zhang     CUdeviceptr device_env_Ptr;
671e1c7a46dSGuansong Zhang     size_t cusize;
672e1c7a46dSGuansong Zhang 
673e1c7a46dSGuansong Zhang     err = cuModuleGetGlobal(&device_env_Ptr, &cusize, cumod, device_env_Name);
674e1c7a46dSGuansong Zhang 
675e1c7a46dSGuansong Zhang     if (err == CUDA_SUCCESS) {
676e1c7a46dSGuansong Zhang       if ((size_t)cusize != sizeof(device_env)) {
677e1c7a46dSGuansong Zhang         DP("Global device_environment '%s' - size mismatch (%zu != %zu)\n",
678e1c7a46dSGuansong Zhang             device_env_Name, cusize, sizeof(int32_t));
679e1c7a46dSGuansong Zhang         CUDA_ERR_STRING(err);
680e1c7a46dSGuansong Zhang         return NULL;
681e1c7a46dSGuansong Zhang       }
682e1c7a46dSGuansong Zhang 
683e1c7a46dSGuansong Zhang       err = cuMemcpyHtoD(device_env_Ptr, &device_env, cusize);
684e1c7a46dSGuansong Zhang       if (err != CUDA_SUCCESS) {
685e1c7a46dSGuansong Zhang         DP("Error when copying data from host to device. Pointers: "
686e1c7a46dSGuansong Zhang             "host = " DPxMOD ", device = " DPxMOD ", size = %zu\n",
687e1c7a46dSGuansong Zhang             DPxPTR(&device_env), DPxPTR(device_env_Ptr), cusize);
688e1c7a46dSGuansong Zhang         CUDA_ERR_STRING(err);
689e1c7a46dSGuansong Zhang         return NULL;
690e1c7a46dSGuansong Zhang       }
691e1c7a46dSGuansong Zhang 
692e1c7a46dSGuansong Zhang       DP("Sending global device environment data %zu bytes\n", (size_t)cusize);
693e1c7a46dSGuansong Zhang     } else {
694e1c7a46dSGuansong Zhang       DP("Finding global device environment '%s' - symbol missing.\n", device_env_Name);
6954c6a098aSKazuaki Ishizaki       DP("Continue, considering this is a device RTL which does not accept environment setting.\n");
696e1c7a46dSGuansong Zhang     }
697e1c7a46dSGuansong Zhang   }
698e1c7a46dSGuansong Zhang 
6993de4cd12SGeorge Rokos   return DeviceInfo.getOffloadEntriesTable(device_id);
7003de4cd12SGeorge Rokos }
7013de4cd12SGeorge Rokos 
7021546d319SGeorge Rokos void *__tgt_rtl_data_alloc(int32_t device_id, int64_t size, void *hst_ptr) {
7033de4cd12SGeorge Rokos   if (size == 0) {
7043de4cd12SGeorge Rokos     return NULL;
7053de4cd12SGeorge Rokos   }
7063de4cd12SGeorge Rokos 
7073de4cd12SGeorge Rokos   // Set the context we are using.
7083de4cd12SGeorge Rokos   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[device_id]);
7093de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
7103de4cd12SGeorge Rokos     DP("Error while trying to set CUDA current context\n");
7113de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
7123de4cd12SGeorge Rokos     return NULL;
7133de4cd12SGeorge Rokos   }
7143de4cd12SGeorge Rokos 
7153de4cd12SGeorge Rokos   CUdeviceptr ptr;
7163de4cd12SGeorge Rokos   err = cuMemAlloc(&ptr, size);
7173de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
7183de4cd12SGeorge Rokos     DP("Error while trying to allocate %d\n", err);
7193de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
7203de4cd12SGeorge Rokos     return NULL;
7213de4cd12SGeorge Rokos   }
7223de4cd12SGeorge Rokos 
7233de4cd12SGeorge Rokos   void *vptr = (void *)ptr;
7243de4cd12SGeorge Rokos   return vptr;
7253de4cd12SGeorge Rokos }
7263de4cd12SGeorge Rokos 
7273de4cd12SGeorge Rokos int32_t __tgt_rtl_data_submit(int32_t device_id, void *tgt_ptr, void *hst_ptr,
728*03ff643dSShilei Tian                               int64_t size) {
72932ed2927SShilei Tian   __tgt_async_info async_info;
730*03ff643dSShilei Tian   int32_t rc = __tgt_rtl_data_submit_async(device_id, tgt_ptr, hst_ptr, size,
731*03ff643dSShilei Tian                                            &async_info);
73232ed2927SShilei Tian   if (rc != OFFLOAD_SUCCESS)
7333de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
7343de4cd12SGeorge Rokos 
73532ed2927SShilei Tian   return __tgt_rtl_synchronize(device_id, &async_info);
7363de4cd12SGeorge Rokos }
7373de4cd12SGeorge Rokos 
738*03ff643dSShilei Tian int32_t __tgt_rtl_data_submit_async(int32_t device_id, void *tgt_ptr,
739*03ff643dSShilei Tian                                     void *hst_ptr, int64_t size,
74032ed2927SShilei Tian                                     __tgt_async_info *async_info_ptr) {
741*03ff643dSShilei Tian   assert(async_info_ptr && "async_info_ptr is nullptr");
742*03ff643dSShilei Tian   return dataSubmit(device_id, tgt_ptr, hst_ptr, size, async_info_ptr);
743*03ff643dSShilei Tian }
74432ed2927SShilei Tian 
745*03ff643dSShilei Tian int32_t __tgt_rtl_data_retrieve(int32_t device_id, void *hst_ptr, void *tgt_ptr,
746*03ff643dSShilei Tian                                 int64_t size) {
74732ed2927SShilei Tian   __tgt_async_info async_info;
748*03ff643dSShilei Tian   int32_t rc = __tgt_rtl_data_retrieve_async(device_id, hst_ptr, tgt_ptr, size,
749*03ff643dSShilei Tian                                              &async_info);
75032ed2927SShilei Tian   if (rc != OFFLOAD_SUCCESS)
7513de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
7523de4cd12SGeorge Rokos 
75332ed2927SShilei Tian   return __tgt_rtl_synchronize(device_id, &async_info);
7543de4cd12SGeorge Rokos }
7553de4cd12SGeorge Rokos 
756*03ff643dSShilei Tian int32_t __tgt_rtl_data_retrieve_async(int32_t device_id, void *hst_ptr,
757*03ff643dSShilei Tian                                       void *tgt_ptr, int64_t size,
758*03ff643dSShilei Tian                                       __tgt_async_info *async_info_ptr) {
759*03ff643dSShilei Tian   assert(async_info_ptr && "async_info_ptr is nullptr");
760*03ff643dSShilei Tian   return dataRetrieve(device_id, hst_ptr, tgt_ptr, size, async_info_ptr);
761*03ff643dSShilei Tian }
762*03ff643dSShilei Tian 
7633de4cd12SGeorge Rokos int32_t __tgt_rtl_data_delete(int32_t device_id, void *tgt_ptr) {
7643de4cd12SGeorge Rokos   // Set the context we are using.
7653de4cd12SGeorge Rokos   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[device_id]);
7663de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
7673de4cd12SGeorge Rokos     DP("Error when setting CUDA context\n");
7683de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
7693de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
7703de4cd12SGeorge Rokos   }
7713de4cd12SGeorge Rokos 
7723de4cd12SGeorge Rokos   err = cuMemFree((CUdeviceptr)tgt_ptr);
7733de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
7743de4cd12SGeorge Rokos     DP("Error when freeing CUDA memory\n");
7753de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
7763de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
7773de4cd12SGeorge Rokos   }
7783de4cd12SGeorge Rokos   return OFFLOAD_SUCCESS;
7793de4cd12SGeorge Rokos }
7803de4cd12SGeorge Rokos 
7813de4cd12SGeorge Rokos int32_t __tgt_rtl_run_target_team_region(int32_t device_id, void *tgt_entry_ptr,
78232ed2927SShilei Tian                                          void **tgt_args,
78332ed2927SShilei Tian                                          ptrdiff_t *tgt_offsets,
78432ed2927SShilei Tian                                          int32_t arg_num, int32_t team_num,
78532ed2927SShilei Tian                                          int32_t thread_limit,
786*03ff643dSShilei Tian                                          uint64_t loop_tripcount) {
787*03ff643dSShilei Tian   __tgt_async_info async_info;
788*03ff643dSShilei Tian   int32_t rc = __tgt_rtl_run_target_team_region_async(
789*03ff643dSShilei Tian       device_id, tgt_entry_ptr, tgt_args, tgt_offsets, arg_num, team_num,
790*03ff643dSShilei Tian       thread_limit, loop_tripcount, &async_info);
791*03ff643dSShilei Tian   if (rc != OFFLOAD_SUCCESS)
792*03ff643dSShilei Tian     return OFFLOAD_FAIL;
793*03ff643dSShilei Tian 
794*03ff643dSShilei Tian   return __tgt_rtl_synchronize(device_id, &async_info);
795*03ff643dSShilei Tian }
796*03ff643dSShilei Tian 
797*03ff643dSShilei Tian int32_t __tgt_rtl_run_target_team_region_async(
798*03ff643dSShilei Tian     int32_t device_id, void *tgt_entry_ptr, void **tgt_args,
799*03ff643dSShilei Tian     ptrdiff_t *tgt_offsets, int32_t arg_num, int32_t team_num,
800*03ff643dSShilei Tian     int32_t thread_limit, uint64_t loop_tripcount,
80132ed2927SShilei Tian     __tgt_async_info *async_info) {
8023de4cd12SGeorge Rokos   // Set the context we are using.
8033de4cd12SGeorge Rokos   CUresult err = cuCtxSetCurrent(DeviceInfo.Contexts[device_id]);
8043de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
8053de4cd12SGeorge Rokos     DP("Error when setting CUDA context\n");
8063de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
8073de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
8083de4cd12SGeorge Rokos   }
8093de4cd12SGeorge Rokos 
8103de4cd12SGeorge Rokos   // All args are references.
8113de4cd12SGeorge Rokos   std::vector<void *> args(arg_num);
8121546d319SGeorge Rokos   std::vector<void *> ptrs(arg_num);
8133de4cd12SGeorge Rokos 
8141546d319SGeorge Rokos   for (int32_t i = 0; i < arg_num; ++i) {
8151546d319SGeorge Rokos     ptrs[i] = (void *)((intptr_t)tgt_args[i] + tgt_offsets[i]);
8161546d319SGeorge Rokos     args[i] = &ptrs[i];
8171546d319SGeorge Rokos   }
8183de4cd12SGeorge Rokos 
8193de4cd12SGeorge Rokos   KernelTy *KernelInfo = (KernelTy *)tgt_entry_ptr;
8203de4cd12SGeorge Rokos 
8213de4cd12SGeorge Rokos   int cudaThreadsPerBlock;
8223de4cd12SGeorge Rokos 
8233de4cd12SGeorge Rokos   if (thread_limit > 0) {
8243de4cd12SGeorge Rokos     cudaThreadsPerBlock = thread_limit;
8253de4cd12SGeorge Rokos     DP("Setting CUDA threads per block to requested %d\n", thread_limit);
8263de4cd12SGeorge Rokos     // Add master warp if necessary
8273de4cd12SGeorge Rokos     if (KernelInfo->ExecutionMode == GENERIC) {
8283de4cd12SGeorge Rokos       cudaThreadsPerBlock += DeviceInfo.WarpSize[device_id];
8293de4cd12SGeorge Rokos       DP("Adding master warp: +%d threads\n", DeviceInfo.WarpSize[device_id]);
8303de4cd12SGeorge Rokos     }
831c13df8e5SGeorge Rokos   } else {
832c13df8e5SGeorge Rokos     cudaThreadsPerBlock = DeviceInfo.NumThreads[device_id];
833c13df8e5SGeorge Rokos     DP("Setting CUDA threads per block to default %d\n",
834c13df8e5SGeorge Rokos         DeviceInfo.NumThreads[device_id]);
835c13df8e5SGeorge Rokos   }
8363de4cd12SGeorge Rokos 
8373de4cd12SGeorge Rokos   if (cudaThreadsPerBlock > DeviceInfo.ThreadsPerBlock[device_id]) {
8383de4cd12SGeorge Rokos     cudaThreadsPerBlock = DeviceInfo.ThreadsPerBlock[device_id];
8393de4cd12SGeorge Rokos     DP("Threads per block capped at device limit %d\n",
8403de4cd12SGeorge Rokos         DeviceInfo.ThreadsPerBlock[device_id]);
8413de4cd12SGeorge Rokos   }
8423de4cd12SGeorge Rokos 
8433de4cd12SGeorge Rokos   int kernel_limit;
8443de4cd12SGeorge Rokos   err = cuFuncGetAttribute(&kernel_limit,
8453de4cd12SGeorge Rokos       CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, KernelInfo->Func);
8463de4cd12SGeorge Rokos   if (err == CUDA_SUCCESS) {
8473de4cd12SGeorge Rokos     if (kernel_limit < cudaThreadsPerBlock) {
8483de4cd12SGeorge Rokos       cudaThreadsPerBlock = kernel_limit;
8493de4cd12SGeorge Rokos       DP("Threads per block capped at kernel limit %d\n", kernel_limit);
8503de4cd12SGeorge Rokos     }
8513de4cd12SGeorge Rokos   }
8523de4cd12SGeorge Rokos 
8533de4cd12SGeorge Rokos   int cudaBlocksPerGrid;
8543de4cd12SGeorge Rokos   if (team_num <= 0) {
8553de4cd12SGeorge Rokos     if (loop_tripcount > 0 && DeviceInfo.EnvNumTeams < 0) {
856c13df8e5SGeorge Rokos       if (KernelInfo->ExecutionMode == SPMD) {
857c13df8e5SGeorge Rokos         // We have a combined construct, i.e. `target teams distribute parallel
858c13df8e5SGeorge Rokos         // for [simd]`. We launch so many teams so that each thread will
859c13df8e5SGeorge Rokos         // execute one iteration of the loop.
8603de4cd12SGeorge Rokos         // round up to the nearest integer
8613de4cd12SGeorge Rokos         cudaBlocksPerGrid = ((loop_tripcount - 1) / cudaThreadsPerBlock) + 1;
862c13df8e5SGeorge Rokos       } else {
863c13df8e5SGeorge Rokos         // If we reach this point, then we have a non-combined construct, i.e.
864c13df8e5SGeorge Rokos         // `teams distribute` with a nested `parallel for` and each team is
865c13df8e5SGeorge Rokos         // assigned one iteration of the `distribute` loop. E.g.:
866c13df8e5SGeorge Rokos         //
867c13df8e5SGeorge Rokos         // #pragma omp target teams distribute
868c13df8e5SGeorge Rokos         // for(...loop_tripcount...) {
869c13df8e5SGeorge Rokos         //   #pragma omp parallel for
870c13df8e5SGeorge Rokos         //   for(...) {}
871c13df8e5SGeorge Rokos         // }
872c13df8e5SGeorge Rokos         //
873c13df8e5SGeorge Rokos         // Threads within a team will execute the iterations of the `parallel`
874c13df8e5SGeorge Rokos         // loop.
875c13df8e5SGeorge Rokos         cudaBlocksPerGrid = loop_tripcount;
876c13df8e5SGeorge Rokos       }
8773de4cd12SGeorge Rokos       DP("Using %d teams due to loop trip count %" PRIu64 " and number of "
8783de4cd12SGeorge Rokos           "threads per block %d\n", cudaBlocksPerGrid, loop_tripcount,
8793de4cd12SGeorge Rokos           cudaThreadsPerBlock);
8803de4cd12SGeorge Rokos     } else {
8813de4cd12SGeorge Rokos       cudaBlocksPerGrid = DeviceInfo.NumTeams[device_id];
8823de4cd12SGeorge Rokos       DP("Using default number of teams %d\n", DeviceInfo.NumTeams[device_id]);
8833de4cd12SGeorge Rokos     }
8843de4cd12SGeorge Rokos   } else if (team_num > DeviceInfo.BlocksPerGrid[device_id]) {
8853de4cd12SGeorge Rokos     cudaBlocksPerGrid = DeviceInfo.BlocksPerGrid[device_id];
8863de4cd12SGeorge Rokos     DP("Capping number of teams to team limit %d\n",
8873de4cd12SGeorge Rokos         DeviceInfo.BlocksPerGrid[device_id]);
8883de4cd12SGeorge Rokos   } else {
8893de4cd12SGeorge Rokos     cudaBlocksPerGrid = team_num;
8903de4cd12SGeorge Rokos     DP("Using requested number of teams %d\n", team_num);
8913de4cd12SGeorge Rokos   }
8923de4cd12SGeorge Rokos 
8933de4cd12SGeorge Rokos   // Run on the device.
8943de4cd12SGeorge Rokos   DP("Launch kernel with %d blocks and %d threads\n", cudaBlocksPerGrid,
8953de4cd12SGeorge Rokos      cudaThreadsPerBlock);
8963de4cd12SGeorge Rokos 
89732ed2927SShilei Tian   CUstream Stream = selectStream(device_id, async_info);
8983de4cd12SGeorge Rokos   err = cuLaunchKernel(KernelInfo->Func, cudaBlocksPerGrid, 1, 1,
899a5153dbcSJohannes Doerfert                        cudaThreadsPerBlock, 1, 1, 0 /*bytes of shared memory*/,
900a5153dbcSJohannes Doerfert                        Stream, &args[0], 0);
9013de4cd12SGeorge Rokos   if (err != CUDA_SUCCESS) {
9023de4cd12SGeorge Rokos     DP("Device kernel launch failed!\n");
9033de4cd12SGeorge Rokos     CUDA_ERR_STRING(err);
9043de4cd12SGeorge Rokos     return OFFLOAD_FAIL;
9053de4cd12SGeorge Rokos   }
9063de4cd12SGeorge Rokos 
9073de4cd12SGeorge Rokos   DP("Launch of entry point at " DPxMOD " successful!\n",
9083de4cd12SGeorge Rokos      DPxPTR(tgt_entry_ptr));
9093de4cd12SGeorge Rokos 
9103de4cd12SGeorge Rokos   return OFFLOAD_SUCCESS;
9113de4cd12SGeorge Rokos }
9123de4cd12SGeorge Rokos 
9133de4cd12SGeorge Rokos int32_t __tgt_rtl_run_target_region(int32_t device_id, void *tgt_entry_ptr,
91432ed2927SShilei Tian                                     void **tgt_args, ptrdiff_t *tgt_offsets,
915*03ff643dSShilei Tian                                     int32_t arg_num) {
916*03ff643dSShilei Tian   __tgt_async_info async_info;
917*03ff643dSShilei Tian   int32_t rc = __tgt_rtl_run_target_region_async(
918*03ff643dSShilei Tian       device_id, tgt_entry_ptr, tgt_args, tgt_offsets, arg_num, &async_info);
919*03ff643dSShilei Tian   if (rc != OFFLOAD_SUCCESS)
920*03ff643dSShilei Tian     return OFFLOAD_FAIL;
921*03ff643dSShilei Tian 
922*03ff643dSShilei Tian   return __tgt_rtl_synchronize(device_id, &async_info);
923*03ff643dSShilei Tian }
924*03ff643dSShilei Tian 
925*03ff643dSShilei Tian int32_t __tgt_rtl_run_target_region_async(int32_t device_id,
926*03ff643dSShilei Tian                                           void *tgt_entry_ptr, void **tgt_args,
927*03ff643dSShilei Tian                                           ptrdiff_t *tgt_offsets,
92832ed2927SShilei Tian                                           int32_t arg_num,
92932ed2927SShilei Tian                                           __tgt_async_info *async_info) {
9303de4cd12SGeorge Rokos   // use one team and the default number of threads.
9313de4cd12SGeorge Rokos   const int32_t team_num = 1;
9323de4cd12SGeorge Rokos   const int32_t thread_limit = 0;
933*03ff643dSShilei Tian   return __tgt_rtl_run_target_team_region_async(
934*03ff643dSShilei Tian       device_id, tgt_entry_ptr, tgt_args, tgt_offsets, arg_num, team_num,
93532ed2927SShilei Tian       thread_limit, 0, async_info);
93632ed2927SShilei Tian }
93732ed2927SShilei Tian 
93832ed2927SShilei Tian int32_t __tgt_rtl_synchronize(int32_t device_id, __tgt_async_info *async_info) {
93932ed2927SShilei Tian   assert(async_info && "async_info is nullptr");
94032ed2927SShilei Tian   assert(async_info->Queue && "async_info->Queue is nullptr");
94132ed2927SShilei Tian 
94232ed2927SShilei Tian   CUstream Stream = reinterpret_cast<CUstream>(async_info->Queue);
94332ed2927SShilei Tian   CUresult Err = cuStreamSynchronize(Stream);
94432ed2927SShilei Tian   if (Err != CUDA_SUCCESS) {
94532ed2927SShilei Tian     DP("Error when synchronizing stream. stream = " DPxMOD
94632ed2927SShilei Tian        ", async info ptr = " DPxMOD "\n",
94732ed2927SShilei Tian        DPxPTR(Stream), DPxPTR(async_info));
94832ed2927SShilei Tian     CUDA_ERR_STRING(Err);
94932ed2927SShilei Tian     return OFFLOAD_FAIL;
95032ed2927SShilei Tian   }
95132ed2927SShilei Tian   return OFFLOAD_SUCCESS;
9523de4cd12SGeorge Rokos }
9533de4cd12SGeorge Rokos 
9543de4cd12SGeorge Rokos #ifdef __cplusplus
9553de4cd12SGeorge Rokos }
9563de4cd12SGeorge Rokos #endif
957