[openmp] Introduce optional plugin init/deinit functions

Will allow plugins to migrate away from using global variables to
manage lifetime, which will fix a segfault discovered in relation to D127432

Reviewed By: jhuber6

Differential Revision: https://reviews.llvm.org/D130712

GitOrigin-RevId: 1f9d3974e444f95ddb600a6964ed14ded559e89c
diff --git a/libomptarget/include/omptargetplugin.h b/libomptarget/include/omptargetplugin.h
index 631678c..c036eb7 100644
--- a/libomptarget/include/omptargetplugin.h
+++ b/libomptarget/include/omptargetplugin.h
@@ -20,6 +20,12 @@
 extern "C" {
 #endif
 
+// First method called on the plugin
+int32_t __tgt_rtl_init_plugin();
+
+// Last method called on the plugin
+int32_t __tgt_rtl_deinit_plugin();
+
 // Return the number of available devices of the type supported by the
 // target RTL.
 int32_t __tgt_rtl_number_of_devices(void);
diff --git a/libomptarget/include/rtl.h b/libomptarget/include/rtl.h
index f2833a1..5e75539 100644
--- a/libomptarget/include/rtl.h
+++ b/libomptarget/include/rtl.h
@@ -25,6 +25,8 @@
 struct __tgt_bin_desc;
 
 struct RTLInfoTy {
+  typedef int32_t(init_plugin_ty)();
+  typedef int32_t(deinit_plugin_ty)();
   typedef int32_t(is_valid_binary_ty)(void *);
   typedef int32_t(is_valid_binary_info_ty)(void *, void *);
   typedef int32_t(is_data_exchangable_ty)(int32_t, int32_t);
@@ -82,6 +84,8 @@
 #endif
 
   // Functions implemented in the RTL.
+  init_plugin_ty *init_plugin = nullptr;
+  deinit_plugin_ty *deinit_plugin = nullptr;
   is_valid_binary_ty *is_valid_binary = nullptr;
   is_valid_binary_info_ty *is_valid_binary_info = nullptr;
   is_data_exchangable_ty *is_data_exchangable = nullptr;
diff --git a/libomptarget/plugins/amdgpu/src/rtl.cpp b/libomptarget/plugins/amdgpu/src/rtl.cpp
index 1bf4938..9b4bf41 100644
--- a/libomptarget/plugins/amdgpu/src/rtl.cpp
+++ b/libomptarget/plugins/amdgpu/src/rtl.cpp
@@ -2048,6 +2048,9 @@
   return true;
 }
 
+int32_t __tgt_rtl_init_plugin() { return OFFLOAD_SUCCESS; }
+int32_t __tgt_rtl_deinit_plugin() { return OFFLOAD_SUCCESS; }
+
 int __tgt_rtl_number_of_devices() {
   // If the construction failed, no methods are safe to call
   if (DeviceInfo.ConstructionSucceeded) {
diff --git a/libomptarget/plugins/exports b/libomptarget/plugins/exports
index fa6a7d9..84381af 100644
--- a/libomptarget/plugins/exports
+++ b/libomptarget/plugins/exports
@@ -1,5 +1,7 @@
 VERS1.0 {
   global:
+    __tgt_rtl_init_plugin;
+    __tgt_rtl_deinit_plugin;
     __tgt_rtl_is_valid_binary;
     __tgt_rtl_is_valid_binary_info;
     __tgt_rtl_is_data_exchangable;
diff --git a/libomptarget/src/rtl.cpp b/libomptarget/src/rtl.cpp
index 9d10712..9acf5f3 100644
--- a/libomptarget/src/rtl.cpp
+++ b/libomptarget/src/rtl.cpp
@@ -109,6 +109,17 @@
     // Retrieve the RTL information from the runtime library.
     RTLInfoTy &R = AllRTLs.back();
 
+    // Remove plugin on failure to call optional init_plugin
+    *((void **)&R.init_plugin) = dlsym(DynlibHandle, "__tgt_rtl_init_plugin");
+    if (R.init_plugin) {
+      int32_t Rc = R.init_plugin();
+      if (Rc != OFFLOAD_SUCCESS) {
+        DP("Unable to initialize library '%s': %u!\n", Name, Rc);
+        AllRTLs.pop_back();
+        continue;
+      }
+    }
+
     bool ValidPlugin = true;
 
     if (!(*((void **)&R.is_valid_binary) =
@@ -167,6 +178,8 @@
        R.NumberOfDevices);
 
     // Optional functions
+    *((void **)&R.deinit_plugin) =
+        dlsym(DynlibHandle, "__tgt_rtl_deinit_plugin");
     *((void **)&R.is_valid_binary_info) =
         dlsym(DynlibHandle, "__tgt_rtl_is_valid_binary_info");
     *((void **)&R.deinit_device) =
@@ -553,8 +566,14 @@
 
   PM->TblMapMtx.unlock();
 
-  // TODO: Remove RTL and the devices it manages if it's not used anymore?
   // TODO: Write some RTL->unload_image(...) function?
+  for (auto *R : UsedRTLs) {
+    if (R->deinit_plugin) {
+      if (R->deinit_plugin() != OFFLOAD_SUCCESS) {
+        DP("Failure deinitializing RTL %s!\n", R->RTLName.c_str());
+      }
+    }
+  }
 
   DP("Done unregistering library!\n");
 }