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 <string>
20 
21 /// Map between Device ID (i.e. openmp device id) and its DeviceTy.
22 DevicesTy Devices;
23 
24 int DeviceTy::associatePtr(void *HstPtrBegin, void *TgtPtrBegin, int64_t Size) {
25   DataMapMtx.lock();
26 
27   // Check if entry exists
28   for (auto &HT : HostDataToTargetMap) {
29     if ((uintptr_t)HstPtrBegin == HT.HstPtrBegin) {
30       // Mapping already exists
31       bool isValid = HT.HstPtrBegin == (uintptr_t) HstPtrBegin &&
32                      HT.HstPtrEnd == (uintptr_t) HstPtrBegin + Size &&
33                      HT.TgtPtrBegin == (uintptr_t) TgtPtrBegin;
34       DataMapMtx.unlock();
35       if (isValid) {
36         DP("Attempt to re-associate the same device ptr+offset with the same "
37             "host ptr, nothing to do\n");
38         return OFFLOAD_SUCCESS;
39       } else {
40         DP("Not allowed to re-associate a different device ptr+offset with the "
41             "same host ptr\n");
42         return OFFLOAD_FAIL;
43       }
44     }
45   }
46 
47   // Mapping does not exist, allocate it
48   HostDataToTargetTy newEntry;
49 
50   // Set up missing fields
51   newEntry.HstPtrBase = (uintptr_t) HstPtrBegin;
52   newEntry.HstPtrBegin = (uintptr_t) HstPtrBegin;
53   newEntry.HstPtrEnd = (uintptr_t) HstPtrBegin + Size;
54   newEntry.TgtPtrBegin = (uintptr_t) TgtPtrBegin;
55   // refCount must be infinite
56   newEntry.RefCount = INF_REF_CNT;
57 
58   DP("Creating new map entry: HstBase=" DPxMOD ", HstBegin=" DPxMOD ", HstEnd="
59       DPxMOD ", TgtBegin=" DPxMOD "\n", DPxPTR(newEntry.HstPtrBase),
60       DPxPTR(newEntry.HstPtrBegin), DPxPTR(newEntry.HstPtrEnd),
61       DPxPTR(newEntry.TgtPtrBegin));
62   HostDataToTargetMap.push_front(newEntry);
63 
64   DataMapMtx.unlock();
65 
66   return OFFLOAD_SUCCESS;
67 }
68 
69 int DeviceTy::disassociatePtr(void *HstPtrBegin) {
70   DataMapMtx.lock();
71 
72   // Check if entry exists
73   for (HostDataToTargetListTy::iterator ii = HostDataToTargetMap.begin();
74       ii != HostDataToTargetMap.end(); ++ii) {
75     if ((uintptr_t)HstPtrBegin == ii->HstPtrBegin) {
76       // Mapping exists
77       if (CONSIDERED_INF(ii->RefCount)) {
78         DP("Association found, removing it\n");
79         HostDataToTargetMap.erase(ii);
80         DataMapMtx.unlock();
81         return OFFLOAD_SUCCESS;
82       } else {
83         DP("Trying to disassociate a pointer which was not mapped via "
84             "omp_target_associate_ptr\n");
85         break;
86       }
87     }
88   }
89 
90   // Mapping not found
91   DataMapMtx.unlock();
92   DP("Association not found\n");
93   return OFFLOAD_FAIL;
94 }
95 
96 // Get ref count of map entry containing HstPtrBegin
97 long DeviceTy::getMapEntryRefCnt(void *HstPtrBegin) {
98   uintptr_t hp = (uintptr_t)HstPtrBegin;
99   long RefCnt = -1;
100 
101   DataMapMtx.lock();
102   for (auto &HT : HostDataToTargetMap) {
103     if (hp >= HT.HstPtrBegin && hp < HT.HstPtrEnd) {
104       DP("DeviceTy::getMapEntry: requested entry found\n");
105       RefCnt = HT.RefCount;
106       break;
107     }
108   }
109   DataMapMtx.unlock();
110 
111   if (RefCnt < 0) {
112     DP("DeviceTy::getMapEntry: requested entry not found\n");
113   }
114 
115   return RefCnt;
116 }
117 
118 LookupResult DeviceTy::lookupMapping(void *HstPtrBegin, int64_t Size) {
119   uintptr_t hp = (uintptr_t)HstPtrBegin;
120   LookupResult lr;
121 
122   DP("Looking up mapping(HstPtrBegin=" DPxMOD ", Size=%ld)...\n", DPxPTR(hp),
123       Size);
124   for (lr.Entry = HostDataToTargetMap.begin();
125       lr.Entry != HostDataToTargetMap.end(); ++lr.Entry) {
126     auto &HT = *lr.Entry;
127     // Is it contained?
128     lr.Flags.IsContained = hp >= HT.HstPtrBegin && hp < HT.HstPtrEnd &&
129         (hp+Size) <= HT.HstPtrEnd;
130     // Does it extend into an already mapped region?
131     lr.Flags.ExtendsBefore = hp < HT.HstPtrBegin && (hp+Size) > HT.HstPtrBegin;
132     // Does it extend beyond the mapped region?
133     lr.Flags.ExtendsAfter = hp < HT.HstPtrEnd && (hp+Size) > HT.HstPtrEnd;
134 
135     if (lr.Flags.IsContained || lr.Flags.ExtendsBefore ||
136         lr.Flags.ExtendsAfter) {
137       break;
138     }
139   }
140 
141   if (lr.Flags.ExtendsBefore) {
142     DP("WARNING: Pointer is not mapped but section extends into already "
143         "mapped data\n");
144   }
145   if (lr.Flags.ExtendsAfter) {
146     DP("WARNING: Pointer is already mapped but section extends beyond mapped "
147         "region\n");
148   }
149 
150   return lr;
151 }
152 
153 // Used by target_data_begin
154 // Return the target pointer begin (where the data will be moved).
155 // Allocate memory if this is the first occurrence if this mapping.
156 // Increment the reference counter.
157 // If NULL is returned, then either data allocation failed or the user tried
158 // to do an illegal mapping.
159 void *DeviceTy::getOrAllocTgtPtr(void *HstPtrBegin, void *HstPtrBase,
160     int64_t Size, bool &IsNew, bool IsImplicit, bool UpdateRefCount) {
161   void *rc = NULL;
162   DataMapMtx.lock();
163   LookupResult lr = lookupMapping(HstPtrBegin, Size);
164 
165   // Check if the pointer is contained.
166   if (lr.Flags.IsContained ||
167       ((lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) && IsImplicit)) {
168     auto &HT = *lr.Entry;
169     IsNew = false;
170 
171     if (UpdateRefCount)
172       ++HT.RefCount;
173 
174     uintptr_t tp = HT.TgtPtrBegin + ((uintptr_t)HstPtrBegin - HT.HstPtrBegin);
175     DP("Mapping exists%s with HstPtrBegin=" DPxMOD ", TgtPtrBegin=" DPxMOD ", "
176         "Size=%ld,%s RefCount=%s\n", (IsImplicit ? " (implicit)" : ""),
177         DPxPTR(HstPtrBegin), DPxPTR(tp), Size,
178         (UpdateRefCount ? " updated" : ""),
179         (CONSIDERED_INF(HT.RefCount)) ? "INF" :
180             std::to_string(HT.RefCount).c_str());
181     rc = (void *)tp;
182   } else if ((lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) && !IsImplicit) {
183     // Explicit extension of mapped data - not allowed.
184     DP("Explicit extension of mapping is not allowed.\n");
185   } else if (Size) {
186     // If it is not contained and Size > 0 we should create a new entry for it.
187     IsNew = true;
188     uintptr_t tp = (uintptr_t)RTL->data_alloc(RTLDeviceID, Size, HstPtrBegin);
189     DP("Creating new map entry: HstBase=" DPxMOD ", HstBegin=" DPxMOD ", "
190         "HstEnd=" DPxMOD ", TgtBegin=" DPxMOD "\n", DPxPTR(HstPtrBase),
191         DPxPTR(HstPtrBegin), DPxPTR((uintptr_t)HstPtrBegin + Size), DPxPTR(tp));
192     HostDataToTargetMap.push_front(HostDataToTargetTy((uintptr_t)HstPtrBase,
193         (uintptr_t)HstPtrBegin, (uintptr_t)HstPtrBegin + Size, tp));
194     rc = (void *)tp;
195   }
196 
197   DataMapMtx.unlock();
198   return rc;
199 }
200 
201 // Used by target_data_begin, target_data_end, target_data_update and target.
202 // Return the target pointer begin (where the data will be moved).
203 // Decrement the reference counter if called from target_data_end.
204 void *DeviceTy::getTgtPtrBegin(void *HstPtrBegin, int64_t Size, bool &IsLast,
205     bool UpdateRefCount) {
206   void *rc = NULL;
207   DataMapMtx.lock();
208   LookupResult lr = lookupMapping(HstPtrBegin, Size);
209 
210   if (lr.Flags.IsContained || lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) {
211     auto &HT = *lr.Entry;
212     IsLast = !(HT.RefCount > 1);
213 
214     if (HT.RefCount > 1 && UpdateRefCount)
215       --HT.RefCount;
216 
217     uintptr_t tp = HT.TgtPtrBegin + ((uintptr_t)HstPtrBegin - HT.HstPtrBegin);
218     DP("Mapping exists with HstPtrBegin=" DPxMOD ", TgtPtrBegin=" DPxMOD ", "
219         "Size=%ld,%s RefCount=%s\n", DPxPTR(HstPtrBegin), DPxPTR(tp), Size,
220         (UpdateRefCount ? " updated" : ""),
221         (CONSIDERED_INF(HT.RefCount)) ? "INF" :
222             std::to_string(HT.RefCount).c_str());
223     rc = (void *)tp;
224   } else {
225     IsLast = false;
226   }
227 
228   DataMapMtx.unlock();
229   return rc;
230 }
231 
232 // Return the target pointer begin (where the data will be moved).
233 // Lock-free version called when loading global symbols from the fat binary.
234 void *DeviceTy::getTgtPtrBegin(void *HstPtrBegin, int64_t Size) {
235   uintptr_t hp = (uintptr_t)HstPtrBegin;
236   LookupResult lr = lookupMapping(HstPtrBegin, Size);
237   if (lr.Flags.IsContained || lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) {
238     auto &HT = *lr.Entry;
239     uintptr_t tp = HT.TgtPtrBegin + (hp - HT.HstPtrBegin);
240     return (void *)tp;
241   }
242 
243   return NULL;
244 }
245 
246 int DeviceTy::deallocTgtPtr(void *HstPtrBegin, int64_t Size, bool ForceDelete) {
247   // Check if the pointer is contained in any sub-nodes.
248   int rc;
249   DataMapMtx.lock();
250   LookupResult lr = lookupMapping(HstPtrBegin, Size);
251   if (lr.Flags.IsContained || lr.Flags.ExtendsBefore || lr.Flags.ExtendsAfter) {
252     auto &HT = *lr.Entry;
253     if (ForceDelete)
254       HT.RefCount = 1;
255     if (--HT.RefCount <= 0) {
256       assert(HT.RefCount == 0 && "did not expect a negative ref count");
257       DP("Deleting tgt data " DPxMOD " of size %ld\n",
258           DPxPTR(HT.TgtPtrBegin), Size);
259       RTL->data_delete(RTLDeviceID, (void *)HT.TgtPtrBegin);
260       DP("Removing%s mapping with HstPtrBegin=" DPxMOD ", TgtPtrBegin=" DPxMOD
261           ", Size=%ld\n", (ForceDelete ? " (forced)" : ""),
262           DPxPTR(HT.HstPtrBegin), DPxPTR(HT.TgtPtrBegin), Size);
263       HostDataToTargetMap.erase(lr.Entry);
264     }
265     rc = OFFLOAD_SUCCESS;
266   } else {
267     DP("Section to delete (hst addr " DPxMOD ") does not exist in the allocated"
268        " memory\n", DPxPTR(HstPtrBegin));
269     rc = OFFLOAD_FAIL;
270   }
271 
272   DataMapMtx.unlock();
273   return rc;
274 }
275 
276 /// Init device, should not be called directly.
277 void DeviceTy::init() {
278   int32_t rc = RTL->init_device(RTLDeviceID);
279   if (rc == OFFLOAD_SUCCESS) {
280     IsInit = true;
281   }
282 }
283 
284 /// Thread-safe method to initialize the device only once.
285 int32_t DeviceTy::initOnce() {
286   std::call_once(InitFlag, &DeviceTy::init, this);
287 
288   // At this point, if IsInit is true, then either this thread or some other
289   // thread in the past successfully initialized the device, so we can return
290   // OFFLOAD_SUCCESS. If this thread executed init() via call_once() and it
291   // failed, return OFFLOAD_FAIL. If call_once did not invoke init(), it means
292   // that some other thread already attempted to execute init() and if IsInit
293   // is still false, return OFFLOAD_FAIL.
294   if (IsInit)
295     return OFFLOAD_SUCCESS;
296   else
297     return OFFLOAD_FAIL;
298 }
299 
300 // Load binary to device.
301 __tgt_target_table *DeviceTy::load_binary(void *Img) {
302   RTL->Mtx.lock();
303   __tgt_target_table *rc = RTL->load_binary(RTLDeviceID, Img);
304   RTL->Mtx.unlock();
305   return rc;
306 }
307 
308 // Submit data to device.
309 int32_t DeviceTy::data_submit(void *TgtPtrBegin, void *HstPtrBegin,
310     int64_t Size) {
311   return RTL->data_submit(RTLDeviceID, TgtPtrBegin, HstPtrBegin, Size);
312 }
313 
314 // Retrieve data from device.
315 int32_t DeviceTy::data_retrieve(void *HstPtrBegin, void *TgtPtrBegin,
316     int64_t Size) {
317   return RTL->data_retrieve(RTLDeviceID, HstPtrBegin, TgtPtrBegin, Size);
318 }
319 
320 // Run region on device
321 int32_t DeviceTy::run_region(void *TgtEntryPtr, void **TgtVarsPtr,
322     ptrdiff_t *TgtOffsets, int32_t TgtVarsSize) {
323   return RTL->run_region(RTLDeviceID, TgtEntryPtr, TgtVarsPtr, TgtOffsets,
324       TgtVarsSize);
325 }
326 
327 // Run team region on device.
328 int32_t DeviceTy::run_team_region(void *TgtEntryPtr, void **TgtVarsPtr,
329     ptrdiff_t *TgtOffsets, int32_t TgtVarsSize, int32_t NumTeams,
330     int32_t ThreadLimit, uint64_t LoopTripCount) {
331   return RTL->run_team_region(RTLDeviceID, TgtEntryPtr, TgtVarsPtr, TgtOffsets,
332       TgtVarsSize, NumTeams, ThreadLimit, LoopTripCount);
333 }
334 
335 /// Check whether a device has an associated RTL and initialize it if it's not
336 /// already initialized.
337 bool device_is_ready(int device_num) {
338   DP("Checking whether device %d is ready.\n", device_num);
339   // Devices.size() can only change while registering a new
340   // library, so try to acquire the lock of RTLs' mutex.
341   RTLsMtx.lock();
342   size_t Devices_size = Devices.size();
343   RTLsMtx.unlock();
344   if (Devices_size <= (size_t)device_num) {
345     DP("Device ID  %d does not have a matching RTL\n", device_num);
346     return false;
347   }
348 
349   // Get device info
350   DeviceTy &Device = Devices[device_num];
351 
352   DP("Is the device %d (local ID %d) initialized? %d\n", device_num,
353        Device.RTLDeviceID, Device.IsInit);
354 
355   // Init the device if not done before
356   if (!Device.IsInit && Device.initOnce() != OFFLOAD_SUCCESS) {
357     DP("Failed to init device %d\n", device_num);
358     return false;
359   }
360 
361   DP("Device %d is ready to use.\n", device_num);
362 
363   return true;
364 }
365