[llvm-branch-commits] [llvm] Users/adurango/lazy liboffload (PR #221270)

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


https://github.com/adurang updated https://github.com/llvm/llvm-project/pull/221270

>From b6ad182e4c9240e3353b35f345264a76b90f29b8 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 1/2] [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 c2cf3db3bacbc..78594d09d6a5d 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;
@@ -493,7 +528,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");
@@ -606,6 +645,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();
@@ -633,14 +674,20 @@ Error olCreateContext_impl(size_t DevicesCount, ol_device_handle_t *Devices,
           ErrorCode::INVALID_DEVICE,
           "all devices in a context must have the same driver ID");
     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);
@@ -705,13 +752,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();
@@ -743,7 +795,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();
       }
@@ -807,8 +859,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();
@@ -875,16 +931,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;
   }
 
@@ -895,17 +955,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.
@@ -922,7 +987,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;
   }
 
@@ -931,7 +999,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];
@@ -963,7 +1034,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);
@@ -991,7 +1065,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();
@@ -1011,7 +1089,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();
@@ -1021,10 +1103,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);
 }
@@ -1044,8 +1130,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);
@@ -1074,14 +1163,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));
     }
@@ -1099,14 +1192,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
@@ -1114,18 +1219,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)
@@ -1136,9 +1242,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;
@@ -1149,8 +1255,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,
@@ -1160,8 +1269,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,
@@ -1172,8 +1284,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");
@@ -1185,9 +1302,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();
 }
 
@@ -1212,7 +1332,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;
 
@@ -1229,7 +1353,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
@@ -1255,7 +1382,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,
@@ -1266,6 +1392,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);
@@ -1445,13 +1576,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();
@@ -1463,14 +1601,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 2186cfb87532fed046ddd5d4912f5eb92049f23e 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 2/2] 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 78594d09d6a5d..9c2a1beb2b53a 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -1302,12 +1302,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();
 }
 



More information about the llvm-branch-commits mailing list