1 //===----------- rtl.cpp - Target independent OpenMP target RTL -----------===//
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
7 //===----------------------------------------------------------------------===//
9 // Functionality for handling RTL plugins.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/Object/OffloadBinary.h"
15 #include "OmptCallback.h"
20 #include "Utilities.h"
29 using namespace llvm::sys
;
30 using namespace llvm::omp::target
;
32 // List of all plugins that can support offloading.
33 static const char *RTLNames
[] = {
34 /* PowerPC target */ "libomptarget.rtl.ppc64",
35 /* x86_64 target */ "libomptarget.rtl.x86_64",
36 /* CUDA target */ "libomptarget.rtl.cuda",
37 /* AArch64 target */ "libomptarget.rtl.aarch64",
38 /* AMDGPU target */ "libomptarget.rtl.amdgpu",
43 static char *ProfileTraceFile
= nullptr;
46 extern void ompt::connectLibrary();
49 __attribute__((constructor(101))) void init() {
50 DP("Init target library!\n");
52 bool UseEventsForAtomicTransfers
= true;
53 if (const char *ForceAtomicMap
= getenv("LIBOMPTARGET_MAP_FORCE_ATOMIC")) {
54 std::string
ForceAtomicMapStr(ForceAtomicMap
);
55 if (ForceAtomicMapStr
== "false" || ForceAtomicMapStr
== "FALSE")
56 UseEventsForAtomicTransfers
= false;
57 else if (ForceAtomicMapStr
!= "true" && ForceAtomicMapStr
!= "TRUE")
59 "Warning: 'LIBOMPTARGET_MAP_FORCE_ATOMIC' accepts only "
60 "'true'/'TRUE' or 'false'/'FALSE' as options, '%s' ignored\n",
64 PM
= new PluginManager(UseEventsForAtomicTransfers
);
66 ProfileTraceFile
= getenv("LIBOMPTARGET_PROFILE");
67 // TODO: add a configuration option for time granularity
69 timeTraceProfilerInitialize(500 /* us */, "libomptarget");
72 // Initialize OMPT first
73 ompt::connectLibrary();
77 PM
->registerDelayedLibraries();
80 __attribute__((destructor(101))) void deinit() {
81 DP("Deinit target library!\n");
84 if (ProfileTraceFile
) {
85 // TODO: add env var for file output
86 if (auto E
= timeTraceProfilerWrite(ProfileTraceFile
, "-"))
87 fprintf(stderr
, "Error writing out the time trace\n");
89 timeTraceProfilerCleanup();
93 void RTLsTy::loadRTLs() {
94 // Parse environment variable OMP_TARGET_OFFLOAD (if set)
95 PM
->TargetOffloadPolicy
=
96 (kmp_target_offload_kind_t
)__kmpc_get_target_offload();
97 if (PM
->TargetOffloadPolicy
== tgt_disabled
) {
101 DP("Loading RTLs...\n");
103 // Attempt to open all the plugins and, if they exist, check if the interface
104 // is correct and if they are supporting any devices.
105 for (const char *Name
: RTLNames
) {
106 AllRTLs
.emplace_back();
108 RTLInfoTy
&RTL
= AllRTLs
.back();
110 const std::string
BaseRTLName(Name
);
111 if (!attemptLoadRTL(BaseRTLName
+ ".so", RTL
))
115 DP("RTLs loaded!\n");
118 bool RTLsTy::attemptLoadRTL(const std::string
&RTLName
, RTLInfoTy
&RTL
) {
119 const char *Name
= RTLName
.c_str();
121 DP("Loading library '%s'...\n", Name
);
124 auto DynLibrary
= std::make_unique
<sys::DynamicLibrary
>(
125 sys::DynamicLibrary::getPermanentLibrary(Name
, &ErrMsg
));
127 if (!DynLibrary
->isValid()) {
128 // Library does not exist or cannot be found.
129 DP("Unable to load library '%s': %s!\n", Name
, ErrMsg
.c_str());
133 DP("Successfully loaded library '%s'!\n", Name
);
135 // Remove plugin on failure to call optional init_plugin
136 *((void **)&RTL
.init_plugin
) =
137 DynLibrary
->getAddressOfSymbol("__tgt_rtl_init_plugin");
138 if (RTL
.init_plugin
) {
139 int32_t Rc
= RTL
.init_plugin();
140 if (Rc
!= OFFLOAD_SUCCESS
) {
141 DP("Unable to initialize library '%s': %u!\n", Name
, Rc
);
146 bool ValidPlugin
= true;
148 if (!(*((void **)&RTL
.is_valid_binary
) =
149 DynLibrary
->getAddressOfSymbol("__tgt_rtl_is_valid_binary")))
151 if (!(*((void **)&RTL
.number_of_devices
) =
152 DynLibrary
->getAddressOfSymbol("__tgt_rtl_number_of_devices")))
154 if (!(*((void **)&RTL
.init_device
) =
155 DynLibrary
->getAddressOfSymbol("__tgt_rtl_init_device")))
157 if (!(*((void **)&RTL
.load_binary
) =
158 DynLibrary
->getAddressOfSymbol("__tgt_rtl_load_binary")))
160 if (!(*((void **)&RTL
.data_alloc
) =
161 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_alloc")))
163 if (!(*((void **)&RTL
.data_submit
) =
164 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_submit")))
166 if (!(*((void **)&RTL
.data_retrieve
) =
167 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_retrieve")))
169 if (!(*((void **)&RTL
.data_delete
) =
170 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_delete")))
172 if (!(*((void **)&RTL
.launch_kernel
) =
173 DynLibrary
->getAddressOfSymbol("__tgt_rtl_launch_kernel")))
178 DP("Invalid plugin as necessary interface is not found.\n");
182 // No devices are supported by this RTL?
183 if (!(RTL
.NumberOfDevices
= RTL
.number_of_devices())) {
184 // The RTL is invalid! Will pop the object from the RTLs list.
185 DP("No devices supported in this RTL\n");
189 #ifdef OMPTARGET_DEBUG
193 DP("Registering RTL %s supporting %d devices!\n", Name
, RTL
.NumberOfDevices
);
195 // Optional functions
196 *((void **)&RTL
.deinit_plugin
) =
197 DynLibrary
->getAddressOfSymbol("__tgt_rtl_deinit_plugin");
198 *((void **)&RTL
.is_valid_binary_info
) =
199 DynLibrary
->getAddressOfSymbol("__tgt_rtl_is_valid_binary_info");
200 *((void **)&RTL
.deinit_device
) =
201 DynLibrary
->getAddressOfSymbol("__tgt_rtl_deinit_device");
202 *((void **)&RTL
.init_requires
) =
203 DynLibrary
->getAddressOfSymbol("__tgt_rtl_init_requires");
204 *((void **)&RTL
.data_submit_async
) =
205 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_submit_async");
206 *((void **)&RTL
.data_retrieve_async
) =
207 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_retrieve_async");
208 *((void **)&RTL
.synchronize
) =
209 DynLibrary
->getAddressOfSymbol("__tgt_rtl_synchronize");
210 *((void **)&RTL
.query_async
) =
211 DynLibrary
->getAddressOfSymbol("__tgt_rtl_query_async");
212 *((void **)&RTL
.data_exchange
) =
213 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_exchange");
214 *((void **)&RTL
.data_exchange_async
) =
215 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_exchange_async");
216 *((void **)&RTL
.is_data_exchangable
) =
217 DynLibrary
->getAddressOfSymbol("__tgt_rtl_is_data_exchangable");
218 *((void **)&RTL
.register_lib
) =
219 DynLibrary
->getAddressOfSymbol("__tgt_rtl_register_lib");
220 *((void **)&RTL
.unregister_lib
) =
221 DynLibrary
->getAddressOfSymbol("__tgt_rtl_unregister_lib");
222 *((void **)&RTL
.supports_empty_images
) =
223 DynLibrary
->getAddressOfSymbol("__tgt_rtl_supports_empty_images");
224 *((void **)&RTL
.set_info_flag
) =
225 DynLibrary
->getAddressOfSymbol("__tgt_rtl_set_info_flag");
226 *((void **)&RTL
.print_device_info
) =
227 DynLibrary
->getAddressOfSymbol("__tgt_rtl_print_device_info");
228 *((void **)&RTL
.create_event
) =
229 DynLibrary
->getAddressOfSymbol("__tgt_rtl_create_event");
230 *((void **)&RTL
.record_event
) =
231 DynLibrary
->getAddressOfSymbol("__tgt_rtl_record_event");
232 *((void **)&RTL
.wait_event
) =
233 DynLibrary
->getAddressOfSymbol("__tgt_rtl_wait_event");
234 *((void **)&RTL
.sync_event
) =
235 DynLibrary
->getAddressOfSymbol("__tgt_rtl_sync_event");
236 *((void **)&RTL
.destroy_event
) =
237 DynLibrary
->getAddressOfSymbol("__tgt_rtl_destroy_event");
238 *((void **)&RTL
.release_async_info
) =
239 DynLibrary
->getAddressOfSymbol("__tgt_rtl_release_async_info");
240 *((void **)&RTL
.init_async_info
) =
241 DynLibrary
->getAddressOfSymbol("__tgt_rtl_init_async_info");
242 *((void **)&RTL
.init_device_info
) =
243 DynLibrary
->getAddressOfSymbol("__tgt_rtl_init_device_info");
244 *((void **)&RTL
.data_lock
) =
245 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_lock");
246 *((void **)&RTL
.data_unlock
) =
247 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_unlock");
248 *((void **)&RTL
.data_notify_mapped
) =
249 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_notify_mapped");
250 *((void **)&RTL
.data_notify_unmapped
) =
251 DynLibrary
->getAddressOfSymbol("__tgt_rtl_data_notify_unmapped");
252 *((void **)&RTL
.set_device_offset
) =
253 DynLibrary
->getAddressOfSymbol("__tgt_rtl_set_device_offset");
256 *((void **)&RTL
.activate_record_replay
) =
257 DynLibrary
->getAddressOfSymbol("__tgt_rtl_initialize_record_replay");
259 RTL
.LibraryHandler
= std::move(DynLibrary
);
261 // Successfully loaded
265 ////////////////////////////////////////////////////////////////////////////////
266 // Functionality for registering libs
268 static void registerImageIntoTranslationTable(TranslationTable
&TT
,
270 __tgt_device_image
*Image
) {
272 // same size, as when we increase one, we also increase the other.
273 assert(TT
.TargetsTable
.size() == TT
.TargetsImages
.size() &&
274 "We should have as many images as we have tables!");
276 // Resize the Targets Table and Images to accommodate the new targets if
278 unsigned TargetsTableMinimumSize
= RTL
.Idx
+ RTL
.NumberOfDevices
;
280 if (TT
.TargetsTable
.size() < TargetsTableMinimumSize
) {
281 TT
.TargetsImages
.resize(TargetsTableMinimumSize
, 0);
282 TT
.TargetsTable
.resize(TargetsTableMinimumSize
, 0);
285 // Register the image in all devices for this target type.
286 for (int32_t I
= 0; I
< RTL
.NumberOfDevices
; ++I
) {
287 // If we are changing the image we are also invalidating the target table.
288 if (TT
.TargetsImages
[RTL
.Idx
+ I
] != Image
) {
289 TT
.TargetsImages
[RTL
.Idx
+ I
] = Image
;
290 TT
.TargetsTable
[RTL
.Idx
+ I
] = 0; // lazy initialization of target table.
295 ////////////////////////////////////////////////////////////////////////////////
296 // Functionality for registering Ctors/Dtors
298 static void registerGlobalCtorsDtorsForImage(__tgt_bin_desc
*Desc
,
299 __tgt_device_image
*Img
,
302 for (int32_t I
= 0; I
< RTL
->NumberOfDevices
; ++I
) {
303 DeviceTy
&Device
= *PM
->Devices
[RTL
->Idx
+ I
];
304 Device
.PendingGlobalsMtx
.lock();
305 Device
.HasPendingGlobals
= true;
306 for (__tgt_offload_entry
*Entry
= Img
->EntriesBegin
;
307 Entry
!= Img
->EntriesEnd
; ++Entry
) {
308 // Globals are not callable and use a different set of flags.
309 if (Entry
->size
!= 0)
312 if (Entry
->flags
& OMP_DECLARE_TARGET_CTOR
) {
313 DP("Adding ctor " DPxMOD
" to the pending list.\n",
314 DPxPTR(Entry
->addr
));
315 Device
.PendingCtorsDtors
[Desc
].PendingCtors
.push_back(Entry
->addr
);
316 } else if (Entry
->flags
& OMP_DECLARE_TARGET_DTOR
) {
317 // Dtors are pushed in reverse order so they are executed from end
318 // to beginning when unregistering the library!
319 DP("Adding dtor " DPxMOD
" to the pending list.\n",
320 DPxPTR(Entry
->addr
));
321 Device
.PendingCtorsDtors
[Desc
].PendingDtors
.push_front(Entry
->addr
);
324 if (Entry
->flags
& OMP_DECLARE_TARGET_LINK
) {
325 DP("The \"link\" attribute is not yet supported!\n");
328 Device
.PendingGlobalsMtx
.unlock();
332 static __tgt_device_image
getExecutableImage(__tgt_device_image
*Image
) {
333 StringRef
ImageStr(static_cast<char *>(Image
->ImageStart
),
334 static_cast<char *>(Image
->ImageEnd
) -
335 static_cast<char *>(Image
->ImageStart
));
337 object::OffloadBinary::create(MemoryBufferRef(ImageStr
, ""));
339 consumeError(BinaryOrErr
.takeError());
343 void *Begin
= const_cast<void *>(
344 static_cast<const void *>((*BinaryOrErr
)->getImage().bytes_begin()));
345 void *End
= const_cast<void *>(
346 static_cast<const void *>((*BinaryOrErr
)->getImage().bytes_end()));
348 return {Begin
, End
, Image
->EntriesBegin
, Image
->EntriesEnd
};
351 static __tgt_image_info
getImageInfo(__tgt_device_image
*Image
) {
352 StringRef
ImageStr(static_cast<char *>(Image
->ImageStart
),
353 static_cast<char *>(Image
->ImageEnd
) -
354 static_cast<char *>(Image
->ImageStart
));
356 object::OffloadBinary::create(MemoryBufferRef(ImageStr
, ""));
358 consumeError(BinaryOrErr
.takeError());
359 return __tgt_image_info
{};
362 return __tgt_image_info
{(*BinaryOrErr
)->getArch().data()};
365 void RTLsTy::registerRequires(int64_t Flags
) {
366 // TODO: add more elaborate check.
367 // Minimal check: only set requires flags if previous value
368 // is undefined. This ensures that only the first call to this
369 // function will set the requires flags. All subsequent calls
370 // will be checked for compatibility.
371 assert(Flags
!= OMP_REQ_UNDEFINED
&&
372 "illegal undefined flag for requires directive!");
373 if (RequiresFlags
== OMP_REQ_UNDEFINED
) {
374 RequiresFlags
= Flags
;
378 // If multiple compilation units are present enforce
379 // consistency across all of them for require clauses:
382 // - unified_shared_memory
383 if ((RequiresFlags
& OMP_REQ_REVERSE_OFFLOAD
) !=
384 (Flags
& OMP_REQ_REVERSE_OFFLOAD
)) {
386 1, "'#pragma omp requires reverse_offload' not used consistently!");
388 if ((RequiresFlags
& OMP_REQ_UNIFIED_ADDRESS
) !=
389 (Flags
& OMP_REQ_UNIFIED_ADDRESS
)) {
391 1, "'#pragma omp requires unified_address' not used consistently!");
393 if ((RequiresFlags
& OMP_REQ_UNIFIED_SHARED_MEMORY
) !=
394 (Flags
& OMP_REQ_UNIFIED_SHARED_MEMORY
)) {
397 "'#pragma omp requires unified_shared_memory' not used consistently!");
400 // TODO: insert any other missing checks
402 DP("New requires flags %" PRId64
" compatible with existing %" PRId64
"!\n",
403 Flags
, RequiresFlags
);
406 void RTLsTy::initRTLonce(RTLInfoTy
&R
) {
407 // If this RTL is not already in use, initialize it.
408 if (!R
.IsUsed
&& R
.NumberOfDevices
!= 0) {
409 // Initialize the device information for the RTL we are about to use.
410 const size_t Start
= PM
->Devices
.size();
411 PM
->Devices
.reserve(Start
+ R
.NumberOfDevices
);
412 for (int32_t DeviceId
= 0; DeviceId
< R
.NumberOfDevices
; DeviceId
++) {
413 PM
->Devices
.push_back(std::make_unique
<DeviceTy
>(&R
));
415 PM
->Devices
[Start
+ DeviceId
]->DeviceID
= Start
+ DeviceId
;
416 // RTL local device ID
417 PM
->Devices
[Start
+ DeviceId
]->RTLDeviceID
= DeviceId
;
420 // Initialize the index of this RTL and save it in the used RTLs.
421 R
.Idx
= (UsedRTLs
.empty())
423 : UsedRTLs
.back()->Idx
+ UsedRTLs
.back()->NumberOfDevices
;
424 assert((size_t)R
.Idx
== Start
&&
425 "RTL index should equal the number of devices used so far.");
427 UsedRTLs
.push_back(&R
);
429 // If possible, set the device identifier offset
430 if (R
.set_device_offset
)
431 R
.set_device_offset(Start
);
433 DP("RTL " DPxMOD
" has index %d!\n", DPxPTR(R
.LibraryHandler
.get()), R
.Idx
);
437 void RTLsTy::initAllRTLs() {
438 for (auto &R
: AllRTLs
)
442 void RTLsTy::registerLib(__tgt_bin_desc
*Desc
) {
445 // Extract the exectuable image and extra information if availible.
446 for (int32_t i
= 0; i
< Desc
->NumDeviceImages
; ++i
)
447 PM
->Images
.emplace_back(getExecutableImage(&Desc
->DeviceImages
[i
]),
448 getImageInfo(&Desc
->DeviceImages
[i
]));
450 // Register the images with the RTLs that understand them, if any.
451 for (auto &ImageAndInfo
: PM
->Images
) {
452 // Obtain the image and information that was previously extracted.
453 __tgt_device_image
*Img
= &ImageAndInfo
.first
;
454 __tgt_image_info
*Info
= &ImageAndInfo
.second
;
456 RTLInfoTy
*FoundRTL
= nullptr;
458 // Scan the RTLs that have associated images until we find one that supports
459 // the current image.
460 for (auto &R
: AllRTLs
) {
461 if (R
.is_valid_binary_info
) {
462 if (!R
.is_valid_binary_info(Img
, Info
)) {
463 DP("Image " DPxMOD
" is NOT compatible with RTL %s!\n",
464 DPxPTR(Img
->ImageStart
), R
.RTLName
.c_str());
467 } else if (!R
.is_valid_binary(Img
)) {
468 DP("Image " DPxMOD
" is NOT compatible with RTL %s!\n",
469 DPxPTR(Img
->ImageStart
), R
.RTLName
.c_str());
473 DP("Image " DPxMOD
" is compatible with RTL %s!\n",
474 DPxPTR(Img
->ImageStart
), R
.RTLName
.c_str());
478 // Initialize (if necessary) translation table for this library.
479 PM
->TrlTblMtx
.lock();
480 if (!PM
->HostEntriesBeginToTransTable
.count(Desc
->HostEntriesBegin
)) {
481 PM
->HostEntriesBeginRegistrationOrder
.push_back(Desc
->HostEntriesBegin
);
482 TranslationTable
&TransTable
=
483 (PM
->HostEntriesBeginToTransTable
)[Desc
->HostEntriesBegin
];
484 TransTable
.HostTable
.EntriesBegin
= Desc
->HostEntriesBegin
;
485 TransTable
.HostTable
.EntriesEnd
= Desc
->HostEntriesEnd
;
488 // Retrieve translation table for this library.
489 TranslationTable
&TransTable
=
490 (PM
->HostEntriesBeginToTransTable
)[Desc
->HostEntriesBegin
];
492 DP("Registering image " DPxMOD
" with RTL %s!\n", DPxPTR(Img
->ImageStart
),
494 registerImageIntoTranslationTable(TransTable
, R
, Img
);
495 R
.UsedImages
.insert(Img
);
497 PM
->TrlTblMtx
.unlock();
500 // Load ctors/dtors for static objects
501 registerGlobalCtorsDtorsForImage(Desc
, Img
, FoundRTL
);
503 // if an RTL was found we are done - proceed to register the next image
508 DP("No RTL found for image " DPxMOD
"!\n", DPxPTR(Img
->ImageStart
));
511 PM
->RTLsMtx
.unlock();
513 DP("Done registering entries!\n");
516 void RTLsTy::unregisterLib(__tgt_bin_desc
*Desc
) {
517 DP("Unloading target library!\n");
520 // Find which RTL understands each image, if any.
521 for (auto &ImageAndInfo
: PM
->Images
) {
522 // Obtain the image and information that was previously extracted.
523 __tgt_device_image
*Img
= &ImageAndInfo
.first
;
525 RTLInfoTy
*FoundRTL
= NULL
;
527 // Scan the RTLs that have associated images until we find one that supports
528 // the current image. We only need to scan RTLs that are already being used.
529 for (auto *R
: UsedRTLs
) {
531 assert(R
->IsUsed
&& "Expecting used RTLs.");
533 // Ensure that we do not use any unused images associated with this RTL.
534 if (!R
->UsedImages
.contains(Img
))
539 // Execute dtors for static objects if the device has been used, i.e.
540 // if its PendingCtors list has been emptied.
541 for (int32_t I
= 0; I
< FoundRTL
->NumberOfDevices
; ++I
) {
542 DeviceTy
&Device
= *PM
->Devices
[FoundRTL
->Idx
+ I
];
543 Device
.PendingGlobalsMtx
.lock();
544 if (Device
.PendingCtorsDtors
[Desc
].PendingCtors
.empty()) {
545 AsyncInfoTy
AsyncInfo(Device
);
546 for (auto &Dtor
: Device
.PendingCtorsDtors
[Desc
].PendingDtors
) {
547 int Rc
= target(nullptr, Device
, Dtor
, CTorDTorKernelArgs
, AsyncInfo
);
548 if (Rc
!= OFFLOAD_SUCCESS
) {
549 DP("Running destructor " DPxMOD
" failed.\n", DPxPTR(Dtor
));
552 // Remove this library's entry from PendingCtorsDtors
553 Device
.PendingCtorsDtors
.erase(Desc
);
554 // All constructors have been issued, wait for them now.
555 if (AsyncInfo
.synchronize() != OFFLOAD_SUCCESS
)
556 DP("Failed synchronizing destructors kernels.\n");
558 Device
.PendingGlobalsMtx
.unlock();
561 DP("Unregistered image " DPxMOD
" from RTL " DPxMOD
"!\n",
562 DPxPTR(Img
->ImageStart
), DPxPTR(R
->LibraryHandler
.get()));
567 // if no RTL was found proceed to unregister the next image
569 DP("No RTLs in use support the image " DPxMOD
"!\n",
570 DPxPTR(Img
->ImageStart
));
573 PM
->RTLsMtx
.unlock();
574 DP("Done unregistering images!\n");
576 // Remove entries from PM->HostPtrToTableMap
577 PM
->TblMapMtx
.lock();
578 for (__tgt_offload_entry
*Cur
= Desc
->HostEntriesBegin
;
579 Cur
< Desc
->HostEntriesEnd
; ++Cur
) {
580 PM
->HostPtrToTableMap
.erase(Cur
->addr
);
583 // Remove translation table for this descriptor.
585 PM
->HostEntriesBeginToTransTable
.find(Desc
->HostEntriesBegin
);
586 if (TransTable
!= PM
->HostEntriesBeginToTransTable
.end()) {
587 DP("Removing translation table for descriptor " DPxMOD
"\n",
588 DPxPTR(Desc
->HostEntriesBegin
));
589 PM
->HostEntriesBeginToTransTable
.erase(TransTable
);
591 DP("Translation table for descriptor " DPxMOD
" cannot be found, probably "
592 "it has been already removed.\n",
593 DPxPTR(Desc
->HostEntriesBegin
));
596 PM
->TblMapMtx
.unlock();
598 // TODO: Write some RTL->unload_image(...) function?
599 for (auto *R
: UsedRTLs
) {
600 if (R
->deinit_plugin
) {
601 if (R
->deinit_plugin() != OFFLOAD_SUCCESS
) {
602 DP("Failure deinitializing RTL %s!\n", R
->RTLName
.c_str());
607 DP("Done unregistering library!\n");