1 //===--------- device.cpp - Target independent OpenMP target RTL ----------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Functionality for managing devices that are handled by RTL plugins.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "device.h"
14 #include "private.h"
15 #include "rtl.h"
16 
17 #include <cassert>
18 #include <climits>
19 #include <cstdio>
20 #include <string>
21 
22 DeviceTy::DeviceTy(const DeviceTy &D)
23     : DeviceID(D.DeviceID), RTL(D.RTL), RTLDeviceID(D.RTLDeviceID),
24       IsInit(D.IsInit), InitFlag(), HasPendingGlobals(D.HasPendingGlobals),
25       HostDataToTargetMap(D.HostDataToTargetMap),
26       PendingCtorsDtors(D.PendingCtorsDtors), ShadowPtrMap(D.ShadowPtrMap),
27       DataMapMtx(), PendingGlobalsMtx(), ShadowMtx(),
28       LoopTripCnt(D.LoopTripCnt) {}
29 
30 DeviceTy &DeviceTy::operator=(const DeviceTy &D) {
31   DeviceID = D.DeviceID;
32   RTL = D.RTL;
33   RTLDeviceID = D.RTLDeviceID;
34   IsInit = D.IsInit;
35   HasPendingGlobals = D.HasPendingGlobals;
36   HostDataToTargetMap = D.HostDataToTargetMap;
37   PendingCtorsDtors = D.PendingCtorsDtors;
38   ShadowPtrMap = D.ShadowPtrMap;
39   LoopTripCnt = D.LoopTripCnt;
40 
41   return *this;
42 }
43 
44 DeviceTy::DeviceTy(RTLInfoTy *RTL)
45     : DeviceID(-1), RTL(RTL), RTLDeviceID(-1), IsInit(false), InitFlag(),
46       HasPendingGlobals(false), HostDataToTargetMap(), PendingCtorsDtors(),
47       ShadowPtrMap(), DataMapMtx(), PendingGlobalsMtx(), ShadowMtx() {}
48 
49 DeviceTy::~DeviceTy() {
50   if (DeviceID == -1 || !(getInfoLevel() & OMP_INFOTYPE_DUMP_TABLE))
51     return;
52 
53   ident_t loc = {0, 0, 0, 0, ";libomptarget;libomptarget;0;0;;"};
54   dumpTargetPointerMappings(&loc, *this);
55 }
56 
57 int DeviceTy::associatePtr(void *HstPtrBegin, void *TgtPtrBegin, int64_t Size) {
58   DataMapMtx.lock();
59 
60   // Check if entry exists
61   auto search = HostDataToTargetMap.find(HstPtrBeginTy{(uintptr_t)HstPtrBegin});
62   if (search != HostDataToTargetMap.end()) {
63     // Mapping already exists
64     bool isValid = search->HstPtrEnd == (uintptr_t)HstPtrBegin + Size &&
65                    search->TgtPtrBegin == (uintptr_t)TgtPtrBegin;
66     DataMapMtx.unlock();
67     if (isValid) {
68       DP("Attempt to re-associate the same device ptr+offset with the same "
69          "host ptr, nothing to do\n");
70       return OFFLOAD_SUCCESS;
71     } else {
72       REPORT("Not allowed to re-associate a different device ptr+offset with "
73              "the same host ptr\n");
74       return OFFLOAD_FAIL;
75     }
76   }
77 
78   // Mapping does not exist, allocate it with refCount=INF
79   const HostDataToTargetTy &newEntry =
80       *HostDataToTargetMap
81            .emplace(
82                /*HstPtrBase=*/(uintptr_t)HstPtrBegin,
83                /*HstPtrBegin=*/(uintptr_t)HstPtrBegin,
84                /*HstPtrEnd=*/(uintptr_t)HstPtrBegin + Size,
85                /*TgtPtrBegin=*/(uintptr_t)TgtPtrBegin, /*Name=*/nullptr,
86                /*IsRefCountINF=*/true)
87            .first;
88   DP("Creating new map entry: HstBase=" DPxMOD ", HstBegin=" DPxMOD
89      ", HstEnd=" DPxMOD ", TgtBegin=" DPxMOD ", RefCount=%s\n",
90      DPxPTR(newEntry.HstPtrBase), DPxPTR(newEntry.HstPtrBegin),
91      DPxPTR(newEntry.HstPtrEnd), DPxPTR(newEntry.TgtPtrBegin),
92      newEntry.refCountToStr().c_str());
93 
94   DataMapMtx.unlock();
95 
96   return OFFLOAD_SUCCESS;
97 }
98 
99 int DeviceTy::disassociatePtr(void *HstPtrBegin) {
100   DataMapMtx.lock();
101 
102   auto search = HostDataToTargetMap.find(HstPtrBeginTy{(uintptr_t)HstPtrBegin});
103   if (search != HostDataToTargetMap.end()) {
104     // Mapping exists
105     if (search->isRefCountInf()) {
106       DP("Association found, removing it\n");
107       HostDataToTargetMap.erase(search);
108       DataMapMtx.unlock();
109       return OFFLOAD_SUCCESS;
110     } else {
111       REPORT("Trying to disassociate a pointer which was not mapped via "
112              "omp_target_associate_ptr\n");
113     }
114   }
115 
116   // Mapping not found
117   DataMapMtx.unlock();
118   REPORT("Association not found\n");
119   return OFFLOAD_FAIL;
120 }
121 
122 // Get ref count of map entry containing HstPtrBegin
123 uint64_t DeviceTy::getMapEntryRefCnt(void *HstPtrBegin) {
124   uintptr_t hp = (uintptr_t)HstPtrBegin;
125   uint64_t RefCnt = 0;
126 
127   DataMapMtx.lock();
128   if (!HostDataToTargetMap.empty()) {
129     auto upper = HostDataToTargetMap.upper_bound(hp);
130     if (upper != HostDataToTargetMap.begin()) {
131       upper--;
132       if (hp >= upper->HstPtrBegin && hp < upper->HstPtrEnd) {
133         DP("DeviceTy::getMapEntry: requested entry found\n");
134         RefCnt = upper->getRefCount();
135       }
136     }
137   }
138   DataMapMtx.unlock();
139 
140   if (RefCnt == 0) {
141     DP("DeviceTy::getMapEntry: requested entry not found\n");
142   }
143 
144   return RefCnt;
145 }
146 
147 LookupResult DeviceTy::lookupMapping(void *HstPtrBegin, int64_t Size) {
148   uintptr_t hp = (uintptr_t)HstPtrBegin;
149   LookupResult lr;
150 
151   DP("Looking up mapping(HstPtrBegin=" DPxMOD ", Size=%" PRId64 ")...\n",
152      DPxPTR(hp), Size);
153 
154   if (HostDataToTargetMap.empty())
155     return lr;
156 
157   auto upper = HostDataToTargetMap.upper_bound(hp);
158   // check the left bin
159   if (upper != HostDataToTargetMap.begin()) {
160     lr.Entry = std::prev(upper);
161     auto &HT = *lr.Entry;
162     // Is it contained?
163     lr.Flags.IsContained = hp >= HT.HstPtrBegin && hp < HT.HstPtrEnd &&
164                            (hp + Size) <= HT.HstPtrEnd;
165     // Does it extend beyond the mapped region?
166     lr.Flags.ExtendsAfter = hp < HT.HstPtrEnd && (hp + Size) > HT.HstPtrEnd;
167   }
168 
169   // check the right bin
170   if (!(lr.Flags.IsContained || lr.Flags.ExtendsAfter) &&
171       upper != HostDataToTargetMap.end()) {
172     lr.Entry = upper;
173     auto &HT = *lr.Entry;
174     // Does it extend into an already mapped region?
175     lr.Flags.ExtendsBefore =
176         hp < HT.HstPtrBegin && (hp + Size) > HT.HstPtrBegin;
177     // Does it extend beyond the mapped region?
178     lr.Flags.ExtendsAfter = hp < HT.HstPtrEnd && (hp + Size) > HT.HstPtrEnd;
179   }
180 
181   if (lr.Flags.ExtendsBefore) {
182     DP("WARNING: Pointer is not mapped but section extends into already "
183        "mapped data\n");
184   }
185   if (lr.Flags.ExtendsAfter) {
186     DP("WARNING: Pointer is already mapped but section extends beyond mapped "
187        "region\n");
188   }
189 
190   return lr;
191 }
192 
193 // Used by targetDataBegin
194 // Return the target pointer begin (where the data will be moved).
195 // Allocate memory if this is the first occurrence of this mapping.
196 // Increment the reference counter.
197 // If NULL is returned, then either data allocation failed or the user tried
198 // to do an illegal mapping.
199 void *DeviceTy::getOrAllocTgtPtr(void *HstPtrBegin, void *HstPtrBase,
200                                  int64_t Size, map_var_info_t HstPtrName,
201                                  bool &IsNew, bool &IsHostPtr, bool IsImplicit,
202                                  bool UpdateRefCount, bool HasCloseModifier,
203                                  bool HasPresentModifier) {
204   void *rc = NULL;
205   IsHostPtr = false;
206   IsNew = false;
207   DataMapMtx.lock();
208   LookupResult lr = lookupMapping(HstPtrBegin, Size);
209 
210   // Check if the pointer is contained.
211   // If a variable is mapped to the device manually by the user - which would
212   // lead to the IsContained flag to be true - then we must ensure that the
213   // device address is returned even under unified memory conditions.
214   if (lr.Flags.IsContained ||
215       ((lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) && IsImplicit)) {
216     auto &HT = *lr.Entry;
217     IsNew = false;
218     if (UpdateRefCount)
219       HT.incRefCount();
220     uintptr_t tp = HT.TgtPtrBegin + ((uintptr_t)HstPtrBegin - HT.HstPtrBegin);
221     INFO(OMP_INFOTYPE_MAPPING_EXISTS, DeviceID,
222          "Mapping exists%s with HstPtrBegin=" DPxMOD ", TgtPtrBegin=" DPxMOD
223          ", "
224          "Size=%" PRId64 ", RefCount=%s (%s), Name=%s\n",
225          (IsImplicit ? " (implicit)" : ""), DPxPTR(HstPtrBegin), DPxPTR(tp),
226          Size, HT.refCountToStr().c_str(),
227          UpdateRefCount ? "incremented" : "update suppressed",
228          (HstPtrName) ? getNameFromMapping(HstPtrName).c_str() : "unknown");
229     rc = (void *)tp;
230   } else if ((lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) && !IsImplicit) {
231     // Explicit extension of mapped data - not allowed.
232     MESSAGE("explicit extension not allowed: host address specified is " DPxMOD
233             " (%" PRId64
234             " bytes), but device allocation maps to host at " DPxMOD
235             " (%" PRId64 " bytes)",
236             DPxPTR(HstPtrBegin), Size, DPxPTR(lr.Entry->HstPtrBegin),
237             lr.Entry->HstPtrEnd - lr.Entry->HstPtrBegin);
238     if (HasPresentModifier)
239       MESSAGE("device mapping required by 'present' map type modifier does not "
240               "exist for host address " DPxMOD " (%" PRId64 " bytes)",
241               DPxPTR(HstPtrBegin), Size);
242   } else if (PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY &&
243              !HasCloseModifier) {
244     // If unified shared memory is active, implicitly mapped variables that are
245     // not privatized use host address. Any explicitly mapped variables also use
246     // host address where correctness is not impeded. In all other cases maps
247     // are respected.
248     // In addition to the mapping rules above, the close map modifier forces the
249     // mapping of the variable to the device.
250     if (Size) {
251       DP("Return HstPtrBegin " DPxMOD " Size=%" PRId64 " for unified shared "
252          "memory\n",
253          DPxPTR((uintptr_t)HstPtrBegin), Size);
254       IsHostPtr = true;
255       rc = HstPtrBegin;
256     }
257   } else if (HasPresentModifier) {
258     DP("Mapping required by 'present' map type modifier does not exist for "
259        "HstPtrBegin=" DPxMOD ", Size=%" PRId64 "\n",
260        DPxPTR(HstPtrBegin), Size);
261     MESSAGE("device mapping required by 'present' map type modifier does not "
262             "exist for host address " DPxMOD " (%" PRId64 " bytes)",
263             DPxPTR(HstPtrBegin), Size);
264   } else if (Size) {
265     // If it is not contained and Size > 0, we should create a new entry for it.
266     IsNew = true;
267     uintptr_t tp = (uintptr_t)allocData(Size, HstPtrBegin);
268     const HostDataToTargetTy &newEntry =
269         *HostDataToTargetMap
270              .emplace((uintptr_t)HstPtrBase, (uintptr_t)HstPtrBegin,
271                       (uintptr_t)HstPtrBegin + Size, tp, HstPtrName)
272              .first;
273     INFO(OMP_INFOTYPE_MAPPING_CHANGED, DeviceID,
274          "Creating new map entry with "
275          "HstPtrBegin=" DPxMOD ", TgtPtrBegin=" DPxMOD ", Size=%ld, "
276          "RefCount=%s, Name=%s\n",
277          DPxPTR(HstPtrBegin), DPxPTR(tp), Size,
278          newEntry.refCountToStr().c_str(),
279          (HstPtrName) ? getNameFromMapping(HstPtrName).c_str() : "unknown");
280     rc = (void *)tp;
281   }
282 
283   DataMapMtx.unlock();
284   return rc;
285 }
286 
287 // Used by targetDataBegin, targetDataEnd, targetDataUpdate and target.
288 // Return the target pointer begin (where the data will be moved).
289 // Decrement the reference counter if called from targetDataEnd.
290 void *DeviceTy::getTgtPtrBegin(void *HstPtrBegin, int64_t Size, bool &IsLast,
291                                bool UpdateRefCount, bool &IsHostPtr,
292                                bool MustContain, bool ForceDelete) {
293   void *rc = NULL;
294   IsHostPtr = false;
295   IsLast = false;
296   DataMapMtx.lock();
297   LookupResult lr = lookupMapping(HstPtrBegin, Size);
298 
299   if (lr.Flags.IsContained ||
300       (!MustContain && (lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter))) {
301     auto &HT = *lr.Entry;
302     // We do not decrement the reference count to zero here.  deallocTgtPtr does
303     // that atomically with removing the mapping.  Otherwise, before this thread
304     // removed the mapping in deallocTgtPtr, another thread could retrieve the
305     // mapping, increment and decrement back to zero, and then both threads
306     // would try to remove the mapping, resulting in a double free.
307     IsLast = HT.decShouldRemove(ForceDelete);
308     const char *RefCountAction;
309     if (!UpdateRefCount) {
310       RefCountAction = "update suppressed";
311     } else if (ForceDelete) {
312       HT.resetRefCount();
313       assert(IsLast == HT.decShouldRemove() &&
314              "expected correct IsLast prediction for reset");
315       if (IsLast)
316         RefCountAction = "reset, deferred final decrement";
317       else
318         RefCountAction = "reset";
319     } else if (IsLast) {
320       RefCountAction = "deferred final decrement";
321     } else {
322       RefCountAction = "decremented";
323       HT.decRefCount();
324     }
325     uintptr_t tp = HT.TgtPtrBegin + ((uintptr_t)HstPtrBegin - HT.HstPtrBegin);
326     INFO(OMP_INFOTYPE_MAPPING_EXISTS, DeviceID,
327          "Mapping exists with HstPtrBegin=" DPxMOD ", TgtPtrBegin=" DPxMOD ", "
328          "Size=%" PRId64 ", RefCount=%s (%s)\n",
329          DPxPTR(HstPtrBegin), DPxPTR(tp), Size, HT.refCountToStr().c_str(),
330          RefCountAction);
331     rc = (void *)tp;
332   } else if (PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY) {
333     // If the value isn't found in the mapping and unified shared memory
334     // is on then it means we have stumbled upon a value which we need to
335     // use directly from the host.
336     DP("Get HstPtrBegin " DPxMOD " Size=%" PRId64 " for unified shared "
337        "memory\n",
338        DPxPTR((uintptr_t)HstPtrBegin), Size);
339     IsHostPtr = true;
340     rc = HstPtrBegin;
341   }
342 
343   DataMapMtx.unlock();
344   return rc;
345 }
346 
347 // Return the target pointer begin (where the data will be moved).
348 // Lock-free version called when loading global symbols from the fat binary.
349 void *DeviceTy::getTgtPtrBegin(void *HstPtrBegin, int64_t Size) {
350   uintptr_t hp = (uintptr_t)HstPtrBegin;
351   LookupResult lr = lookupMapping(HstPtrBegin, Size);
352   if (lr.Flags.IsContained || lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) {
353     auto &HT = *lr.Entry;
354     uintptr_t tp = HT.TgtPtrBegin + (hp - HT.HstPtrBegin);
355     return (void *)tp;
356   }
357 
358   return NULL;
359 }
360 
361 int DeviceTy::deallocTgtPtr(void *HstPtrBegin, int64_t Size,
362                             bool HasCloseModifier) {
363   if (PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY &&
364       !HasCloseModifier)
365     return OFFLOAD_SUCCESS;
366   // Check if the pointer is contained in any sub-nodes.
367   int rc;
368   DataMapMtx.lock();
369   LookupResult lr = lookupMapping(HstPtrBegin, Size);
370   if (lr.Flags.IsContained || lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) {
371     auto &HT = *lr.Entry;
372     if (HT.decRefCount() == 0) {
373       DP("Deleting tgt data " DPxMOD " of size %" PRId64 "\n",
374          DPxPTR(HT.TgtPtrBegin), Size);
375       deleteData((void *)HT.TgtPtrBegin);
376       INFO(OMP_INFOTYPE_MAPPING_CHANGED, DeviceID,
377            "Removing map entry with HstPtrBegin=" DPxMOD ", TgtPtrBegin=" DPxMOD
378            ", Size=%" PRId64 ", Name=%s\n",
379            DPxPTR(HT.HstPtrBegin), DPxPTR(HT.TgtPtrBegin), Size,
380            (HT.HstPtrName) ? getNameFromMapping(HT.HstPtrName).c_str()
381                            : "unknown");
382       HostDataToTargetMap.erase(lr.Entry);
383     }
384     rc = OFFLOAD_SUCCESS;
385   } else {
386     REPORT("Section to delete (hst addr " DPxMOD ") does not exist in the"
387            " allocated memory\n",
388            DPxPTR(HstPtrBegin));
389     rc = OFFLOAD_FAIL;
390   }
391 
392   DataMapMtx.unlock();
393   return rc;
394 }
395 
396 /// Init device, should not be called directly.
397 void DeviceTy::init() {
398   // Make call to init_requires if it exists for this plugin.
399   if (RTL->init_requires)
400     RTL->init_requires(PM->RTLs.RequiresFlags);
401   int32_t Ret = RTL->init_device(RTLDeviceID);
402   if (Ret != OFFLOAD_SUCCESS)
403     return;
404 
405   IsInit = true;
406 }
407 
408 /// Thread-safe method to initialize the device only once.
409 int32_t DeviceTy::initOnce() {
410   std::call_once(InitFlag, &DeviceTy::init, this);
411 
412   // At this point, if IsInit is true, then either this thread or some other
413   // thread in the past successfully initialized the device, so we can return
414   // OFFLOAD_SUCCESS. If this thread executed init() via call_once() and it
415   // failed, return OFFLOAD_FAIL. If call_once did not invoke init(), it means
416   // that some other thread already attempted to execute init() and if IsInit
417   // is still false, return OFFLOAD_FAIL.
418   if (IsInit)
419     return OFFLOAD_SUCCESS;
420   else
421     return OFFLOAD_FAIL;
422 }
423 
424 // Load binary to device.
425 __tgt_target_table *DeviceTy::load_binary(void *Img) {
426   RTL->Mtx.lock();
427   __tgt_target_table *rc = RTL->load_binary(RTLDeviceID, Img);
428   RTL->Mtx.unlock();
429   return rc;
430 }
431 
432 void *DeviceTy::allocData(int64_t Size, void *HstPtr, int32_t Kind) {
433   return RTL->data_alloc(RTLDeviceID, Size, HstPtr, Kind);
434 }
435 
436 int32_t DeviceTy::deleteData(void *TgtPtrBegin) {
437   return RTL->data_delete(RTLDeviceID, TgtPtrBegin);
438 }
439 
440 // Submit data to device
441 int32_t DeviceTy::submitData(void *TgtPtrBegin, void *HstPtrBegin, int64_t Size,
442                              AsyncInfoTy &AsyncInfo) {
443   if (getInfoLevel() & OMP_INFOTYPE_DATA_TRANSFER) {
444     LookupResult LR = lookupMapping(HstPtrBegin, Size);
445     auto *HT = &*LR.Entry;
446 
447     INFO(OMP_INFOTYPE_DATA_TRANSFER, DeviceID,
448          "Copying data from host to device, HstPtr=" DPxMOD ", TgtPtr=" DPxMOD
449          ", Size=%" PRId64 ", Name=%s\n",
450          DPxPTR(HstPtrBegin), DPxPTR(TgtPtrBegin), Size,
451          (HT && HT->HstPtrName) ? getNameFromMapping(HT->HstPtrName).c_str()
452                                 : "unknown");
453   }
454 
455   if (!AsyncInfo || !RTL->data_submit_async || !RTL->synchronize)
456     return RTL->data_submit(RTLDeviceID, TgtPtrBegin, HstPtrBegin, Size);
457   else
458     return RTL->data_submit_async(RTLDeviceID, TgtPtrBegin, HstPtrBegin, Size,
459                                   AsyncInfo);
460 }
461 
462 // Retrieve data from device
463 int32_t DeviceTy::retrieveData(void *HstPtrBegin, void *TgtPtrBegin,
464                                int64_t Size, AsyncInfoTy &AsyncInfo) {
465   if (getInfoLevel() & OMP_INFOTYPE_DATA_TRANSFER) {
466     LookupResult LR = lookupMapping(HstPtrBegin, Size);
467     auto *HT = &*LR.Entry;
468     INFO(OMP_INFOTYPE_DATA_TRANSFER, DeviceID,
469          "Copying data from device to host, TgtPtr=" DPxMOD ", HstPtr=" DPxMOD
470          ", Size=%" PRId64 ", Name=%s\n",
471          DPxPTR(TgtPtrBegin), DPxPTR(HstPtrBegin), Size,
472          (HT && HT->HstPtrName) ? getNameFromMapping(HT->HstPtrName).c_str()
473                                 : "unknown");
474   }
475 
476   if (!RTL->data_retrieve_async || !RTL->synchronize)
477     return RTL->data_retrieve(RTLDeviceID, HstPtrBegin, TgtPtrBegin, Size);
478   else
479     return RTL->data_retrieve_async(RTLDeviceID, HstPtrBegin, TgtPtrBegin, Size,
480                                     AsyncInfo);
481 }
482 
483 // Copy data from current device to destination device directly
484 int32_t DeviceTy::dataExchange(void *SrcPtr, DeviceTy &DstDev, void *DstPtr,
485                                int64_t Size, AsyncInfoTy &AsyncInfo) {
486   if (!AsyncInfo || !RTL->data_exchange_async || !RTL->synchronize) {
487     assert(RTL->data_exchange && "RTL->data_exchange is nullptr");
488     return RTL->data_exchange(RTLDeviceID, SrcPtr, DstDev.RTLDeviceID, DstPtr,
489                               Size);
490   } else
491     return RTL->data_exchange_async(RTLDeviceID, SrcPtr, DstDev.RTLDeviceID,
492                                     DstPtr, Size, AsyncInfo);
493 }
494 
495 // Run region on device
496 int32_t DeviceTy::runRegion(void *TgtEntryPtr, void **TgtVarsPtr,
497                             ptrdiff_t *TgtOffsets, int32_t TgtVarsSize,
498                             AsyncInfoTy &AsyncInfo) {
499   if (!RTL->run_region || !RTL->synchronize)
500     return RTL->run_region(RTLDeviceID, TgtEntryPtr, TgtVarsPtr, TgtOffsets,
501                            TgtVarsSize);
502   else
503     return RTL->run_region_async(RTLDeviceID, TgtEntryPtr, TgtVarsPtr,
504                                  TgtOffsets, TgtVarsSize, AsyncInfo);
505 }
506 
507 // Run team region on device.
508 int32_t DeviceTy::runTeamRegion(void *TgtEntryPtr, void **TgtVarsPtr,
509                                 ptrdiff_t *TgtOffsets, int32_t TgtVarsSize,
510                                 int32_t NumTeams, int32_t ThreadLimit,
511                                 uint64_t LoopTripCount,
512                                 AsyncInfoTy &AsyncInfo) {
513   if (!RTL->run_team_region_async || !RTL->synchronize)
514     return RTL->run_team_region(RTLDeviceID, TgtEntryPtr, TgtVarsPtr,
515                                 TgtOffsets, TgtVarsSize, NumTeams, ThreadLimit,
516                                 LoopTripCount);
517   else
518     return RTL->run_team_region_async(RTLDeviceID, TgtEntryPtr, TgtVarsPtr,
519                                       TgtOffsets, TgtVarsSize, NumTeams,
520                                       ThreadLimit, LoopTripCount, AsyncInfo);
521 }
522 
523 // Whether data can be copied to DstDevice directly
524 bool DeviceTy::isDataExchangable(const DeviceTy &DstDevice) {
525   if (RTL != DstDevice.RTL || !RTL->is_data_exchangable)
526     return false;
527 
528   if (RTL->is_data_exchangable(RTLDeviceID, DstDevice.RTLDeviceID))
529     return (RTL->data_exchange != nullptr) ||
530            (RTL->data_exchange_async != nullptr);
531 
532   return false;
533 }
534 
535 int32_t DeviceTy::synchronize(AsyncInfoTy &AsyncInfo) {
536   if (RTL->synchronize)
537     return RTL->synchronize(RTLDeviceID, AsyncInfo);
538   return OFFLOAD_SUCCESS;
539 }
540 
541 /// Check whether a device has an associated RTL and initialize it if it's not
542 /// already initialized.
543 bool device_is_ready(int device_num) {
544   DP("Checking whether device %d is ready.\n", device_num);
545   // Devices.size() can only change while registering a new
546   // library, so try to acquire the lock of RTLs' mutex.
547   PM->RTLsMtx.lock();
548   size_t DevicesSize = PM->Devices.size();
549   PM->RTLsMtx.unlock();
550   if (DevicesSize <= (size_t)device_num) {
551     DP("Device ID  %d does not have a matching RTL\n", device_num);
552     return false;
553   }
554 
555   // Get device info
556   DeviceTy &Device = PM->Devices[device_num];
557 
558   DP("Is the device %d (local ID %d) initialized? %d\n", device_num,
559      Device.RTLDeviceID, Device.IsInit);
560 
561   // Init the device if not done before
562   if (!Device.IsInit && Device.initOnce() != OFFLOAD_SUCCESS) {
563     DP("Failed to init device %d\n", device_num);
564     return false;
565   }
566 
567   DP("Device %d is ready to use.\n", device_num);
568 
569   return true;
570 }
571