[llvm] [offload] lazily load liboffload (PR #221731)

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 21 06:20:28 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-offload

Author: Alex Duran (adurang)

<details>
<summary>Changes</summary>

Currently liboffload initialized all plugins and devices when olInit is called. This poses a problem for libomptarget as it needs to filter platforms based on the image, and for OMPT initialization.

This PR changes liboffload to delay initialization of platforms and devices to the point where it is absolutely needed.

Assisted by Claude.

---

Patch is 27.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/221731.diff


1 Files Affected:

- (modified) offload/liboffload/src/OffloadImpl.cpp (+242-90) 


``````````diff
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 7f804c53142a4..1d323b253d755 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,59 @@ 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 +128,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 +222,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 +249,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 +366,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 +410,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,14 +509,21 @@ 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;
 
-  case OL_DEVICE_INFO_DRIVER_ID:
-    return Info.write<uint32_t>(Device->Device->getDriverId());
+  case OL_DEVICE_INFO_DRIVER_ID: {
+    auto DeviceOrErr = Device->getDevice();
+    if (!DeviceOrErr)
+      return DeviceOrErr.takeError();
+    return Info.write<uint32_t>((*DeviceOrErr)->getDriverId());
+  } break;
 
   default:
     break;
@@ -493,7 +533,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 +650,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();
@@ -621,25 +667,38 @@ Error olCreateContext_impl(size_t DevicesCount, ol_device_handle_t *Devices,
   ol_platform_impl_t *Platform = &Devices[0]->Platform;
   llvm::SmallVector<ol_device_handle_t> DeviceList;
   llvm::SmallVector<plugin::GenericDeviceTy *> PluginDevices;
+
+  auto DeviceOrErr0 = Devices[0]->getDevice();
+  if (!DeviceOrErr0)
+    return DeviceOrErr0.takeError();
+  auto RTLDevice0 = *DeviceOrErr0;
+
   DeviceList.reserve(DevicesCount);
   PluginDevices.reserve(DevicesCount);
   for (size_t I = 0; I < DevicesCount; I++) {
+    auto DeviceOrErr = Devices[I]->getDevice();
+    if (!DeviceOrErr)
+      return DeviceOrErr.takeError();
+    GenericDeviceTy *RTLDevice = *DeviceOrErr;
+
     if (&Devices[I]->Platform != Platform)
       return createOffloadError(
           ErrorCode::INVALID_DEVICE,
           "all devices in a context must belong to the same platform");
-    if (Devices[I]->Device->getDriverId() != Devices[0]->Device->getDriverId())
+    if (RTLDevice->getDriverId() != RTLDevice0->getDriverId())
       return createOffloadError(
           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);
+    PluginDevices.push_back(RTLDevice);
   }
 
   // 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) {
+    if (auto Err = Platform->init())
+      return Err;
     auto PluginCtxOrErr = Platform->Plugin->createPluginContext(PluginDevices);
     if (!PluginCtxOrErr)
       return PluginCtxOrErr.takeError();
@@ -705,13 +764,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 +807,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 +871,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 +943,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 +967,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 +999,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 +1011,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 +1046,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 +1077,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,8 +1101,13 @@ 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(
-      StartEvent->EventInfo, EndEvent->EventInfo);
+  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 +1116,15 @@ 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 +1144,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,15 +1177,19 @@ 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(
-          Event->EventInfo, Queue->AsyncInfo, EnableProfiling)) {
+  if (auto Err = DeviceImpl->recordEvent(Event->EventInfo, Queue->AsyncInfo,
+                                         EnableProfiling)) {
     if (Event->EventInfo) {
-      if (auto DestroyErr = Queue->Device->Device->destroyEvent(
-              Event->EventInfo, EnableProfiling))
+      if (auto DestroyErr =
+              DeviceImpl->destroyEvent(Event->EventInfo, EnableProfiling))
         return joinErrors(std::move(Err), std::move(DestroyErr));
     }
 
@@ -1099,33 +1206,42 @@ 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
   auto QueueImpl = Queue ? Queue->AsyncInfo : nullptr;
 
   if (IsDstHost) {
-    if (auto Res =
-            SrcDevice->Device->dataRetrieve(DstPtr, SrcPtr, Size, QueueImpl))
+    if (auto Res = SrcDeviceImpl->dataRetrieve(DstPtr, SrcPtr, Size, QueueImpl))
       return Res;
   } else if (IsSrcHost) {
-    if (auto Res =
-            DstDevice->Device->dataSubmit(DstPtr, SrcPtr, Size, QueueImpl))
+    if (auto Res = DstDeviceImpl->dataSubmit(DstPtr, SrcPtr, Size, QueueImpl))
       return Res;
   } 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 +1252,9 @@ Error olMemcpy_impl(ol_queue_handle_t Queue, void *DstPtr,
     if (!Buffer)
       return createOffloadError(ErrorCode::OUT_OF_RESOURCES,
                            ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/221731


More information about the llvm-commits mailing list