[llvm] [offload][l0] Implement context groups and add actual L0 driver version to info (PR #217562)

Jan Trusiłło via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 02:39:41 PDT 2026


https://github.com/311Volt created https://github.com/llvm/llvm-project/pull/217562

Prior to the introduction of liboffload contexts, Level Zero devices under different driver instances used to each receive their own `L0ContextTy`s.

https://github.com/llvm/llvm-project/pull/209144 introduced contexts, which are designed to map to a single `ze_context` on L0. `olCreateContext` accepts a user-defined device list and is restricted to accept devices within a single platform. This restriction is insufficient for L0 - passing devices from different driver instances is allowed under this contract, but does not make sense on L0 as `zeContextCreate` needs a concrete driver instance as a parameter.

Introduce the concept of a "context group" - a set of devices that can be grouped in a single context. CUDA and AMDGPU plugins are restricted to a single context group, while L0 creates a distinct context group for each detected driver instance. A set of devices can share a context if and only if they have the same context group index (as obtained from the new DeviceInfo property `CONTEXT_GROUP_INDEX`).

Change libsycl to store multiple contexts inside of `PlatformImpl` and change relevant getters to add a device key. No changes are needed in `sycl-ls`.

As a semi-related fix, make the L0 plugin report the actual driver version for a device, as obtained by `zeDriverGetApiVersion`. The driver version is exposed as a raw decimal string.

Assisted-by: Codex (5.6 Sol)


>From a5ddfbc66c79c4467ff3378be843f4533b6222d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Trusi=C5=82=C5=82o?= <jan.trusillo at intel.com>
Date: Wed, 19 Aug 2026 15:17:21 +0000
Subject: [PATCH] implement context groups and add actual l0 driver ver to info

---
 libsycl/src/detail/device_impl.hpp            | 13 ++++++-
 libsycl/src/detail/platform_impl.cpp          | 30 +++++++++++-----
 libsycl/src/detail/platform_impl.hpp          | 13 +++----
 libsycl/src/detail/queue_impl.cpp             |  2 +-
 .../device_selector/get_device_preference.cpp | 10 ++++++
 libsycl/unittests/mock/helpers.cpp            |  9 +++++
 offload/liboffload/API/Context.td             |  5 +--
 offload/liboffload/API/Device.td              |  3 ++
 offload/liboffload/src/OffloadImpl.cpp        | 20 +++++++++++
 .../common/include/PluginInterface.h          |  6 ++++
 .../level_zero/dynamic_l0/L0DynWrapper.cpp    |  1 +
 .../level_zero/dynamic_l0/level_zero/ze_api.h | 17 ++++++++-
 .../level_zero/include/L0Context.h            |  5 +++
 .../level_zero/include/L0Device.h             |  4 +++
 .../level_zero/include/L0Plugin.h             |  4 +++
 .../level_zero/src/L0Context.cpp              |  2 +-
 .../level_zero/src/L0Device.cpp               | 14 ++------
 .../level_zero/src/L0Plugin.cpp               | 36 ++++++++++++++++---
 .../OffloadAPI/common/Properties.hpp          |  5 +++
 .../OffloadAPI/device/olGetDeviceInfo.cpp     | 13 +++++--
 .../OffloadAPI/device/olGetDeviceInfoSize.cpp | 12 ++++++-
 21 files changed, 183 insertions(+), 41 deletions(-)

diff --git a/libsycl/src/detail/device_impl.hpp b/libsycl/src/detail/device_impl.hpp
index f5012fe84c069..c057a07d2807d 100644
--- a/libsycl/src/detail/device_impl.hpp
+++ b/libsycl/src/detail/device_impl.hpp
@@ -44,7 +44,14 @@ class DeviceImpl {
   /// All device impls must be created in corresponding platform ctor.
   explicit DeviceImpl(ol_device_handle_t Device, PlatformImpl &Platform,
                       PrivateTag)
-      : MOffloadDevice(Device), MPlatform(Platform) {}
+      : MOffloadDevice(Device), MPlatform(Platform) {
+    ol_result_t Res =
+        callNoCheck(olGetDeviceInfo, MOffloadDevice,
+                    OL_DEVICE_INFO_CONTEXT_GROUP_INDEX,
+                    sizeof(MContextGroupIndex), &MContextGroupIndex);
+    if (isFailed(Res))
+      MContextGroupIndex = 0;
+  }
 
   ~DeviceImpl() = default;
 
@@ -124,9 +131,13 @@ class DeviceImpl {
   /// \return the corresponding liboffload device handle.
   ol_device_handle_t getOLHandle() const { return MOffloadDevice; }
 
+  /// \return the context compatibility group this device belongs to.
+  uint32_t getContextGroupIndex() const { return MContextGroupIndex; }
+
 private:
   ol_device_handle_t MOffloadDevice = {};
   PlatformImpl &MPlatform;
+  uint32_t MContextGroupIndex = 0;
 };
 
 } // namespace detail
diff --git a/libsycl/src/detail/platform_impl.cpp b/libsycl/src/detail/platform_impl.cpp
index 1d81b848a21d3..3e3b3f338ac03 100644
--- a/libsycl/src/detail/platform_impl.cpp
+++ b/libsycl/src/detail/platform_impl.cpp
@@ -16,6 +16,7 @@
 #include <detail/platform_impl.hpp>
 
 #include <algorithm>
+#include <map>
 #include <memory>
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
@@ -93,13 +94,16 @@ PlatformImpl::PlatformImpl(ol_platform_handle_t Platform, size_t PlatformIndex,
                       Device, *this, DeviceImpl::PrivateTag{}));
                 });
 
-  std::vector<DeviceImpl *> DeviceImpls;
-  DeviceImpls.reserve(MRootDevices.size());
+  std::map<uint32_t, std::vector<DeviceImpl *>> GroupedDevices;
   for (const auto &Device : MRootDevices)
-    DeviceImpls.push_back(Device.get());
+    GroupedDevices[Device->getContextGroupIndex()].push_back(Device.get());
 
-  MDefaultContext = ContextImpl::create(std::move(DeviceImpls),
-                                        defaultAsyncHandler, property_list{});
+  MDefaultContexts.reserve(GroupedDevices.size());
+  for (auto &[GroupIdx, DeviceImpls] : GroupedDevices) {
+    MDefaultContexts.push_back(
+        {GroupIdx, ContextImpl::create(std::move(DeviceImpls),
+                                       defaultAsyncHandler, property_list{})});
+  }
 }
 
 const std::vector<DeviceImplUPtr> &PlatformImpl::getRootDevices() const {
@@ -144,10 +148,18 @@ void PlatformImpl::iterateDevices(
   }
 }
 
-ContextImpl &PlatformImpl::getDefaultContext() {
-  assert(MDefaultContext &&
-         "Default context for platform must be created in platform ctor");
-  return *MDefaultContext.get();
+ContextImpl &PlatformImpl::getDefaultContext(const DeviceImpl &Device) {
+  assert(!MDefaultContexts.empty() &&
+         "Default contexts must be created in platform ctor");
+
+  uint32_t GroupIdx = Device.getContextGroupIndex();
+  for (auto &Entry : MDefaultContexts) {
+    if (Entry.ContextGroupIndex == GroupIdx)
+      return *Entry.Context;
+  }
+
+  assert(false && "No default context for device's context group");
+  __builtin_unreachable();
 }
 
 } // namespace detail
diff --git a/libsycl/src/detail/platform_impl.hpp b/libsycl/src/detail/platform_impl.hpp
index 83ad3453ab765..af253ca61f23d 100644
--- a/libsycl/src/detail/platform_impl.hpp
+++ b/libsycl/src/detail/platform_impl.hpp
@@ -132,11 +132,8 @@ class PlatformImpl {
   void iterateDevices(info::device_type DeviceType,
                       std::function<void(DeviceImpl *)> callback) const;
 
-  // TODO: liboffload doesn't support context now, l0 plugin creates default
-  // context for all devices on its level. This method should be removed or
-  // reimplemented once native context support is added to liboffload.
-  /// \return the default context that represents all devices in platform.
-  ContextImpl &getDefaultContext();
+  /// \return the default context compatible with Device.
+  ContextImpl &getDefaultContext(const DeviceImpl &Device);
 
 private:
   /// \return reference to collection of root devices for platform
@@ -150,7 +147,11 @@ class PlatformImpl {
 
   std::vector<DeviceImplUPtr> MRootDevices;
 
-  std::shared_ptr<ContextImpl> MDefaultContext;
+  struct DefaultContextEntry {
+    uint32_t ContextGroupIndex;
+    std::shared_ptr<ContextImpl> Context;
+  };
+  std::vector<DefaultContextEntry> MDefaultContexts;
 
   // Single initialization of platforms and devices doesn't allow to implement
   // unittests for this behavior. This flag and friend class allows to force
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index bd76f2aaa2a6e..10c9183df022a 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -66,7 +66,7 @@ QueueImpl::QueueImpl(DeviceImpl &deviceImpl, const async_handler &asyncHandler,
                      const property_list &propList, PrivateTag)
     : MIsInorder(false), MAsyncHandler(asyncHandler), MPropList(propList),
       MDevice(deviceImpl),
-      MContext(MDevice.getPlatformImpl().getDefaultContext()) {
+      MContext(MDevice.getPlatformImpl().getDefaultContext(MDevice)) {
   assert(MContext.getOLHandleRef() &&
          "Queue must be associated with a valid offload context");
   callAndThrow(olCreateQueue, MContext.getOLHandleRef(), MDevice.getOLHandle(),
diff --git a/libsycl/unittests/device_selector/get_device_preference.cpp b/libsycl/unittests/device_selector/get_device_preference.cpp
index 79920b9611a10..f3c162215d258 100644
--- a/libsycl/unittests/device_selector/get_device_preference.cpp
+++ b/libsycl/unittests/device_selector/get_device_preference.cpp
@@ -49,6 +49,16 @@ class DeviceSelectorScoreTest : public ::testing::Test {
           *static_cast<ol_platform_handle_t *>(PropValue) = Platform;
           return OL_SUCCESS;
         });
+
+    EXPECT_CALL(Helper.Mock.get(),
+                olGetDeviceInfo(_, OL_DEVICE_INFO_CONTEXT_GROUP_INDEX, _, _))
+        .WillRepeatedly([](ol_device_handle_t /*Device*/,
+                           ol_device_info_t /*PropName*/, size_t PropSize,
+                           void *PropValue) -> ol_result_t {
+          EXPECT_EQ(PropSize, sizeof(uint32_t));
+          *static_cast<uint32_t *>(PropValue) = 0;
+          return OL_SUCCESS;
+        });
   }
 
   void TearDown() override {
diff --git a/libsycl/unittests/mock/helpers.cpp b/libsycl/unittests/mock/helpers.cpp
index f12e8509f1dce..091b8cfba2bb9 100644
--- a/libsycl/unittests/mock/helpers.cpp
+++ b/libsycl/unittests/mock/helpers.cpp
@@ -113,6 +113,11 @@ void mock::MockLiboffload::initDefault() {
           assignAs<ol_device_type_t>(PropValue, OL_DEVICE_TYPE_GPU);
           return OL_SUCCESS;
         }
+        case OL_DEVICE_INFO_CONTEXT_GROUP_INDEX: {
+          EXPECT_EQ(PropSize, sizeof(uint32_t));
+          assignAs<uint32_t>(PropValue, 0);
+          return OL_SUCCESS;
+        }
         default:
           ADD_FAILURE();
           return makeEmptyStrError(OL_ERRC_UNIMPLEMENTED);
@@ -134,6 +139,10 @@ void mock::MockLiboffload::initDefault() {
           *PropSizeRet = sizeof(ol_device_type_t);
           return OL_SUCCESS;
         }
+        case OL_DEVICE_INFO_CONTEXT_GROUP_INDEX: {
+          *PropSizeRet = sizeof(uint32_t);
+          return OL_SUCCESS;
+        }
         default:
           ADD_FAILURE();
           return makeEmptyStrError(OL_ERRC_UNIMPLEMENTED);
diff --git a/offload/liboffload/API/Context.td b/offload/liboffload/API/Context.td
index 1b407265cd879..f2203c6645aaf 100644
--- a/offload/liboffload/API/Context.td
+++ b/offload/liboffload/API/Context.td
@@ -13,7 +13,8 @@
 def olCreateContext : Function {
   let desc = "Create a context grouping the given devices.";
   let details = [
-    "All devices must belong to the same platform.",
+    "All devices must belong to the same platform and context group (as reported by "
+    "`OL_DEVICE_INFO_CONTEXT_GROUP_INDEX`). Context groups typically correspond to a driver instance.",
     "A context enables resource sharing between the devices it groups and "
     "isolates those resources from other contexts: resources (such as memory "
     "allocations) created within a context cannot be used outside of it.",
@@ -31,7 +32,7 @@ def olCreateContext : Function {
       "`DevicesCount == 0`"
     ]>,
     Return<"OL_ERRC_INVALID_DEVICE", [
-      "the devices in `Devices` do not all belong to the same platform"
+      "the devices in `Devices` do not all belong to the same context group"
     ]>
   ];
 }
diff --git a/offload/liboffload/API/Device.td b/offload/liboffload/API/Device.td
index cee053b1ff336..21c226784410f 100644
--- a/offload/liboffload/API/Device.td
+++ b/offload/liboffload/API/Device.td
@@ -57,6 +57,9 @@ def ol_device_info_t : Enum {
   list<TaggedEtor> basic_etors2 =
       [TaggedEtor<"COOPERATIVE_LAUNCH_SUPPORT", "bool",
                   "Is cooperative kernel launch supported">,
+       TaggedEtor<"CONTEXT_GROUP_INDEX", "uint32_t",
+                  "Context group index; devices with the same value can be "
+                  "grouped in a single context">,
   ];
   let etors = !listconcat(basic_etors, fp_configs, native_vec_widths,
                           fp_support, basic_etors2);
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 48feac0b6c780..d4ecc78cb2caa 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -45,6 +45,7 @@ struct ol_platform_impl_t {
                      ol_platform_backend_t BackendType)
       : BackendType(BackendType), Plugin(std::move(Plugin)) {}
   ol_platform_backend_t BackendType;
+  uint32_t ContextGroupBase = 0;
 
   /// Complete all pending work for this platform and perform any needed
   /// cleanup.
@@ -77,6 +78,11 @@ struct ol_device_impl_t {
   InfoTreeNode Info;
 };
 
+static uint32_t getContextGroupIndex(ol_device_handle_t Device) {
+  return Device->Platform.ContextGroupBase +
+         Device->Device->getContextGroupOffset();
+}
+
 llvm::Error ol_platform_impl_t::destroy() { return Plugin->deinit(); }
 
 llvm::Error ol_platform_impl_t::init() {
@@ -333,6 +339,13 @@ Error initPlugins(OffloadContext &Context, const ol_init_args_t *InitArgs) {
       return Err;
   }
 
+  uint32_t CurCtxGroupBase = 0;
+  for (auto &Platform : Context.Platforms) {
+    Platform->ContextGroupBase = CurCtxGroupBase;
+    CurCtxGroupBase +=
+        Platform->Plugin ? Platform->Plugin->getNumContextGroups() : 1;
+  }
+
   Context.TracingEnabled = std::getenv("OFFLOAD_TRACE");
   Context.ValidationEnabled = !std::getenv("OFFLOAD_DISABLE_VALIDATION");
 
@@ -483,6 +496,9 @@ Error olGetDeviceInfoImplDetail(ol_device_handle_t Device,
     return Info.write<uint64_t>(Mem);
   } break;
 
+  case OL_DEVICE_INFO_CONTEXT_GROUP_INDEX:
+    return Info.write<uint32_t>(getContextGroupIndex(Device));
+
   default:
     break;
   }
@@ -626,6 +642,10 @@ Error olCreateContext_impl(size_t DevicesCount, ol_device_handle_t *Devices,
       return createOffloadError(
           ErrorCode::INVALID_DEVICE,
           "all devices in a context must belong to the same platform");
+    if (getContextGroupIndex(Devices[I]) != getContextGroupIndex(Devices[0]))
+      return createOffloadError(
+          ErrorCode::INVALID_DEVICE,
+          "all devices in a context must belong to the same context group");
     DeviceList.push_back(Devices[I]);
     PluginDevices.push_back(Devices[I]->Device);
   }
diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index 30c79e28f2ea4..09cff34a1bd75 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -959,6 +959,9 @@ struct GenericDeviceTy : public DeviceAllocatorTy {
   /// this id is not unique between different plugins; they may overlap.
   int32_t getDeviceId() const { return DeviceId; }
 
+  /// Get the index of the context group this device belongs to.
+  virtual uint32_t getContextGroupOffset() const { return 0; }
+
   /// Get the unique identifier of the device.
   const char *getDeviceUid() const { return DeviceUid.c_str(); }
 
@@ -1547,6 +1550,9 @@ struct GenericPluginTy {
   /// Get the number of active devices.
   int32_t getNumDevices() const { return NumDevices; }
 
+  /// Get the number of context groups supported by this plugin.
+  virtual uint32_t getNumContextGroups() const { return 1; }
+
   /// Get the plugin-specific device identifier.
   int32_t getUserId(int32_t DeviceId) const {
     assert(UserDeviceIds.contains(DeviceId) && "No user-id registered");
diff --git a/offload/plugins-nextgen/level_zero/dynamic_l0/L0DynWrapper.cpp b/offload/plugins-nextgen/level_zero/dynamic_l0/L0DynWrapper.cpp
index 55e4b6b856a96..758d3cb1406c3 100644
--- a/offload/plugins-nextgen/level_zero/dynamic_l0/L0DynWrapper.cpp
+++ b/offload/plugins-nextgen/level_zero/dynamic_l0/L0DynWrapper.cpp
@@ -57,6 +57,7 @@ DLWRAP(zeDeviceGetMemoryProperties, 3)
 DLWRAP(zeDeviceGetCacheProperties, 3)
 DLWRAP(zeDeviceGetGlobalTimestamps, 3)
 DLWRAP(zeDriverGetApiVersion, 2)
+DLWRAP(zeDriverGetProperties, 2)
 DLWRAP(zeDriverGetExtensionFunctionAddress, 3)
 DLWRAP(zeDriverGetExtensionProperties, 3)
 DLWRAP(zeEventCreate, 3)
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 4d8cf1e9367c0..2b760cfde4e12 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
@@ -334,11 +334,24 @@ typedef struct _ze_uuid_t {
   uint8_t id[16];
 } ze_uuid_t;
 
+/* Driver UUID size */
+#ifndef ZE_MAX_DRIVER_UUID_SIZE
+#define ZE_MAX_DRIVER_UUID_SIZE 16
+#endif
+
 /* Driver UUID */
 typedef struct _ze_driver_uuid_t {
-  ze_uuid_t id;
+  uint8_t id[ZE_MAX_DRIVER_UUID_SIZE];
 } ze_driver_uuid_t;
 
+/* Driver properties */
+typedef struct _ze_driver_properties_t {
+  ze_structure_type_t stype;
+  void *pNext;
+  ze_driver_uuid_t uuid;
+  uint32_t driverVersion;
+} ze_driver_properties_t;
+
 /* Device UUID */
 typedef struct _ze_device_uuid_t {
   ze_uuid_t id;
@@ -666,6 +679,8 @@ ZE_APIEXPORT ze_result_t ZE_APICALL zeDriverGet(uint32_t *pCount,
                                                 ze_driver_handle_t *phDrivers);
 ZE_APIEXPORT ze_result_t ZE_APICALL
 zeDriverGetApiVersion(ze_driver_handle_t hDriver, ze_api_version_t *version);
+ZE_APIEXPORT ze_result_t ZE_APICALL zeDriverGetProperties(
+    ze_driver_handle_t hDriver, ze_driver_properties_t *pDriverProperties);
 ZE_APIEXPORT ze_result_t ZE_APICALL zeDriverGetExtensionFunctionAddress(
     ze_driver_handle_t hDriver, const char *name, void **ppFunctionAddress);
 ZE_APIEXPORT ze_result_t ZE_APICALL zeDriverGetExtensionProperties(
diff --git a/offload/plugins-nextgen/level_zero/include/L0Context.h b/offload/plugins-nextgen/level_zero/include/L0Context.h
index 09a19702baa23..ff4ee162c9f0b 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Context.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Context.h
@@ -50,6 +50,9 @@ class L0ContextTy {
   /// Level Zero Driver handle.
   ze_driver_handle_t zeDriver = nullptr;
 
+  /// Context group index within the Level Zero plugin.
+  uint32_t ContextGroupIdx;
+
   /// Common Level Zero context.
   ze_context_handle_t zeContext = nullptr;
 
@@ -122,6 +125,8 @@ class L0ContextTy {
 
   ze_driver_handle_t getZeDriver() const { return zeDriver; }
 
+  uint32_t getContextGroupIdx() const { return ContextGroupIdx; }
+
   /// Return context associated with the driver.
   ze_context_handle_t getZeContext() const { return zeContext; }
 
diff --git a/offload/plugins-nextgen/level_zero/include/L0Device.h b/offload/plugins-nextgen/level_zero/include/L0Device.h
index 84df2a2140446..5272dec2f02b4 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Device.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Device.h
@@ -185,6 +185,10 @@ class L0DeviceTy final : public GenericDeviceTy {
   Error deinitImpl() override;
   ze_device_handle_t getZeDevice() const { return zeDevice; }
 
+  uint32_t getContextGroupOffset() const override {
+    return L0Context.getContextGroupIdx();
+  }
+
   bool supportsCooperativeKernels() const {
     return QueueConfig.SupportsCooperativeKernels;
   }
diff --git a/offload/plugins-nextgen/level_zero/include/L0Plugin.h b/offload/plugins-nextgen/level_zero/include/L0Plugin.h
index 82e47e052f339..81385ff5d0946 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Plugin.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Plugin.h
@@ -64,6 +64,8 @@ class LevelZeroPluginTy final : public GenericPluginTy {
   struct DeviceInfoTy {
     L0DeviceIdTy Id;
     L0ContextTy *Driver;
+    uint32_t DriverVersion;
+    ze_driver_uuid_t DriverUuid;
     bool isRoot() const { return Id.SubId < 0 && Id.CCSId < 0; }
   };
   llvm::SmallVector<DeviceInfoTy> DetectedDevices;
@@ -111,6 +113,8 @@ class LevelZeroPluginTy final : public GenericPluginTy {
   uint16_t getMagicElfBits() const override { return ELF::EM_INTELGT; }
   Triple::ArchType getTripleArch() const override { return Triple::spirv64; }
   const char *getName() const override { return GETNAME(TARGET_NAME); }
+  uint32_t getNumContextGroups() const override { return ContextList.size(); }
+  std::string getDriverVersion(int32_t DeviceId) const;
 
   Expected<bool> isELFCompatible(uint32_t DeviceId,
                                  StringRef Image) const override;
diff --git a/offload/plugins-nextgen/level_zero/src/L0Context.cpp b/offload/plugins-nextgen/level_zero/src/L0Context.cpp
index cdcd1210cbcf1..62f4582469f9a 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Context.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Context.cpp
@@ -17,7 +17,7 @@ namespace llvm::omp::target::plugin {
 
 L0ContextTy::L0ContextTy(LevelZeroPluginTy &Plugin, ze_driver_handle_t zeDriver,
                          int32_t DriverId)
-    : Plugin(Plugin), zeDriver(zeDriver) {}
+    : Plugin(Plugin), zeDriver(zeDriver), ContextGroupIdx(DriverId) {}
 
 L0ContextTy::~L0ContextTy() = default;
 
diff --git a/offload/plugins-nextgen/level_zero/src/L0Device.cpp b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
index 08c45b534e16c..b09a3b4e49b13 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Device.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
@@ -428,12 +428,6 @@ const char *L0DeviceTy::getArchCStr() const {
   }
 }
 
-static const char *DriverVersionToStrTable[] = {
-    "1.0", "1.1", "1.2", "1.3",  "1.4",  "1.5", "1.6",
-    "1.7", "1.8", "1.9", "1.10", "1.11", "1.12"};
-constexpr size_t DriverVersionToStrTableSize =
-    sizeof(DriverVersionToStrTable) / sizeof(DriverVersionToStrTable[0]);
-
 Expected<InfoTreeNode> L0DeviceTy::obtainInfoImpl() {
   InfoTreeNode Info;
   Info.add("Device Number", getDeviceId());
@@ -442,12 +436,8 @@ Expected<InfoTreeNode> L0DeviceTy::obtainInfoImpl() {
   Info.add("Device Type", "GPU", "", DeviceInfo::TYPE);
   Info.add("Vendor", "Intel", "", DeviceInfo::VENDOR);
   Info.add("Vendor ID", getVendorId(), "", DeviceInfo::VENDOR_ID);
-  auto DriverVersion = getDriverAPIVersion();
-  if (DriverVersion < DriverVersionToStrTableSize)
-    Info.add("Driver Version", DriverVersionToStrTable[DriverVersion], "",
-             DeviceInfo::DRIVER_VERSION);
-  else
-    Info.add("Driver Version", "Unknown", "", DeviceInfo::DRIVER_VERSION);
+  Info.add("Driver Version", getPlugin().getDriverVersion(getDeviceId()), "",
+           DeviceInfo::DRIVER_VERSION);
   Info.add("Device PCI ID", getPCIId());
   Info.add("Device UUID", getUuid().data());
   Info.add("Number of total EUs", getNumEUs(), "",
diff --git a/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp b/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp
index a8c3e522c36ba..fbbce836e7e3c 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp
@@ -55,6 +55,8 @@ Expected<int32_t> LevelZeroPluginTy::findDevices() {
     uint32_t OrderId;
     ze_device_handle_t ZeDevice;
     L0ContextTy *Driver;
+    uint32_t DriverVersion;
+    ze_driver_uuid_t DriverUuid;
     bool IsDiscrete;
   };
   llvm::SmallVector<RootInfoTy> RootDevices;
@@ -70,8 +72,15 @@ Expected<int32_t> LevelZeroPluginTy::findDevices() {
                       << ".";
       continue;
     }
+
+    ze_driver_properties_t DriverProperties{};
+    DriverProperties.stype = ZE_STRUCTURE_TYPE_DRIVER_PROPERTIES;
+    DriverProperties.pNext = nullptr;
+    CALL_ZE_RET_ERROR(zeDriverGetProperties, Driver, &DriverProperties);
+
     // We have a driver that supports at least one device.
-    ContextList.emplace_back(*this, Driver, DriverId);
+    ContextList.emplace_back(*this, Driver,
+                             static_cast<int32_t>(ContextList.size()));
     auto &DrvInfo = ContextList.back();
     if (auto Err = DrvInfo.init()) {
       // Remove the partially initialized context from the list
@@ -83,7 +92,8 @@ Expected<int32_t> LevelZeroPluginTy::findDevices() {
 
     for (auto &zeDevice : FoundDevices)
       RootDevices.push_back(
-          {OrderId++, zeDevice, &DrvInfo, L0DeviceTy::isDiscrete(zeDevice)});
+          {OrderId++, zeDevice, &DrvInfo, DriverProperties.driverVersion,
+           DriverProperties.uuid, L0DeviceTy::isDiscrete(zeDevice)});
   }
 
   // Move discrete devices to the front.
@@ -103,8 +113,13 @@ Expected<int32_t> LevelZeroPluginTy::findDevices() {
   for (size_t RootId = 0; RootId < RootDevices.size(); RootId++) {
     const auto ZeDevice = RootDevices[RootId].ZeDevice;
     auto *RootDriver = RootDevices[RootId].Driver;
-    DetectedDevices.push_back(DeviceInfoTy{
-        {ZeDevice, static_cast<int32_t>(RootId), -1, -1}, RootDriver});
+    auto DriverVersion = RootDevices[RootId].DriverVersion;
+    auto DriverUuid = RootDevices[RootId].DriverUuid;
+    DetectedDevices.push_back(
+        DeviceInfoTy{{ZeDevice, static_cast<int32_t>(RootId), -1, -1},
+                     RootDriver,
+                     DriverVersion,
+                     DriverUuid});
   }
   int32_t NumDevices = DetectedDevices.size();
 
@@ -124,6 +139,19 @@ Expected<int32_t> LevelZeroPluginTy::findDevices() {
   return NumDevices;
 }
 
+std::string LevelZeroPluginTy::getDriverVersion(int32_t DeviceId) const {
+  assert(DeviceId >= 0 &&
+         static_cast<size_t>(DeviceId) < DetectedDevices.size());
+  const auto &DeviceInfo = DetectedDevices[DeviceId];
+
+  std::string Result;
+  llvm::raw_string_ostream OS(Result);
+  OS << DeviceInfo.DriverVersion << " (";
+  OS.write_uuid(DeviceInfo.DriverUuid.id);
+  OS << ")";
+  return OS.str();
+}
+
 Expected<int32_t> LevelZeroPluginTy::initImpl() {
   ODBG(OLDT_Init) << "Level0 NG plugin initialization";
   // Process options before anything else.
diff --git a/offload/unittests/OffloadAPI/common/Properties.hpp b/offload/unittests/OffloadAPI/common/Properties.hpp
index 849f4ea05f29a..3be2bfe9dab21 100644
--- a/offload/unittests/OffloadAPI/common/Properties.hpp
+++ b/offload/unittests/OffloadAPI/common/Properties.hpp
@@ -98,6 +98,11 @@ inline const DeviceInfoProp PropUint32{
 inline const DeviceInfoProperties Uint32Properties =
     createPropertiesWithSizeContainer(sizeof(uint32_t), PropUint32);
 
+inline const DeviceInfoProp PropContextGroupIndex{
+    OL_DEVICE_INFO_CONTEXT_GROUP_INDEX};
+inline const DeviceInfoProperties ContextGroupIndexProperties =
+    createPropertiesWithSizeContainer(sizeof(uint32_t), PropContextGroupIndex);
+
 inline const DeviceInfoProp PropUint64{
     OL_DEVICE_INFO_MAX_MEM_ALLOC_SIZE, OL_DEVICE_INFO_GLOBAL_MEM_SIZE,
     OL_DEVICE_INFO_WORK_GROUP_LOCAL_MEM_SIZE};
diff --git a/offload/unittests/OffloadAPI/device/olGetDeviceInfo.cpp b/offload/unittests/OffloadAPI/device/olGetDeviceInfo.cpp
index 18882c5af4818..e34749f111e0f 100644
--- a/offload/unittests/OffloadAPI/device/olGetDeviceInfo.cpp
+++ b/offload/unittests/OffloadAPI/device/olGetDeviceInfo.cpp
@@ -10,9 +10,9 @@
 #include <OffloadAPI.h>
 #include <gtest/gtest.h>
 
-DeviceInfoProperties JustSupportedProperties =
-    mergeProperties({BoolProperties, IrrelevantForHostGTCapabilitiesProperties,
-                     IrrelevantForHostGTUint32Properties});
+DeviceInfoProperties JustSupportedProperties = mergeProperties(
+    {BoolProperties, IrrelevantForHostGTCapabilitiesProperties,
+     IrrelevantForHostGTUint32Properties, ContextGroupIndexProperties});
 
 DeviceInfoProperties NonZeroProperties =
     mergeProperties({RelevantGTCapabilitiesProperties,
@@ -91,6 +91,13 @@ TEST_P(olGetHostDeviceInfoTest, SuccessPlatform) {
   ASSERT_NE(Platform, nullptr);
 }
 
+TEST_P(olGetHostDeviceInfoTest, SuccessContextGroupIndex) {
+  uint32_t ContextGroupIndex = 0;
+  ASSERT_SUCCESS(olGetDeviceInfo(Device, OL_DEVICE_INFO_CONTEXT_GROUP_INDEX,
+                                 sizeof(ContextGroupIndex),
+                                 &ContextGroupIndex));
+}
+
 TEST_P(olGetHostDeviceInfoTest, InvalidNullHandleDevice) {
   ol_device_type_t DeviceType;
   ASSERT_ERROR(OL_ERRC_INVALID_NULL_HANDLE,
diff --git a/offload/unittests/OffloadAPI/device/olGetDeviceInfoSize.cpp b/offload/unittests/OffloadAPI/device/olGetDeviceInfoSize.cpp
index 82e0d36f027d9..4d7811ec8e64e 100644
--- a/offload/unittests/OffloadAPI/device/olGetDeviceInfoSize.cpp
+++ b/offload/unittests/OffloadAPI/device/olGetDeviceInfoSize.cpp
@@ -17,7 +17,7 @@ using olGetDeviceInfoSizeNonZeroTest = olGetHostDeviceInfoPropertyTest;
 
 DeviceInfoProperties answerSizeEqualToTypeSizeProperties = mergeProperties(
     {Uint32Properties, Uint64Properties, CapabilitesFlagsProperties,
-     PlatformProperties, DeviceTypeProperties});
+     PlatformProperties, DeviceTypeProperties, ContextGroupIndexProperties});
 
 OFFLOAD_TESTS_INSTANTIATE_DEVICE_FIXTURE_WITH_PARAM(
     olGetDeviceInfoSizeEqualTest, answerSizeEqualToTypeSizeProperties,
@@ -55,6 +55,16 @@ TEST_P(olGetDeviceInfoSizeTest, SuccessMaxWorkSizePerDimension) {
   ASSERT_EQ(Size, sizeof(uint32_t) * 3);
 }
 
+TEST(olGetDeviceInfoSizeHostTest, SuccessContextGroupIndex) {
+  ol_device_handle_t Host = TestEnvironment::getHostDevice();
+  ASSERT_NE(Host, nullptr);
+
+  size_t Size = 0;
+  ASSERT_SUCCESS(
+      olGetDeviceInfoSize(Host, OL_DEVICE_INFO_CONTEXT_GROUP_INDEX, &Size));
+  ASSERT_EQ(Size, sizeof(uint32_t));
+}
+
 TEST_P(olGetDeviceInfoSizeTest, InvalidNullHandle) {
   size_t Size = 0;
   ASSERT_ERROR(OL_ERRC_INVALID_NULL_HANDLE,



More information about the llvm-commits mailing list