1 //===----------- rtl.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 handling RTL plugins.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "rtl.h"
14 #include "device.h"
15 #include "private.h"
16 
17 #include "llvm/Object/OffloadBinary.h"
18 
19 #include <cassert>
20 #include <cstdlib>
21 #include <cstring>
22 #include <dlfcn.h>
23 #include <mutex>
24 #include <string>
25 
26 // List of all plugins that can support offloading.
27 static const char *RTLNames[] = {
28     /* PowerPC target       */ "libomptarget.rtl.ppc64.so",
29     /* x86_64 target        */ "libomptarget.rtl.x86_64.so",
30     /* CUDA target          */ "libomptarget.rtl.cuda.so",
31     /* AArch64 target       */ "libomptarget.rtl.aarch64.so",
32     /* SX-Aurora VE target  */ "libomptarget.rtl.ve.so",
33     /* AMDGPU target        */ "libomptarget.rtl.amdgpu.so",
34     /* Remote target        */ "libomptarget.rtl.rpc.so",
35 };
36 
37 PluginManager *PM;
38 
39 #if OMPTARGET_PROFILE_ENABLED
40 static char *ProfileTraceFile = nullptr;
41 #endif
42 
43 __attribute__((constructor(101))) void init() {
44   DP("Init target library!\n");
45 
46   bool UseEventsForAtomicTransfers = true;
47   if (const char *ForceAtomicMap = getenv("LIBOMPTARGET_MAP_FORCE_ATOMIC")) {
48     std::string ForceAtomicMapStr(ForceAtomicMap);
49     if (ForceAtomicMapStr == "false" || ForceAtomicMapStr == "FALSE")
50       UseEventsForAtomicTransfers = false;
51     else if (ForceAtomicMapStr != "true" && ForceAtomicMapStr != "TRUE")
52       fprintf(stderr,
53               "Warning: 'LIBOMPTARGET_MAP_FORCE_ATOMIC' accepts only "
54               "'true'/'TRUE' or 'false'/'FALSE' as options, '%s' ignored\n",
55               ForceAtomicMap);
56   }
57 
58   PM = new PluginManager(UseEventsForAtomicTransfers);
59 
60 #ifdef OMPTARGET_PROFILE_ENABLED
61   ProfileTraceFile = getenv("LIBOMPTARGET_PROFILE");
62   // TODO: add a configuration option for time granularity
63   if (ProfileTraceFile)
64     llvm::timeTraceProfilerInitialize(500 /* us */, "libomptarget");
65 #endif
66 }
67 
68 __attribute__((destructor(101))) void deinit() {
69   DP("Deinit target library!\n");
70 
71   for (auto *R : PM->RTLs.UsedRTLs) {
72     // Plugins can either destroy their local state using global variables
73     // or attribute(destructor) functions or by implementing deinit_plugin
74     // The hazard with plugin local destructors is they may be called before
75     // or after this destructor. If the plugin is destroyed using global
76     // state before this library finishes calling into it the plugin is
77     // likely to crash. If good fortune means the plugin outlives this
78     // library then there may be no crash.
79     // Using deinit_plugin and no global destructors from the plugin works.
80     if (R->deinit_plugin) {
81       if (R->deinit_plugin() != OFFLOAD_SUCCESS) {
82         DP("Failure deinitializing RTL %s!\n", R->RTLName.c_str());
83       }
84     }
85   }
86 
87   delete PM;
88 
89 #ifdef OMPTARGET_PROFILE_ENABLED
90   if (ProfileTraceFile) {
91     // TODO: add env var for file output
92     if (auto E = llvm::timeTraceProfilerWrite(ProfileTraceFile, "-"))
93       fprintf(stderr, "Error writing out the time trace\n");
94 
95     llvm::timeTraceProfilerCleanup();
96   }
97 #endif
98 }
99 
100 void RTLsTy::loadRTLs() {
101   // Parse environment variable OMP_TARGET_OFFLOAD (if set)
102   PM->TargetOffloadPolicy =
103       (kmp_target_offload_kind_t)__kmpc_get_target_offload();
104   if (PM->TargetOffloadPolicy == tgt_disabled) {
105     return;
106   }
107 
108   DP("Loading RTLs...\n");
109 
110   // Attempt to open all the plugins and, if they exist, check if the interface
111   // is correct and if they are supporting any devices.
112   for (auto *Name : RTLNames) {
113     DP("Loading library '%s'...\n", Name);
114     void *DynlibHandle = dlopen(Name, RTLD_NOW);
115 
116     if (!DynlibHandle) {
117       // Library does not exist or cannot be found.
118       DP("Unable to load library '%s': %s!\n", Name, dlerror());
119       continue;
120     }
121 
122     DP("Successfully loaded library '%s'!\n", Name);
123 
124     AllRTLs.emplace_back();
125 
126     // Retrieve the RTL information from the runtime library.
127     RTLInfoTy &R = AllRTLs.back();
128 
129     // Remove plugin on failure to call optional init_plugin
130     *((void **)&R.init_plugin) = dlsym(DynlibHandle, "__tgt_rtl_init_plugin");
131     if (R.init_plugin) {
132       int32_t Rc = R.init_plugin();
133       if (Rc != OFFLOAD_SUCCESS) {
134         DP("Unable to initialize library '%s': %u!\n", Name, Rc);
135         AllRTLs.pop_back();
136         continue;
137       }
138     }
139 
140     bool ValidPlugin = true;
141 
142     if (!(*((void **)&R.is_valid_binary) =
143               dlsym(DynlibHandle, "__tgt_rtl_is_valid_binary")))
144       ValidPlugin = false;
145     if (!(*((void **)&R.number_of_devices) =
146               dlsym(DynlibHandle, "__tgt_rtl_number_of_devices")))
147       ValidPlugin = false;
148     if (!(*((void **)&R.init_device) =
149               dlsym(DynlibHandle, "__tgt_rtl_init_device")))
150       ValidPlugin = false;
151     if (!(*((void **)&R.load_binary) =
152               dlsym(DynlibHandle, "__tgt_rtl_load_binary")))
153       ValidPlugin = false;
154     if (!(*((void **)&R.data_alloc) =
155               dlsym(DynlibHandle, "__tgt_rtl_data_alloc")))
156       ValidPlugin = false;
157     if (!(*((void **)&R.data_submit) =
158               dlsym(DynlibHandle, "__tgt_rtl_data_submit")))
159       ValidPlugin = false;
160     if (!(*((void **)&R.data_retrieve) =
161               dlsym(DynlibHandle, "__tgt_rtl_data_retrieve")))
162       ValidPlugin = false;
163     if (!(*((void **)&R.data_delete) =
164               dlsym(DynlibHandle, "__tgt_rtl_data_delete")))
165       ValidPlugin = false;
166     if (!(*((void **)&R.run_region) =
167               dlsym(DynlibHandle, "__tgt_rtl_run_target_region")))
168       ValidPlugin = false;
169     if (!(*((void **)&R.run_team_region) =
170               dlsym(DynlibHandle, "__tgt_rtl_run_target_team_region")))
171       ValidPlugin = false;
172 
173     // Invalid plugin
174     if (!ValidPlugin) {
175       DP("Invalid plugin as necessary interface is not found.\n");
176       AllRTLs.pop_back();
177       continue;
178     }
179 
180     // No devices are supported by this RTL?
181     if (!(R.NumberOfDevices = R.number_of_devices())) {
182       // The RTL is invalid! Will pop the object from the RTLs list.
183       DP("No devices supported in this RTL\n");
184       AllRTLs.pop_back();
185       continue;
186     }
187 
188     R.LibraryHandler = DynlibHandle;
189 
190 #ifdef OMPTARGET_DEBUG
191     R.RTLName = Name;
192 #endif
193 
194     DP("Registering RTL %s supporting %d devices!\n", R.RTLName.c_str(),
195        R.NumberOfDevices);
196 
197     // Optional functions
198     *((void **)&R.deinit_plugin) =
199         dlsym(DynlibHandle, "__tgt_rtl_deinit_plugin");
200     *((void **)&R.is_valid_binary_info) =
201         dlsym(DynlibHandle, "__tgt_rtl_is_valid_binary_info");
202     *((void **)&R.deinit_device) =
203         dlsym(DynlibHandle, "__tgt_rtl_deinit_device");
204     *((void **)&R.init_requires) =
205         dlsym(DynlibHandle, "__tgt_rtl_init_requires");
206     *((void **)&R.data_submit_async) =
207         dlsym(DynlibHandle, "__tgt_rtl_data_submit_async");
208     *((void **)&R.data_retrieve_async) =
209         dlsym(DynlibHandle, "__tgt_rtl_data_retrieve_async");
210     *((void **)&R.run_region_async) =
211         dlsym(DynlibHandle, "__tgt_rtl_run_target_region_async");
212     *((void **)&R.run_team_region_async) =
213         dlsym(DynlibHandle, "__tgt_rtl_run_target_team_region_async");
214     *((void **)&R.synchronize) = dlsym(DynlibHandle, "__tgt_rtl_synchronize");
215     *((void **)&R.data_exchange) =
216         dlsym(DynlibHandle, "__tgt_rtl_data_exchange");
217     *((void **)&R.data_exchange_async) =
218         dlsym(DynlibHandle, "__tgt_rtl_data_exchange_async");
219     *((void **)&R.is_data_exchangable) =
220         dlsym(DynlibHandle, "__tgt_rtl_is_data_exchangable");
221     *((void **)&R.register_lib) = dlsym(DynlibHandle, "__tgt_rtl_register_lib");
222     *((void **)&R.unregister_lib) =
223         dlsym(DynlibHandle, "__tgt_rtl_unregister_lib");
224     *((void **)&R.supports_empty_images) =
225         dlsym(DynlibHandle, "__tgt_rtl_supports_empty_images");
226     *((void **)&R.set_info_flag) =
227         dlsym(DynlibHandle, "__tgt_rtl_set_info_flag");
228     *((void **)&R.print_device_info) =
229         dlsym(DynlibHandle, "__tgt_rtl_print_device_info");
230     *((void **)&R.create_event) = dlsym(DynlibHandle, "__tgt_rtl_create_event");
231     *((void **)&R.record_event) = dlsym(DynlibHandle, "__tgt_rtl_record_event");
232     *((void **)&R.wait_event) = dlsym(DynlibHandle, "__tgt_rtl_wait_event");
233     *((void **)&R.sync_event) = dlsym(DynlibHandle, "__tgt_rtl_sync_event");
234     *((void **)&R.destroy_event) =
235         dlsym(DynlibHandle, "__tgt_rtl_destroy_event");
236     *((void **)&R.release_async_info) =
237         dlsym(DynlibHandle, "__tgt_rtl_release_async_info");
238     *((void **)&R.init_async_info) =
239         dlsym(DynlibHandle, "__tgt_rtl_init_async_info");
240     *((void **)&R.init_device_info) =
241         dlsym(DynlibHandle, "__tgt_rtl_init_device_info");
242   }
243 
244   DP("RTLs loaded!\n");
245 
246   return;
247 }
248 
249 ////////////////////////////////////////////////////////////////////////////////
250 // Functionality for registering libs
251 
252 static void registerImageIntoTranslationTable(TranslationTable &TT,
253                                               RTLInfoTy &RTL,
254                                               __tgt_device_image *Image) {
255 
256   // same size, as when we increase one, we also increase the other.
257   assert(TT.TargetsTable.size() == TT.TargetsImages.size() &&
258          "We should have as many images as we have tables!");
259 
260   // Resize the Targets Table and Images to accommodate the new targets if
261   // required
262   unsigned TargetsTableMinimumSize = RTL.Idx + RTL.NumberOfDevices;
263 
264   if (TT.TargetsTable.size() < TargetsTableMinimumSize) {
265     TT.TargetsImages.resize(TargetsTableMinimumSize, 0);
266     TT.TargetsTable.resize(TargetsTableMinimumSize, 0);
267   }
268 
269   // Register the image in all devices for this target type.
270   for (int32_t I = 0; I < RTL.NumberOfDevices; ++I) {
271     // If we are changing the image we are also invalidating the target table.
272     if (TT.TargetsImages[RTL.Idx + I] != Image) {
273       TT.TargetsImages[RTL.Idx + I] = Image;
274       TT.TargetsTable[RTL.Idx + I] = 0; // lazy initialization of target table.
275     }
276   }
277 }
278 
279 ////////////////////////////////////////////////////////////////////////////////
280 // Functionality for registering Ctors/Dtors
281 
282 static void registerGlobalCtorsDtorsForImage(__tgt_bin_desc *Desc,
283                                              __tgt_device_image *Img,
284                                              RTLInfoTy *RTL) {
285 
286   for (int32_t I = 0; I < RTL->NumberOfDevices; ++I) {
287     DeviceTy &Device = *PM->Devices[RTL->Idx + I];
288     Device.PendingGlobalsMtx.lock();
289     Device.HasPendingGlobals = true;
290     for (__tgt_offload_entry *Entry = Img->EntriesBegin;
291          Entry != Img->EntriesEnd; ++Entry) {
292       if (Entry->flags & OMP_DECLARE_TARGET_CTOR) {
293         DP("Adding ctor " DPxMOD " to the pending list.\n",
294            DPxPTR(Entry->addr));
295         Device.PendingCtorsDtors[Desc].PendingCtors.push_back(Entry->addr);
296       } else if (Entry->flags & OMP_DECLARE_TARGET_DTOR) {
297         // Dtors are pushed in reverse order so they are executed from end
298         // to beginning when unregistering the library!
299         DP("Adding dtor " DPxMOD " to the pending list.\n",
300            DPxPTR(Entry->addr));
301         Device.PendingCtorsDtors[Desc].PendingDtors.push_front(Entry->addr);
302       }
303 
304       if (Entry->flags & OMP_DECLARE_TARGET_LINK) {
305         DP("The \"link\" attribute is not yet supported!\n");
306       }
307     }
308     Device.PendingGlobalsMtx.unlock();
309   }
310 }
311 
312 static __tgt_device_image getExecutableImage(__tgt_device_image *Image) {
313   llvm::StringRef ImageStr(static_cast<char *>(Image->ImageStart),
314                            static_cast<char *>(Image->ImageEnd) -
315                                static_cast<char *>(Image->ImageStart));
316   auto BinaryOrErr =
317       llvm::object::OffloadBinary::create(llvm::MemoryBufferRef(ImageStr, ""));
318   if (!BinaryOrErr) {
319     llvm::consumeError(BinaryOrErr.takeError());
320     return *Image;
321   }
322 
323   void *Begin = const_cast<void *>(
324       static_cast<const void *>((*BinaryOrErr)->getImage().bytes_begin()));
325   void *End = const_cast<void *>(
326       static_cast<const void *>((*BinaryOrErr)->getImage().bytes_end()));
327 
328   return {Begin, End, Image->EntriesBegin, Image->EntriesEnd};
329 }
330 
331 static __tgt_image_info getImageInfo(__tgt_device_image *Image) {
332   llvm::StringRef ImageStr(static_cast<char *>(Image->ImageStart),
333                            static_cast<char *>(Image->ImageEnd) -
334                                static_cast<char *>(Image->ImageStart));
335   auto BinaryOrErr =
336       llvm::object::OffloadBinary::create(llvm::MemoryBufferRef(ImageStr, ""));
337   if (!BinaryOrErr) {
338     llvm::consumeError(BinaryOrErr.takeError());
339     return __tgt_image_info{};
340   }
341 
342   return __tgt_image_info{(*BinaryOrErr)->getArch().data()};
343 }
344 
345 void RTLsTy::registerRequires(int64_t Flags) {
346   // TODO: add more elaborate check.
347   // Minimal check: only set requires flags if previous value
348   // is undefined. This ensures that only the first call to this
349   // function will set the requires flags. All subsequent calls
350   // will be checked for compatibility.
351   assert(Flags != OMP_REQ_UNDEFINED &&
352          "illegal undefined flag for requires directive!");
353   if (RequiresFlags == OMP_REQ_UNDEFINED) {
354     RequiresFlags = Flags;
355     return;
356   }
357 
358   // If multiple compilation units are present enforce
359   // consistency across all of them for require clauses:
360   //  - reverse_offload
361   //  - unified_address
362   //  - unified_shared_memory
363   if ((RequiresFlags & OMP_REQ_REVERSE_OFFLOAD) !=
364       (Flags & OMP_REQ_REVERSE_OFFLOAD)) {
365     FATAL_MESSAGE0(
366         1, "'#pragma omp requires reverse_offload' not used consistently!");
367   }
368   if ((RequiresFlags & OMP_REQ_UNIFIED_ADDRESS) !=
369       (Flags & OMP_REQ_UNIFIED_ADDRESS)) {
370     FATAL_MESSAGE0(
371         1, "'#pragma omp requires unified_address' not used consistently!");
372   }
373   if ((RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY) !=
374       (Flags & OMP_REQ_UNIFIED_SHARED_MEMORY)) {
375     FATAL_MESSAGE0(
376         1,
377         "'#pragma omp requires unified_shared_memory' not used consistently!");
378   }
379 
380   // TODO: insert any other missing checks
381 
382   DP("New requires flags %" PRId64 " compatible with existing %" PRId64 "!\n",
383      Flags, RequiresFlags);
384 }
385 
386 void RTLsTy::initRTLonce(RTLInfoTy &R) {
387   // If this RTL is not already in use, initialize it.
388   if (!R.IsUsed && R.NumberOfDevices != 0) {
389     // Initialize the device information for the RTL we are about to use.
390     const size_t Start = PM->Devices.size();
391     PM->Devices.reserve(Start + R.NumberOfDevices);
392     for (int32_t DeviceId = 0; DeviceId < R.NumberOfDevices; DeviceId++) {
393       PM->Devices.push_back(std::make_unique<DeviceTy>(&R));
394       // global device ID
395       PM->Devices[Start + DeviceId]->DeviceID = Start + DeviceId;
396       // RTL local device ID
397       PM->Devices[Start + DeviceId]->RTLDeviceID = DeviceId;
398     }
399 
400     // Initialize the index of this RTL and save it in the used RTLs.
401     R.Idx = (UsedRTLs.empty())
402                 ? 0
403                 : UsedRTLs.back()->Idx + UsedRTLs.back()->NumberOfDevices;
404     assert((size_t)R.Idx == Start &&
405            "RTL index should equal the number of devices used so far.");
406     R.IsUsed = true;
407     UsedRTLs.push_back(&R);
408 
409     DP("RTL " DPxMOD " has index %d!\n", DPxPTR(R.LibraryHandler), R.Idx);
410   }
411 }
412 
413 void RTLsTy::initAllRTLs() {
414   for (auto &R : AllRTLs)
415     initRTLonce(R);
416 }
417 
418 void RTLsTy::registerLib(__tgt_bin_desc *Desc) {
419   PM->RTLsMtx.lock();
420 
421   // Extract the exectuable image and extra information if availible.
422   for (int32_t i = 0; i < Desc->NumDeviceImages; ++i)
423     PM->Images.emplace_back(getExecutableImage(&Desc->DeviceImages[i]),
424                             getImageInfo(&Desc->DeviceImages[i]));
425 
426   // Register the images with the RTLs that understand them, if any.
427   for (auto &ImageAndInfo : PM->Images) {
428     // Obtain the image and information that was previously extracted.
429     __tgt_device_image *Img = &ImageAndInfo.first;
430     __tgt_image_info *Info = &ImageAndInfo.second;
431 
432     RTLInfoTy *FoundRTL = nullptr;
433 
434     // Scan the RTLs that have associated images until we find one that supports
435     // the current image.
436     for (auto &R : AllRTLs) {
437       if (R.is_valid_binary_info) {
438         if (!R.is_valid_binary_info(Img, Info)) {
439           DP("Image " DPxMOD " is NOT compatible with RTL %s!\n",
440              DPxPTR(Img->ImageStart), R.RTLName.c_str());
441           continue;
442         }
443       } else if (!R.is_valid_binary(Img)) {
444         DP("Image " DPxMOD " is NOT compatible with RTL %s!\n",
445            DPxPTR(Img->ImageStart), R.RTLName.c_str());
446         continue;
447       }
448 
449       DP("Image " DPxMOD " is compatible with RTL %s!\n",
450          DPxPTR(Img->ImageStart), R.RTLName.c_str());
451 
452       initRTLonce(R);
453 
454       // Initialize (if necessary) translation table for this library.
455       PM->TrlTblMtx.lock();
456       if (!PM->HostEntriesBeginToTransTable.count(Desc->HostEntriesBegin)) {
457         PM->HostEntriesBeginRegistrationOrder.push_back(Desc->HostEntriesBegin);
458         TranslationTable &TransTable =
459             (PM->HostEntriesBeginToTransTable)[Desc->HostEntriesBegin];
460         TransTable.HostTable.EntriesBegin = Desc->HostEntriesBegin;
461         TransTable.HostTable.EntriesEnd = Desc->HostEntriesEnd;
462       }
463 
464       // Retrieve translation table for this library.
465       TranslationTable &TransTable =
466           (PM->HostEntriesBeginToTransTable)[Desc->HostEntriesBegin];
467 
468       DP("Registering image " DPxMOD " with RTL %s!\n", DPxPTR(Img->ImageStart),
469          R.RTLName.c_str());
470       registerImageIntoTranslationTable(TransTable, R, Img);
471       PM->TrlTblMtx.unlock();
472       FoundRTL = &R;
473 
474       // Load ctors/dtors for static objects
475       registerGlobalCtorsDtorsForImage(Desc, Img, FoundRTL);
476 
477       // if an RTL was found we are done - proceed to register the next image
478       break;
479     }
480 
481     if (!FoundRTL) {
482       DP("No RTL found for image " DPxMOD "!\n", DPxPTR(Img->ImageStart));
483     }
484   }
485   PM->RTLsMtx.unlock();
486 
487   DP("Done registering entries!\n");
488 }
489 
490 void RTLsTy::unregisterLib(__tgt_bin_desc *Desc) {
491   DP("Unloading target library!\n");
492 
493   PM->RTLsMtx.lock();
494   // Find which RTL understands each image, if any.
495   for (auto &ImageAndInfo : PM->Images) {
496     // Obtain the image and information that was previously extracted.
497     __tgt_device_image *Img = &ImageAndInfo.first;
498     __tgt_image_info *Info = &ImageAndInfo.second;
499 
500     RTLInfoTy *FoundRTL = NULL;
501 
502     // Scan the RTLs that have associated images until we find one that supports
503     // the current image. We only need to scan RTLs that are already being used.
504     for (auto *R : UsedRTLs) {
505 
506       assert(R->IsUsed && "Expecting used RTLs.");
507 
508       if (R->is_valid_binary_info) {
509         if (!R->is_valid_binary_info(Img, Info)) {
510           DP("Image " DPxMOD " is NOT compatible with RTL %s!\n",
511              DPxPTR(Img->ImageStart), R->RTLName.c_str());
512           continue;
513         }
514       } else if (!R->is_valid_binary(Img)) {
515         DP("Image " DPxMOD " is NOT compatible with RTL %s!\n",
516            DPxPTR(Img->ImageStart), R->RTLName.c_str());
517         continue;
518       }
519 
520       DP("Image " DPxMOD " is compatible with RTL " DPxMOD "!\n",
521          DPxPTR(Img->ImageStart), DPxPTR(R->LibraryHandler));
522 
523       FoundRTL = R;
524 
525       // Execute dtors for static objects if the device has been used, i.e.
526       // if its PendingCtors list has been emptied.
527       for (int32_t I = 0; I < FoundRTL->NumberOfDevices; ++I) {
528         DeviceTy &Device = *PM->Devices[FoundRTL->Idx + I];
529         Device.PendingGlobalsMtx.lock();
530         if (Device.PendingCtorsDtors[Desc].PendingCtors.empty()) {
531           AsyncInfoTy AsyncInfo(Device);
532           for (auto &Dtor : Device.PendingCtorsDtors[Desc].PendingDtors) {
533             int Rc = target(nullptr, Device, Dtor, 0, nullptr, nullptr, nullptr,
534                             nullptr, nullptr, nullptr, 1, 1, 0, true /*team*/,
535                             AsyncInfo);
536             if (Rc != OFFLOAD_SUCCESS) {
537               DP("Running destructor " DPxMOD " failed.\n", DPxPTR(Dtor));
538             }
539           }
540           // Remove this library's entry from PendingCtorsDtors
541           Device.PendingCtorsDtors.erase(Desc);
542           // All constructors have been issued, wait for them now.
543           if (AsyncInfo.synchronize() != OFFLOAD_SUCCESS)
544             DP("Failed synchronizing destructors kernels.\n");
545         }
546         Device.PendingGlobalsMtx.unlock();
547       }
548 
549       DP("Unregistered image " DPxMOD " from RTL " DPxMOD "!\n",
550          DPxPTR(Img->ImageStart), DPxPTR(R->LibraryHandler));
551 
552       break;
553     }
554 
555     // if no RTL was found proceed to unregister the next image
556     if (!FoundRTL) {
557       DP("No RTLs in use support the image " DPxMOD "!\n",
558          DPxPTR(Img->ImageStart));
559     }
560   }
561   PM->RTLsMtx.unlock();
562   DP("Done unregistering images!\n");
563 
564   // Remove entries from PM->HostPtrToTableMap
565   PM->TblMapMtx.lock();
566   for (__tgt_offload_entry *Cur = Desc->HostEntriesBegin;
567        Cur < Desc->HostEntriesEnd; ++Cur) {
568     PM->HostPtrToTableMap.erase(Cur->addr);
569   }
570 
571   // Remove translation table for this descriptor.
572   auto TransTable =
573       PM->HostEntriesBeginToTransTable.find(Desc->HostEntriesBegin);
574   if (TransTable != PM->HostEntriesBeginToTransTable.end()) {
575     DP("Removing translation table for descriptor " DPxMOD "\n",
576        DPxPTR(Desc->HostEntriesBegin));
577     PM->HostEntriesBeginToTransTable.erase(TransTable);
578   } else {
579     DP("Translation table for descriptor " DPxMOD " cannot be found, probably "
580        "it has been already removed.\n",
581        DPxPTR(Desc->HostEntriesBegin));
582   }
583 
584   PM->TblMapMtx.unlock();
585 
586   // TODO: Write some RTL->unload_image(...) function?
587 
588   DP("Done unregistering library!\n");
589 }
590