[llvm-branch-commits] [llvm] [offload][omp] Use liboffload queues for memory operations (PR #221278)

Alex Duran via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Sep 4 09:30:20 PDT 2026


https://github.com/adurang created https://github.com/llvm/llvm-project/pull/221278

None

>From 4c59b842d97a70b3dc01de1c4f836134ab5340ba Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 3 Sep 2026 07:43:13 -0700
Subject: [PATCH 01/14] [offload][omp] link libomptarget against liboffload

---
 offload/liboffload/exports             | 58 ++++++++++++++++++++++++++
 offload/liboffload/src/OffloadImpl.cpp | 12 ++++--
 offload/libomptarget/CMakeLists.txt    | 10 ++---
 offload/libomptarget/exports           |  6 +++
 offload/libomptarget/interface.cpp     |  3 ++
 5 files changed, 81 insertions(+), 8 deletions(-)

diff --git a/offload/liboffload/exports b/offload/liboffload/exports
index 168341aa7d938..5487e16168681 100644
--- a/offload/liboffload/exports
+++ b/offload/liboffload/exports
@@ -1,6 +1,64 @@
 VERS1.0 {
 global:
   ol*;
+  # Symbols currently used by libomptarget. They should be removed from here as
+  # they become replaced by calls to the liboffload API.
+  # No new symbols should be added here.
+  extern "C++" {
+    error::OffloadError::ID;
+    "error::OffloadErrCategory()";
+    "llvm::omp::target::RPCServerTy::registerCallback(unsigned int (*)(void*, unsigned int))";
+    "llvm::omp::target::plugin::GenericDeviceTy::dataFill(void*, void const*, long, long, __tgt_async_info*)";
+    "llvm::omp::target::plugin::GenericPluginTy::async_barrier(omp_interop_val_t*)";
+    "llvm::omp::target::plugin::GenericPluginTy::create_event(int, void**)";
+    "llvm::omp::target::plugin::GenericPluginTy::create_interop(int, int, interop_spec_t*)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_alloc(int, long, void*, int)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_delete(int, void*, int)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_exchange(int, void*, int, void*, long)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_exchange_async(int, void*, int, void*, long, __tgt_async_info*)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_fence(int, __tgt_async_info*)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_lock(int, void*, long, void**)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_notify_mapped(int, void*, long)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_notify_unmapped(int, void*)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_retrieve_async(int, void*, void*, long, __tgt_async_info*)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_submit(int, void*, void*, long)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_submit_async(int, void*, void*, long, __tgt_async_info*)";
+    "llvm::omp::target::plugin::GenericPluginTy::data_unlock(int, void*)";
+    "llvm::omp::target::plugin::GenericPluginTy::destroy_event(int, void*)";
+    "llvm::omp::target::plugin::GenericPluginTy::flush_queue(omp_interop_val_t*)";
+    "llvm::omp::target::plugin::GenericPluginTy::get_function(__tgt_device_binary, char const*, void**)";
+    "llvm::omp::target::plugin::GenericPluginTy::get_global(__tgt_device_binary, unsigned long, char const*, void**)";
+    "llvm::omp::target::plugin::GenericPluginTy::init()";
+    "llvm::omp::target::plugin::GenericPluginTy::deinit()";
+    "llvm::omp::target::plugin::GenericPluginTy::init_device(int)";
+    "llvm::omp::target::plugin::GenericPluginTy::initialize_record_replay(int, long, void*, bool, bool, bool, bool, char const*, char const*)";
+    "llvm::omp::target::plugin::GenericPluginTy::isDeviceCompatible(int, llvm::StringRef)";
+    "llvm::omp::target::plugin::GenericPluginTy::isPluginCompatible(llvm::StringRef)";
+    "llvm::omp::target::plugin::GenericPluginTy::is_accessible_ptr(int, void const*, unsigned long)";
+    "llvm::omp::target::plugin::GenericPluginTy::is_data_exchangable(int, int)";
+    "llvm::omp::target::plugin::GenericPluginTy::is_device_initialized(int) const";
+    "llvm::omp::target::plugin::GenericPluginTy::is_initialized() const";
+    "llvm::omp::target::plugin::GenericPluginTy::launch_kernel(int, void*, llvm::omp::target::plugin::KernelLaunchArgsTy&, __tgt_async_info*)";
+    "llvm::omp::target::plugin::GenericPluginTy::load_binary(int, __tgt_device_image*, __tgt_device_binary*)";
+    "llvm::omp::target::plugin::GenericPluginTy::number_of_devices()";
+    "llvm::omp::target::plugin::GenericPluginTy::obtain_device_info(int)";
+    "llvm::omp::target::plugin::GenericPluginTy::print_device_info(int)";
+    "llvm::omp::target::plugin::GenericPluginTy::query_async(int, __tgt_async_info*)";
+    "llvm::omp::target::plugin::GenericPluginTy::record_event(int, void*, __tgt_async_info*)";
+    "llvm::omp::target::plugin::GenericPluginTy::release_interop(int, omp_interop_val_t*)";
+    "llvm::omp::target::plugin::GenericPluginTy::set_device_identifier(int, int)";
+    "llvm::omp::target::plugin::GenericPluginTy::sync_barrier(omp_interop_val_t*)";
+    "llvm::omp::target::plugin::GenericPluginTy::sync_event(int, void*)";
+    "llvm::omp::target::plugin::GenericPluginTy::synchronize(int, __tgt_async_info*)";
+    "llvm::omp::target::plugin::GenericPluginTy::use_auto_zero_copy(int)";
+    "llvm::omp::target::plugin::GenericPluginTy::wait_event(int, void*, __tgt_async_info*)";
+    llvm::omp::target::ompt::Initialized;
+    llvm::omp::target::ompt::lookupCallbackByCode;
+    llvm::omp::target::ompt::lookupCallbackByName;
+    createPlugin_*;
+    # Temporary helpers to help transition of libomptarget to liboffload.
+    __ol_tgt_setInfoFlag;
+  };
 local:
   *;
 };
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 5c0c00f0165f1..6e608e3f965c4 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -29,9 +29,9 @@
 #ifdef OMPT_SUPPORT
 namespace llvm::omp::target {
 namespace ompt {
-bool Initialized = false;
-ompt_get_callback_t lookupCallbackByCode = nullptr;
-ompt_function_lookup_t lookupCallbackByName = nullptr;
+LLVM_ATTRIBUTE_WEAK bool Initialized = false;
+LLVM_ATTRIBUTE_WEAK ompt_get_callback_t lookupCallbackByCode = nullptr;
+LLVM_ATTRIBUTE_WEAK ompt_function_lookup_t lookupCallbackByName = nullptr;
 } // namespace ompt
 } // namespace llvm::omp::target
 #endif
@@ -1472,5 +1472,11 @@ Error olQueryQueue_impl(ol_queue_handle_t Queue, bool *IsQueueWorkCompleted) {
   return Error::success();
 }
 
+// Temporary helpers to help transition of libomptarget to liboffload
+extern "C" void __ol_tgt_setInfoFlag(uint32_t NewInfoLevel) {
+  std::atomic<uint32_t> &InfoLevel = getInfoLevelInternal();
+  InfoLevel.store(NewInfoLevel);
+}
+
 } // namespace offload
 } // namespace llvm
diff --git a/offload/libomptarget/CMakeLists.txt b/offload/libomptarget/CMakeLists.txt
index 403b3e5dad269..d0b24c4ff4e65 100644
--- a/offload/libomptarget/CMakeLists.txt
+++ b/offload/libomptarget/CMakeLists.txt
@@ -28,8 +28,12 @@ else()
   llvm_map_components_to_libnames(llvm_libs FrontendOpenMP Support Object)
 endif()
 target_link_libraries(omptarget PRIVATE omp ${llvm_libs})
+target_link_libraries(omptarget PRIVATE LLVMOffload)
 target_include_directories(omptarget PRIVATE
-  ${LIBOMPTARGET_INCLUDE_DIR} ${LIBOMPTARGET_BINARY_INCLUDE_DIR}
+  ${LIBOMPTARGET_INCLUDE_DIR}
+  ${LIBOMPTARGET_BINARY_INCLUDE_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/../plugins-nextgen/common/include
+  ${CMAKE_CURRENT_BINARY_DIR}/../plugins-nextgen/common/include
 )
 
 if(LLVM_HAVE_LINK_VERSION_SCRIPT)
@@ -42,10 +46,6 @@ target_compile_definitions(omptarget PRIVATE
   DEBUG_PREFIX="omptarget"
 )
 
-foreach(plugin IN LISTS LIBOMPTARGET_PLUGINS_TO_BUILD)
-  target_link_libraries(omptarget PRIVATE omptarget.rtl.${plugin})
-endforeach()
-
 target_compile_options(omptarget PRIVATE ${offload_compile_flags})
 target_link_options(omptarget PRIVATE ${offload_link_flags})
 
diff --git a/offload/libomptarget/exports b/offload/libomptarget/exports
index 1831c43cc5f29..6aaddc64cf508 100644
--- a/offload/libomptarget/exports
+++ b/offload/libomptarget/exports
@@ -83,6 +83,12 @@ VERS1.0 {
     __llvmPushCallConfiguration;
     __llvmPopCallConfiguration;
     llvmLaunchKernel;
+    # OMPT symbols that currently need to be seen by liboffload.
+    extern "C++" {
+      llvm::omp::target::ompt::Initialized;
+      llvm::omp::target::ompt::lookupCallbackByCode;
+      llvm::omp::target::ompt::lookupCallbackByName;
+    };
   local:
     *;
 };
diff --git a/offload/libomptarget/interface.cpp b/offload/libomptarget/interface.cpp
index 5d7d948711b99..5b5cc310b1b31 100644
--- a/offload/libomptarget/interface.cpp
+++ b/offload/libomptarget/interface.cpp
@@ -585,10 +585,13 @@ EXTERN void __tgt_push_mapper_component(void *RtMapperHandle, void *Base,
       MapComponentInfoTy(Base, Begin, Size, Type, Name));
 }
 
+extern "C" void __ol_tgt_setInfoFlag(uint32_t NewInfoLevel);
 EXTERN void __tgt_set_info_flag(uint32_t NewInfoLevel) {
   assert(PM && "Runtime not initialized");
   std::atomic<uint32_t> &InfoLevel = getInfoLevelInternal();
   InfoLevel.store(NewInfoLevel);
+  // Notify the liboffload runtime about the new info level.
+  __ol_tgt_setInfoFlag(NewInfoLevel);
 }
 
 EXTERN int __tgt_print_device_info(int64_t DeviceId) {

>From e09a0da04b33c18f988df09c13d60c9a797800bc Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 3 Sep 2026 10:32:33 -0700
Subject: [PATCH 02/14] [OFFLOAD] Initialize Platforms and Devices lazily

---
 offload/liboffload/src/OffloadImpl.cpp | 308 ++++++++++++++++++-------
 1 file changed, 226 insertions(+), 82 deletions(-)

diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 6e608e3f965c4..670179c21f054 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -56,9 +56,11 @@ struct ol_platform_impl_t {
   /// Initialize the associated plugin and devices.
   llvm::Error init();
 
-  /// Direct access to the plugin, may be uninitialized if accessed here.
+  bool Initialized = false;
   std::unique_ptr<GenericPluginTy> Plugin;
 
+  bool isInitialized() const { return Initialized; }
+
   llvm::SmallVector<std::unique_ptr<ol_device_impl_t>> Devices;
 };
 
@@ -66,20 +68,58 @@ struct ol_platform_impl_t {
 // we add some additional data here for now to avoid churn in the plugin
 // interface.
 struct ol_device_impl_t {
-  ol_device_impl_t(int DeviceNum, GenericDeviceTy *Device,
-                   ol_platform_impl_t &Platform, InfoTreeNode &&DevInfo)
-      : DeviceNum(DeviceNum), Device(Device), Platform(Platform),
-        Info(std::forward<InfoTreeNode>(DevInfo)) {}
-
+  ol_device_impl_t(int DeviceNum, ol_platform_impl_t &Platform)
+      : DeviceNum(DeviceNum), Platform(Platform) {}
   int DeviceNum;
-  GenericDeviceTy *Device;
   ol_platform_impl_t &Platform;
+
+  llvm::Error init() {
+    if (!Platform.isInitialized()) {
+      if (auto Err = Platform.init())
+        return Err;
+    }
+
+    if (llvm::Error Err = Platform.Plugin->initDevice(DeviceNum))
+      return Err;
+
+    Device = &Platform.Plugin->getDevice(DeviceNum);
+    llvm::Expected<InfoTreeNode> InfoOrErr = Device->obtainInfo();
+    if (!InfoOrErr)
+      return InfoOrErr.takeError();
+    Info = std::move(*InfoOrErr);
+
+    return llvm::Error::success();
+  }
+
+  llvm::Expected<GenericDeviceTy *> getDevice() {
+    if (!Device) {
+      if (llvm::Error Err = init())
+        return Err;
+    }
+
+    return Device;
+  }
+
+  llvm::Expected<InfoTreeNode &> getInfo() {
+    if (!Device) {
+      if (llvm::Error Err = init())
+        return Err;
+    }
+
+    return Info;
+  }
+private:
+  GenericDeviceTy *Device = nullptr;
   InfoTreeNode Info;
 };
 
 llvm::Error ol_platform_impl_t::destroy() { return Plugin->deinit(); }
 
 llvm::Error ol_platform_impl_t::init() {
+  if (Initialized)
+    return llvm::Error::success();
+  Initialized = true;
+
   if (!Plugin)
     return llvm::Error::success();
 
@@ -87,15 +127,7 @@ llvm::Error ol_platform_impl_t::init() {
     return Err;
 
   for (auto Id = 0, End = Plugin->getNumDevices(); Id != End; Id++) {
-    if (llvm::Error Err = Plugin->initDevice(Id))
-      return Err;
-
-    GenericDeviceTy *Device = &Plugin->getDevice(Id);
-    llvm::Expected<InfoTreeNode> Info = Device->obtainInfo();
-    if (llvm::Error Err = Info.takeError())
-      return Err;
-    Devices.emplace_back(std::make_unique<ol_device_impl_t>(Id, Device, *this,
-                                                            std::move(*Info)));
+    Devices.emplace_back(std::make_unique<ol_device_impl_t>(Id, *this));
   }
 
   return llvm::Error::success();
@@ -189,10 +221,12 @@ struct ol_context_impl_t {
       return nullptr;
     auto &Bucket = It->second;
 
+    GenericDeviceTy *DeviceImpl = llvm::cantFail(Device->getDevice());
+
     // As queues are pulled and popped from this list, longer running queues
     // naturally bubble to the start of the array. Hence looping backwards.
     for (auto Q = Bucket.rbegin(); Q != Bucket.rend(); Q++) {
-      if (!Device->Device->hasPendingWork(*Q)) {
+      if (!DeviceImpl->hasPendingWork(*Q)) {
         auto OutstandingQueue = *Q;
         *Q = Bucket.back();
         Bucket.pop_back();
@@ -214,8 +248,13 @@ struct ol_context_impl_t {
     llvm::Error Result = Plugin::success();
     for (auto &Bucket : OutstandingQueues) {
       auto *Device = Bucket.first;
+      auto DeviceOrErr = Device->getDevice();
+      if (!DeviceOrErr) {
+        Result = llvm::joinErrors(std::move(Result), DeviceOrErr.takeError());
+        continue;
+      }
       for (auto *AI : Bucket.second)
-        if (auto Err = Device->Device->synchronize(AI, /*Release=*/true))
+        if (auto Err = (*DeviceOrErr)->synchronize(AI, /*Release=*/true))
           Result = llvm::joinErrors(std::move(Result), std::move(Err));
     }
     OutstandingQueues.clear();
@@ -326,14 +365,6 @@ Error initPlugins(OffloadContext &Context, const ol_init_args_t *InitArgs) {
   } while (false);
 #include "Shared/Targets.def"
 
-  // Eagerly initialize all of the plugins and devices. We need to make sure
-  // that the platform is initialized at a consistent point to maintain the
-  // expected teardown order in the vendor libraries.
-  for (auto &Platform : Context.Platforms) {
-    if (Error Err = Platform->init())
-      return Err;
-  }
-
   Context.TracingEnabled = std::getenv("OFFLOAD_TRACE");
   Context.ValidationEnabled = !std::getenv("OFFLOAD_DISABLE_VALIDATION");
 
@@ -378,7 +409,8 @@ Error olShutDown_impl() {
 
   for (auto &Platform : OldContext->Platforms) {
     // Host plugin is nullptr and has no deinit
-    if (!Platform->Plugin || !Platform->Plugin->is_initialized())
+    if (!Platform->isInitialized() || !Platform->Plugin ||
+        !Platform->Plugin->is_initialized())
       continue;
 
     if (auto Res = Platform->destroy())
@@ -476,8 +508,11 @@ Error olGetDeviceInfoImplDetail(ol_device_handle_t Device,
   // AMD doesn't provide the global memory size (trivially) with the device info
   // struct, so use the plugin interface
   case OL_DEVICE_INFO_GLOBAL_MEM_SIZE: {
+    auto DeviceOrErr = Device->getDevice();
+    if (!DeviceOrErr)
+      return DeviceOrErr.takeError();
     uint64_t Mem;
-    if (auto Err = Device->Device->getDeviceMemorySize(Mem))
+    if (auto Err = (*DeviceOrErr)->getDeviceMemorySize(Mem))
       return Err;
     return Info.write<uint64_t>(Mem);
   } break;
@@ -490,7 +525,11 @@ Error olGetDeviceInfoImplDetail(ol_device_handle_t Device,
     return createOffloadError(ErrorCode::INVALID_ENUMERATION,
                               "getDeviceInfo enum '%i' is invalid", PropName);
 
-  auto EntryOpt = Device->Info.get(static_cast<DeviceInfo>(PropName));
+  auto InfoOrErr = Device->getInfo();
+  if (!InfoOrErr)
+    return InfoOrErr.takeError();
+
+  auto EntryOpt = InfoOrErr->get(static_cast<DeviceInfo>(PropName));
   if (!EntryOpt)
     return makeError(ErrorCode::UNIMPLEMENTED,
                      "plugin did not provide a response for this information");
@@ -603,6 +642,8 @@ Error olGetDeviceInfoSize_impl(ol_device_handle_t Device,
 
 Error olIterateDevices_impl(ol_device_iterate_cb_t Callback, void *UserData) {
   for (auto &Platform : OffloadContext::get().Platforms) {
+    if (auto Err = Platform->init())
+      return Err;
     for (auto &Device : Platform->Devices) {
       if (!Callback(Device.get(), UserData)) {
         return Error::success();
@@ -626,14 +667,20 @@ Error olCreateContext_impl(size_t DevicesCount, ol_device_handle_t *Devices,
           ErrorCode::INVALID_DEVICE,
           "all devices in a context must belong to the same platform");
     DeviceList.push_back(Devices[I]);
-    PluginDevices.push_back(Devices[I]->Device);
+    auto DeviceOrErr = Devices[I]->getDevice();
+    if (!DeviceOrErr)
+      return DeviceOrErr.takeError();
+    PluginDevices.push_back(*DeviceOrErr);
   }
 
   // The host plugin has no GenericPluginTy instance; skip the plugin-side
   // context in that case and just record the device set.
   std::unique_ptr<plugin::PluginContextTy> PluginCtx;
   if (Platform->Plugin) {
-    auto PluginCtxOrErr = Platform->Plugin->createPluginContext(PluginDevices);
+    if (auto Err = Platform->init())
+      return Err;
+    auto PluginCtxOrErr =
+        Platform->Plugin->createPluginContext(PluginDevices);
     if (!PluginCtxOrErr)
       return PluginCtxOrErr.takeError();
     PluginCtx = std::move(*PluginCtxOrErr);
@@ -698,13 +745,18 @@ constexpr size_t MAX_ALLOC_TRIES = 50;
 Error olMemAllocImplHelper(ol_device_handle_t Device, ol_alloc_type_t Type,
                            size_t Size, size_t Alignment,
                            void **AllocationOut) {
+  auto DeviceOrErr = Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  GenericDeviceTy *DeviceImpl = *DeviceOrErr;
+
   SmallVector<void *> Rejects;
 
   // Repeat the allocation up to a certain amount of times. If it happens to
   // already be allocated (e.g. by a device from another vendor) throw it away
   // and try again.
   for (size_t Count = 0; Count < MAX_ALLOC_TRIES; Count++) {
-    auto NewAlloc = Device->Device->dataAlloc(
+    auto NewAlloc = DeviceImpl->dataAlloc(
         Size, nullptr, convertOlToPluginAllocTy(Type), Alignment);
     if (!NewAlloc)
       return NewAlloc.takeError();
@@ -736,7 +788,7 @@ Error olMemAllocImplHelper(ol_device_handle_t Device, ol_alloc_type_t Type,
 
         for (void *R : Rejects)
           if (auto Err =
-                  Device->Device->dataDelete(R, convertOlToPluginAllocTy(Type)))
+                  DeviceImpl->dataDelete(R, convertOlToPluginAllocTy(Type)))
             return Err;
         return Error::success();
       }
@@ -800,8 +852,12 @@ Error olMemFree_impl(void *Address) {
     Bases.erase(std::lower_bound(Bases.begin(), Bases.end(), Address));
   }
 
+  auto DeviceOrErr = Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+
   if (auto Res =
-          Device->Device->dataDelete(Address, convertOlToPluginAllocTy(Type)))
+          (*DeviceOrErr)->dataDelete(Address, convertOlToPluginAllocTy(Type)))
     return Res;
 
   return Error::success();
@@ -868,16 +924,20 @@ Error olCreateQueue_impl(ol_context_handle_t Context, ol_device_handle_t Device,
   auto CreatedQueue =
       std::make_unique<ol_queue_impl_t>(nullptr, Context, Device);
 
+  auto DeviceOrErr = Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+
   auto OutstandingQueue = Context->getOutstandingQueue(Device);
   if (OutstandingQueue) {
     // The queue is empty, but we still need to sync it to release any temporary
     // memory allocations or do other cleanup.
     if (auto Err =
-            Device->Device->synchronize(OutstandingQueue, /*Release=*/false))
+            (*DeviceOrErr)->synchronize(OutstandingQueue, /*Release=*/false))
       return Err;
     CreatedQueue->AsyncInfo = OutstandingQueue;
   } else if (auto Err = Context->PluginCtx->initAsyncInfo(
-                 *Device->Device, &(CreatedQueue->AsyncInfo))) {
+                 **DeviceOrErr, &(CreatedQueue->AsyncInfo))) {
     return Err;
   }
 
@@ -888,17 +948,22 @@ Error olCreateQueue_impl(ol_context_handle_t Context, ol_device_handle_t Device,
 Error olDestroyQueue_impl(ol_queue_handle_t Queue) {
   auto *Device = Queue->Device;
   auto *Context = Queue->Context;
+  auto DeviceOrErr = Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  auto *DeviceImpl = *DeviceOrErr;
+
   // This is safe; as soon as olDestroyQueue is called it is not possible to add
   // any more work to the queue, so if it's finished now it will remain finished
   // forever.
-  auto Res = Device->Device->hasPendingWork(Queue->AsyncInfo);
+  auto Res = DeviceImpl->hasPendingWork(Queue->AsyncInfo);
   if (!Res)
     return Res.takeError();
 
   if (!*Res) {
     // The queue is complete, so sync it and throw it back into the pool.
-    if (auto Err = Device->Device->synchronize(Queue->AsyncInfo,
-                                               /*Release=*/true))
+    if (auto Err = DeviceImpl->synchronize(Queue->AsyncInfo,
+                                           /*Release=*/true))
       return Err;
   } else {
     // The queue still has outstanding work. Store it so we can check it later.
@@ -915,7 +980,10 @@ Error olSyncQueue_impl(ol_queue_handle_t Queue) {
     // We don't need to release the queue and we would like the ability for
     // other offload threads to submit work concurrently, so pass "false" here
     // so we don't release the underlying queue object.
-    if (auto Err = Queue->Device->Device->synchronize(Queue->AsyncInfo, false))
+    auto DeviceOrErr = Queue->Device->getDevice();
+    if (!DeviceOrErr)
+      return DeviceOrErr.takeError();
+    if (auto Err = (*DeviceOrErr)->synchronize(Queue->AsyncInfo, false))
       return Err;
   }
 
@@ -924,7 +992,10 @@ Error olSyncQueue_impl(ol_queue_handle_t Queue) {
 
 Error olWaitEvents_impl(ol_queue_handle_t Queue, ol_event_handle_t *Events,
                         size_t NumEvents) {
-  auto *Device = Queue->Device->Device;
+  auto DeviceOrErr = Queue->Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  auto *Device = *DeviceOrErr;
 
   for (size_t I = 0; I < NumEvents; I++) {
     auto *Event = Events[I];
@@ -956,7 +1027,10 @@ Error olGetQueueInfoImplDetail(ol_queue_handle_t Queue,
   case OL_QUEUE_INFO_CONTEXT:
     return Info.write<ol_context_handle_t>(Queue->Context);
   case OL_QUEUE_INFO_EMPTY: {
-    auto Pending = Queue->Device->Device->hasPendingWork(Queue->AsyncInfo);
+    auto DeviceOrErr = Queue->Device->getDevice();
+    if (!DeviceOrErr)
+      return DeviceOrErr.takeError();
+    auto Pending = (*DeviceOrErr)->hasPendingWork(Queue->AsyncInfo);
     if (auto Err = Pending.takeError())
       return Err;
     return Info.write<bool>(!*Pending);
@@ -984,7 +1058,11 @@ Error olSyncEvent_impl(ol_event_handle_t Event) {
   if (!Event->EventInfo)
     return Plugin::success();
 
-  if (auto Res = Event->Device->Device->syncEvent(Event->EventInfo))
+  auto DeviceOrErr = Event->Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+
+  if (auto Res = (*DeviceOrErr)->syncEvent(Event->EventInfo))
     return Res;
 
   return Error::success();
@@ -1004,7 +1082,11 @@ Error olGetEventElapsedTime_impl(ol_event_handle_t StartEvent,
         ErrorCode::INVALID_DEVICE,
         "StartEvent and EndEvent must belong to the same device");
 
-  auto ElapsedTimeOrErr = StartEvent->Device->Device->getEventElapsedTime(
+  auto DeviceOrErr = StartEvent->Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+
+  auto ElapsedTimeOrErr = (*DeviceOrErr)->getEventElapsedTime(
       StartEvent->EventInfo, EndEvent->EventInfo);
   if (!ElapsedTimeOrErr)
     return ElapsedTimeOrErr.takeError();
@@ -1014,10 +1096,14 @@ Error olGetEventElapsedTime_impl(ol_event_handle_t StartEvent,
 }
 
 Error olDestroyEvent_impl(ol_event_handle_t Event) {
-  if (Event->EventInfo)
-    if (auto Res = Event->Device->Device->destroyEvent(Event->EventInfo,
-                                                       Event->ProfilingEnabled))
+  if (Event->EventInfo) {
+    auto DeviceOrErr = Event->Device->getDevice();
+    if (!DeviceOrErr)
+      return DeviceOrErr.takeError();
+    if (auto Res = (*DeviceOrErr)->destroyEvent(Event->EventInfo,
+                                                Event->ProfilingEnabled))
       return Res;
+  }
 
   return olDestroy(Event);
 }
@@ -1037,8 +1123,11 @@ Error olGetEventInfoImplDetail(ol_event_handle_t Event,
     if (!Event->EventInfo)
       return Info.write<bool>(true);
 
-    auto Res = Queue->Device->Device->isEventComplete(Event->EventInfo,
-                                                      Queue->AsyncInfo);
+    auto DeviceOrErr = Queue->Device->getDevice();
+    if (!DeviceOrErr)
+      return DeviceOrErr.takeError();
+    auto Res = (*DeviceOrErr)->isEventComplete(Event->EventInfo,
+                                               Queue->AsyncInfo);
     if (auto Err = Res.takeError())
       return Err;
     return Info.write<bool>(*Res);
@@ -1067,14 +1156,18 @@ Error olCreateEvent_impl(ol_queue_handle_t Queue, ol_event_flags_t Flags,
   auto Event = std::make_unique<ol_event_impl_t>(nullptr, Queue->Device, Queue,
                                                  EnableProfiling);
 
-  if (auto Err = Queue->Device->Device->createEvent(&Event->EventInfo,
-                                                    EnableProfiling))
+  auto DeviceOrErr = Queue->Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  auto *DeviceImpl = *DeviceOrErr;
+
+  if (auto Err = DeviceImpl->createEvent(&Event->EventInfo, EnableProfiling))
     return Err;
 
-  if (auto Err = Queue->Device->Device->recordEvent(
+  if (auto Err = DeviceImpl->recordEvent(
           Event->EventInfo, Queue->AsyncInfo, EnableProfiling)) {
     if (Event->EventInfo) {
-      if (auto DestroyErr = Queue->Device->Device->destroyEvent(
+      if (auto DestroyErr = DeviceImpl->destroyEvent(
               Event->EventInfo, EnableProfiling))
         return joinErrors(std::move(Err), std::move(DestroyErr));
     }
@@ -1092,14 +1185,26 @@ Error olMemcpy_impl(ol_queue_handle_t Queue, void *DstPtr,
   bool IsDstHost = DstDevice->Platform.BackendType == OL_PLATFORM_BACKEND_HOST;
   bool IsSrcHost = SrcDevice->Platform.BackendType == OL_PLATFORM_BACKEND_HOST;
 
+  auto DstDeviceImplOrErr = DstDevice->getDevice();
+  if (!DstDeviceImplOrErr)
+    return DstDeviceImplOrErr.takeError();
+  auto SrcDeviceImplOrErr = SrcDevice->getDevice();
+  if (!SrcDeviceImplOrErr)
+    return SrcDeviceImplOrErr.takeError();
+  auto *DstDeviceImpl = *DstDeviceImplOrErr;
+  auto *SrcDeviceImpl = *SrcDeviceImplOrErr;
+
   if (IsDstHost && IsSrcHost) {
     if (!Queue) {
       std::memcpy(DstPtr, SrcPtr, Size);
       return Error::success();
     }
 
-    return Queue->Device->Device->dataMemcpy(DstPtr, SrcPtr, Size,
-                                             Queue->AsyncInfo);
+    auto QueueDeviceOrErr = Queue->Device->getDevice();
+    if (!QueueDeviceOrErr)
+      return QueueDeviceOrErr.takeError();
+    return (*QueueDeviceOrErr)->dataMemcpy(DstPtr, SrcPtr, Size,
+                                           Queue->AsyncInfo);
   }
 
   // If no queue is given the memcpy will be synchronous
@@ -1107,18 +1212,19 @@ Error olMemcpy_impl(ol_queue_handle_t Queue, void *DstPtr,
 
   if (IsDstHost) {
     if (auto Res =
-            SrcDevice->Device->dataRetrieve(DstPtr, SrcPtr, Size, QueueImpl))
+            SrcDeviceImpl->dataRetrieve(DstPtr, SrcPtr, Size, QueueImpl))
       return Res;
   } else if (IsSrcHost) {
     if (auto Res =
-            DstDevice->Device->dataSubmit(DstPtr, SrcPtr, Size, QueueImpl))
+            DstDeviceImpl->dataSubmit(DstPtr, SrcPtr, Size, QueueImpl))
       return Res;
-  } else if (SrcDevice->Platform.Plugin == DstDevice->Platform.Plugin &&
+  } else if (SrcDevice->Platform.Plugin ==
+                 DstDevice->Platform.Plugin &&
              SrcDevice->Platform.Plugin->isDataExchangable(
-                 SrcDevice->Device->getDeviceId(),
-                 DstDevice->Device->getDeviceId())) {
-    if (auto Res = SrcDevice->Device->dataExchange(SrcPtr, *DstDevice->Device,
-                                                   DstPtr, Size, QueueImpl))
+                 SrcDeviceImpl->getDeviceId(),
+                 DstDeviceImpl->getDeviceId())) {
+    if (auto Res = SrcDeviceImpl->dataExchange(SrcPtr, *DstDeviceImpl,
+                                               DstPtr, Size, QueueImpl))
       return Res;
   } else {
     if (Queue)
@@ -1129,9 +1235,9 @@ Error olMemcpy_impl(ol_queue_handle_t Queue, void *DstPtr,
     if (!Buffer)
       return createOffloadError(ErrorCode::OUT_OF_RESOURCES,
                                 "Couldn't allocate a buffer for transfer");
-    Error Res = SrcDevice->Device->dataRetrieve(Buffer, SrcPtr, Size, nullptr);
+    Error Res = SrcDeviceImpl->dataRetrieve(Buffer, SrcPtr, Size, nullptr);
     if (!Res)
-      Res = DstDevice->Device->dataSubmit(DstPtr, Buffer, Size, nullptr);
+      Res = DstDeviceImpl->dataSubmit(DstPtr, Buffer, Size, nullptr);
 
     free(Buffer);
     return Res;
@@ -1142,8 +1248,11 @@ Error olMemcpy_impl(ol_queue_handle_t Queue, void *DstPtr,
 
 Error olMemFill_impl(ol_queue_handle_t Queue, void *Ptr, size_t PatternSize,
                      const void *PatternPtr, size_t FillSize) {
-  return Queue->Device->Device->dataFill(Ptr, PatternPtr, PatternSize, FillSize,
-                                         Queue->AsyncInfo);
+  auto DeviceOrErr = Queue->Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  return (*DeviceOrErr)->dataFill(Ptr, PatternPtr, PatternSize, FillSize,
+                                  Queue->AsyncInfo);
 }
 
 Error olMemPrefetch_impl(ol_queue_handle_t Queue, size_t Count,
@@ -1153,8 +1262,11 @@ Error olMemPrefetch_impl(ol_queue_handle_t Queue, size_t Count,
     return Error::success();
 
   bool ToHost = (Flags & OL_MEM_MIGRATION_FLAG_DEVICE_TO_HOST) != 0;
-  return Queue->Device->Device->dataPrefetch(Count, Mems, Sizes, ToHost,
-                                             Queue->AsyncInfo);
+  auto DeviceOrErr = Queue->Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  return (*DeviceOrErr)->dataPrefetch(Count, Mems, Sizes, ToHost,
+                                      Queue->AsyncInfo);
 }
 
 Error olCreateProgram_impl(ol_context_handle_t Context,
@@ -1165,8 +1277,13 @@ Error olCreateProgram_impl(ol_context_handle_t Context,
                               "device does not belong to the given context");
 
   StringRef Buffer(reinterpret_cast<const char *>(ProgData), ProgDataSize);
-  Expected<plugin::DeviceImageTy *> Res = Device->Device->loadBinary(
-      Device->Device->Plugin, Buffer, Context->PluginCtx.get());
+  auto DeviceOrErr = Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  auto *DeviceImpl = *DeviceOrErr;
+
+  Expected<plugin::DeviceImageTy *> Res = DeviceImpl->loadBinary(
+      DeviceImpl->Plugin, Buffer, Context->PluginCtx.get());
   if (!Res)
     return Res.takeError();
   assert(*Res && "loadBinary returned nullptr");
@@ -1178,9 +1295,12 @@ Error olCreateProgram_impl(ol_context_handle_t Context,
 Error olIsValidBinary_impl(ol_device_handle_t Device, const void *ProgData,
                            size_t ProgDataSize, bool *IsValid) {
   StringRef Buffer(reinterpret_cast<const char *>(ProgData), ProgDataSize);
-  *IsValid = Device->Device ? Device->Device->Plugin.isDeviceCompatible(
-                                  Device->Device->getDeviceId(), Buffer)
-                            : false;
+  auto DeviceOrErr = Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  auto *DeviceImpl = *DeviceOrErr;
+  *IsValid =
+      DeviceImpl->Plugin.isDeviceCompatible(DeviceImpl->getDeviceId(), Buffer);
   return Error::success();
 }
 
@@ -1205,7 +1325,11 @@ Error olCalculateOptimalOccupancy_impl(ol_device_handle_t Device,
                               "provided symbol is not a kernel");
   auto *KernelImpl = std::get<GenericKernelTy *>(Kernel->PluginImpl);
 
-  auto Res = KernelImpl->maxGroupSize(*Device->Device, DynamicMemSize);
+  auto DeviceOrErr = Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+
+  auto Res = KernelImpl->maxGroupSize(**DeviceOrErr, DynamicMemSize);
   if (auto Err = Res.takeError())
     return Err;
 
@@ -1222,7 +1346,10 @@ Error olGetKernelMaxCooperativeGroupCount_impl(
     return createOffloadError(ErrorCode::SYMBOL_KIND,
                               "provided symbol is not a kernel");
 
-  GenericDeviceTy *DeviceImpl = Device->Device;
+  auto DeviceOrErr = Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  GenericDeviceTy *DeviceImpl = *DeviceOrErr;
   auto *KernelImpl = std::get<GenericKernelTy *>(Kernel->PluginImpl);
 
   // Extract work group size from LaunchSizeArgs
@@ -1248,7 +1375,6 @@ Error olLaunchKernel_impl(ol_queue_handle_t Queue, ol_device_handle_t Device,
                           const ol_kernel_launch_prop_t *Properties,
                           size_t NumArgs, void **ArgPtrs,
                           const size_t *ArgSizes) {
-  auto *DeviceImpl = Device->Device;
   if (Queue && Device != Queue->Device) {
     return createOffloadError(
         ErrorCode::INVALID_DEVICE,
@@ -1259,6 +1385,11 @@ Error olLaunchKernel_impl(ol_queue_handle_t Queue, ol_device_handle_t Device,
     return createOffloadError(ErrorCode::SYMBOL_KIND,
                               "provided symbol is not a kernel");
 
+  auto DeviceOrErr = Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  auto *DeviceImpl = *DeviceOrErr;
+
   auto *QueueImpl = Queue ? Queue->AsyncInfo : nullptr;
   KernelLaunchArgsTy LaunchArgs{};
   LaunchArgs.NumArgs = static_cast<uint32_t>(NumArgs);
@@ -1438,13 +1569,20 @@ Error olGetSymbolInfoSize_impl(ol_symbol_handle_t Symbol,
 Error olLaunchHostFunction_impl(ol_queue_handle_t Queue,
                                 ol_host_function_cb_t Callback,
                                 void *UserData) {
-  return Queue->Device->Device->enqueueHostCall(Callback, UserData,
-                                                Queue->AsyncInfo);
+  auto DeviceOrErr = Queue->Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  return (*DeviceOrErr)->enqueueHostCall(Callback, UserData,
+                                         Queue->AsyncInfo);
 }
 
 Error olMemRegister_impl(ol_device_handle_t Device, void *Ptr, size_t Size,
                          ol_memory_register_flags_t Flags, void **LockedPtr) {
-  Expected<void *> LockedPtrOrErr = Device->Device->registerMemory(
+  auto DeviceOrErr = Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+
+  Expected<void *> LockedPtrOrErr = (*DeviceOrErr)->registerMemory(
       Ptr, Size, Flags & OL_MEMORY_REGISTER_FLAG_LOCK_MEMORY);
   if (!LockedPtrOrErr)
     return LockedPtrOrErr.takeError();
@@ -1456,14 +1594,20 @@ Error olMemRegister_impl(ol_device_handle_t Device, void *Ptr, size_t Size,
 
 Error olMemUnregister_impl(ol_device_handle_t Device, void *Ptr,
                            ol_memory_register_flags_t Flags) {
-  return Device->Device->unregisterMemory(
+  auto DeviceOrErr = Device->getDevice();
+  if (!DeviceOrErr)
+    return DeviceOrErr.takeError();
+  return (*DeviceOrErr)->unregisterMemory(
       Ptr, Flags & OL_MEMORY_REGISTER_FLAG_UNLOCK_MEMORY);
 }
 
 Error olQueryQueue_impl(ol_queue_handle_t Queue, bool *IsQueueWorkCompleted) {
   if (Queue->AsyncInfo->Queue) {
-    if (auto Err = Queue->Device->Device->queryAsync(Queue->AsyncInfo, false,
-                                                     IsQueueWorkCompleted))
+    auto DeviceOrErr = Queue->Device->getDevice();
+    if (!DeviceOrErr)
+      return DeviceOrErr.takeError();
+    if (auto Err = (*DeviceOrErr)->queryAsync(Queue->AsyncInfo, false,
+                                              IsQueueWorkCompleted))
       return Err;
   } else if (IsQueueWorkCompleted) {
     // No underlying queue means there's no work to complete.

>From ffefd3cf7b2fd1e2fe12d126e6a8a23d60ee519f Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 3 Sep 2026 10:39:20 -0700
Subject: [PATCH 03/14] [OFFLOAD]add olIteratePlatforms

---
 offload/liboffload/API/Platform.td     | 23 +++++++++++++++++++++++
 offload/liboffload/src/OffloadImpl.cpp | 11 +++++++++++
 2 files changed, 34 insertions(+)

diff --git a/offload/liboffload/API/Platform.td b/offload/liboffload/API/Platform.td
index 62810e8fdb7ca..65efec2b8af4a 100644
--- a/offload/liboffload/API/Platform.td
+++ b/offload/liboffload/API/Platform.td
@@ -97,3 +97,26 @@ def olPlatformRegisterRPCCallback : Function {
                       "RPC callback function pointer", PARAM_IN>];
   let returns = [Return<"OL_ERRC_INVALID_PLATFORM">, Return<"OL_ERRC_SUCCESS">];
 }
+
+def ol_platform_iterate_cb_t : FptrTypedef {
+  let desc = "User-provided function to be used with `olIteratePlatforms`";
+  let params = [
+    Param<"ol_platform_handle_t", "Platform", "the platform handle of the current iteration", PARAM_IN>,
+    Param<"void*", "UserData", "optional user data", PARAM_IN_OPTIONAL>
+  ];
+  let return = "bool";
+}
+
+def olIteratePlatforms : Function {
+  let desc = "Iterates over all available platforms, calling the callback for each platform.";
+  let details = [
+    "If the user-provided callback returns `false`, the iteration is stopped."
+  ];
+  let params = [
+    Param<"ol_platform_iterate_cb_t", "Callback", "User-provided function called for each available platform", PARAM_IN>,
+    Param<"void*", "UserData", "Optional user data to pass to the callback", PARAM_IN_OPTIONAL>
+  ];
+  let returns = [
+    Return<"OL_ERRC_INVALID_PLATFORM">
+  ];
+}
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 670179c21f054..e06bd8d445bc3 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -468,6 +468,17 @@ Error olPlatformRegisterRPCCallback_impl(ol_platform_handle_t Platform,
   return Error::success();
 }
 
+Error olIteratePlatforms_impl(ol_platform_iterate_cb_t Callback,
+                              void *UserData) {
+  for (auto &Platform : OffloadContext::get().Platforms) {
+    if (!Callback(Platform.get(), UserData)) {
+      return Error::success();
+    }
+  }
+
+  return Error::success();
+}
+
 Error olGetDeviceInfoImplDetail(ol_device_handle_t Device,
                                 ol_device_info_t PropName, size_t PropSize,
                                 void *PropValue, size_t *PropSizeRet) {

>From 9951e27875c24fc8a0d984f1c848bfbe5ed3c55a Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 3 Sep 2026 10:55:00 -0700
Subject: [PATCH 04/14] [offload][omp] Load plugins through liboffload

---
 offload/include/PluginManager.h            |  3 ++-
 offload/liboffload/exports                 |  2 +-
 offload/liboffload/src/OffloadImpl.cpp     |  5 ++++
 offload/libompaccsupport/PluginManager.cpp | 29 +++++++++++++---------
 4 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/offload/include/PluginManager.h b/offload/include/PluginManager.h
index 6c6fdebe76dff..eea8b62a8c39d 100644
--- a/offload/include/PluginManager.h
+++ b/offload/include/PluginManager.h
@@ -13,6 +13,7 @@
 #ifndef OMPTARGET_PLUGIN_MANAGER_H
 #define OMPTARGET_PLUGIN_MANAGER_H
 
+#include "OffloadAPI.h"
 #include "PluginInterface.h"
 
 #include "DeviceImage.h"
@@ -155,7 +156,7 @@ struct PluginManager {
   llvm::SmallVector<__tgt_bin_desc *> DelayedBinDesc;
 
   // List of all plugins, in use or not.
-  llvm::SmallVector<std::unique_ptr<GenericPluginTy>> Plugins;
+  llvm::SmallVector<GenericPluginTy *> Plugins;
 
   // Mapping of plugins to the OpenMP device identifier.
   llvm::DenseMap<std::pair<const GenericPluginTy *, int32_t>, int32_t>
diff --git a/offload/liboffload/exports b/offload/liboffload/exports
index 5487e16168681..75b1cfd049c82 100644
--- a/offload/liboffload/exports
+++ b/offload/liboffload/exports
@@ -55,9 +55,9 @@ global:
     llvm::omp::target::ompt::Initialized;
     llvm::omp::target::ompt::lookupCallbackByCode;
     llvm::omp::target::ompt::lookupCallbackByName;
-    createPlugin_*;
     # Temporary helpers to help transition of libomptarget to liboffload.
     __ol_tgt_setInfoFlag;
+    __ol_tgt_GetPluginFromPlatform;
   };
 local:
   *;
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index e06bd8d445bc3..6e403df9544e3 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -1633,5 +1633,10 @@ extern "C" void __ol_tgt_setInfoFlag(uint32_t NewInfoLevel) {
   InfoLevel.store(NewInfoLevel);
 }
 
+extern "C" GenericPluginTy *
+__ol_tgt_GetPluginFromPlatform(ol_platform_handle_t Platform) {
+  return Platform->Plugin.get();
+}
+
 } // namespace offload
 } // namespace llvm
diff --git a/offload/libompaccsupport/PluginManager.cpp b/offload/libompaccsupport/PluginManager.cpp
index 41b653a60adfd..b0cb40fb32e8d 100644
--- a/offload/libompaccsupport/PluginManager.cpp
+++ b/offload/libompaccsupport/PluginManager.cpp
@@ -26,9 +26,8 @@ using namespace llvm::omp::target::debug;
 
 PluginManager *PM = nullptr;
 
-// Every plugin exports this method to create an instance of the plugin type.
-#define PLUGIN_TARGET(Name) extern "C" GenericPluginTy *createPlugin_##Name();
-#include "Shared/Targets.def"
+extern "C" GenericPluginTy *
+__ol_tgt_GetPluginFromPlatform(ol_platform_handle_t Platform);
 
 void PluginManager::init() {
   TIMESCOPE();
@@ -38,14 +37,21 @@ void PluginManager::init() {
   }
 
   ODBG(ODT_Init) << "Loading RTLs";
-
-  // Attempt to create an instance of each supported plugin.
-#define PLUGIN_TARGET(Name)                                                    \
-  do {                                                                         \
-    Plugins.emplace_back(                                                      \
-        std::unique_ptr<GenericPluginTy>(createPlugin_##Name()));              \
-  } while (false);
-#include "Shared/Targets.def"
+  if (ol_result_t Res = olInit(nullptr))
+    REPORT() << "Failed to initialize liboffload: " << Res->Details;
+
+  
+  if (ol_result_t Res = olIteratePlatforms(
+          [](ol_platform_handle_t Platform, void *Data) {
+                  auto *PM = static_cast<PluginManager *>(Data);
+                  auto *Plugin = __ol_tgt_GetPluginFromPlatform(Platform);
+                  ODBG(ODT_Init) << "Adding plugin " << Plugin->getName()
+                           << " from liboffload";
+                  PM->Plugins.push_back(Plugin);
+                  return true;
+                },
+                this))
+    REPORT() << "Failed to iterate platforms: " << Res->Details;
 
   ODBG(ODT_Init) << "RTLs loaded!";
 }
@@ -62,7 +68,6 @@ void PluginManager::deinit() {
       std::string InfoMsg = toString(std::move(Err));
       ODBG(ODT_Deinit) << "Failed to deinit plugin: " << InfoMsg;
     }
-    Plugin.release();
   }
 
   ODBG(ODT_Deinit) << "RTLs unloaded!";

>From 3d5895acbe4e736e78e48fc5d32f2a94b4295af8 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 3 Sep 2026 10:56:25 -0700
Subject: [PATCH 05/14] add test

---
 .../platform/olIteratePlatforms.cpp           | 45 +++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 offload/unittests/OffloadAPI/platform/olIteratePlatforms.cpp

diff --git a/offload/unittests/OffloadAPI/platform/olIteratePlatforms.cpp b/offload/unittests/OffloadAPI/platform/olIteratePlatforms.cpp
new file mode 100644
index 0000000000000..4f8f4e35df23b
--- /dev/null
+++ b/offload/unittests/OffloadAPI/platform/olIteratePlatforms.cpp
@@ -0,0 +1,45 @@
+//===------- Offload API tests - olIteratePlatforms -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/Fixtures.hpp"
+#include <OffloadAPI.h>
+#include <gtest/gtest.h>
+
+using olIteratePlatformsTest = OffloadTest;
+
+TEST_F(olIteratePlatformsTest, SuccessEmptyCallback) {
+  ASSERT_SUCCESS(olIteratePlatforms(
+      [](ol_platform_handle_t, void *) { return false; }, nullptr));
+}
+
+TEST_F(olIteratePlatformsTest, SuccessGetPlatform) {
+  uint32_t PlatformCount = 0;
+  ol_platform_handle_t Platform = nullptr;
+
+  ASSERT_SUCCESS(olIteratePlatforms(
+      [](ol_platform_handle_t, void *Data) {
+        auto Count = static_cast<uint32_t *>(Data);
+        *Count += 1;
+        return true;
+      },
+      &PlatformCount));
+
+  if (PlatformCount == 0) {
+    GTEST_SKIP() << "No available platforms.";
+  }
+
+  ASSERT_SUCCESS(olIteratePlatforms(
+      [](ol_platform_handle_t P, void *Data) {
+        auto PlatformPtr = static_cast<ol_platform_handle_t *>(Data);
+        *PlatformPtr = P;
+        return true;
+      },
+      &Platform));
+
+  ASSERT_NE(Platform, nullptr);
+}

>From 2fb211366d56097e84b5eb4f7349771246489c6c Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 3 Sep 2026 10:55:00 -0700
Subject: [PATCH 06/14] [offload][omp] Load plugins through liboffload

---
 offload/include/PluginManager.h            |  3 ++-
 offload/liboffload/exports                 |  2 +-
 offload/liboffload/src/OffloadImpl.cpp     |  5 ++++
 offload/libompaccsupport/PluginManager.cpp | 29 +++++++++++++---------
 4 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/offload/include/PluginManager.h b/offload/include/PluginManager.h
index 6c6fdebe76dff..eea8b62a8c39d 100644
--- a/offload/include/PluginManager.h
+++ b/offload/include/PluginManager.h
@@ -13,6 +13,7 @@
 #ifndef OMPTARGET_PLUGIN_MANAGER_H
 #define OMPTARGET_PLUGIN_MANAGER_H
 
+#include "OffloadAPI.h"
 #include "PluginInterface.h"
 
 #include "DeviceImage.h"
@@ -155,7 +156,7 @@ struct PluginManager {
   llvm::SmallVector<__tgt_bin_desc *> DelayedBinDesc;
 
   // List of all plugins, in use or not.
-  llvm::SmallVector<std::unique_ptr<GenericPluginTy>> Plugins;
+  llvm::SmallVector<GenericPluginTy *> Plugins;
 
   // Mapping of plugins to the OpenMP device identifier.
   llvm::DenseMap<std::pair<const GenericPluginTy *, int32_t>, int32_t>
diff --git a/offload/liboffload/exports b/offload/liboffload/exports
index 5487e16168681..75b1cfd049c82 100644
--- a/offload/liboffload/exports
+++ b/offload/liboffload/exports
@@ -55,9 +55,9 @@ global:
     llvm::omp::target::ompt::Initialized;
     llvm::omp::target::ompt::lookupCallbackByCode;
     llvm::omp::target::ompt::lookupCallbackByName;
-    createPlugin_*;
     # Temporary helpers to help transition of libomptarget to liboffload.
     __ol_tgt_setInfoFlag;
+    __ol_tgt_GetPluginFromPlatform;
   };
 local:
   *;
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index e06bd8d445bc3..6e403df9544e3 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -1633,5 +1633,10 @@ extern "C" void __ol_tgt_setInfoFlag(uint32_t NewInfoLevel) {
   InfoLevel.store(NewInfoLevel);
 }
 
+extern "C" GenericPluginTy *
+__ol_tgt_GetPluginFromPlatform(ol_platform_handle_t Platform) {
+  return Platform->Plugin.get();
+}
+
 } // namespace offload
 } // namespace llvm
diff --git a/offload/libompaccsupport/PluginManager.cpp b/offload/libompaccsupport/PluginManager.cpp
index 41b653a60adfd..b0cb40fb32e8d 100644
--- a/offload/libompaccsupport/PluginManager.cpp
+++ b/offload/libompaccsupport/PluginManager.cpp
@@ -26,9 +26,8 @@ using namespace llvm::omp::target::debug;
 
 PluginManager *PM = nullptr;
 
-// Every plugin exports this method to create an instance of the plugin type.
-#define PLUGIN_TARGET(Name) extern "C" GenericPluginTy *createPlugin_##Name();
-#include "Shared/Targets.def"
+extern "C" GenericPluginTy *
+__ol_tgt_GetPluginFromPlatform(ol_platform_handle_t Platform);
 
 void PluginManager::init() {
   TIMESCOPE();
@@ -38,14 +37,21 @@ void PluginManager::init() {
   }
 
   ODBG(ODT_Init) << "Loading RTLs";
-
-  // Attempt to create an instance of each supported plugin.
-#define PLUGIN_TARGET(Name)                                                    \
-  do {                                                                         \
-    Plugins.emplace_back(                                                      \
-        std::unique_ptr<GenericPluginTy>(createPlugin_##Name()));              \
-  } while (false);
-#include "Shared/Targets.def"
+  if (ol_result_t Res = olInit(nullptr))
+    REPORT() << "Failed to initialize liboffload: " << Res->Details;
+
+  
+  if (ol_result_t Res = olIteratePlatforms(
+          [](ol_platform_handle_t Platform, void *Data) {
+                  auto *PM = static_cast<PluginManager *>(Data);
+                  auto *Plugin = __ol_tgt_GetPluginFromPlatform(Platform);
+                  ODBG(ODT_Init) << "Adding plugin " << Plugin->getName()
+                           << " from liboffload";
+                  PM->Plugins.push_back(Plugin);
+                  return true;
+                },
+                this))
+    REPORT() << "Failed to iterate platforms: " << Res->Details;
 
   ODBG(ODT_Init) << "RTLs loaded!";
 }
@@ -62,7 +68,6 @@ void PluginManager::deinit() {
       std::string InfoMsg = toString(std::move(Err));
       ODBG(ODT_Deinit) << "Failed to deinit plugin: " << InfoMsg;
     }
-    Plugin.release();
   }
 
   ODBG(ODT_Deinit) << "RTLs unloaded!";

>From 8c787fbc0924057160ab05b5fc928e5ca6dad0da Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Fri, 4 Sep 2026 07:31:12 -0700
Subject: [PATCH 07/14] [offload][omp] Initialize devices through liboffload

---
 offload/include/PluginManager.h               |  12 +-
 offload/include/device.h                      |   5 +-
 offload/liboffload/API/Program.td             |  15 +
 offload/liboffload/exports                    |   5 +-
 offload/liboffload/src/OffloadImpl.cpp        |  30 ++
 offload/libompaccsupport/PluginManager.cpp    | 256 +++++++++---------
 offload/libompaccsupport/device.cpp           |  17 +-
 .../common/include/PluginInterface.h          |   6 -
 .../common/src/PluginInterface.cpp            |  15 -
 9 files changed, 197 insertions(+), 164 deletions(-)

diff --git a/offload/include/PluginManager.h b/offload/include/PluginManager.h
index eea8b62a8c39d..f1937a368186f 100644
--- a/offload/include/PluginManager.h
+++ b/offload/include/PluginManager.h
@@ -120,11 +120,9 @@ struct PluginManager {
     return Devices.getExclusiveAccessor();
   }
 
-  /// Initialize \p Plugin. Returns true on success.
-  bool initializePlugin(GenericPluginTy &Plugin);
-
-  /// Initialize device \p DeviceNo of \p Plugin. Returns true on success.
-  bool initializeDevice(GenericPluginTy &Plugin, int32_t DeviceId);
+  /// Initialize device \p DeviceHandle as on OpenMP device. Returns true on
+  /// success.
+  bool initializeDevice(ol_device_handle_t DeviceHandle);
 
   /// Eagerly initialize all plugins and their devices.
   void initializeAllDevices();
@@ -158,8 +156,8 @@ struct PluginManager {
   // List of all plugins, in use or not.
   llvm::SmallVector<GenericPluginTy *> Plugins;
 
-  // Mapping of plugins to the OpenMP device identifier.
-  llvm::DenseMap<std::pair<const GenericPluginTy *, int32_t>, int32_t>
+  // Mapping of device handles to the OpenMP device identifier.
+  llvm::DenseMap<ol_device_handle_t, int32_t>
       DeviceIds;
 
   // Set of all device images currently in use.
diff --git a/offload/include/device.h b/offload/include/device.h
index 266a2a675df0c..ca32a7b26d1b4 100644
--- a/offload/include/device.h
+++ b/offload/include/device.h
@@ -34,6 +34,7 @@
 #include "llvm/ADT/SmallVector.h"
 
 #include "GlobalHandler.h"
+#include "OffloadAPI.h"
 #include "PluginInterface.h"
 
 using GenericPluginTy = llvm::omp::target::plugin::GenericPluginTy;
@@ -48,8 +49,10 @@ struct DeviceTy {
   int32_t DeviceID;
   GenericPluginTy *RTL;
   int32_t RTLDeviceID;
+  ol_device_handle_t DeviceHandle;
 
-  DeviceTy(GenericPluginTy *RTL, int32_t DeviceID, int32_t RTLDeviceID);
+  DeviceTy(GenericPluginTy *RTL, int32_t DeviceID, int32_t RTLDeviceID,
+           ol_device_handle_t DeviceHandle);
   // DeviceTy is not copyable
   DeviceTy(const DeviceTy &D) = delete;
   DeviceTy &operator=(const DeviceTy &D) = delete;
diff --git a/offload/liboffload/API/Program.td b/offload/liboffload/API/Program.td
index ecc8fb73dad3f..9f945872aec3c 100644
--- a/offload/liboffload/API/Program.td
+++ b/offload/liboffload/API/Program.td
@@ -46,6 +46,21 @@ def olIsValidBinary : Function {
     let returns = [];
 }
 
+def olIterateCompatibleDevices : Function {
+    let desc = "Iterates over all available devices that are compatible with the binary image pointed to by `ProgData`, calling the callback for each device.";
+    let details = [
+        "The provided `ProgData` will not be loaded onto any device",
+        "If the user-provided callback returns `false`, the iteration is stopped."
+    ];
+    let params = [
+        Param<"const void*", "ProgData", "pointer to the program binary data", PARAM_IN>,
+        Param<"size_t", "ProgDataSize", "size of the program binary in bytes", PARAM_IN>,
+        Param<"ol_device_iterate_cb_t", "Callback", "User-provided function called for each compatible device", PARAM_IN>,
+        Param<"void*", "UserData", "Optional user data to pass to the callback", PARAM_IN_OPTIONAL>
+    ];
+    let returns = [];
+}
+
 def olDestroyProgram : Function {
     let desc = "Destroy the program and free all underlying resources.";
     let details = [];
diff --git a/offload/liboffload/exports b/offload/liboffload/exports
index 75b1cfd049c82..c43fbc7830393 100644
--- a/offload/liboffload/exports
+++ b/offload/liboffload/exports
@@ -28,15 +28,11 @@ global:
     "llvm::omp::target::plugin::GenericPluginTy::flush_queue(omp_interop_val_t*)";
     "llvm::omp::target::plugin::GenericPluginTy::get_function(__tgt_device_binary, char const*, void**)";
     "llvm::omp::target::plugin::GenericPluginTy::get_global(__tgt_device_binary, unsigned long, char const*, void**)";
-    "llvm::omp::target::plugin::GenericPluginTy::init()";
-    "llvm::omp::target::plugin::GenericPluginTy::deinit()";
-    "llvm::omp::target::plugin::GenericPluginTy::init_device(int)";
     "llvm::omp::target::plugin::GenericPluginTy::initialize_record_replay(int, long, void*, bool, bool, bool, bool, char const*, char const*)";
     "llvm::omp::target::plugin::GenericPluginTy::isDeviceCompatible(int, llvm::StringRef)";
     "llvm::omp::target::plugin::GenericPluginTy::isPluginCompatible(llvm::StringRef)";
     "llvm::omp::target::plugin::GenericPluginTy::is_accessible_ptr(int, void const*, unsigned long)";
     "llvm::omp::target::plugin::GenericPluginTy::is_data_exchangable(int, int)";
-    "llvm::omp::target::plugin::GenericPluginTy::is_device_initialized(int) const";
     "llvm::omp::target::plugin::GenericPluginTy::is_initialized() const";
     "llvm::omp::target::plugin::GenericPluginTy::launch_kernel(int, void*, llvm::omp::target::plugin::KernelLaunchArgsTy&, __tgt_async_info*)";
     "llvm::omp::target::plugin::GenericPluginTy::load_binary(int, __tgt_device_image*, __tgt_device_binary*)";
@@ -58,6 +54,7 @@ global:
     # Temporary helpers to help transition of libomptarget to liboffload.
     __ol_tgt_setInfoFlag;
     __ol_tgt_GetPluginFromPlatform;
+    __ol_tgt_GetPluginDeviceId;
   };
 local:
   *;
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 6e403df9544e3..2005c5b4b2034 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -1315,6 +1315,32 @@ Error olIsValidBinary_impl(ol_device_handle_t Device, const void *ProgData,
   return Error::success();
 }
 
+Error olIterateCompatibleDevices_impl(const void *ProgData, size_t ProgDataSize,
+                                      ol_device_iterate_cb_t Callback,
+                                      void *UserData) {
+  StringRef Buffer(reinterpret_cast<const char *>(ProgData), ProgDataSize);
+
+  for (auto &Platform : OffloadContext::get().Platforms) {
+    if (!Platform->Plugin || !Platform->Plugin->isPluginCompatible(Buffer))
+      continue;
+
+    // If  the image is compatible, initialize the platform.
+    if (auto Err = Platform->init())
+      return Err;
+
+    for (auto &Device : Platform->Devices) {
+      if (!Device->Platform.Plugin->isDeviceCompatible(Device->DeviceNum,
+                                                       Buffer))
+        continue;
+
+      if (!Callback(Device.get(), UserData))
+        return Error::success();
+    }
+  }
+
+  return Error::success();
+}
+
 Error olDestroyProgram_impl(ol_program_handle_t Program) {
   auto &Device = Program->Image->getDevice();
   if (auto Err = Device.unloadBinary(Program->Image))
@@ -1638,5 +1664,9 @@ __ol_tgt_GetPluginFromPlatform(ol_platform_handle_t Platform) {
   return Platform->Plugin.get();
 }
 
+extern "C" int32_t __ol_tgt_GetPluginDeviceId(ol_device_handle_t Device) {
+  return Device->DeviceNum;
+}
+
 } // namespace offload
 } // namespace llvm
diff --git a/offload/libompaccsupport/PluginManager.cpp b/offload/libompaccsupport/PluginManager.cpp
index b0cb40fb32e8d..bb81ede7031e2 100644
--- a/offload/libompaccsupport/PluginManager.cpp
+++ b/offload/libompaccsupport/PluginManager.cpp
@@ -28,6 +28,7 @@ PluginManager *PM = nullptr;
 
 extern "C" GenericPluginTy *
 __ol_tgt_GetPluginFromPlatform(ol_platform_handle_t Platform);
+extern "C" int32_t __ol_tgt_GetPluginDeviceId(ol_device_handle_t Device);
 
 void PluginManager::init() {
   TIMESCOPE();
@@ -60,48 +61,35 @@ void PluginManager::deinit() {
   TIMESCOPE();
   ODBG(ODT_Deinit) << "Unloading RTLs...";
 
-  for (auto &Plugin : Plugins) {
-    if (!Plugin->is_initialized())
-      continue;
-
-    if (auto Err = Plugin->deinit()) {
-      std::string InfoMsg = toString(std::move(Err));
-      ODBG(ODT_Deinit) << "Failed to deinit plugin: " << InfoMsg;
-    }
-  }
+  Plugins.clear();
+  if (auto Res = olShutDown())
+    REPORT() << "Failed to deinitialize liboffload: " << Res->Details;
 
   ODBG(ODT_Deinit) << "RTLs unloaded!";
 }
 
-bool PluginManager::initializePlugin(GenericPluginTy &Plugin) {
-  if (Plugin.is_initialized())
+bool PluginManager::initializeDevice(ol_device_handle_t DeviceHandle) {
+  if (PM->DeviceIds.find(DeviceHandle) != PM->DeviceIds.end()) {
+    auto ExclusiveDevicesAccessor = getExclusiveDevicesAccessor();
+    (*ExclusiveDevicesAccessor)[PM->DeviceIds[DeviceHandle]]
+        ->setHasPendingImages(true);
     return true;
+  }
 
-  if (auto Err = Plugin.init()) {
-    std::string InfoMsg = toString(std::move(Err));
-    ODBG(ODT_Init) << "Failed to init plugin: " << InfoMsg;
+  ol_platform_handle_t PlatformHandle;
+  if (auto Ret = olGetDeviceInfo(DeviceHandle, OL_DEVICE_INFO_PLATFORM,
+                                 sizeof(PlatformHandle), &PlatformHandle);
+      Ret != OL_SUCCESS) {
+    REPORT() << "Failed to get platform while initializing device "
+             << DeviceHandle;
     return false;
   }
 
-  ODBG(ODT_Init) << "Registered plugin " << Plugin.getName() << " with "
-                 << Plugin.number_of_devices() << " visible device(s)";
+  GenericPluginTy &Plugin = *__ol_tgt_GetPluginFromPlatform(PlatformHandle);
+  int32_t DeviceId = __ol_tgt_GetPluginDeviceId(DeviceHandle);
 
-  return true;
-}
-
-bool PluginManager::initializeDevice(GenericPluginTy &Plugin,
-                                     int32_t DeviceId) {
-  if (Plugin.is_device_initialized(DeviceId)) {
-    auto ExclusiveDevicesAccessor = getExclusiveDevicesAccessor();
-    (*ExclusiveDevicesAccessor)[PM->DeviceIds[std::make_pair(&Plugin,
-                                                             DeviceId)]]
-        ->setHasPendingImages(true);
-    return true;
-  }
-
-  // Initialize the device information for the RTL we are about to use.
   auto ExclusiveDevicesAccessor = getExclusiveDevicesAccessor();
-
+  // Initialize the device information for the RTL we are about to use.
   int32_t UserId = ExclusiveDevicesAccessor->size();
 
   // Set the device identifier offset in the plugin.
@@ -109,7 +97,8 @@ bool PluginManager::initializeDevice(GenericPluginTy &Plugin,
   Plugin.set_device_identifier(UserId, DeviceId);
 #endif
 
-  auto Device = std::make_unique<DeviceTy>(&Plugin, UserId, DeviceId);
+  auto Device =
+      std::make_unique<DeviceTy>(&Plugin, UserId, DeviceId, DeviceHandle);
   if (auto Err = Device->init()) {
     std::string InfoMsg = toString(std::move(Err));
     ODBG(ODT_Init) << "Failed to init device " << DeviceId << ": " << InfoMsg;
@@ -120,21 +109,18 @@ bool PluginManager::initializeDevice(GenericPluginTy &Plugin,
 
   // We need to map between the plugin's device identifier and the one
   // that OpenMP will use.
-  PM->DeviceIds[std::make_pair(&Plugin, DeviceId)] = UserId;
+  PM->DeviceIds[DeviceHandle] = UserId;
 
   return true;
 }
 
 void PluginManager::initializeAllDevices() {
-  for (auto &Plugin : plugins()) {
-    if (!initializePlugin(Plugin))
-      continue;
-
-    for (int32_t DeviceId = 0; DeviceId < Plugin.number_of_devices();
-         ++DeviceId) {
-      initializeDevice(Plugin, DeviceId);
-    }
-  }
+  olIterateDevices(
+      [](ol_device_handle_t Device, void *UserData) {
+        PM->initializeDevice(Device);
+        return true;
+      },
+      nullptr);
   // After all plugins are initialized, register atExit cleanup handlers
   std::atexit([]() {
     // Interop cleanup should be done before the plugins are deinitialized as
@@ -216,91 +202,115 @@ void PluginManager::registerLib(__tgt_bin_desc *Desc) {
     PM->addDeviceImage(*Desc, Desc->DeviceImages[i]);
 
   // Register the images with the RTLs that understand them, if any.
-  llvm::DenseMap<GenericPluginTy *, llvm::DenseSet<int32_t>> UsedDevices;
+  llvm::SmallVector<ol_device_handle_t> UsedDevices;
   for (int32_t i = 0; i < Desc->NumDeviceImages; ++i) {
     // Obtain the image and information that was previously extracted.
     __tgt_device_image *Img = &Desc->DeviceImages[i];
 
-    GenericPluginTy *FoundRTL = nullptr;
-
-    // Scan the RTLs that have associated images until we find one that supports
-    // the current image.
-    for (auto &R : plugins()) {
-      StringRef Buffer(reinterpret_cast<const char *>(Img->ImageStart),
-                       utils::getPtrDiff(Img->ImageEnd, Img->ImageStart));
-
-      if (!R.isPluginCompatible(Buffer))
-        continue;
-
-      if (!initializePlugin(R))
-        continue;
-
-      if (!R.number_of_devices()) {
-        ODBG(ODT_Init) << "Skipping plugin " << R.getName()
-                       << " with no visible devices";
-        continue;
-      }
-
-      for (int32_t DeviceId = 0; DeviceId < R.number_of_devices(); ++DeviceId) {
-        // We only want a single matching image to be registered for each binary
-        // descriptor. This prevents multiple of the same image from being
-        // registered for the same device in the case that they are mutually
-        // compatible, such as sm_80 and sm_89.
-        if (UsedDevices[&R].contains(DeviceId)) {
-          ODBG(ODT_Init) << "Image " << Img->ImageStart
-                         << " is a duplicate, not loaded on RTL " << R.getName()
-                         << " device " << DeviceId;
-          continue;
-        }
-
-        if (!R.isDeviceCompatible(DeviceId, Buffer))
-          continue;
-
-        ODBG(ODT_Init) << "Image " << Img->ImageStart
-                       << " is compatible with RTL " << R.getName()
-                       << " device " << DeviceId;
-
-        if (!initializeDevice(R, DeviceId))
-          continue;
-
-        // Initialize (if necessary) translation table for this library.
-        PM->TrlTblMtx.lock();
-        if (!PM->HostEntriesBeginToTransTable.count(Desc->HostEntriesBegin)) {
-          PM->HostEntriesBeginRegistrationOrder.push_back(
-              Desc->HostEntriesBegin);
-          TranslationTable &TT =
-              (PM->HostEntriesBeginToTransTable)[Desc->HostEntriesBegin];
-          TT.HostTable.EntriesBegin = Desc->HostEntriesBegin;
-          TT.HostTable.EntriesEnd = Desc->HostEntriesEnd;
-        }
-
-        // Retrieve translation table for this library.
-        TranslationTable &TT =
-            (PM->HostEntriesBeginToTransTable)[Desc->HostEntriesBegin];
-
-        ODBG(ODT_Init) << "Registering image " << Img->ImageStart
-                       << " with RTL " << R.getName();
-
-        auto UserId = PM->DeviceIds[std::make_pair(&R, DeviceId)];
-        if (TT.TargetsTable.size() < static_cast<size_t>(UserId + 1)) {
-          TT.DeviceTables.resize(UserId + 1, {});
-          TT.TargetsImages.resize(UserId + 1, nullptr);
-          TT.TargetsEntries.resize(UserId + 1, {});
-          TT.TargetsTable.resize(UserId + 1, nullptr);
-        }
-
-        // Register the image for this target type and invalidate the table.
-        TT.TargetsImages[UserId] = Img;
-        TT.TargetsTable[UserId] = nullptr;
-
-        UsedDevices[&R].insert(DeviceId);
-        PM->UsedImages.insert(Img);
-        FoundRTL = &R;
-
-        PM->TrlTblMtx.unlock();
-      }
-    }
-    if (!FoundRTL)
+    struct RegisterImageState {
+      __tgt_bin_desc *Desc;
+      __tgt_device_image *Img;
+      llvm::SmallVector<ol_device_handle_t> &UsedDevices;
+      bool FoundRTL = false;
+    } State{Desc, Img, UsedDevices, false};
+
+    if (ol_result_t Res = olIterateCompatibleDevices(
+            Img->ImageStart, utils::getPtrDiff(Img->ImageEnd, Img->ImageStart),
+            [](ol_device_handle_t DeviceHandle, void *Data) {
+              auto &State = *static_cast<RegisterImageState *>(Data);
+
+              ol_platform_handle_t PlatformHandle;
+              if (auto Res =
+                      olGetDeviceInfo(DeviceHandle, OL_DEVICE_INFO_PLATFORM,
+                                      sizeof(PlatformHandle), &PlatformHandle);
+                  Res != OL_SUCCESS) {
+                REPORT() << "Failed to get platform info for device "
+                         << DeviceHandle << ":" << Res->Details;
+                PlatformHandle = nullptr;
+              }
+
+              llvm::SmallString<256> PlatformName("Unknown");
+              if (PlatformHandle) {
+                size_t PlatformNameSize = 0;
+                if (auto Res = olGetPlatformInfoSize(PlatformHandle,
+                                                     OL_PLATFORM_INFO_NAME,
+                                                     &PlatformNameSize);
+                    Res != OL_SUCCESS)
+                  PlatformNameSize = 0;
+
+                PlatformName.resize(PlatformNameSize);
+                if (PlatformNameSize > 0) {
+                  if (auto Res = olGetPlatformInfo(
+                          PlatformHandle, OL_PLATFORM_INFO_NAME,
+                          PlatformNameSize, PlatformName.data());
+                      Res != OL_SUCCESS)
+                    PlatformName = "Unknown";
+                } else
+                  PlatformName = "Unknown";
+              }
+
+              // We only want a single matching image to be registered for each
+              // binary descriptor. This prevents multiple of the same image
+              // from being registered for the same device in the case that
+              // they are mutually compatible, such as sm_80 and sm_89.
+              if (llvm::is_contained(State.UsedDevices, DeviceHandle)) {
+                ODBG(ODT_Init) << "Image " << State.Img->ImageStart
+                               << " is a duplicate, not loaded on RTL "
+                               << PlatformName << " on device " << DeviceHandle;
+                return true;
+              }
+
+              ODBG(ODT_Init)
+                  << "Image " << State.Img->ImageStart << " with RTL "
+                  << PlatformName << " on device " << DeviceHandle;
+
+              PM->initializeDevice(DeviceHandle);
+
+              // Initialize (if necessary) translation table for this library.
+              PM->TrlTblMtx.lock();
+              if (!PM->HostEntriesBeginToTransTable.count(
+                      State.Desc->HostEntriesBegin)) {
+                PM->HostEntriesBeginRegistrationOrder.push_back(
+                    State.Desc->HostEntriesBegin);
+                TranslationTable &TT =
+                    (PM->HostEntriesBeginToTransTable)[State.Desc
+                                                           ->HostEntriesBegin];
+                TT.HostTable.EntriesBegin = State.Desc->HostEntriesBegin;
+                TT.HostTable.EntriesEnd = State.Desc->HostEntriesEnd;
+              }
+
+              // Retrieve translation table for this library.
+              TranslationTable &TT =
+                  (PM->HostEntriesBeginToTransTable)[State.Desc
+                                                         ->HostEntriesBegin];
+
+              ODBG(ODT_Init) << "Registering image " << State.Img->ImageStart
+                             << " with RTL " << PlatformName;
+
+              auto UserId = PM->DeviceIds[DeviceHandle];
+              if (TT.TargetsTable.size() < static_cast<size_t>(UserId + 1)) {
+                TT.DeviceTables.resize(UserId + 1, {});
+                TT.TargetsImages.resize(UserId + 1, nullptr);
+                TT.TargetsEntries.resize(UserId + 1, {});
+                TT.TargetsTable.resize(UserId + 1, nullptr);
+              }
+
+              // Register the image for this target type and invalidate the
+              // table.
+              TT.TargetsImages[UserId] = State.Img;
+              TT.TargetsTable[UserId] = nullptr;
+
+              State.UsedDevices.push_back(DeviceHandle);
+              PM->UsedImages.insert(State.Img);
+              State.FoundRTL = true;
+
+              PM->TrlTblMtx.unlock();
+              return true;
+            },
+            &State))
+      REPORT() << "Failed to iterate compatible devices: " << Res->Details;
+
+    if (!State.FoundRTL)
       ODBG(ODT_Init) << "No RTL found for image " << Img->ImageStart << "!";
   }
   PM->RTLsMtx.unlock();
diff --git a/offload/libompaccsupport/device.cpp b/offload/libompaccsupport/device.cpp
index 688746477861c..51d1194d94882 100644
--- a/offload/libompaccsupport/device.cpp
+++ b/offload/libompaccsupport/device.cpp
@@ -68,9 +68,10 @@ int HostDataToTargetTy::addEventIfNecessary(DeviceTy &Device,
   return OFFLOAD_SUCCESS;
 }
 
-DeviceTy::DeviceTy(GenericPluginTy *RTL, int32_t DeviceID, int32_t RTLDeviceID)
+DeviceTy::DeviceTy(GenericPluginTy *RTL, int32_t DeviceID, int32_t RTLDeviceID,
+                   ol_device_handle_t DeviceHandle)
     : DeviceID(DeviceID), RTL(RTL), RTLDeviceID(RTLDeviceID),
-      MappingInfo(*this) {}
+      DeviceHandle(DeviceHandle), MappingInfo(*this) {}
 
 DeviceTy::~DeviceTy() {
   if (DeviceID == -1 || !(getInfoLevel() & OMP_INFOTYPE_DUMP_TABLE))
@@ -81,11 +82,11 @@ DeviceTy::~DeviceTy() {
 }
 
 llvm::Error DeviceTy::init() {
-  int32_t Ret = RTL->init_device(RTLDeviceID);
-  if (Ret != OFFLOAD_SUCCESS)
-    return error::createOffloadError(error::ErrorCode::BACKEND_FAILURE,
-                                     "failed to initialize device %d\n",
-                                     DeviceID);
+  // TODO: Remove this once all device operations go through liboffload
+  // This just ensures the device is initialized for cases where we go through the
+  // plugin interface.
+  size_t Size = 0;
+  olGetDeviceInfoSize(DeviceHandle, OL_DEVICE_INFO_GLOBAL_MEM_SIZE, &Size);
 
   // Enables recording kernels if set.
   BoolEnvar OMPX_RecordKernel("LIBOMPTARGET_RECORD", false);
@@ -105,7 +106,7 @@ llvm::Error DeviceTy::init() {
     bool EmitReport =
         OMPX_EmitRecordReport || !OMPX_RecordReportFilename.get().empty();
 
-    Ret = RTL->initialize_record_replay(
+    int32_t Ret = RTL->initialize_record_replay(
         RTLDeviceID, OMPX_RecordMemSize, nullptr,
         /*IsRecord=*/true, /*IsNative=*/true, OMPX_RecordOutput, EmitReport,
         OMPX_RecordReportFilename.get().c_str(),
diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index 7b7a62bc2d467..bb196fc7e0df7 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -1703,12 +1703,6 @@ struct GenericPluginTy {
   /// Returns non-zero if the \p Image is compatible with the device.
   int32_t isDeviceCompatible(int32_t DeviceId, StringRef Image);
 
-  /// Returns non-zero if the plugin device has been initialized.
-  int32_t is_device_initialized(int32_t DeviceId) const;
-
-  /// Initialize the device inside of the plugin.
-  int32_t init_device(int32_t DeviceId);
-
   /// Return the number of devices this plugin can support.
   int32_t number_of_devices();
 
diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp
index 573369dc3ba57..1b1764e557d7a 100644
--- a/offload/plugins-nextgen/common/src/PluginInterface.cpp
+++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp
@@ -1498,21 +1498,6 @@ int32_t GenericPluginTy::isDeviceCompatible(int32_t DeviceId, StringRef Image) {
   }
 }
 
-int32_t GenericPluginTy::is_device_initialized(int32_t DeviceId) const {
-  return isValidDeviceId(DeviceId) && Devices[DeviceId] != nullptr;
-}
-
-int32_t GenericPluginTy::init_device(int32_t DeviceId) {
-  auto Err = initDevice(DeviceId);
-  if (Err) {
-    REPORT() << "Failure to initialize device " << DeviceId << ": "
-             << toString(std::move(Err));
-    return OFFLOAD_FAIL;
-  }
-
-  return OFFLOAD_SUCCESS;
-}
-
 int32_t GenericPluginTy::number_of_devices() { return getNumDevices(); }
 
 int32_t GenericPluginTy::is_data_exchangable(int32_t SrcDeviceId,

>From 5310f78f51e7d3f7ca593608ff487cbaa125f2b0 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Fri, 4 Sep 2026 08:59:52 -0700
Subject: [PATCH 08/14] don't initialize devices when validating the image

---
 offload/liboffload/src/OffloadImpl.cpp | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 670179c21f054..62f9878e7cd2b 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -1295,12 +1295,8 @@ Error olCreateProgram_impl(ol_context_handle_t Context,
 Error olIsValidBinary_impl(ol_device_handle_t Device, const void *ProgData,
                            size_t ProgDataSize, bool *IsValid) {
   StringRef Buffer(reinterpret_cast<const char *>(ProgData), ProgDataSize);
-  auto DeviceOrErr = Device->getDevice();
-  if (!DeviceOrErr)
-    return DeviceOrErr.takeError();
-  auto *DeviceImpl = *DeviceOrErr;
   *IsValid =
-      DeviceImpl->Plugin.isDeviceCompatible(DeviceImpl->getDeviceId(), Buffer);
+      Device->Platform.Plugin->isDeviceCompatible(Device->DeviceNum, Buffer);
   return Error::success();
 }
 

>From 80ab4cdb51a82a703bbbe792082013805a712586 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 3 Sep 2026 10:39:20 -0700
Subject: [PATCH 09/14] [OFFLOAD]add olIteratePlatforms

---
 offload/liboffload/API/Platform.td     | 23 +++++++++++++++++++++++
 offload/liboffload/src/OffloadImpl.cpp | 11 +++++++++++
 2 files changed, 34 insertions(+)

diff --git a/offload/liboffload/API/Platform.td b/offload/liboffload/API/Platform.td
index 62810e8fdb7ca..65efec2b8af4a 100644
--- a/offload/liboffload/API/Platform.td
+++ b/offload/liboffload/API/Platform.td
@@ -97,3 +97,26 @@ def olPlatformRegisterRPCCallback : Function {
                       "RPC callback function pointer", PARAM_IN>];
   let returns = [Return<"OL_ERRC_INVALID_PLATFORM">, Return<"OL_ERRC_SUCCESS">];
 }
+
+def ol_platform_iterate_cb_t : FptrTypedef {
+  let desc = "User-provided function to be used with `olIteratePlatforms`";
+  let params = [
+    Param<"ol_platform_handle_t", "Platform", "the platform handle of the current iteration", PARAM_IN>,
+    Param<"void*", "UserData", "optional user data", PARAM_IN_OPTIONAL>
+  ];
+  let return = "bool";
+}
+
+def olIteratePlatforms : Function {
+  let desc = "Iterates over all available platforms, calling the callback for each platform.";
+  let details = [
+    "If the user-provided callback returns `false`, the iteration is stopped."
+  ];
+  let params = [
+    Param<"ol_platform_iterate_cb_t", "Callback", "User-provided function called for each available platform", PARAM_IN>,
+    Param<"void*", "UserData", "Optional user data to pass to the callback", PARAM_IN_OPTIONAL>
+  ];
+  let returns = [
+    Return<"OL_ERRC_INVALID_PLATFORM">
+  ];
+}
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 62f9878e7cd2b..775b4b9ac2f95 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -468,6 +468,17 @@ Error olPlatformRegisterRPCCallback_impl(ol_platform_handle_t Platform,
   return Error::success();
 }
 
+Error olIteratePlatforms_impl(ol_platform_iterate_cb_t Callback,
+                              void *UserData) {
+  for (auto &Platform : OffloadContext::get().Platforms) {
+    if (!Callback(Platform.get(), UserData)) {
+      return Error::success();
+    }
+  }
+
+  return Error::success();
+}
+
 Error olGetDeviceInfoImplDetail(ol_device_handle_t Device,
                                 ol_device_info_t PropName, size_t PropSize,
                                 void *PropValue, size_t *PropSizeRet) {

>From ed5966525f7e71dad216fb585c89a0b93ae51b0c Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 3 Sep 2026 10:56:25 -0700
Subject: [PATCH 10/14] add test

---
 .../platform/olIteratePlatforms.cpp           | 45 +++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 offload/unittests/OffloadAPI/platform/olIteratePlatforms.cpp

diff --git a/offload/unittests/OffloadAPI/platform/olIteratePlatforms.cpp b/offload/unittests/OffloadAPI/platform/olIteratePlatforms.cpp
new file mode 100644
index 0000000000000..4f8f4e35df23b
--- /dev/null
+++ b/offload/unittests/OffloadAPI/platform/olIteratePlatforms.cpp
@@ -0,0 +1,45 @@
+//===------- Offload API tests - olIteratePlatforms -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/Fixtures.hpp"
+#include <OffloadAPI.h>
+#include <gtest/gtest.h>
+
+using olIteratePlatformsTest = OffloadTest;
+
+TEST_F(olIteratePlatformsTest, SuccessEmptyCallback) {
+  ASSERT_SUCCESS(olIteratePlatforms(
+      [](ol_platform_handle_t, void *) { return false; }, nullptr));
+}
+
+TEST_F(olIteratePlatformsTest, SuccessGetPlatform) {
+  uint32_t PlatformCount = 0;
+  ol_platform_handle_t Platform = nullptr;
+
+  ASSERT_SUCCESS(olIteratePlatforms(
+      [](ol_platform_handle_t, void *Data) {
+        auto Count = static_cast<uint32_t *>(Data);
+        *Count += 1;
+        return true;
+      },
+      &PlatformCount));
+
+  if (PlatformCount == 0) {
+    GTEST_SKIP() << "No available platforms.";
+  }
+
+  ASSERT_SUCCESS(olIteratePlatforms(
+      [](ol_platform_handle_t P, void *Data) {
+        auto PlatformPtr = static_cast<ol_platform_handle_t *>(Data);
+        *PlatformPtr = P;
+        return true;
+      },
+      &Platform));
+
+  ASSERT_NE(Platform, nullptr);
+}

>From cf68ec1391130fa116ec4016cc712e3ad810309f Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 3 Sep 2026 10:55:00 -0700
Subject: [PATCH 11/14] [offload][omp] Load plugins through liboffload

---
 offload/include/PluginManager.h            |  3 ++-
 offload/liboffload/exports                 |  2 +-
 offload/liboffload/src/OffloadImpl.cpp     |  5 ++++
 offload/libompaccsupport/PluginManager.cpp | 29 +++++++++++++---------
 4 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/offload/include/PluginManager.h b/offload/include/PluginManager.h
index 6c6fdebe76dff..eea8b62a8c39d 100644
--- a/offload/include/PluginManager.h
+++ b/offload/include/PluginManager.h
@@ -13,6 +13,7 @@
 #ifndef OMPTARGET_PLUGIN_MANAGER_H
 #define OMPTARGET_PLUGIN_MANAGER_H
 
+#include "OffloadAPI.h"
 #include "PluginInterface.h"
 
 #include "DeviceImage.h"
@@ -155,7 +156,7 @@ struct PluginManager {
   llvm::SmallVector<__tgt_bin_desc *> DelayedBinDesc;
 
   // List of all plugins, in use or not.
-  llvm::SmallVector<std::unique_ptr<GenericPluginTy>> Plugins;
+  llvm::SmallVector<GenericPluginTy *> Plugins;
 
   // Mapping of plugins to the OpenMP device identifier.
   llvm::DenseMap<std::pair<const GenericPluginTy *, int32_t>, int32_t>
diff --git a/offload/liboffload/exports b/offload/liboffload/exports
index 5487e16168681..75b1cfd049c82 100644
--- a/offload/liboffload/exports
+++ b/offload/liboffload/exports
@@ -55,9 +55,9 @@ global:
     llvm::omp::target::ompt::Initialized;
     llvm::omp::target::ompt::lookupCallbackByCode;
     llvm::omp::target::ompt::lookupCallbackByName;
-    createPlugin_*;
     # Temporary helpers to help transition of libomptarget to liboffload.
     __ol_tgt_setInfoFlag;
+    __ol_tgt_GetPluginFromPlatform;
   };
 local:
   *;
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 775b4b9ac2f95..b5cc82a764806 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -1629,5 +1629,10 @@ extern "C" void __ol_tgt_setInfoFlag(uint32_t NewInfoLevel) {
   InfoLevel.store(NewInfoLevel);
 }
 
+extern "C" GenericPluginTy *
+__ol_tgt_GetPluginFromPlatform(ol_platform_handle_t Platform) {
+  return Platform->Plugin.get();
+}
+
 } // namespace offload
 } // namespace llvm
diff --git a/offload/libompaccsupport/PluginManager.cpp b/offload/libompaccsupport/PluginManager.cpp
index 41b653a60adfd..b0cb40fb32e8d 100644
--- a/offload/libompaccsupport/PluginManager.cpp
+++ b/offload/libompaccsupport/PluginManager.cpp
@@ -26,9 +26,8 @@ using namespace llvm::omp::target::debug;
 
 PluginManager *PM = nullptr;
 
-// Every plugin exports this method to create an instance of the plugin type.
-#define PLUGIN_TARGET(Name) extern "C" GenericPluginTy *createPlugin_##Name();
-#include "Shared/Targets.def"
+extern "C" GenericPluginTy *
+__ol_tgt_GetPluginFromPlatform(ol_platform_handle_t Platform);
 
 void PluginManager::init() {
   TIMESCOPE();
@@ -38,14 +37,21 @@ void PluginManager::init() {
   }
 
   ODBG(ODT_Init) << "Loading RTLs";
-
-  // Attempt to create an instance of each supported plugin.
-#define PLUGIN_TARGET(Name)                                                    \
-  do {                                                                         \
-    Plugins.emplace_back(                                                      \
-        std::unique_ptr<GenericPluginTy>(createPlugin_##Name()));              \
-  } while (false);
-#include "Shared/Targets.def"
+  if (ol_result_t Res = olInit(nullptr))
+    REPORT() << "Failed to initialize liboffload: " << Res->Details;
+
+  
+  if (ol_result_t Res = olIteratePlatforms(
+          [](ol_platform_handle_t Platform, void *Data) {
+                  auto *PM = static_cast<PluginManager *>(Data);
+                  auto *Plugin = __ol_tgt_GetPluginFromPlatform(Platform);
+                  ODBG(ODT_Init) << "Adding plugin " << Plugin->getName()
+                           << " from liboffload";
+                  PM->Plugins.push_back(Plugin);
+                  return true;
+                },
+                this))
+    REPORT() << "Failed to iterate platforms: " << Res->Details;
 
   ODBG(ODT_Init) << "RTLs loaded!";
 }
@@ -62,7 +68,6 @@ void PluginManager::deinit() {
       std::string InfoMsg = toString(std::move(Err));
       ODBG(ODT_Deinit) << "Failed to deinit plugin: " << InfoMsg;
     }
-    Plugin.release();
   }
 
   ODBG(ODT_Deinit) << "RTLs unloaded!";

>From a91e909fd957893da9818887a8970a9f9a9e1edb Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 3 Sep 2026 10:55:00 -0700
Subject: [PATCH 12/14] [offload][omp] Load plugins through liboffload


>From 236bf828ef9a39b1d9bab83a175738a0b1d4b18d Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Fri, 4 Sep 2026 07:31:12 -0700
Subject: [PATCH 13/14] [offload][omp] Initialize devices through liboffload

---
 offload/include/PluginManager.h               |  12 +-
 offload/include/device.h                      |   5 +-
 offload/liboffload/API/Program.td             |  15 +
 offload/liboffload/exports                    |   5 +-
 offload/liboffload/src/OffloadImpl.cpp        |  30 ++
 offload/libompaccsupport/PluginManager.cpp    | 256 +++++++++---------
 offload/libompaccsupport/device.cpp           |  17 +-
 .../common/include/PluginInterface.h          |   6 -
 .../common/src/PluginInterface.cpp            |  15 -
 9 files changed, 197 insertions(+), 164 deletions(-)

diff --git a/offload/include/PluginManager.h b/offload/include/PluginManager.h
index eea8b62a8c39d..f1937a368186f 100644
--- a/offload/include/PluginManager.h
+++ b/offload/include/PluginManager.h
@@ -120,11 +120,9 @@ struct PluginManager {
     return Devices.getExclusiveAccessor();
   }
 
-  /// Initialize \p Plugin. Returns true on success.
-  bool initializePlugin(GenericPluginTy &Plugin);
-
-  /// Initialize device \p DeviceNo of \p Plugin. Returns true on success.
-  bool initializeDevice(GenericPluginTy &Plugin, int32_t DeviceId);
+  /// Initialize device \p DeviceHandle as on OpenMP device. Returns true on
+  /// success.
+  bool initializeDevice(ol_device_handle_t DeviceHandle);
 
   /// Eagerly initialize all plugins and their devices.
   void initializeAllDevices();
@@ -158,8 +156,8 @@ struct PluginManager {
   // List of all plugins, in use or not.
   llvm::SmallVector<GenericPluginTy *> Plugins;
 
-  // Mapping of plugins to the OpenMP device identifier.
-  llvm::DenseMap<std::pair<const GenericPluginTy *, int32_t>, int32_t>
+  // Mapping of device handles to the OpenMP device identifier.
+  llvm::DenseMap<ol_device_handle_t, int32_t>
       DeviceIds;
 
   // Set of all device images currently in use.
diff --git a/offload/include/device.h b/offload/include/device.h
index 266a2a675df0c..ca32a7b26d1b4 100644
--- a/offload/include/device.h
+++ b/offload/include/device.h
@@ -34,6 +34,7 @@
 #include "llvm/ADT/SmallVector.h"
 
 #include "GlobalHandler.h"
+#include "OffloadAPI.h"
 #include "PluginInterface.h"
 
 using GenericPluginTy = llvm::omp::target::plugin::GenericPluginTy;
@@ -48,8 +49,10 @@ struct DeviceTy {
   int32_t DeviceID;
   GenericPluginTy *RTL;
   int32_t RTLDeviceID;
+  ol_device_handle_t DeviceHandle;
 
-  DeviceTy(GenericPluginTy *RTL, int32_t DeviceID, int32_t RTLDeviceID);
+  DeviceTy(GenericPluginTy *RTL, int32_t DeviceID, int32_t RTLDeviceID,
+           ol_device_handle_t DeviceHandle);
   // DeviceTy is not copyable
   DeviceTy(const DeviceTy &D) = delete;
   DeviceTy &operator=(const DeviceTy &D) = delete;
diff --git a/offload/liboffload/API/Program.td b/offload/liboffload/API/Program.td
index ecc8fb73dad3f..9f945872aec3c 100644
--- a/offload/liboffload/API/Program.td
+++ b/offload/liboffload/API/Program.td
@@ -46,6 +46,21 @@ def olIsValidBinary : Function {
     let returns = [];
 }
 
+def olIterateCompatibleDevices : Function {
+    let desc = "Iterates over all available devices that are compatible with the binary image pointed to by `ProgData`, calling the callback for each device.";
+    let details = [
+        "The provided `ProgData` will not be loaded onto any device",
+        "If the user-provided callback returns `false`, the iteration is stopped."
+    ];
+    let params = [
+        Param<"const void*", "ProgData", "pointer to the program binary data", PARAM_IN>,
+        Param<"size_t", "ProgDataSize", "size of the program binary in bytes", PARAM_IN>,
+        Param<"ol_device_iterate_cb_t", "Callback", "User-provided function called for each compatible device", PARAM_IN>,
+        Param<"void*", "UserData", "Optional user data to pass to the callback", PARAM_IN_OPTIONAL>
+    ];
+    let returns = [];
+}
+
 def olDestroyProgram : Function {
     let desc = "Destroy the program and free all underlying resources.";
     let details = [];
diff --git a/offload/liboffload/exports b/offload/liboffload/exports
index 75b1cfd049c82..c43fbc7830393 100644
--- a/offload/liboffload/exports
+++ b/offload/liboffload/exports
@@ -28,15 +28,11 @@ global:
     "llvm::omp::target::plugin::GenericPluginTy::flush_queue(omp_interop_val_t*)";
     "llvm::omp::target::plugin::GenericPluginTy::get_function(__tgt_device_binary, char const*, void**)";
     "llvm::omp::target::plugin::GenericPluginTy::get_global(__tgt_device_binary, unsigned long, char const*, void**)";
-    "llvm::omp::target::plugin::GenericPluginTy::init()";
-    "llvm::omp::target::plugin::GenericPluginTy::deinit()";
-    "llvm::omp::target::plugin::GenericPluginTy::init_device(int)";
     "llvm::omp::target::plugin::GenericPluginTy::initialize_record_replay(int, long, void*, bool, bool, bool, bool, char const*, char const*)";
     "llvm::omp::target::plugin::GenericPluginTy::isDeviceCompatible(int, llvm::StringRef)";
     "llvm::omp::target::plugin::GenericPluginTy::isPluginCompatible(llvm::StringRef)";
     "llvm::omp::target::plugin::GenericPluginTy::is_accessible_ptr(int, void const*, unsigned long)";
     "llvm::omp::target::plugin::GenericPluginTy::is_data_exchangable(int, int)";
-    "llvm::omp::target::plugin::GenericPluginTy::is_device_initialized(int) const";
     "llvm::omp::target::plugin::GenericPluginTy::is_initialized() const";
     "llvm::omp::target::plugin::GenericPluginTy::launch_kernel(int, void*, llvm::omp::target::plugin::KernelLaunchArgsTy&, __tgt_async_info*)";
     "llvm::omp::target::plugin::GenericPluginTy::load_binary(int, __tgt_device_image*, __tgt_device_binary*)";
@@ -58,6 +54,7 @@ global:
     # Temporary helpers to help transition of libomptarget to liboffload.
     __ol_tgt_setInfoFlag;
     __ol_tgt_GetPluginFromPlatform;
+    __ol_tgt_GetPluginDeviceId;
   };
 local:
   *;
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index b5cc82a764806..08318e98d49ee 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -1311,6 +1311,32 @@ Error olIsValidBinary_impl(ol_device_handle_t Device, const void *ProgData,
   return Error::success();
 }
 
+Error olIterateCompatibleDevices_impl(const void *ProgData, size_t ProgDataSize,
+                                      ol_device_iterate_cb_t Callback,
+                                      void *UserData) {
+  StringRef Buffer(reinterpret_cast<const char *>(ProgData), ProgDataSize);
+
+  for (auto &Platform : OffloadContext::get().Platforms) {
+    if (!Platform->Plugin || !Platform->Plugin->isPluginCompatible(Buffer))
+      continue;
+
+    // If  the image is compatible, initialize the platform.
+    if (auto Err = Platform->init())
+      return Err;
+
+    for (auto &Device : Platform->Devices) {
+      if (!Device->Platform.Plugin->isDeviceCompatible(Device->DeviceNum,
+                                                       Buffer))
+        continue;
+
+      if (!Callback(Device.get(), UserData))
+        return Error::success();
+    }
+  }
+
+  return Error::success();
+}
+
 Error olDestroyProgram_impl(ol_program_handle_t Program) {
   auto &Device = Program->Image->getDevice();
   if (auto Err = Device.unloadBinary(Program->Image))
@@ -1634,5 +1660,9 @@ __ol_tgt_GetPluginFromPlatform(ol_platform_handle_t Platform) {
   return Platform->Plugin.get();
 }
 
+extern "C" int32_t __ol_tgt_GetPluginDeviceId(ol_device_handle_t Device) {
+  return Device->DeviceNum;
+}
+
 } // namespace offload
 } // namespace llvm
diff --git a/offload/libompaccsupport/PluginManager.cpp b/offload/libompaccsupport/PluginManager.cpp
index b0cb40fb32e8d..bb81ede7031e2 100644
--- a/offload/libompaccsupport/PluginManager.cpp
+++ b/offload/libompaccsupport/PluginManager.cpp
@@ -28,6 +28,7 @@ PluginManager *PM = nullptr;
 
 extern "C" GenericPluginTy *
 __ol_tgt_GetPluginFromPlatform(ol_platform_handle_t Platform);
+extern "C" int32_t __ol_tgt_GetPluginDeviceId(ol_device_handle_t Device);
 
 void PluginManager::init() {
   TIMESCOPE();
@@ -60,48 +61,35 @@ void PluginManager::deinit() {
   TIMESCOPE();
   ODBG(ODT_Deinit) << "Unloading RTLs...";
 
-  for (auto &Plugin : Plugins) {
-    if (!Plugin->is_initialized())
-      continue;
-
-    if (auto Err = Plugin->deinit()) {
-      std::string InfoMsg = toString(std::move(Err));
-      ODBG(ODT_Deinit) << "Failed to deinit plugin: " << InfoMsg;
-    }
-  }
+  Plugins.clear();
+  if (auto Res = olShutDown())
+    REPORT() << "Failed to deinitialize liboffload: " << Res->Details;
 
   ODBG(ODT_Deinit) << "RTLs unloaded!";
 }
 
-bool PluginManager::initializePlugin(GenericPluginTy &Plugin) {
-  if (Plugin.is_initialized())
+bool PluginManager::initializeDevice(ol_device_handle_t DeviceHandle) {
+  if (PM->DeviceIds.find(DeviceHandle) != PM->DeviceIds.end()) {
+    auto ExclusiveDevicesAccessor = getExclusiveDevicesAccessor();
+    (*ExclusiveDevicesAccessor)[PM->DeviceIds[DeviceHandle]]
+        ->setHasPendingImages(true);
     return true;
+  }
 
-  if (auto Err = Plugin.init()) {
-    std::string InfoMsg = toString(std::move(Err));
-    ODBG(ODT_Init) << "Failed to init plugin: " << InfoMsg;
+  ol_platform_handle_t PlatformHandle;
+  if (auto Ret = olGetDeviceInfo(DeviceHandle, OL_DEVICE_INFO_PLATFORM,
+                                 sizeof(PlatformHandle), &PlatformHandle);
+      Ret != OL_SUCCESS) {
+    REPORT() << "Failed to get platform while initializing device "
+             << DeviceHandle;
     return false;
   }
 
-  ODBG(ODT_Init) << "Registered plugin " << Plugin.getName() << " with "
-                 << Plugin.number_of_devices() << " visible device(s)";
+  GenericPluginTy &Plugin = *__ol_tgt_GetPluginFromPlatform(PlatformHandle);
+  int32_t DeviceId = __ol_tgt_GetPluginDeviceId(DeviceHandle);
 
-  return true;
-}
-
-bool PluginManager::initializeDevice(GenericPluginTy &Plugin,
-                                     int32_t DeviceId) {
-  if (Plugin.is_device_initialized(DeviceId)) {
-    auto ExclusiveDevicesAccessor = getExclusiveDevicesAccessor();
-    (*ExclusiveDevicesAccessor)[PM->DeviceIds[std::make_pair(&Plugin,
-                                                             DeviceId)]]
-        ->setHasPendingImages(true);
-    return true;
-  }
-
-  // Initialize the device information for the RTL we are about to use.
   auto ExclusiveDevicesAccessor = getExclusiveDevicesAccessor();
-
+  // Initialize the device information for the RTL we are about to use.
   int32_t UserId = ExclusiveDevicesAccessor->size();
 
   // Set the device identifier offset in the plugin.
@@ -109,7 +97,8 @@ bool PluginManager::initializeDevice(GenericPluginTy &Plugin,
   Plugin.set_device_identifier(UserId, DeviceId);
 #endif
 
-  auto Device = std::make_unique<DeviceTy>(&Plugin, UserId, DeviceId);
+  auto Device =
+      std::make_unique<DeviceTy>(&Plugin, UserId, DeviceId, DeviceHandle);
   if (auto Err = Device->init()) {
     std::string InfoMsg = toString(std::move(Err));
     ODBG(ODT_Init) << "Failed to init device " << DeviceId << ": " << InfoMsg;
@@ -120,21 +109,18 @@ bool PluginManager::initializeDevice(GenericPluginTy &Plugin,
 
   // We need to map between the plugin's device identifier and the one
   // that OpenMP will use.
-  PM->DeviceIds[std::make_pair(&Plugin, DeviceId)] = UserId;
+  PM->DeviceIds[DeviceHandle] = UserId;
 
   return true;
 }
 
 void PluginManager::initializeAllDevices() {
-  for (auto &Plugin : plugins()) {
-    if (!initializePlugin(Plugin))
-      continue;
-
-    for (int32_t DeviceId = 0; DeviceId < Plugin.number_of_devices();
-         ++DeviceId) {
-      initializeDevice(Plugin, DeviceId);
-    }
-  }
+  olIterateDevices(
+      [](ol_device_handle_t Device, void *UserData) {
+        PM->initializeDevice(Device);
+        return true;
+      },
+      nullptr);
   // After all plugins are initialized, register atExit cleanup handlers
   std::atexit([]() {
     // Interop cleanup should be done before the plugins are deinitialized as
@@ -216,91 +202,115 @@ void PluginManager::registerLib(__tgt_bin_desc *Desc) {
     PM->addDeviceImage(*Desc, Desc->DeviceImages[i]);
 
   // Register the images with the RTLs that understand them, if any.
-  llvm::DenseMap<GenericPluginTy *, llvm::DenseSet<int32_t>> UsedDevices;
+  llvm::SmallVector<ol_device_handle_t> UsedDevices;
   for (int32_t i = 0; i < Desc->NumDeviceImages; ++i) {
     // Obtain the image and information that was previously extracted.
     __tgt_device_image *Img = &Desc->DeviceImages[i];
 
-    GenericPluginTy *FoundRTL = nullptr;
-
-    // Scan the RTLs that have associated images until we find one that supports
-    // the current image.
-    for (auto &R : plugins()) {
-      StringRef Buffer(reinterpret_cast<const char *>(Img->ImageStart),
-                       utils::getPtrDiff(Img->ImageEnd, Img->ImageStart));
-
-      if (!R.isPluginCompatible(Buffer))
-        continue;
-
-      if (!initializePlugin(R))
-        continue;
-
-      if (!R.number_of_devices()) {
-        ODBG(ODT_Init) << "Skipping plugin " << R.getName()
-                       << " with no visible devices";
-        continue;
-      }
-
-      for (int32_t DeviceId = 0; DeviceId < R.number_of_devices(); ++DeviceId) {
-        // We only want a single matching image to be registered for each binary
-        // descriptor. This prevents multiple of the same image from being
-        // registered for the same device in the case that they are mutually
-        // compatible, such as sm_80 and sm_89.
-        if (UsedDevices[&R].contains(DeviceId)) {
-          ODBG(ODT_Init) << "Image " << Img->ImageStart
-                         << " is a duplicate, not loaded on RTL " << R.getName()
-                         << " device " << DeviceId;
-          continue;
-        }
-
-        if (!R.isDeviceCompatible(DeviceId, Buffer))
-          continue;
-
-        ODBG(ODT_Init) << "Image " << Img->ImageStart
-                       << " is compatible with RTL " << R.getName()
-                       << " device " << DeviceId;
-
-        if (!initializeDevice(R, DeviceId))
-          continue;
-
-        // Initialize (if necessary) translation table for this library.
-        PM->TrlTblMtx.lock();
-        if (!PM->HostEntriesBeginToTransTable.count(Desc->HostEntriesBegin)) {
-          PM->HostEntriesBeginRegistrationOrder.push_back(
-              Desc->HostEntriesBegin);
-          TranslationTable &TT =
-              (PM->HostEntriesBeginToTransTable)[Desc->HostEntriesBegin];
-          TT.HostTable.EntriesBegin = Desc->HostEntriesBegin;
-          TT.HostTable.EntriesEnd = Desc->HostEntriesEnd;
-        }
-
-        // Retrieve translation table for this library.
-        TranslationTable &TT =
-            (PM->HostEntriesBeginToTransTable)[Desc->HostEntriesBegin];
-
-        ODBG(ODT_Init) << "Registering image " << Img->ImageStart
-                       << " with RTL " << R.getName();
-
-        auto UserId = PM->DeviceIds[std::make_pair(&R, DeviceId)];
-        if (TT.TargetsTable.size() < static_cast<size_t>(UserId + 1)) {
-          TT.DeviceTables.resize(UserId + 1, {});
-          TT.TargetsImages.resize(UserId + 1, nullptr);
-          TT.TargetsEntries.resize(UserId + 1, {});
-          TT.TargetsTable.resize(UserId + 1, nullptr);
-        }
-
-        // Register the image for this target type and invalidate the table.
-        TT.TargetsImages[UserId] = Img;
-        TT.TargetsTable[UserId] = nullptr;
-
-        UsedDevices[&R].insert(DeviceId);
-        PM->UsedImages.insert(Img);
-        FoundRTL = &R;
-
-        PM->TrlTblMtx.unlock();
-      }
-    }
-    if (!FoundRTL)
+    struct RegisterImageState {
+      __tgt_bin_desc *Desc;
+      __tgt_device_image *Img;
+      llvm::SmallVector<ol_device_handle_t> &UsedDevices;
+      bool FoundRTL = false;
+    } State{Desc, Img, UsedDevices, false};
+
+    if (ol_result_t Res = olIterateCompatibleDevices(
+            Img->ImageStart, utils::getPtrDiff(Img->ImageEnd, Img->ImageStart),
+            [](ol_device_handle_t DeviceHandle, void *Data) {
+              auto &State = *static_cast<RegisterImageState *>(Data);
+
+              ol_platform_handle_t PlatformHandle;
+              if (auto Res =
+                      olGetDeviceInfo(DeviceHandle, OL_DEVICE_INFO_PLATFORM,
+                                      sizeof(PlatformHandle), &PlatformHandle);
+                  Res != OL_SUCCESS) {
+                REPORT() << "Failed to get platform info for device "
+                         << DeviceHandle << ":" << Res->Details;
+                PlatformHandle = nullptr;
+              }
+
+              llvm::SmallString<256> PlatformName("Unknown");
+              if (PlatformHandle) {
+                size_t PlatformNameSize = 0;
+                if (auto Res = olGetPlatformInfoSize(PlatformHandle,
+                                                     OL_PLATFORM_INFO_NAME,
+                                                     &PlatformNameSize);
+                    Res != OL_SUCCESS)
+                  PlatformNameSize = 0;
+
+                PlatformName.resize(PlatformNameSize);
+                if (PlatformNameSize > 0) {
+                  if (auto Res = olGetPlatformInfo(
+                          PlatformHandle, OL_PLATFORM_INFO_NAME,
+                          PlatformNameSize, PlatformName.data());
+                      Res != OL_SUCCESS)
+                    PlatformName = "Unknown";
+                } else
+                  PlatformName = "Unknown";
+              }
+
+              // We only want a single matching image to be registered for each
+              // binary descriptor. This prevents multiple of the same image
+              // from being registered for the same device in the case that
+              // they are mutually compatible, such as sm_80 and sm_89.
+              if (llvm::is_contained(State.UsedDevices, DeviceHandle)) {
+                ODBG(ODT_Init) << "Image " << State.Img->ImageStart
+                               << " is a duplicate, not loaded on RTL "
+                               << PlatformName << " on device " << DeviceHandle;
+                return true;
+              }
+
+              ODBG(ODT_Init)
+                  << "Image " << State.Img->ImageStart << " with RTL "
+                  << PlatformName << " on device " << DeviceHandle;
+
+              PM->initializeDevice(DeviceHandle);
+
+              // Initialize (if necessary) translation table for this library.
+              PM->TrlTblMtx.lock();
+              if (!PM->HostEntriesBeginToTransTable.count(
+                      State.Desc->HostEntriesBegin)) {
+                PM->HostEntriesBeginRegistrationOrder.push_back(
+                    State.Desc->HostEntriesBegin);
+                TranslationTable &TT =
+                    (PM->HostEntriesBeginToTransTable)[State.Desc
+                                                           ->HostEntriesBegin];
+                TT.HostTable.EntriesBegin = State.Desc->HostEntriesBegin;
+                TT.HostTable.EntriesEnd = State.Desc->HostEntriesEnd;
+              }
+
+              // Retrieve translation table for this library.
+              TranslationTable &TT =
+                  (PM->HostEntriesBeginToTransTable)[State.Desc
+                                                         ->HostEntriesBegin];
+
+              ODBG(ODT_Init) << "Registering image " << State.Img->ImageStart
+                             << " with RTL " << PlatformName;
+
+              auto UserId = PM->DeviceIds[DeviceHandle];
+              if (TT.TargetsTable.size() < static_cast<size_t>(UserId + 1)) {
+                TT.DeviceTables.resize(UserId + 1, {});
+                TT.TargetsImages.resize(UserId + 1, nullptr);
+                TT.TargetsEntries.resize(UserId + 1, {});
+                TT.TargetsTable.resize(UserId + 1, nullptr);
+              }
+
+              // Register the image for this target type and invalidate the
+              // table.
+              TT.TargetsImages[UserId] = State.Img;
+              TT.TargetsTable[UserId] = nullptr;
+
+              State.UsedDevices.push_back(DeviceHandle);
+              PM->UsedImages.insert(State.Img);
+              State.FoundRTL = true;
+
+              PM->TrlTblMtx.unlock();
+              return true;
+            },
+            &State))
+      REPORT() << "Failed to iterate compatible devices: " << Res->Details;
+
+    if (!State.FoundRTL)
       ODBG(ODT_Init) << "No RTL found for image " << Img->ImageStart << "!";
   }
   PM->RTLsMtx.unlock();
diff --git a/offload/libompaccsupport/device.cpp b/offload/libompaccsupport/device.cpp
index 688746477861c..51d1194d94882 100644
--- a/offload/libompaccsupport/device.cpp
+++ b/offload/libompaccsupport/device.cpp
@@ -68,9 +68,10 @@ int HostDataToTargetTy::addEventIfNecessary(DeviceTy &Device,
   return OFFLOAD_SUCCESS;
 }
 
-DeviceTy::DeviceTy(GenericPluginTy *RTL, int32_t DeviceID, int32_t RTLDeviceID)
+DeviceTy::DeviceTy(GenericPluginTy *RTL, int32_t DeviceID, int32_t RTLDeviceID,
+                   ol_device_handle_t DeviceHandle)
     : DeviceID(DeviceID), RTL(RTL), RTLDeviceID(RTLDeviceID),
-      MappingInfo(*this) {}
+      DeviceHandle(DeviceHandle), MappingInfo(*this) {}
 
 DeviceTy::~DeviceTy() {
   if (DeviceID == -1 || !(getInfoLevel() & OMP_INFOTYPE_DUMP_TABLE))
@@ -81,11 +82,11 @@ DeviceTy::~DeviceTy() {
 }
 
 llvm::Error DeviceTy::init() {
-  int32_t Ret = RTL->init_device(RTLDeviceID);
-  if (Ret != OFFLOAD_SUCCESS)
-    return error::createOffloadError(error::ErrorCode::BACKEND_FAILURE,
-                                     "failed to initialize device %d\n",
-                                     DeviceID);
+  // TODO: Remove this once all device operations go through liboffload
+  // This just ensures the device is initialized for cases where we go through the
+  // plugin interface.
+  size_t Size = 0;
+  olGetDeviceInfoSize(DeviceHandle, OL_DEVICE_INFO_GLOBAL_MEM_SIZE, &Size);
 
   // Enables recording kernels if set.
   BoolEnvar OMPX_RecordKernel("LIBOMPTARGET_RECORD", false);
@@ -105,7 +106,7 @@ llvm::Error DeviceTy::init() {
     bool EmitReport =
         OMPX_EmitRecordReport || !OMPX_RecordReportFilename.get().empty();
 
-    Ret = RTL->initialize_record_replay(
+    int32_t Ret = RTL->initialize_record_replay(
         RTLDeviceID, OMPX_RecordMemSize, nullptr,
         /*IsRecord=*/true, /*IsNative=*/true, OMPX_RecordOutput, EmitReport,
         OMPX_RecordReportFilename.get().c_str(),
diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index 7b7a62bc2d467..bb196fc7e0df7 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -1703,12 +1703,6 @@ struct GenericPluginTy {
   /// Returns non-zero if the \p Image is compatible with the device.
   int32_t isDeviceCompatible(int32_t DeviceId, StringRef Image);
 
-  /// Returns non-zero if the plugin device has been initialized.
-  int32_t is_device_initialized(int32_t DeviceId) const;
-
-  /// Initialize the device inside of the plugin.
-  int32_t init_device(int32_t DeviceId);
-
   /// Return the number of devices this plugin can support.
   int32_t number_of_devices();
 
diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp
index 573369dc3ba57..1b1764e557d7a 100644
--- a/offload/plugins-nextgen/common/src/PluginInterface.cpp
+++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp
@@ -1498,21 +1498,6 @@ int32_t GenericPluginTy::isDeviceCompatible(int32_t DeviceId, StringRef Image) {
   }
 }
 
-int32_t GenericPluginTy::is_device_initialized(int32_t DeviceId) const {
-  return isValidDeviceId(DeviceId) && Devices[DeviceId] != nullptr;
-}
-
-int32_t GenericPluginTy::init_device(int32_t DeviceId) {
-  auto Err = initDevice(DeviceId);
-  if (Err) {
-    REPORT() << "Failure to initialize device " << DeviceId << ": "
-             << toString(std::move(Err));
-    return OFFLOAD_FAIL;
-  }
-
-  return OFFLOAD_SUCCESS;
-}
-
 int32_t GenericPluginTy::number_of_devices() { return getNumDevices(); }
 
 int32_t GenericPluginTy::is_data_exchangable(int32_t SrcDeviceId,

>From b13ff37fe0faaaa373b1f263a0e769e72ae86d8d Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Fri, 4 Sep 2026 09:27:41 -0700
Subject: [PATCH 14/14] [offload][omp] Use liboffload queues for memory
 operations- #8

---
 offload/include/PluginManager.h               |  6 ++
 offload/include/device.h                      |  7 +-
 offload/include/omptarget.h                   | 21 +++--
 offload/liboffload/exports                    |  9 +-
 offload/liboffload/src/OffloadImpl.cpp        | 31 ++-----
 offload/libompaccsupport/PluginManager.cpp    | 64 ++++++++++---
 offload/libompaccsupport/device.cpp           | 84 +++++++++++++----
 offload/libomptarget/OpenMP/API.cpp           | 33 +++----
 offload/libomptarget/omptarget.cpp            | 29 +++++-
 .../common/include/PluginInterface.h          | 31 -------
 .../common/src/PluginInterface.cpp            | 90 -------------------
 offload/tools/kernelreplay/CMakeLists.txt     |  1 +
 12 files changed, 188 insertions(+), 218 deletions(-)

diff --git a/offload/include/PluginManager.h b/offload/include/PluginManager.h
index f1937a368186f..ab4726b391843 100644
--- a/offload/include/PluginManager.h
+++ b/offload/include/PluginManager.h
@@ -149,6 +149,8 @@ struct PluginManager {
     return count;
   }
 
+  ol_device_handle_t getHostDevice();
+
 private:
   bool RTLsLoaded = false;
   llvm::SmallVector<__tgt_bin_desc *> DelayedBinDesc;
@@ -180,6 +182,10 @@ struct PluginManager {
   std::list<llvm::SmallVector<__tgt_device_image, 0>> LegacyImages;
   llvm::DenseMap<__tgt_bin_desc *, __tgt_bin_desc> UpgradedDescriptors;
   __tgt_bin_desc *upgradeLegacyEntries(__tgt_bin_desc *Desc);
+
+  // Platform and device handles for host device operations.
+  ol_platform_handle_t HostPlatform = nullptr;
+  ol_device_handle_t HostDevice = nullptr;
 };
 
 /// Initialize the plugin manager and OpenMP runtime.
diff --git a/offload/include/device.h b/offload/include/device.h
index ca32a7b26d1b4..a7293013e0daf 100644
--- a/offload/include/device.h
+++ b/offload/include/device.h
@@ -50,6 +50,7 @@ struct DeviceTy {
   GenericPluginTy *RTL;
   int32_t RTLDeviceID;
   ol_device_handle_t DeviceHandle;
+  ol_context_handle_t Context;
 
   DeviceTy(GenericPluginTy *RTL, int32_t DeviceID, int32_t RTLDeviceID,
            ol_device_handle_t DeviceHandle);
@@ -62,6 +63,9 @@ struct DeviceTy {
   /// Try to initialize the device and return any failure.
   llvm::Error init();
 
+  /// Deinitialize the OpenMP device.
+  llvm::Error deinit();
+
   /// Provide access to the mapping handler.
   MappingInfoTy &getMappingInfo() { return MappingInfo; }
 
@@ -188,9 +192,6 @@ struct DeviceTy {
   }
 
 private:
-  /// Deinitialize the device (and plugin).
-  void deinit();
-
   /// All offload entries available on this device.
   using DeviceOffloadEntriesMapTy =
       llvm::DenseMap<llvm::StringRef, OffloadEntryTy>;
diff --git a/offload/include/omptarget.h b/offload/include/omptarget.h
index db9590844b2fd..73f89c6ef176f 100644
--- a/offload/include/omptarget.h
+++ b/offload/include/omptarget.h
@@ -14,6 +14,8 @@
 #ifndef _OMPTARGET_H_
 #define _OMPTARGET_H_
 
+#include "OffloadAPI.h"
+
 #include "Shared/APITypes.h"
 #include "Shared/Environment.h"
 #include "Shared/SourceInfo.h"
@@ -112,6 +114,11 @@ enum TargetAllocTy : int32_t {
 
 struct DeviceTy;
 
+// temporary helper from liboffload until all usage of AsyncInfo
+// are migrated to use liboffload queues.
+extern "C" __tgt_async_info *
+__ol__tgt_GetAsyncInfoFromQueue(ol_queue_handle_t Queue);
+
 /// The libomptarget wrapper around a __tgt_async_info object directly
 /// associated with a libomptarget layer device. RAII semantics to avoid
 /// mistakes.
@@ -130,20 +137,24 @@ class AsyncInfoTy {
   using PostProcFuncTy = std::function<int()>;
   llvm::SmallVector<PostProcFuncTy> PostProcessingFunctions;
 
-  __tgt_async_info AsyncInfo;
+  ol_queue_handle_t Queue;
   DeviceTy &Device;
 
 public:
   /// Synchronization method to be used.
   SyncTy SyncType;
 
-  AsyncInfoTy(DeviceTy &Device, SyncTy SyncType = SyncTy::BLOCKING)
-      : Device(Device), SyncType(SyncType) {}
-  ~AsyncInfoTy() { synchronize(); }
+  AsyncInfoTy(DeviceTy &Device, SyncTy SyncType = SyncTy::BLOCKING);
+  ~AsyncInfoTy();
 
   /// Implicit conversion to the __tgt_async_info which is used in the
   /// plugin interface.
-  operator __tgt_async_info *() { return &AsyncInfo; }
+  operator __tgt_async_info *() {
+    return __ol__tgt_GetAsyncInfoFromQueue(Queue);
+  }
+
+  /// Get the underlying queue handle.
+  ol_queue_handle_t getQueue() const { return Queue; }
 
   /// Synchronize all pending actions.
   ///
diff --git a/offload/liboffload/exports b/offload/liboffload/exports
index c43fbc7830393..97ac4b32b041b 100644
--- a/offload/liboffload/exports
+++ b/offload/liboffload/exports
@@ -8,21 +8,15 @@ global:
     error::OffloadError::ID;
     "error::OffloadErrCategory()";
     "llvm::omp::target::RPCServerTy::registerCallback(unsigned int (*)(void*, unsigned int))";
-    "llvm::omp::target::plugin::GenericDeviceTy::dataFill(void*, void const*, long, long, __tgt_async_info*)";
     "llvm::omp::target::plugin::GenericPluginTy::async_barrier(omp_interop_val_t*)";
     "llvm::omp::target::plugin::GenericPluginTy::create_event(int, void**)";
     "llvm::omp::target::plugin::GenericPluginTy::create_interop(int, int, interop_spec_t*)";
     "llvm::omp::target::plugin::GenericPluginTy::data_alloc(int, long, void*, int)";
     "llvm::omp::target::plugin::GenericPluginTy::data_delete(int, void*, int)";
-    "llvm::omp::target::plugin::GenericPluginTy::data_exchange(int, void*, int, void*, long)";
-    "llvm::omp::target::plugin::GenericPluginTy::data_exchange_async(int, void*, int, void*, long, __tgt_async_info*)";
     "llvm::omp::target::plugin::GenericPluginTy::data_fence(int, __tgt_async_info*)";
     "llvm::omp::target::plugin::GenericPluginTy::data_lock(int, void*, long, void**)";
     "llvm::omp::target::plugin::GenericPluginTy::data_notify_mapped(int, void*, long)";
     "llvm::omp::target::plugin::GenericPluginTy::data_notify_unmapped(int, void*)";
-    "llvm::omp::target::plugin::GenericPluginTy::data_retrieve_async(int, void*, void*, long, __tgt_async_info*)";
-    "llvm::omp::target::plugin::GenericPluginTy::data_submit(int, void*, void*, long)";
-    "llvm::omp::target::plugin::GenericPluginTy::data_submit_async(int, void*, void*, long, __tgt_async_info*)";
     "llvm::omp::target::plugin::GenericPluginTy::data_unlock(int, void*)";
     "llvm::omp::target::plugin::GenericPluginTy::destroy_event(int, void*)";
     "llvm::omp::target::plugin::GenericPluginTy::flush_queue(omp_interop_val_t*)";
@@ -39,13 +33,11 @@ global:
     "llvm::omp::target::plugin::GenericPluginTy::number_of_devices()";
     "llvm::omp::target::plugin::GenericPluginTy::obtain_device_info(int)";
     "llvm::omp::target::plugin::GenericPluginTy::print_device_info(int)";
-    "llvm::omp::target::plugin::GenericPluginTy::query_async(int, __tgt_async_info*)";
     "llvm::omp::target::plugin::GenericPluginTy::record_event(int, void*, __tgt_async_info*)";
     "llvm::omp::target::plugin::GenericPluginTy::release_interop(int, omp_interop_val_t*)";
     "llvm::omp::target::plugin::GenericPluginTy::set_device_identifier(int, int)";
     "llvm::omp::target::plugin::GenericPluginTy::sync_barrier(omp_interop_val_t*)";
     "llvm::omp::target::plugin::GenericPluginTy::sync_event(int, void*)";
-    "llvm::omp::target::plugin::GenericPluginTy::synchronize(int, __tgt_async_info*)";
     "llvm::omp::target::plugin::GenericPluginTy::use_auto_zero_copy(int)";
     "llvm::omp::target::plugin::GenericPluginTy::wait_event(int, void*, __tgt_async_info*)";
     llvm::omp::target::ompt::Initialized;
@@ -55,6 +47,7 @@ global:
     __ol_tgt_setInfoFlag;
     __ol_tgt_GetPluginFromPlatform;
     __ol_tgt_GetPluginDeviceId;
+    __ol__tgt_GetAsyncInfoFromQueue;
   };
 local:
   *;
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index c307315d13a02..46c2f658b8bec 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -1337,32 +1337,6 @@ Error olIterateCompatibleDevices_impl(const void *ProgData, size_t ProgDataSize,
   return Error::success();
 }
 
-Error olIterateCompatibleDevices_impl(const void *ProgData, size_t ProgDataSize,
-                                      ol_device_iterate_cb_t Callback,
-                                      void *UserData) {
-  StringRef Buffer(reinterpret_cast<const char *>(ProgData), ProgDataSize);
-
-  for (auto &Platform : OffloadContext::get().Platforms) {
-    if (!Platform->Plugin || !Platform->Plugin->isPluginCompatible(Buffer))
-      continue;
-
-    // If  the image is compatible, initialize the platform.
-    if (auto Err = Platform->init())
-      return Err;
-
-    for (auto &Device : Platform->Devices) {
-      if (!Device->Platform.Plugin->isDeviceCompatible(Device->DeviceNum,
-                                                       Buffer))
-        continue;
-
-      if (!Callback(Device.get(), UserData))
-        return Error::success();
-    }
-  }
-
-  return Error::success();
-}
-
 Error olDestroyProgram_impl(ol_program_handle_t Program) {
   auto &Device = Program->Image->getDevice();
   if (auto Err = Device.unloadBinary(Program->Image))
@@ -1690,5 +1664,10 @@ extern "C" int32_t __ol_tgt_GetPluginDeviceId(ol_device_handle_t Device) {
   return Device->DeviceNum;
 }
 
+extern "C" __tgt_async_info *
+__ol__tgt_GetAsyncInfoFromQueue(ol_queue_handle_t Queue) {
+  return Queue->AsyncInfo;
+}
+
 } // namespace offload
 } // namespace llvm
diff --git a/offload/libompaccsupport/PluginManager.cpp b/offload/libompaccsupport/PluginManager.cpp
index bb81ede7031e2..94f2beb6a703d 100644
--- a/offload/libompaccsupport/PluginManager.cpp
+++ b/offload/libompaccsupport/PluginManager.cpp
@@ -41,17 +41,21 @@ void PluginManager::init() {
   if (ol_result_t Res = olInit(nullptr))
     REPORT() << "Failed to initialize liboffload: " << Res->Details;
 
-  
   if (ol_result_t Res = olIteratePlatforms(
           [](ol_platform_handle_t Platform, void *Data) {
-                  auto *PM = static_cast<PluginManager *>(Data);
-                  auto *Plugin = __ol_tgt_GetPluginFromPlatform(Platform);
-                  ODBG(ODT_Init) << "Adding plugin " << Plugin->getName()
+            auto *PM = static_cast<PluginManager *>(Data);
+            auto *Plugin = __ol_tgt_GetPluginFromPlatform(Platform);
+            ODBG(ODT_Init) << "Adding plugin " << Plugin->getName()
                            << " from liboffload";
-                  PM->Plugins.push_back(Plugin);
-                  return true;
-                },
-                this))
+            PM->Plugins.push_back(Plugin);
+            ol_platform_backend_t Backend;
+            olGetPlatformInfo(Platform, OL_PLATFORM_INFO_BACKEND,
+                              sizeof(Backend), &Backend);
+            if (Backend == OL_PLATFORM_BACKEND_HOST)
+              PM->HostPlatform = Platform;
+            return true;
+          },
+          this))
     REPORT() << "Failed to iterate platforms: " << Res->Details;
 
   ODBG(ODT_Init) << "RTLs loaded!";
@@ -61,6 +65,12 @@ void PluginManager::deinit() {
   TIMESCOPE();
   ODBG(ODT_Deinit) << "Unloading RTLs...";
 
+  auto ExclusiveDevicesAccessor = getExclusiveDevicesAccessor();
+  for (auto &Device : *ExclusiveDevicesAccessor)
+    if (auto Err = Device->deinit())
+      REPORT() << "Failed to deinitialize device " << Device->DeviceID
+               << ": " << toString(std::move(Err));
+
   Plugins.clear();
   if (auto Res = olShutDown())
     REPORT() << "Failed to deinitialize liboffload: " << Res->Details;
@@ -68,6 +78,33 @@ void PluginManager::deinit() {
   ODBG(ODT_Deinit) << "RTLs unloaded!";
 }
 
+ol_device_handle_t PluginManager::getHostDevice() {
+  if (!HostDevice) {
+    olIterateDevices(
+        [](ol_device_handle_t D, void *Data) {
+          ol_platform_handle_t Platform;
+          olGetDeviceInfo(D, OL_DEVICE_INFO_PLATFORM, sizeof(Platform),
+                          &Platform);
+          ol_platform_backend_t Backend;
+          olGetPlatformInfo(Platform, OL_PLATFORM_INFO_BACKEND, sizeof(Backend),
+                            &Backend);
+
+          if (Backend == OL_PLATFORM_BACKEND_HOST) {
+            GenericPluginTy *HostPlugin =
+                __ol_tgt_GetPluginFromPlatform(Platform);
+            HostPlugin->set_device_identifier(omp_initial_device, 0);
+            *(static_cast<ol_device_handle_t *>(Data)) = D;
+            return false;
+          }
+
+          return true;
+        },
+        &HostDevice);
+  }
+
+  return HostDevice;
+}
+
 bool PluginManager::initializeDevice(ol_device_handle_t DeviceHandle) {
   if (PM->DeviceIds.find(DeviceHandle) != PM->DeviceIds.end()) {
     auto ExclusiveDevicesAccessor = getExclusiveDevicesAccessor();
@@ -467,11 +504,14 @@ static int loadImagesOntoDevice(DeviceTy &Device) {
           if (!(Entry.Flags & OMP_DECLARE_TARGET_INDIRECT_VTABLE) &&
               !(Entry.Flags & OMP_DECLARE_TARGET_INDIRECT) &&
               ((PM->getRequirements() & OMP_REQ_UNIFIED_SHARED_MEMORY) ||
-               (PM->getRequirements() & OMPX_REQ_AUTO_ZERO_COPY)))
-            if (Device.RTL->data_submit(DeviceId, DeviceEntry.Address,
-                                        Entry.Address,
-                                        Entry.Size) != OFFLOAD_SUCCESS)
+               (PM->getRequirements() & OMPX_REQ_AUTO_ZERO_COPY))) {
+            AsyncInfoTy AsyncInfo(Device);
+            if (Device.submitData(DeviceEntry.Address,
+                                  Entry.Address,
+                                  Entry.Size,
+                                  AsyncInfo) != OFFLOAD_SUCCESS)
               REPORT() << "Failed to write symbol for USM " << Entry.SymbolName;
+           }
         } else if (Entry.Address) {
           if (Device.RTL->get_function(Binary, Entry.SymbolName,
                                        &DeviceEntry.Address) != OFFLOAD_SUCCESS)
diff --git a/offload/libompaccsupport/device.cpp b/offload/libompaccsupport/device.cpp
index 51d1194d94882..254021f30041f 100644
--- a/offload/libompaccsupport/device.cpp
+++ b/offload/libompaccsupport/device.cpp
@@ -82,11 +82,11 @@ DeviceTy::~DeviceTy() {
 }
 
 llvm::Error DeviceTy::init() {
-  // TODO: Remove this once all device operations go through liboffload
-  // This just ensures the device is initialized for cases where we go through the
-  // plugin interface.
-  size_t Size = 0;
-  olGetDeviceInfoSize(DeviceHandle, OL_DEVICE_INFO_GLOBAL_MEM_SIZE, &Size);
+  if (olCreateContext(1, &DeviceHandle, &Context)) {
+    return error::createOffloadError(error::ErrorCode::BACKEND_FAILURE,
+                                     "failed to create context for device %d\n",
+                                     DeviceID);
+  }
 
   // Enables recording kernels if set.
   BoolEnvar OMPX_RecordKernel("LIBOMPTARGET_RECORD", false);
@@ -120,6 +120,15 @@ llvm::Error DeviceTy::init() {
   return llvm::Error::success();
 }
 
+llvm::Error DeviceTy::deinit() {
+  if (olDestroyContext(Context)) {
+    return error::createOffloadError(error::ErrorCode::BACKEND_FAILURE,
+                                     "failed to destroy context for device %d\n",
+                                     DeviceID);
+  }
+  return llvm::Error::success();
+}
+
 // Extract the mapping of host function pointers to device function pointers
 // from the entry table. Functions marked as 'indirect' in OpenMP will have
 // offloading entries generated for them which map the host's function pointer
@@ -292,8 +301,18 @@ int32_t DeviceTy::submitData(void *TgtPtrBegin, void *HstPtrBegin, int64_t Size,
           omp_initial_device, HstPtrBegin, DeviceID, TgtPtrBegin, Size,
           /*CodePtr=*/OMPT_GET_RETURN_ADDRESS);)
 
-  return RTL->data_submit_async(RTLDeviceID, TgtPtrBegin, HstPtrBegin, Size,
-                                AsyncInfo);
+  ol_queue_handle_t queue = AsyncInfo.getQueue();
+  if (!queue)
+    return OFFLOAD_FAIL;
+
+  if (auto Res = olMemcpy(queue, TgtPtrBegin, DeviceHandle, HstPtrBegin,
+                          PM->getHostDevice(), Size)) {
+    REPORT() << "Failure to copy data from host to device. Pointers: host "
+             << "= " << HstPtrBegin << ", device = " << TgtPtrBegin
+             << ", size = " << Size << ": " << Res->Details;
+    return OFFLOAD_FAIL;
+  }
+  return OFFLOAD_SUCCESS;
 }
 
 // Retrieve data from device
@@ -312,8 +331,17 @@ int32_t DeviceTy::retrieveData(void *HstPtrBegin, void *TgtPtrBegin,
           DeviceID, TgtPtrBegin, omp_initial_device, HstPtrBegin, Size,
           /*CodePtr=*/OMPT_GET_RETURN_ADDRESS);)
 
-  return RTL->data_retrieve_async(RTLDeviceID, HstPtrBegin, TgtPtrBegin, Size,
-                                  AsyncInfo);
+  ol_queue_handle_t queue = AsyncInfo.getQueue();
+  if (!queue)
+    return OFFLOAD_FAIL;
+  if (auto Res = olMemcpy(queue, HstPtrBegin, PM->getHostDevice(), TgtPtrBegin,
+                          DeviceHandle, Size)) {
+    REPORT() << "Failure to copy data from device to host. Pointers: host "
+             << "= " << HstPtrBegin << ", device = " << TgtPtrBegin
+             << ", size = " << Size << ": " << Res->Details;
+    return OFFLOAD_FAIL;
+  }
+  return OFFLOAD_SUCCESS;
 }
 
 // Copy data from current device to destination device directly
@@ -330,12 +358,19 @@ int32_t DeviceTy::dataExchange(void *SrcPtr, DeviceTy &DstDev, void *DstPtr,
           RegionInterface.getCallbacks<ompt_target_data_transfer_from_device>(),
           RTLDeviceID, SrcPtr, DstDev.RTLDeviceID, DstPtr, Size,
           /*CodePtr=*/OMPT_GET_RETURN_ADDRESS);)
-  if (!AsyncInfo) {
-    return RTL->data_exchange(RTLDeviceID, SrcPtr, DstDev.RTLDeviceID, DstPtr,
-                              Size);
+
+  ol_queue_handle_t queue = AsyncInfo.getQueue();
+  if (!queue)
+    return OFFLOAD_FAIL;
+  if (auto Res = olMemcpy(queue, DstPtr, DstDev.DeviceHandle, SrcPtr,
+                          DeviceHandle, Size)) {
+    REPORT() << "Failure to copy data from device (" << RTLDeviceID
+             << ") to device (" << DstDev.RTLDeviceID
+             << "). Pointers: host = " << SrcPtr << ", device = " << DstPtr
+             << ", size = " << Size << ": " << Res->Details;
+    return OFFLOAD_FAIL;
   }
-  return RTL->data_exchange_async(RTLDeviceID, SrcPtr, DstDev.RTLDeviceID,
-                                  DstPtr, Size, AsyncInfo);
+  return OFFLOAD_SUCCESS;
 }
 
 int32_t DeviceTy::dataFence(AsyncInfoTy &AsyncInfo) {
@@ -463,11 +498,28 @@ bool DeviceTy::isDataExchangable(const DeviceTy &DstDevice) {
 }
 
 int32_t DeviceTy::synchronize(AsyncInfoTy &AsyncInfo) {
-  return RTL->synchronize(RTLDeviceID, AsyncInfo);
+  ol_queue_handle_t Queue = AsyncInfo.getQueue();
+  if (!Queue)
+    return OFFLOAD_SUCCESS;
+  if (auto Res = olSyncQueue(Queue)) {
+    REPORT() << "Failure to synchronize stream " << Queue << ": "
+             << Res->Details;
+    return OFFLOAD_FAIL;
+  }
+  return OFFLOAD_SUCCESS;
 }
 
 int32_t DeviceTy::queryAsync(AsyncInfoTy &AsyncInfo) {
-  return RTL->query_async(RTLDeviceID, AsyncInfo);
+  ol_queue_handle_t Queue = AsyncInfo.getQueue();
+  if (!Queue)
+    return OFFLOAD_SUCCESS;
+
+  bool isComplete;
+  if (auto Res = olQueryQueue(Queue, &isComplete)) {
+    REPORT() << "Failure to query stream " << Queue << ": " << Res->Details;
+    return OFFLOAD_FAIL;
+  }
+  return OFFLOAD_SUCCESS;
 }
 
 int32_t DeviceTy::createEvent(void **Event) {
diff --git a/offload/libomptarget/OpenMP/API.cpp b/offload/libomptarget/OpenMP/API.cpp
index 1590aab702e26..80a0325a788d1 100644
--- a/offload/libomptarget/OpenMP/API.cpp
+++ b/offload/libomptarget/OpenMP/API.cpp
@@ -335,31 +335,13 @@ EXTERN int omp_target_memcpy(void *Dst, const void *Src, size_t Length,
     if (!SrcDeviceOrErr)
       FATAL_MESSAGE(SrcDevice, "%s",
                     toString(SrcDeviceOrErr.takeError()).c_str());
-    AsyncInfoTy AsyncInfo(*SrcDeviceOrErr);
     auto DstDeviceOrErr = PM->getDevice(DstDevice);
     if (!DstDeviceOrErr)
       FATAL_MESSAGE(DstDevice, "%s",
                     toString(DstDeviceOrErr.takeError()).c_str());
-    // First try to use D2D memcpy which is more efficient. If fails, fall back
-    // to inefficient way.
-    if (SrcDeviceOrErr->isDataExchangable(*DstDeviceOrErr)) {
-      AsyncInfoTy AsyncInfo(*SrcDeviceOrErr);
-      Rc = SrcDeviceOrErr->dataExchange(SrcAddr, *DstDeviceOrErr, DstAddr,
+    AsyncInfoTy AsyncInfo(*SrcDeviceOrErr);
+    Rc = SrcDeviceOrErr->dataExchange(SrcAddr, *DstDeviceOrErr, DstAddr,
                                         Length, AsyncInfo);
-      if (Rc == OFFLOAD_SUCCESS)
-        return OFFLOAD_SUCCESS;
-    }
-
-    void *Buffer = malloc(Length);
-    {
-      AsyncInfoTy AsyncInfo(*SrcDeviceOrErr);
-      Rc = SrcDeviceOrErr->retrieveData(Buffer, SrcAddr, Length, AsyncInfo);
-    }
-    if (Rc == OFFLOAD_SUCCESS) {
-      AsyncInfoTy AsyncInfo(*DstDeviceOrErr);
-      Rc = DstDeviceOrErr->submitData(DstAddr, Buffer, Length, AsyncInfo);
-    }
-    free(Buffer);
   }
 
   ODBG(ODT_Interface) << __func__ << " returns " << Rc;
@@ -486,10 +468,15 @@ EXTERN void *omp_target_memset(void *Ptr, int ByteVal, size_t NumBytes,
     if (!DeviceOrErr)
       FATAL_MESSAGE(DeviceNum, "%s", toString(DeviceOrErr.takeError()).c_str());
     AsyncInfoTy AsyncInfo(*DeviceOrErr);
-    if (auto Error = DeviceOrErr->RTL->getDevice(DeviceOrErr->RTLDeviceID)
-                         .dataFill(Ptr, &ByteVal, 1, NumBytes, AsyncInfo)) {
+    ol_queue_handle_t Queue = AsyncInfo.getQueue();
+    if (!Queue) {
+      ODBG(ODT_Interface) << "Failed to get queue for async memset on device " << DeviceNum;
+      return Ptr;
+    }
+    if (auto Res = olMemFill(Queue, Ptr, 1, &ByteVal, NumBytes)) {
       ODBG(ODT_Interface)
-          << __func__ << " failed to fill memory due to error with dataFill";
+          << __func__ << " failed to fill memory due to error with dataFill:"
+          << Res->Details;
       // If the dataFill failed, let's just not do anything.
       // omp_target_memset does not have any good way to fail.
       // Depending on the RTL implementation, the application will
diff --git a/offload/libomptarget/omptarget.cpp b/offload/libomptarget/omptarget.cpp
index 9e93bca77b292..07286cd75cfae 100644
--- a/offload/libomptarget/omptarget.cpp
+++ b/offload/libomptarget/omptarget.cpp
@@ -43,6 +43,23 @@ using namespace llvm::omp::target::ompt;
 #endif
 using namespace llvm::omp::target::debug;
 
+AsyncInfoTy::AsyncInfoTy(DeviceTy &Device, SyncTy SyncType)
+      : Device(Device), SyncType(SyncType) {
+
+  if (auto Res = olCreateQueue(Device.Context, Device.DeviceHandle, &Queue)) {
+    REPORT() << "Failed to create queue for device " << Device.DeviceHandle
+             << ": " << Res->Details;
+    Queue = nullptr;
+  }
+}
+
+AsyncInfoTy::~AsyncInfoTy() {
+  if (Queue) {
+    synchronize();
+    olDestroyQueue(Queue);
+  }
+}
+
 int AsyncInfoTy::synchronize() {
   int Result = OFFLOAD_SUCCESS;
   if (!isQueueEmpty()) {
@@ -50,9 +67,6 @@ int AsyncInfoTy::synchronize() {
     case SyncTy::BLOCKING:
       // If we have a queue we need to synchronize it now.
       Result = Device.synchronize(*this);
-      assert(AsyncInfo.Queue == nullptr &&
-             "The device plugin should have nulled the queue to indicate there "
-             "are no outstanding actions!");
       break;
     case SyncTy::NON_BLOCKING:
       Result = Device.queryAsync(*this);
@@ -93,7 +107,14 @@ int32_t AsyncInfoTy::runPostProcessing() {
   return OFFLOAD_SUCCESS;
 }
 
-bool AsyncInfoTy::isQueueEmpty() const { return AsyncInfo.Queue == nullptr; }
+bool AsyncInfoTy::isQueueEmpty() const {
+  bool isComplete;
+  if (auto Res = olQueryQueue(Queue, &isComplete)) {
+    REPORT() << "Failed to query queue " << Queue << ": " << Res->Details;
+    return false;
+  }
+  return isComplete;
+}
 
 /* All begin addresses for partially mapped structs must be aligned, up to 16,
  * in order to ensure proper alignment of members. E.g.
diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index bb196fc7e0df7..a53672f0f1288 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -1739,31 +1739,6 @@ struct GenericPluginTy {
   /// Notify t he runtime about a mapping that has been deleted.
   int32_t data_notify_unmapped(int32_t DeviceId, void *HstPtr);
 
-  /// Copy data to the given device.
-  int32_t data_submit(int32_t DeviceId, void *TgtPtr, void *HstPtr,
-                      int64_t Size);
-
-  /// Copy data to the given device asynchronously.
-  int32_t data_submit_async(int32_t DeviceId, void *TgtPtr, void *HstPtr,
-                            int64_t Size, __tgt_async_info *AsyncInfoPtr);
-
-  /// Copy data from the given device.
-  int32_t data_retrieve(int32_t DeviceId, void *HstPtr, void *TgtPtr,
-                        int64_t Size);
-
-  /// Copy data from the given device asynchronously.
-  int32_t data_retrieve_async(int32_t DeviceId, void *HstPtr, void *TgtPtr,
-                              int64_t Size, __tgt_async_info *AsyncInfoPtr);
-
-  /// Exchange memory addresses between two devices.
-  int32_t data_exchange(int32_t SrcDeviceId, void *SrcPtr, int32_t DstDeviceId,
-                        void *DstPtr, int64_t Size);
-
-  /// Exchange memory addresses between two devices asynchronously.
-  int32_t data_exchange_async(int32_t SrcDeviceId, void *SrcPtr,
-                              int DstDeviceId, void *DstPtr, int64_t Size,
-                              __tgt_async_info *AsyncInfo);
-
   /// Places a fence between previous data movements and following data
   /// movements if necessary on the device
   int32_t data_fence(int32_t DeviceId, __tgt_async_info *AsyncInfo);
@@ -1773,12 +1748,6 @@ struct GenericPluginTy {
                         KernelLaunchArgsTy &LaunchArgs,
                         __tgt_async_info *AsyncInfoPtr);
 
-  /// Synchronize an asyncrhonous queue with the plugin runtime.
-  int32_t synchronize(int32_t DeviceId, __tgt_async_info *AsyncInfoPtr);
-
-  /// Query the current state of an asynchronous queue.
-  int32_t query_async(int32_t DeviceId, __tgt_async_info *AsyncInfoPtr);
-
   /// Obtain information about the given device.
   InfoTreeNode obtain_device_info(int32_t DeviceId);
 
diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp
index 1b1764e557d7a..390c0d266aa9b 100644
--- a/offload/plugins-nextgen/common/src/PluginInterface.cpp
+++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp
@@ -1627,72 +1627,6 @@ int32_t GenericPluginTy::data_notify_unmapped(int32_t DeviceId, void *HstPtr) {
   return OFFLOAD_SUCCESS;
 }
 
-int32_t GenericPluginTy::data_submit(int32_t DeviceId, void *TgtPtr,
-                                     void *HstPtr, int64_t Size) {
-  return data_submit_async(DeviceId, TgtPtr, HstPtr, Size,
-                           /*AsyncInfoPtr=*/nullptr);
-}
-
-int32_t GenericPluginTy::data_submit_async(int32_t DeviceId, void *TgtPtr,
-                                           void *HstPtr, int64_t Size,
-                                           __tgt_async_info *AsyncInfoPtr) {
-  auto Err = getDevice(DeviceId).dataSubmit(TgtPtr, HstPtr, Size, AsyncInfoPtr);
-  if (Err) {
-    REPORT() << "Failure to copy data from host to device. Pointers: host "
-             << "= " << HstPtr << ", device = " << TgtPtr << ", size = " << Size
-             << ": " << toString(std::move(Err));
-    return OFFLOAD_FAIL;
-  }
-
-  return OFFLOAD_SUCCESS;
-}
-
-int32_t GenericPluginTy::data_retrieve(int32_t DeviceId, void *HstPtr,
-                                       void *TgtPtr, int64_t Size) {
-  return data_retrieve_async(DeviceId, HstPtr, TgtPtr, Size,
-                             /*AsyncInfoPtr=*/nullptr);
-}
-
-int32_t GenericPluginTy::data_retrieve_async(int32_t DeviceId, void *HstPtr,
-                                             void *TgtPtr, int64_t Size,
-                                             __tgt_async_info *AsyncInfoPtr) {
-  auto Err =
-      getDevice(DeviceId).dataRetrieve(HstPtr, TgtPtr, Size, AsyncInfoPtr);
-  if (Err) {
-    REPORT() << "Failure to copy data from device to host. Pointers: host "
-             << "= " << HstPtr << ", device = " << TgtPtr << ", size = " << Size
-             << ": " << toString(std::move(Err));
-    return OFFLOAD_FAIL;
-  }
-
-  return OFFLOAD_SUCCESS;
-}
-
-int32_t GenericPluginTy::data_exchange(int32_t SrcDeviceId, void *SrcPtr,
-                                       int32_t DstDeviceId, void *DstPtr,
-                                       int64_t Size) {
-  return data_exchange_async(SrcDeviceId, SrcPtr, DstDeviceId, DstPtr, Size,
-                             /*AsyncInfoPtr=*/nullptr);
-}
-
-int32_t GenericPluginTy::data_exchange_async(int32_t SrcDeviceId, void *SrcPtr,
-                                             int DstDeviceId, void *DstPtr,
-                                             int64_t Size,
-                                             __tgt_async_info *AsyncInfo) {
-  GenericDeviceTy &SrcDevice = getDevice(SrcDeviceId);
-  GenericDeviceTy &DstDevice = getDevice(DstDeviceId);
-  auto Err = SrcDevice.dataExchange(SrcPtr, DstDevice, DstPtr, Size, AsyncInfo);
-  if (Err) {
-    REPORT() << "Failure to copy data from device (" << SrcDeviceId
-             << ") to device (" << DstDeviceId
-             << "). Pointers: host = " << SrcPtr << ", device = " << DstPtr
-             << ", size = " << Size << ": " << toString(std::move(Err));
-    return OFFLOAD_FAIL;
-  }
-
-  return OFFLOAD_SUCCESS;
-}
-
 int32_t GenericPluginTy::launch_kernel(int32_t DeviceId, void *TgtEntryPtr,
                                        KernelLaunchArgsTy &LaunchArgs,
                                        __tgt_async_info *AsyncInfoPtr) {
@@ -1707,30 +1641,6 @@ int32_t GenericPluginTy::launch_kernel(int32_t DeviceId, void *TgtEntryPtr,
   return OFFLOAD_SUCCESS;
 }
 
-int32_t GenericPluginTy::synchronize(int32_t DeviceId,
-                                     __tgt_async_info *AsyncInfoPtr) {
-  auto Err = getDevice(DeviceId).synchronize(AsyncInfoPtr);
-  if (Err) {
-    REPORT() << "Failure to synchronize stream " << AsyncInfoPtr->Queue << ": "
-             << toString(std::move(Err));
-    return OFFLOAD_FAIL;
-  }
-
-  return OFFLOAD_SUCCESS;
-}
-
-int32_t GenericPluginTy::query_async(int32_t DeviceId,
-                                     __tgt_async_info *AsyncInfoPtr) {
-  auto Err = getDevice(DeviceId).queryAsync(AsyncInfoPtr);
-  if (Err) {
-    REPORT() << "Failure to query stream " << AsyncInfoPtr->Queue << ": "
-             << toString(std::move(Err));
-    return OFFLOAD_FAIL;
-  }
-
-  return OFFLOAD_SUCCESS;
-}
-
 InfoTreeNode GenericPluginTy::obtain_device_info(int32_t DeviceId) {
   auto InfoOrErr = getDevice(DeviceId).obtainInfo();
   if (auto Err = InfoOrErr.takeError()) {
diff --git a/offload/tools/kernelreplay/CMakeLists.txt b/offload/tools/kernelreplay/CMakeLists.txt
index 66c4b2159822a..d3825ed6801f2 100644
--- a/offload/tools/kernelreplay/CMakeLists.txt
+++ b/offload/tools/kernelreplay/CMakeLists.txt
@@ -6,6 +6,7 @@ llvm_update_compile_flags(llvm-omp-kernel-replay)
 
 target_include_directories(llvm-omp-kernel-replay PRIVATE
   ${LIBOMPTARGET_INCLUDE_DIR}
+  ${CMAKE_CURRENT_BINARY_DIR}/../../liboffload/API
 )
 target_link_libraries(llvm-omp-kernel-replay PRIVATE
   LLVMSupport



More information about the llvm-branch-commits mailing list