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