[llvm] [OFFLOAD][L0] Implement Event APIs (PR #201306)

Alex Duran via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 3 23:50:24 PDT 2026


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

>From 0b381b100489581626288303ea3c00196569895f Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Wed, 3 Jun 2026 03:12:52 -0700
Subject: [PATCH 1/7] [OFFLOAD][L0] Implement Event APIs

---
 .../plugins-nextgen/level_zero/CMakeLists.txt |   1 +
 .../level_zero/dynamic_l0/L0DynWrapper.cpp    |   3 +
 .../level_zero/dynamic_l0/level_zero/ze_api.h |   9 ++
 .../level_zero/include/L0CmdListManager.h     |  18 +++
 .../level_zero/include/L0Context.h            |   1 +
 .../level_zero/include/L0Device.h             |  49 ++-----
 .../level_zero/include/L0Event.h              | 124 ++++++++++++++++++
 .../level_zero/include/L0Memory.h             |  52 --------
 .../level_zero/include/L0Queue.h              |  37 ++++++
 .../level_zero/src/L0Device.cpp               |  61 +++++++++
 .../level_zero/src/L0Event.cpp                |  93 +++++++++++++
 .../level_zero/src/L0Memory.cpp               |  50 -------
 12 files changed, 361 insertions(+), 137 deletions(-)
 create mode 100644 offload/plugins-nextgen/level_zero/include/L0Event.h
 create mode 100644 offload/plugins-nextgen/level_zero/src/L0Event.cpp

diff --git a/offload/plugins-nextgen/level_zero/CMakeLists.txt b/offload/plugins-nextgen/level_zero/CMakeLists.txt
index c11f436861284..c40a375166f8b 100644
--- a/offload/plugins-nextgen/level_zero/CMakeLists.txt
+++ b/offload/plugins-nextgen/level_zero/CMakeLists.txt
@@ -11,6 +11,7 @@ add_target_library(omptarget.rtl.level_zero LEVEL_ZERO)
 set(LEVEL_ZERO_SRC_FILES
   src/L0Context.cpp
   src/L0Device.cpp
+  src/L0Event.cpp
   src/L0Kernel.cpp
   src/L0Memory.cpp
   src/L0Program.cpp
diff --git a/offload/plugins-nextgen/level_zero/dynamic_l0/L0DynWrapper.cpp b/offload/plugins-nextgen/level_zero/dynamic_l0/L0DynWrapper.cpp
index a338e1f024d2c..0e19c6373340a 100644
--- a/offload/plugins-nextgen/level_zero/dynamic_l0/L0DynWrapper.cpp
+++ b/offload/plugins-nextgen/level_zero/dynamic_l0/L0DynWrapper.cpp
@@ -91,6 +91,9 @@ DLWRAP(zeModuleGetFunctionPointer, 3)
 DLWRAP(zesDeviceEnumMemoryModules, 3)
 DLWRAP(zesMemoryGetState, 2)
 DLWRAP(zeCommandListHostSynchronize, 2)
+DLWRAP(zeCommandListAppendSignalEvent, 2)
+DLWRAP(zeCommandListAppendWaitOnEvents, 3)
+DLWRAP(zeEventQueryStatus, 1)
 
 DLWRAP_FINALIZE()
 
diff --git a/offload/plugins-nextgen/level_zero/dynamic_l0/level_zero/ze_api.h b/offload/plugins-nextgen/level_zero/dynamic_l0/level_zero/ze_api.h
index 1e011c2a933a5..b3214378e5da1 100644
--- a/offload/plugins-nextgen/level_zero/dynamic_l0/level_zero/ze_api.h
+++ b/offload/plugins-nextgen/level_zero/dynamic_l0/level_zero/ze_api.h
@@ -862,6 +862,15 @@ ZE_APIEXPORT ze_result_t ZE_APICALL
 zeEventHostSynchronize(ze_event_handle_t hEvent, uint64_t timeout);
 ZE_APIEXPORT ze_result_t ZE_APICALL zeEventQueryKernelTimestamp(
     ze_event_handle_t hEvent, ze_kernel_timestamp_result_t *dstptr);
+ZE_APIEXPORT ze_result_t ZE_APICALL
+zeEventQueryStatus(ze_event_handle_t hEvent);
+
+/* Event append functions on command lists */
+ZE_APIEXPORT ze_result_t ZE_APICALL zeCommandListAppendSignalEvent(
+    ze_command_list_handle_t hCommandList, ze_event_handle_t hEvent);
+ZE_APIEXPORT ze_result_t ZE_APICALL zeCommandListAppendWaitOnEvents(
+    ze_command_list_handle_t hCommandList, uint32_t numEvents,
+    ze_event_handle_t *phEvents);
 
 /* Fence functions */
 ZE_APIEXPORT ze_result_t ZE_APICALL
diff --git a/offload/plugins-nextgen/level_zero/include/L0CmdListManager.h b/offload/plugins-nextgen/level_zero/include/L0CmdListManager.h
index ec4906a1636e9..f17c1149d88cb 100644
--- a/offload/plugins-nextgen/level_zero/include/L0CmdListManager.h
+++ b/offload/plugins-nextgen/level_zero/include/L0CmdListManager.h
@@ -127,6 +127,24 @@ class L0CmdListManagerTy {
     return Plugin::success();
   }
 
+  Error appendSignalEvent(ze_event_handle_t Event) {
+    std::lock_guard<std::mutex> Lock(Mtx);
+    CALL_ZE_RET_ERROR(zeCommandListAppendSignalEvent, CmdList, Event);
+    return Plugin::success();
+  }
+
+  Error appendWaitOnEvents(size_t NumWaitEvents,
+                           ze_event_handle_t *WaitEvents) {
+    std::lock_guard<std::mutex> Lock(Mtx);
+    CALL_ZE_RET_ERROR(zeCommandListAppendWaitOnEvents, CmdList, NumWaitEvents,
+                      WaitEvents);
+    return Plugin::success();
+  }
+
+  Error appendWaitOnEvent(ze_event_handle_t Event) {
+    return appendWaitOnEvents(1, &Event);
+  }
+
   Error appendBarrier(ze_event_handle_t SignalEvent, uint32_t NumWaitEvents,
                       ze_event_handle_t *WaitEvents) {
     std::lock_guard<std::mutex> Lock(Mtx);
diff --git a/offload/plugins-nextgen/level_zero/include/L0Context.h b/offload/plugins-nextgen/level_zero/include/L0Context.h
index 14cb6da9c8da6..3ea68d8dd07a1 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Context.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Context.h
@@ -13,6 +13,7 @@
 #ifndef OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_LEVEL_ZERO_L0CONTEXT_H
 #define OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_LEVEL_ZERO_L0CONTEXT_H
 
+#include "L0Event.h"
 #include "L0Memory.h"
 #include "PerThreadTable.h"
 
diff --git a/offload/plugins-nextgen/level_zero/include/L0Device.h b/offload/plugins-nextgen/level_zero/include/L0Device.h
index f2f75a9d904bf..eb0cfba504188 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Device.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Device.h
@@ -423,10 +423,16 @@ class L0DeviceTy final : public GenericDeviceTy {
   Expected<ze_event_handle_t> getEvent() {
     return l0Context.getEventPool().getEvent();
   }
+  Expected<L0EventTy *> getEventObject() {
+    return l0Context.getEventPool().getEventObject();
+  }
 
   /// Release event to the pool associated to this device.
   Error releaseEvent(ze_event_handle_t Event) {
-    return l0Context.getEventPool().releaseEvent(Event, *this);
+    return l0Context.getEventPool().releaseEvent(Event);
+  }
+  Error releaseEventObject(L0EventTy *EventObj) {
+    return l0Context.getEventPool().releaseEventObject(EventObj);
   }
 
   StagingBufferTy &getStagingBuffer() { return l0Context.getStagingBuffer(); }
@@ -518,44 +524,17 @@ class L0DeviceTy final : public GenericDeviceTy {
                          "enqueueHostCallImpl not implemented yet");
   }
 
-  // Event routines are used to ensure ordering between dataTransfers. Instead
-  // of adding extra events in the queues, we make sure they're ordered by
-  // using the events from the data submission APIs so we don't need to support
-  // these routines.
-  // They still need to report succes to indicate the event are handled
-  // somewhere waitEvent and syncEvent should remain unimplemented.
   Expected<bool> isEventCompleteImpl(void *EventPtr,
-                                     AsyncInfoWrapperTy &) override {
-    return true;
-  }
-
-  Error createEventImpl(void **EventPtrStorage, bool EnableProfiling) override {
-    return Plugin::success();
-  }
-  Error destroyEventImpl(void *EventPtr, bool EnableProfiling) override {
-    return Plugin::success();
-  }
+                                     AsyncInfoWrapperTy &) override;
+  Error createEventImpl(void **EventPtrStorage, bool EnableProfiling) override;
+  Error destroyEventImpl(void *EventPtr, bool EnableProfiling) override;
   Error recordEventImpl(void *EventPtr, AsyncInfoWrapperTy &AsyncInfoWrapper,
-                        bool EnableProfiling) override {
-    return Plugin::success();
-  }
-
+                        bool EnableProfiling) override;
   Error waitEventImpl(void *EventPtr,
-                      AsyncInfoWrapperTy &AsyncInfoWrapper) override {
-    return Plugin::error(error::ErrorCode::UNKNOWN, "%s not implemented yet\n",
-                         __func__);
-  }
-
-  Error syncEventImpl(void *EventPtr) override {
-    return Plugin::error(error::ErrorCode::UNKNOWN, "%s not implemented yet\n",
-                         __func__);
-  }
-
+                      AsyncInfoWrapperTy &AsyncInfoWrapper) override;
+  Error syncEventImpl(void *EventPtr) override;
   Expected<float> getEventElapsedTimeImpl(void *StartEventPtr,
-                                          void *EndEventPtr) override {
-    return Plugin::error(error::ErrorCode::UNKNOWN, "%s not implemented yet\n",
-                         __func__);
-  }
+                                          void *EndEventPtr) override;
 
   Expected<InfoTreeNode> obtainInfoImpl() override;
   uint64_t getClockFrequency() const override { return getClockRate(); }
diff --git a/offload/plugins-nextgen/level_zero/include/L0Event.h b/offload/plugins-nextgen/level_zero/include/L0Event.h
new file mode 100644
index 0000000000000..0400fa33c29d5
--- /dev/null
+++ b/offload/plugins-nextgen/level_zero/include/L0Event.h
@@ -0,0 +1,124 @@
+//===--- Level Zero Target RTL Implementation -----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Level Zero Event and Event Pool abstractions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_LEVEL_ZERO_L0EVENT_H
+#define OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_LEVEL_ZERO_L0EVENT_H
+
+#include <level_zero/ze_api.h>
+#include <memory>
+#include <mutex>
+
+#include "L0Defs.h"
+#include "L0Trace.h"
+
+namespace llvm::omp::target::plugin {
+
+class L0QueueTy;
+
+
+class L0EventTy {
+  ze_event_handle_t ZeEvent;
+  L0QueueTy *Queue = nullptr;
+
+public:
+  L0EventTy(ze_event_handle_t ZeEvent)
+      : ZeEvent(ZeEvent) {}
+  ze_event_handle_t getZeEvent() const { return ZeEvent; }
+  L0QueueTy *getQueue() const { return Queue; }
+
+  Error reset () {
+    Queue = nullptr;
+    CALL_ZE_RET_ERROR(zeEventHostReset, ZeEvent);
+    return Plugin::success();
+  }
+
+  void recordQueue(L0QueueTy &Q) { Queue = &Q; }
+
+  Error synchronize() {
+    CALL_ZE_RET_ERROR(zeEventHostSynchronize, ZeEvent, L0DefaultTimeout);
+    return Plugin::success();
+  }
+
+  Expected<bool> isComplete() {
+    ze_result_t Result;
+
+    CALL_ZE(Result, zeEventQueryStatus, ZeEvent);
+    if (Result == ZE_RESULT_SUCCESS)
+      return true;
+    if (Result == ZE_RESULT_NOT_READY)
+      return false;
+    return Plugin::error(ErrorCode::UNKNOWN, "failed to query event status: %s",
+                         getZeErrorName(Result));
+  }
+};
+
+/// Common event pool used in the plugin. This event pool assumes all events
+/// from the pool are host-visible and use the same event pool flag.
+class EventPoolTy {
+  /// Size of L0 event pool created on demand.
+  size_t PoolSize = 64;
+
+  /// Context of the events.
+  ze_context_handle_t Context = nullptr;
+
+  /// Additional event pool flags common to this pool.
+  uint32_t Flags = 0;
+
+  /// Protection.
+  std::unique_ptr<std::mutex> Mtx;
+
+  /// Created L0 event pools.
+  llvm::SmallVector<ze_event_pool_handle_t> Pools;
+
+  /// L0 events cache.
+  llvm::SmallVector<ze_event_handle_t> Events;
+
+  /// L0 event objects cache.
+  llvm::SmallVector<L0EventTy *> EventObjects;
+
+  // Internal method to get an event from the pool. The caller must hold the lock.
+  Expected<ze_event_handle_t> getEventLocked();
+public:
+  /// Initialize context, flags, and mutex.
+  Error init(ze_context_handle_t ContextIn, uint32_t FlagsIn) {
+    Context = ContextIn;
+    Flags = FlagsIn;
+    Mtx.reset(new std::mutex);
+    return Plugin::success();
+  }
+
+  /// Destroys L0 resources.
+  Error deinit() {
+    for (auto *EventObj : EventObjects)
+      delete EventObj;
+    for (auto E : Events)
+      CALL_ZE_RET_ERROR(zeEventDestroy, E);
+    for (auto P : Pools)
+      CALL_ZE_RET_ERROR(zeEventPoolDestroy, P);
+    return Plugin::success();
+  }
+
+  /// Get a free event from the pool.
+  Expected<ze_event_handle_t> getEvent() {
+    std::lock_guard<std::mutex> Lock(*Mtx);
+    return getEventLocked();
+  }
+  Expected<L0EventTy *> getEventObject();
+
+  /// Return an event to the pool.
+  Error releaseEvent(ze_event_handle_t Event);
+  Error releaseEventObject(L0EventTy *EventObj);
+};
+
+} // namespace llvm::omp::target::plugin
+
+#endif // OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_LEVEL_ZERO_L0EVENT_H
diff --git a/offload/plugins-nextgen/level_zero/include/L0Memory.h b/offload/plugins-nextgen/level_zero/include/L0Memory.h
index 2e0f43497eb2f..e666062d005eb 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Memory.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Memory.h
@@ -422,58 +422,6 @@ class MemAllocatorTy {
   }
 }; /// MemAllocatorTy
 
-/// Common event pool used in the plugin. This event pool assumes all events
-/// from the pool are host-visible and use the same event pool flag.
-class EventPoolTy {
-  /// Size of L0 event pool created on demand.
-  size_t PoolSize = 64;
-
-  /// Context of the events.
-  ze_context_handle_t Context = nullptr;
-
-  /// Additional event pool flags common to this pull.
-  uint32_t Flags = 0;
-
-  /// Protection.
-  std::unique_ptr<std::mutex> Mtx;
-
-  /// List of created L0 event pools.
-  std::list<ze_event_pool_handle_t> Pools;
-
-  /// List of free L0 events.
-  std::list<ze_event_handle_t> Events;
-
-#ifdef OMPT_SUPPORT
-  /// Event to OMPT record map. The timestamp information is recorded to the
-  /// OMPT record before the event is recycled.
-  std::unordered_map<ze_event_handle_t, ompt_record_ompt_t *> EventToRecord;
-#endif // OMPT_SUPPORT
-
-public:
-  /// Initialize context, flags, and mutex.
-  Error init(ze_context_handle_t ContextIn, uint32_t FlagsIn) {
-    Context = ContextIn;
-    Flags = FlagsIn;
-    Mtx.reset(new std::mutex);
-    return Plugin::success();
-  }
-
-  /// Destroys L0 resources.
-  Error deinit() {
-    for (auto E : Events)
-      CALL_ZE_RET_ERROR(zeEventDestroy, E);
-    for (auto P : Pools)
-      CALL_ZE_RET_ERROR(zeEventPoolDestroy, P);
-    return Plugin::success();
-  }
-
-  /// Get a free event from the pool.
-  Expected<ze_event_handle_t> getEvent();
-
-  /// Return an event to the pool.
-  Error releaseEvent(ze_event_handle_t Event, L0DeviceTy &Device);
-};
-
 /// Staging buffer.
 /// A single staging buffer is not enough when batching is enabled since there
 /// can be multiple pending copy operations.
diff --git a/offload/plugins-nextgen/level_zero/include/L0Queue.h b/offload/plugins-nextgen/level_zero/include/L0Queue.h
index 4f888b997e120..ee5b9491ca2ec 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Queue.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Queue.h
@@ -14,6 +14,7 @@
 #define OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_LEVEL_ZERO_ASYNCQUEUE_H
 
 #include "L0Defs.h"
+#include "L0Event.h"
 #include "L0Trace.h"
 #include "PluginInterface.h"
 
@@ -83,12 +84,38 @@ class L0QueueTy {
 
   Error dataFence() { return dataFenceImpl(); }
 
+  Error appendSignalEvent(L0EventTy *Event) {
+    return appendSignalEventImpl(Event->getZeEvent());
+  }
+  Error appendWaitOnEvent(L0EventTy *Event) {
+    if (Event->getQueue() == this)
+      return Plugin::success();
+    return appendWaitOnEventImpl(Event->getZeEvent());
+  }
+  Error synchronizeEvent(L0EventTy *Event) {
+    if (hasPendingMemoryCopies())
+      if (auto Err = synchronize())
+        return Err;
+    return Event->synchronize();
+  }
+  Expected<bool> isEventComplete(L0EventTy *Event) {
+    if (hasPendingMemoryCopies()) {
+      auto PendingWorkOrErr = hasPendingWork();
+      if (!PendingWorkOrErr)
+        return PendingWorkOrErr.takeError();
+      if (*PendingWorkOrErr)
+        return false;
+    }
+    return Event->isComplete();
+  }
+
   virtual Error initImpl() { return Plugin::success(); }
   virtual Error deinitImpl() { return Plugin::success(); }
   virtual void resetImpl() {}
 
   virtual Error synchronizeImpl() = 0;
   virtual Expected<bool> hasPendingWorkImpl() = 0;
+  virtual bool hasPendingMemoryCopies() { return false; }
   virtual Error memoryCopyImpl(void *Dst, const void *Src, size_t Size) = 0;
   virtual Error dataRetrieveImpl(void *HstPtr, const void *TgtPtr,
                                  int64_t Size) {
@@ -105,6 +132,13 @@ class L0QueueTy {
     return CmdList->appendMemoryFill(Ptr, Pattern, PatternSize, Size);
   }
   virtual Error dataFenceImpl() = 0;
+
+  virtual Error appendSignalEventImpl(ze_event_handle_t Event) {
+    return CmdList->appendSignalEvent(Event);
+  }
+  virtual Error appendWaitOnEventImpl(ze_event_handle_t Event) {
+    return CmdList->appendWaitOnEvent(Event);
+  }
 };
 
 class L0AsyncQueueTy : public L0QueueTy {
@@ -143,6 +177,9 @@ class L0AsyncQueueTy : public L0QueueTy {
   void resetImpl() override;
   Error synchronizeImpl() override;
   Expected<bool> hasPendingWorkImpl() override;
+  bool hasPendingMemoryCopies() override {
+    return !H2MList.empty() || !USM2MList.empty();
+  }
   Error memoryCopyImpl(void *Dst, const void *Src, size_t Size) override;
   Error dataRetrieveImpl(void *HstPtr, const void *TgtPtr,
                          int64_t Size) override;
diff --git a/offload/plugins-nextgen/level_zero/src/L0Device.cpp b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
index 1afed11b965f0..389005ce9e99a 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Device.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
@@ -12,6 +12,7 @@
 
 #include "L0Device.h"
 #include "L0Defs.h"
+#include "L0Event.h"
 #include "L0Interop.h"
 #include "L0Plugin.h"
 #include "L0Program.h"
@@ -745,6 +746,66 @@ Expected<bool> L0DeviceTy::isAccessiblePtrImpl(const void *Ptr, size_t Size) {
   return getMemAllocator(Ptr).contains(Ptr, Size);
 }
 
+Error L0DeviceTy::createEventImpl(void **EventPtrStorage,
+                                  bool EnableProfiling) {
+  auto EventOrErr = getEventObject();
+  if (!EventOrErr)
+    return EventOrErr.takeError();
+  *EventPtrStorage = *EventOrErr;
+  ;
+  return Plugin::success();
+}
+
+Error L0DeviceTy::destroyEventImpl(void *EventPtr, bool EnableProfiling) {
+  L0EventTy *Event = static_cast<L0EventTy *>(EventPtr);
+  return releaseEventObject(Event);
+}
+
+Error L0DeviceTy::recordEventImpl(void *EventPtr,
+                                  AsyncInfoWrapperTy &AsyncInfoWrapper,
+                                  bool EnableProfiling) {
+  auto QueueOrErr = getOrCreateQueue(AsyncInfoWrapper);
+  if (!QueueOrErr)
+    return QueueOrErr.takeError();
+  L0QueueTy *Queue = *QueueOrErr;
+  L0EventTy *Event = static_cast<L0EventTy *>(EventPtr);
+  Event->recordQueue(*Queue);
+  return Queue->appendSignalEvent(Event);
+}
+
+Error L0DeviceTy::waitEventImpl(void *EventPtr,
+                                AsyncInfoWrapperTy &AsyncInfoWrapper) {
+  auto QueueOrErr = getOrCreateQueue(AsyncInfoWrapper);
+  if (!QueueOrErr)
+    return QueueOrErr.takeError();
+  L0QueueTy *Queue = *QueueOrErr;
+  L0EventTy *Event = static_cast<L0EventTy *>(EventPtr);
+  return Queue->appendWaitOnEvent(Event);
+}
+
+Error L0DeviceTy::syncEventImpl(void *EventPtr) {
+  L0EventTy *Event = static_cast<L0EventTy *>(EventPtr);
+  if (!Event->getQueue())
+    return Plugin::error(ErrorCode::INVALID_ARGUMENT,
+                         "event does not have any associated queue");
+  return Event->getQueue()->synchronizeEvent(Event);
+}
+
+Expected<bool> L0DeviceTy::isEventCompleteImpl(void *EventPtr,
+                                               AsyncInfoWrapperTy &) {
+  L0EventTy *Event = static_cast<L0EventTy *>(EventPtr);
+  if (!Event->getQueue())
+    return Plugin::error(ErrorCode::INVALID_ARGUMENT,
+                         "event does not have any associated queue");
+  return Event->getQueue()->isEventComplete(Event);
+}
+
+Expected<float> L0DeviceTy::getEventElapsedTimeImpl(void *StartEventPtr,
+                                                    void *EndEventPtr) {
+  return Plugin::error(error::ErrorCode::UNKNOWN, "%s not implemented yet\n",
+                       __func__);
+}
+
 Error L0DeviceTy::callGlobalConstructors(GenericPluginTy &Plugin,
                                          DeviceImageTy &Image) {
   return callGlobalCtorDtorCommon(Plugin, Image, /*IsCtor=*/true);
diff --git a/offload/plugins-nextgen/level_zero/src/L0Event.cpp b/offload/plugins-nextgen/level_zero/src/L0Event.cpp
new file mode 100644
index 0000000000000..7dd60acbe2a51
--- /dev/null
+++ b/offload/plugins-nextgen/level_zero/src/L0Event.cpp
@@ -0,0 +1,93 @@
+//===--- Level Zero Target RTL Implementation -----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Level Zero Event and Event Pool implementations.
+//
+//===----------------------------------------------------------------------===//
+
+#include "L0Event.h"
+#include "L0Device.h"
+#include "L0Trace.h"
+
+namespace llvm::omp::target::plugin {
+
+Expected<ze_event_handle_t> EventPoolTy::getEventLocked() {
+  if (Events.empty()) {
+    // Need to create a new L0 pool.
+    ze_event_pool_desc_t Desc{ZE_STRUCTURE_TYPE_EVENT_POOL_DESC, nullptr, 0, 0};
+    Desc.flags = ZE_EVENT_POOL_FLAG_HOST_VISIBLE | Flags;
+    Desc.count = PoolSize;
+    ze_event_pool_handle_t Pool;
+    CALL_ZE_RET_ERROR(zeEventPoolCreate, Context, &Desc, 0, nullptr, &Pool);
+    Pools.push_back(Pool);
+
+    // Create events.
+    ze_event_desc_t EventDesc{ZE_STRUCTURE_TYPE_EVENT_DESC, nullptr, 0, 0, 0};
+    EventDesc.wait = 0;
+    EventDesc.signal = ZE_EVENT_SCOPE_FLAG_HOST;
+    uint32_t CreatedEvents = 0;
+    for (uint32_t I = 0; I < PoolSize; I++) {
+      EventDesc.index = I;
+      ze_event_handle_t Event;
+      ze_result_t RC;
+      CALL_ZE(RC, zeEventCreate, Pool, &EventDesc, &Event);
+      if (RC != ZE_RESULT_SUCCESS) {
+        // Log the error and skip this event.
+        ODBG(OLDT_Init) << "Warning: zeEventCreate failed at index " << I
+                        << " with code " << RC << ". Skipping this event.";
+        continue;
+      }
+      Events.push_back(Event);
+      CreatedEvents++;
+    }
+    PoolSize = CreatedEvents;
+    ODBG(OLDT_Init) << "Created a new event pool " << Pool << " with "
+                    << PoolSize << " events";
+  }
+
+  auto Ret = Events.back();
+  Events.pop_back();
+
+  return Ret;
+}
+
+/// Return an event to the pool.
+Error EventPoolTy::releaseEvent(ze_event_handle_t Event) {
+  std::lock_guard<std::mutex> Lock(*Mtx);
+  CALL_ZE_RET_ERROR(zeEventHostReset, Event);
+  Events.push_back(Event);
+  return Plugin::success();
+}
+
+Expected<L0EventTy *> EventPoolTy::getEventObject() {
+  std::lock_guard<std::mutex> Lock(*Mtx);
+
+  if (EventObjects.empty()) {
+    auto EventOrErr = getEventLocked();
+    if (!EventOrErr)
+      return EventOrErr.takeError();
+    auto Event = *EventOrErr;
+    auto *EventObj = new L0EventTy(Event);
+    return EventObj;
+  }
+
+  auto *Ret = EventObjects.back();
+  if (auto Err = Ret->reset())
+    return std::move(Err);
+
+  EventObjects.pop_back();
+  return Ret;
+}
+
+Error EventPoolTy::releaseEventObject(L0EventTy *EventObj) {
+  std::lock_guard<std::mutex> Lock(*Mtx);
+  EventObjects.push_back(EventObj);
+  return Plugin::success();
+}
+
+} // namespace llvm::omp::target::plugin
diff --git a/offload/plugins-nextgen/level_zero/src/L0Memory.cpp b/offload/plugins-nextgen/level_zero/src/L0Memory.cpp
index 828ea426c5a7b..7aa25ea529d08 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Memory.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Memory.cpp
@@ -696,54 +696,4 @@ Error MemAllocatorTy::deallocFromL0(void *Ptr) {
   return Plugin::success();
 }
 
-Expected<ze_event_handle_t> EventPoolTy::getEvent() {
-  std::lock_guard<std::mutex> Lock(*Mtx);
-
-  if (Events.empty()) {
-    // Need to create a new L0 pool.
-    ze_event_pool_desc_t Desc{ZE_STRUCTURE_TYPE_EVENT_POOL_DESC, nullptr, 0, 0};
-    Desc.flags = ZE_EVENT_POOL_FLAG_HOST_VISIBLE | Flags;
-    Desc.count = PoolSize;
-    ze_event_pool_handle_t Pool;
-    CALL_ZE_RET_ERROR(zeEventPoolCreate, Context, &Desc, 0, nullptr, &Pool);
-    Pools.push_back(Pool);
-
-    // Create events.
-    ze_event_desc_t EventDesc{ZE_STRUCTURE_TYPE_EVENT_DESC, nullptr, 0, 0, 0};
-    EventDesc.wait = 0;
-    EventDesc.signal = ZE_EVENT_SCOPE_FLAG_HOST;
-    uint32_t CreatedEvents = 0;
-    for (uint32_t I = 0; I < PoolSize; I++) {
-      EventDesc.index = I;
-      ze_event_handle_t Event;
-      ze_result_t RC;
-      CALL_ZE(RC, zeEventCreate, Pool, &EventDesc, &Event);
-      if (RC != ZE_RESULT_SUCCESS) {
-        // Log the error and skip this event.
-        ODBG(OLDT_Init) << "Warning: zeEventCreate failed at index " << I
-                        << " with code " << RC << ". Skipping this event.";
-        continue;
-      }
-      Events.push_back(Event);
-      CreatedEvents++;
-    }
-    PoolSize = CreatedEvents;
-    ODBG(OLDT_Init) << "Created a new event pool " << Pool << " with "
-                    << PoolSize << " events";
-  }
-
-  auto Ret = Events.back();
-  Events.pop_back();
-
-  return Ret;
-}
-
-/// Return an event to the pool.
-Error EventPoolTy::releaseEvent(ze_event_handle_t Event, L0DeviceTy &Device) {
-  std::lock_guard<std::mutex> Lock(*Mtx);
-  CALL_ZE_RET_ERROR(zeEventHostReset, Event);
-  Events.push_back(Event);
-  return Plugin::success();
-}
-
 } // namespace llvm::omp::target::plugin

>From df219b1fc4e14ec868e423baf16dacf1cc7d7ce6 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Wed, 3 Jun 2026 03:20:21 -0700
Subject: [PATCH 2/7] format

---
 offload/plugins-nextgen/level_zero/include/L0Event.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/offload/plugins-nextgen/level_zero/include/L0Event.h b/offload/plugins-nextgen/level_zero/include/L0Event.h
index 0400fa33c29d5..50c8badf37f43 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Event.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Event.h
@@ -24,18 +24,16 @@ namespace llvm::omp::target::plugin {
 
 class L0QueueTy;
 
-
 class L0EventTy {
   ze_event_handle_t ZeEvent;
   L0QueueTy *Queue = nullptr;
 
 public:
-  L0EventTy(ze_event_handle_t ZeEvent)
-      : ZeEvent(ZeEvent) {}
+  L0EventTy(ze_event_handle_t ZeEvent) : ZeEvent(ZeEvent) {}
   ze_event_handle_t getZeEvent() const { return ZeEvent; }
   L0QueueTy *getQueue() const { return Queue; }
 
-  Error reset () {
+  Error reset() {
     Queue = nullptr;
     CALL_ZE_RET_ERROR(zeEventHostReset, ZeEvent);
     return Plugin::success();
@@ -85,8 +83,10 @@ class EventPoolTy {
   /// L0 event objects cache.
   llvm::SmallVector<L0EventTy *> EventObjects;
 
-  // Internal method to get an event from the pool. The caller must hold the lock.
+  // Internal method to get an event from the pool. The caller must hold the
+  // lock.
   Expected<ze_event_handle_t> getEventLocked();
+
 public:
   /// Initialize context, flags, and mutex.
   Error init(ze_context_handle_t ContextIn, uint32_t FlagsIn) {

>From 3aac9e97e28a01b57badee6192031a14acb4ceef Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Wed, 3 Jun 2026 12:45:18 -0700
Subject: [PATCH 3/7] Address comments

---
 .../plugins-nextgen/level_zero/include/L0Event.h    |  4 ++--
 offload/plugins-nextgen/level_zero/src/L0Device.cpp |  2 +-
 offload/plugins-nextgen/level_zero/src/L0Event.cpp  | 13 ++++++++-----
 3 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/offload/plugins-nextgen/level_zero/include/L0Event.h b/offload/plugins-nextgen/level_zero/include/L0Event.h
index 50c8badf37f43..fc01bdcca7ace 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Event.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Event.h
@@ -25,7 +25,7 @@ namespace llvm::omp::target::plugin {
 class L0QueueTy;
 
 class L0EventTy {
-  ze_event_handle_t ZeEvent;
+  ze_event_handle_t ZeEvent = nullptr;
   L0QueueTy *Queue = nullptr;
 
 public:
@@ -39,7 +39,7 @@ class L0EventTy {
     return Plugin::success();
   }
 
-  void recordQueue(L0QueueTy &Q) { Queue = &Q; }
+  void setQueue(L0QueueTy &Q) { Queue = &Q; }
 
   Error synchronize() {
     CALL_ZE_RET_ERROR(zeEventHostSynchronize, ZeEvent, L0DefaultTimeout);
diff --git a/offload/plugins-nextgen/level_zero/src/L0Device.cpp b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
index 389005ce9e99a..bd2d39f9b3a5e 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Device.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
@@ -769,7 +769,7 @@ Error L0DeviceTy::recordEventImpl(void *EventPtr,
     return QueueOrErr.takeError();
   L0QueueTy *Queue = *QueueOrErr;
   L0EventTy *Event = static_cast<L0EventTy *>(EventPtr);
-  Event->recordQueue(*Queue);
+  Event->setQueue(*Queue);
   return Queue->appendSignalEvent(Event);
 }
 
diff --git a/offload/plugins-nextgen/level_zero/src/L0Event.cpp b/offload/plugins-nextgen/level_zero/src/L0Event.cpp
index 7dd60acbe2a51..cb70df71da74d 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Event.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Event.cpp
@@ -19,9 +19,12 @@ namespace llvm::omp::target::plugin {
 Expected<ze_event_handle_t> EventPoolTy::getEventLocked() {
   if (Events.empty()) {
     // Need to create a new L0 pool.
-    ze_event_pool_desc_t Desc{ZE_STRUCTURE_TYPE_EVENT_POOL_DESC, nullptr, 0, 0};
+    ze_event_pool_desc_t Desc{/* stype */ ZE_STRUCTURE_TYPE_EVENT_POOL_DESC,
+                              /* pNext */ nullptr,
+                              /* flags */ 0,
+                              /* count */ 0};
     Desc.flags = ZE_EVENT_POOL_FLAG_HOST_VISIBLE | Flags;
-    Desc.count = PoolSize;
+    Desc.count = static_cast<uint32_t>(PoolSize);
     ze_event_pool_handle_t Pool;
     CALL_ZE_RET_ERROR(zeEventPoolCreate, Context, &Desc, 0, nullptr, &Pool);
     Pools.push_back(Pool);
@@ -31,14 +34,14 @@ Expected<ze_event_handle_t> EventPoolTy::getEventLocked() {
     EventDesc.wait = 0;
     EventDesc.signal = ZE_EVENT_SCOPE_FLAG_HOST;
     uint32_t CreatedEvents = 0;
-    for (uint32_t I = 0; I < PoolSize; I++) {
-      EventDesc.index = I;
+    for (uint32_t EventIdx = 0; EventIdx < PoolSize; EventIdx++) {
+      EventDesc.index = EventIdx;
       ze_event_handle_t Event;
       ze_result_t RC;
       CALL_ZE(RC, zeEventCreate, Pool, &EventDesc, &Event);
       if (RC != ZE_RESULT_SUCCESS) {
         // Log the error and skip this event.
-        ODBG(OLDT_Init) << "Warning: zeEventCreate failed at index " << I
+        ODBG(OLDT_Init) << "Warning: zeEventCreate failed at index " << EventIdx
                         << " with code " << RC << ". Skipping this event.";
         continue;
       }

>From 1b16059be63f5b562746d1db1af7d6a87fee0344 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Wed, 3 Jun 2026 13:07:44 -0700
Subject: [PATCH 4/7] rm ;

---
 offload/plugins-nextgen/level_zero/src/L0Device.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/offload/plugins-nextgen/level_zero/src/L0Device.cpp b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
index bd2d39f9b3a5e..b6994fa98d9fd 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Device.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
@@ -752,7 +752,6 @@ Error L0DeviceTy::createEventImpl(void **EventPtrStorage,
   if (!EventOrErr)
     return EventOrErr.takeError();
   *EventPtrStorage = *EventOrErr;
-  ;
   return Plugin::success();
 }
 

>From 9a1cfd55aea087fc410a3e2a8829860c079fa4f7 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Wed, 3 Jun 2026 23:25:54 -0700
Subject: [PATCH 5/7] Document new routines

---
 offload/plugins-nextgen/level_zero/include/L0Device.h | 7 +++++--
 offload/plugins-nextgen/level_zero/include/L0Event.h  | 9 +++++++--
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/offload/plugins-nextgen/level_zero/include/L0Device.h b/offload/plugins-nextgen/level_zero/include/L0Device.h
index eb0cfba504188..275182faebfd6 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Device.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Device.h
@@ -419,18 +419,21 @@ class L0DeviceTy final : public GenericDeviceTy {
     return l0Context.getDriverAPIVersion();
   }
 
-  /// Return an event from the driver associated to this device.
+  /// Get a low-level L0 event from the driver associated to this device.
   Expected<ze_event_handle_t> getEvent() {
     return l0Context.getEventPool().getEvent();
   }
+  /// Get a high-level L0EventTy object from the driver associated to this
+  /// device.
   Expected<L0EventTy *> getEventObject() {
     return l0Context.getEventPool().getEventObject();
   }
 
-  /// Release event to the pool associated to this device.
+  /// Release a L0 event to the pool associated to this device.
   Error releaseEvent(ze_event_handle_t Event) {
     return l0Context.getEventPool().releaseEvent(Event);
   }
+  /// Release an L0EventTy object to the pool associated to this device.
   Error releaseEventObject(L0EventTy *EventObj) {
     return l0Context.getEventPool().releaseEventObject(EventObj);
   }
diff --git a/offload/plugins-nextgen/level_zero/include/L0Event.h b/offload/plugins-nextgen/level_zero/include/L0Event.h
index fc01bdcca7ace..82361e4c74200 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Event.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Event.h
@@ -107,15 +107,20 @@ class EventPoolTy {
     return Plugin::success();
   }
 
-  /// Get a free event from the pool.
+  /// Get a L0 Event (ze_event_handle_t) from the pool.
   Expected<ze_event_handle_t> getEvent() {
     std::lock_guard<std::mutex> Lock(*Mtx);
     return getEventLocked();
   }
+  /// Get an L0EventTy object that wraps a ze_event_handle_t from the pool.
+  /// This is the preferred way to get an event unless a raw event is really
+  /// needed.
   Expected<L0EventTy *> getEventObject();
 
-  /// Return an event to the pool.
+  /// Return a ze_event_handle_t to the pool.
   Error releaseEvent(ze_event_handle_t Event);
+  /// Returns an L0EventTy object to the pool so it can be reused. This does not
+  /// return the underlying ze_event_handle_t to the handle pool.
   Error releaseEventObject(L0EventTy *EventObj);
 };
 

>From a15bae84a55d7be1cc8639f00c7a913f91b32ae7 Mon Sep 17 00:00:00 2001
From: Alex Duran <alejandro.duran at intel.com>
Date: Thu, 4 Jun 2026 08:33:07 +0200
Subject: [PATCH 6/7] Update
 offload/plugins-nextgen/level_zero/src/L0Device.cpp

Co-authored-by: Kevin Sala Penades <kevinsala.ks at gmail.com>
---
 offload/plugins-nextgen/level_zero/src/L0Device.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/offload/plugins-nextgen/level_zero/src/L0Device.cpp b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
index b6994fa98d9fd..bf8394939d8d3 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Device.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
@@ -801,7 +801,7 @@ Expected<bool> L0DeviceTy::isEventCompleteImpl(void *EventPtr,
 
 Expected<float> L0DeviceTy::getEventElapsedTimeImpl(void *StartEventPtr,
                                                     void *EndEventPtr) {
-  return Plugin::error(error::ErrorCode::UNKNOWN, "%s not implemented yet\n",
+  return Plugin::error(error::ErrorCode::UNSUPPORTED, "%s not implemented yet\n",
                        __func__);
 }
 

>From 0d4212dd2b131dcc1f3cf2fdb53c4d6287016d15 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Wed, 3 Jun 2026 23:50:09 -0700
Subject: [PATCH 7/7] format

---
 offload/plugins-nextgen/level_zero/src/L0Device.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/offload/plugins-nextgen/level_zero/src/L0Device.cpp b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
index bf8394939d8d3..3f3922036c8c3 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Device.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
@@ -801,8 +801,8 @@ Expected<bool> L0DeviceTy::isEventCompleteImpl(void *EventPtr,
 
 Expected<float> L0DeviceTy::getEventElapsedTimeImpl(void *StartEventPtr,
                                                     void *EndEventPtr) {
-  return Plugin::error(error::ErrorCode::UNSUPPORTED, "%s not implemented yet\n",
-                       __func__);
+  return Plugin::error(error::ErrorCode::UNSUPPORTED,
+                       "%s not implemented yet\n", __func__);
 }
 
 Error L0DeviceTy::callGlobalConstructors(GenericPluginTy &Plugin,



More information about the llvm-commits mailing list