[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
Mon Aug 24 02:01:48 PDT 2026
https://github.com/311Volt updated https://github.com/llvm/llvm-project/pull/217562
>From e13332099d0f4441afaeaee4da89eb939de14bed 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 1/4] 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..02d38112eba25 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`). A context group typically corresponds 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,
>From ba8c223c2f6759fede398de66da8d6878770931f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Trusi=C5=82=C5=82o?= <jan.trusillo at intel.com>
Date: Thu, 20 Aug 2026 09:42:43 +0000
Subject: [PATCH 2/4] run clang-format
---
libsycl/src/detail/device_impl.hpp | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/libsycl/src/detail/device_impl.hpp b/libsycl/src/detail/device_impl.hpp
index c057a07d2807d..9ec7acebfa305 100644
--- a/libsycl/src/detail/device_impl.hpp
+++ b/libsycl/src/detail/device_impl.hpp
@@ -45,10 +45,9 @@ class DeviceImpl {
explicit DeviceImpl(ol_device_handle_t Device, PlatformImpl &Platform,
PrivateTag)
: MOffloadDevice(Device), MPlatform(Platform) {
- ol_result_t Res =
- callNoCheck(olGetDeviceInfo, MOffloadDevice,
- OL_DEVICE_INFO_CONTEXT_GROUP_INDEX,
- sizeof(MContextGroupIndex), &MContextGroupIndex);
+ ol_result_t Res = callNoCheck(
+ olGetDeviceInfo, MOffloadDevice, OL_DEVICE_INFO_CONTEXT_GROUP_INDEX,
+ sizeof(MContextGroupIndex), &MContextGroupIndex);
if (isFailed(Res))
MContextGroupIndex = 0;
}
>From d6efe7e4f298a76b359a58df5ba47b3877d9bc6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Trusi=C5=82=C5=82o?= <jan.trusillo at intel.com>
Date: Thu, 20 Aug 2026 11:08:23 +0000
Subject: [PATCH 3/4] create SYCL platform for each context group
---
libsycl/src/detail/device_impl.hpp | 12 +-
.../src/detail/offload/offload_topology.cpp | 59 +++-----
.../src/detail/offload/offload_topology.hpp | 58 +++----
libsycl/src/detail/platform_impl.cpp | 80 +++-------
libsycl/src/detail/platform_impl.hpp | 27 +---
libsycl/src/detail/queue_impl.cpp | 10 +-
libsycl/unittests/common/unittests_helper.hpp | 17 ++-
libsycl/unittests/platform/get_platforms.cpp | 143 ++++++++++++++++++
8 files changed, 235 insertions(+), 171 deletions(-)
diff --git a/libsycl/src/detail/device_impl.hpp b/libsycl/src/detail/device_impl.hpp
index 9ec7acebfa305..f5012fe84c069 100644
--- a/libsycl/src/detail/device_impl.hpp
+++ b/libsycl/src/detail/device_impl.hpp
@@ -44,13 +44,7 @@ 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) {
- ol_result_t Res = callNoCheck(
- olGetDeviceInfo, MOffloadDevice, OL_DEVICE_INFO_CONTEXT_GROUP_INDEX,
- sizeof(MContextGroupIndex), &MContextGroupIndex);
- if (isFailed(Res))
- MContextGroupIndex = 0;
- }
+ : MOffloadDevice(Device), MPlatform(Platform) {}
~DeviceImpl() = default;
@@ -130,13 +124,9 @@ 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/offload/offload_topology.cpp b/libsycl/src/detail/offload/offload_topology.cpp
index c04024eef48c5..25e8e49a86758 100644
--- a/libsycl/src/detail/offload/offload_topology.cpp
+++ b/libsycl/src/detail/offload/offload_topology.cpp
@@ -10,54 +10,31 @@
#include <detail/offload/offload_topology.hpp>
#include <detail/offload/offload_utils.hpp>
+#include <algorithm>
#include <array>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
-// Platforms for this backend
-range_view<const ol_platform_handle_t> OffloadTopology::getPlatforms() const {
- return {MPlatforms.data(), MPlatforms.size()};
-}
-
-// Devices for a specific platform (PlatformId is index into Platforms)
-range_view<ol_device_handle_t>
-OffloadTopology::getDevices(size_t PlatformId) const {
- if (PlatformId >= MDeviceRange.size()) {
- return {nullptr, 0};
- }
- return MDeviceRange[PlatformId];
-}
-
void OffloadTopology::registerNewPlatformsAndDevices(
- Platform2DevContainer &PlatformsAndDev) {
- if (!PlatformsAndDev.size())
- return;
-
- // MDeviceRange is populated with iterators of MDevices. Allocate required
- // space in advance to keep them valid.
- MDevices.reserve(PlatformsAndDev.size());
-
- for (auto &[Platform, NewDev] : PlatformsAndDev) {
- MDevices.push_back(NewDev);
-
- // Platform is not unique within PlatformsAndDev but the container is sorted
- if (MPlatforms.empty() || MPlatforms.back() != Platform) {
- MPlatforms.push_back(Platform);
- range_view<ol_device_handle_t> R{&MDevices.back(), 1 /*Size == 1*/};
- MDeviceRange.push_back(R);
- } else {
- // Device is inserted already, just increment device count for the current
- // platform
- MDeviceRange.back().len++;
+ const Platform2DevContainer &PlatformsAndDev) {
+ for (const OffloadDeviceDesc &Entry : PlatformsAndDev) {
+ auto GroupIt = std::find_if(MPlatformGroups.begin(), MPlatformGroups.end(),
+ [&](const OffloadPlatformGroup &Group) {
+ return Group.Platform == Entry.Platform &&
+ Group.ContextGroupIndex ==
+ Entry.ContextGroupIndex;
+ });
+ if (GroupIt == MPlatformGroups.end()) {
+ MPlatformGroups.push_back({Entry.Platform, Entry.ContextGroupIndex, {}});
+ GroupIt = MPlatformGroups.end() - 1;
}
+ GroupIt->Devices.push_back(Entry.Device);
}
}
void discoverOffloadDevices() {
- // liboffload returns devices sorted by backend + platform. We rely on this
- // behavior during device enumeration.
using PerBackendDataType =
std::array<Platform2DevContainer, OL_PLATFORM_BACKEND_LAST>;
@@ -96,7 +73,15 @@ void discoverOffloadDevices() {
if (OlBackend >= OL_PLATFORM_BACKEND_LAST)
return true;
- (*Data)[static_cast<size_t>(OlBackend)].push_back({Platform, Dev});
+ uint32_t ContextGroupIndex = 0;
+ Res = callNoCheck(olGetDeviceInfo, Dev,
+ OL_DEVICE_INFO_CONTEXT_GROUP_INDEX,
+ sizeof(ContextGroupIndex), &ContextGroupIndex);
+ if (Res != OL_SUCCESS)
+ ContextGroupIndex = 0;
+
+ (*Data)[static_cast<size_t>(OlBackend)].push_back(
+ {Platform, Dev, ContextGroupIndex});
return true;
},
&Mapping);
diff --git a/libsycl/src/detail/offload/offload_topology.hpp b/libsycl/src/detail/offload/offload_topology.hpp
index f8634a19de31e..107f02d47234a 100644
--- a/libsycl/src/detail/offload/offload_topology.hpp
+++ b/libsycl/src/detail/offload/offload_topology.hpp
@@ -19,28 +19,28 @@
#include <OffloadAPI.h>
-#include <cassert>
+#include <cstdint>
#include <vector>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
-// Minimal span-like view.
-template <class T> struct range_view {
- T *ptr{};
- size_t len{};
- T *begin() const { return ptr; }
- T *end() const { return ptr + len; }
- T &operator[](size_t i) const { return ptr[i]; }
- size_t size() const { return len; }
+struct OffloadDeviceDesc {
+ ol_platform_handle_t Platform;
+ ol_device_handle_t Device;
+ uint32_t ContextGroupIndex;
};
-using Platform2DevContainer =
- std::vector<std::pair<ol_platform_handle_t, ol_device_handle_t>>;
+using Platform2DevContainer = std::vector<OffloadDeviceDesc>;
-/// Contiguous global storage of platform handlers and device handles (grouped
-/// by platform) for a backend.
+struct OffloadPlatformGroup {
+ ol_platform_handle_t Platform;
+ uint32_t ContextGroupIndex;
+ std::vector<ol_device_handle_t> Devices;
+};
+
+/// Storage of platform context groups and their device handles for a backend.
struct OffloadTopology {
OffloadTopology() : MBackend(OL_PLATFORM_BACKEND_UNKNOWN) {}
OffloadTopology(ol_platform_backend_t OlBackend) : MBackend(OlBackend) {}
@@ -55,37 +55,23 @@ struct OffloadTopology {
/// \returns backend of this topology.
ol_platform_backend_t getBackend() const { return MBackend; }
- /// Returns all platforms associated with this topology.
+ /// Returns all platform context groups associated with this topology.
///
- /// \returns minimal span-like view to platforms associated with this
- /// topology.
- range_view<const ol_platform_handle_t> getPlatforms() const;
+ /// \returns platform context groups associated with this topology.
+ const std::vector<OffloadPlatformGroup> &getPlatformGroups() const {
+ return MPlatformGroups;
+ }
- /// Returns all devices associated with specific platform.
- ///
- /// \param PlatformId is index into MPlatforms.
- ///
- /// \returns minimal span-like view to devices associated with specified
- /// platform.
- range_view<ol_device_handle_t> getDevices(size_t PlatformId) const;
-
- /// Register new platform and devices into this topology.
+ /// Registers platform context groups and devices into this topology.
///
/// \param PlatformsAndDev collection of platforms & devices.
- void registerNewPlatformsAndDevices(Platform2DevContainer &PlatformsAndDev);
+ void
+ registerNewPlatformsAndDevices(const Platform2DevContainer &PlatformsAndDev);
private:
ol_platform_backend_t MBackend = OL_PLATFORM_BACKEND_UNKNOWN;
- // Platforms and devices belonging to this backend (flattened)
- std::vector<ol_platform_handle_t> MPlatforms;
-
- // Devices are sorted by platform (guarantee from liboffload)
- std::vector<ol_device_handle_t> MDevices;
-
- // Vector holding range of devices for each platform (index is platform index
- // within Platforms), so MDeviceRange.size() == MPlatforms.size()
- std::vector<range_view<ol_device_handle_t>> MDeviceRange;
+ std::vector<OffloadPlatformGroup> MPlatformGroups;
};
// Initialize the topologies by calling olIterateDevices.
diff --git a/libsycl/src/detail/platform_impl.cpp b/libsycl/src/detail/platform_impl.cpp
index 3e3b3f338ac03..3e09741a9d6c8 100644
--- a/libsycl/src/detail/platform_impl.cpp
+++ b/libsycl/src/detail/platform_impl.cpp
@@ -16,7 +16,6 @@
#include <detail/platform_impl.hpp>
#include <algorithm>
-#include <map>
#include <memory>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
@@ -25,20 +24,6 @@ namespace detail {
bool PlatformImpl::rediscoverIfEmpty = false;
-PlatformImpl &PlatformImpl::getPlatformImpl(ol_platform_handle_t Platform) {
- auto &PlatformCache = getPlatformCache();
- for (auto &PlatImpl : PlatformCache) {
- assert(PlatImpl && "Platform impl can not be nullptr");
- if (PlatImpl->getOLHandleRef() == Platform)
- return *PlatImpl;
- }
-
- throw sycl::exception(
- sycl::make_error_code(sycl::errc::runtime),
- "Platform for requested handle can't be created. This handle is not in "
- "the list of platforms discovered by liboffload");
-}
-
const std::vector<PlatformImplUPtr> &PlatformImpl::getPlatforms() {
static auto InitPlatforms = []() {
discoverOffloadDevices();
@@ -49,10 +34,9 @@ const std::vector<PlatformImplUPtr> &PlatformImpl::getPlatforms() {
for (const auto &Topo : getOffloadTopologies()) {
if (Topo.getBackend() == OL_PLATFORM_BACKEND_HOST)
continue;
- size_t PlatformIndex = 0;
- for (const auto &OffloadPlatform : Topo.getPlatforms()) {
+ for (const auto &PlatformGroup : Topo.getPlatformGroups()) {
PlatformCache.emplace_back(std::make_unique<PlatformImpl>(
- OffloadPlatform, PlatformIndex++, PrivateTag{}));
+ PlatformGroup.Platform, PlatformGroup.Devices, PrivateTag{}));
}
}
};
@@ -69,41 +53,30 @@ const std::vector<PlatformImplUPtr> &PlatformImpl::getPlatforms() {
return PlatformCache;
}
-PlatformImpl::PlatformImpl(ol_platform_handle_t Platform, size_t PlatformIndex,
+PlatformImpl::PlatformImpl(ol_platform_handle_t Platform,
+ const std::vector<ol_device_handle_t> &Devices,
PrivateTag)
- : MOffloadPlatform(Platform), MOffloadPlatformIndex(PlatformIndex) {
+ : MOffloadPlatform(Platform) {
+ assert(!Devices.empty() && "Platform must contain at least one device");
+
ol_platform_backend_t Backend = OL_PLATFORM_BACKEND_UNKNOWN;
callAndThrow(olGetPlatformInfo, MOffloadPlatform, OL_PLATFORM_INFO_BACKEND,
sizeof(Backend), &Backend);
MBackend = convertBackend(Backend);
- MOffloadBackend = Backend;
-
- const auto &Topologies = getOffloadTopologies();
- auto RootTopologyIt = std::find_if(
- Topologies.begin(), Topologies.end(), [&](const OffloadTopology &Topo) {
- return Topo.getBackend() == MOffloadBackend;
- });
-
- assert(RootTopologyIt != Topologies.end() &&
- "Root topology for platform must always exist");
- auto DevRange = RootTopologyIt->getDevices(MOffloadPlatformIndex);
- MRootDevices.reserve(DevRange.size());
- std::for_each(DevRange.begin(), DevRange.end(),
- [&](const ol_device_handle_t &Device) {
- MRootDevices.emplace_back(std::make_unique<DeviceImpl>(
- Device, *this, DeviceImpl::PrivateTag{}));
- });
-
- std::map<uint32_t, std::vector<DeviceImpl *>> GroupedDevices;
- for (const auto &Device : MRootDevices)
- GroupedDevices[Device->getContextGroupIndex()].push_back(Device.get());
- MDefaultContexts.reserve(GroupedDevices.size());
- for (auto &[GroupIdx, DeviceImpls] : GroupedDevices) {
- MDefaultContexts.push_back(
- {GroupIdx, ContextImpl::create(std::move(DeviceImpls),
- defaultAsyncHandler, property_list{})});
+ MRootDevices.reserve(Devices.size());
+ for (const ol_device_handle_t &Device : Devices) {
+ MRootDevices.emplace_back(
+ std::make_unique<DeviceImpl>(Device, *this, DeviceImpl::PrivateTag{}));
}
+
+ std::vector<DeviceImpl *> DeviceImpls;
+ DeviceImpls.reserve(MRootDevices.size());
+ for (const auto &Device : MRootDevices)
+ DeviceImpls.push_back(Device.get());
+
+ MDefaultContext = ContextImpl::create(std::move(DeviceImpls),
+ defaultAsyncHandler, property_list{});
}
const std::vector<DeviceImplUPtr> &PlatformImpl::getRootDevices() const {
@@ -148,18 +121,9 @@ void PlatformImpl::iterateDevices(
}
}
-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();
+ContextImpl &PlatformImpl::getDefaultContext() {
+ assert(MDefaultContext && "Default context must be created in platform ctor");
+ return *MDefaultContext;
}
} // namespace detail
diff --git a/libsycl/src/detail/platform_impl.hpp b/libsycl/src/detail/platform_impl.hpp
index af253ca61f23d..5926403f96b1e 100644
--- a/libsycl/src/detail/platform_impl.hpp
+++ b/libsycl/src/detail/platform_impl.hpp
@@ -56,10 +56,10 @@ class PlatformImpl {
/// Constructs PlatformImpl from a platform handle.
///
/// \param Platform is a raw offload library handle representing platform.
- /// \param PlatformIndex is a platform index in a backend (needed for a proper
- /// indexing in device selector).
+ /// \param Devices are the devices in one platform context group.
/// All platform impls are created during first getPlatforms() call.
- PlatformImpl(ol_platform_handle_t Platform, size_t PlatformIndex, PrivateTag);
+ PlatformImpl(ol_platform_handle_t Platform,
+ const std::vector<ol_device_handle_t> &Devices, PrivateTag);
~PlatformImpl() = default;
@@ -83,15 +83,6 @@ class PlatformImpl {
return MOffloadPlatform;
}
- /// Queries the cache to get the implementation for specified offloading RT
- /// platform. All platform implementation objects are created at first
- /// get_platforms call.
- ///
- /// \param Platform is the offloading RT Platform handle representing the
- /// platform.
- /// \return the PlatformImpl representing the offloading RT platform.
- static PlatformImpl &getPlatformImpl(ol_platform_handle_t Platform);
-
/// Indicates if all of the SYCL devices on this platform have the
/// given aspect.
///
@@ -132,26 +123,20 @@ class PlatformImpl {
void iterateDevices(info::device_type DeviceType,
std::function<void(DeviceImpl *)> callback) const;
- /// \return the default context compatible with Device.
- ContextImpl &getDefaultContext(const DeviceImpl &Device);
+ /// \return the default context containing all devices in this platform.
+ ContextImpl &getDefaultContext();
private:
/// \return reference to collection of root devices for platform
const std::vector<DeviceImplUPtr> &getRootDevices() const;
const ol_platform_handle_t MOffloadPlatform{};
- const size_t MOffloadPlatformIndex{};
- ol_platform_backend_t MOffloadBackend{OL_PLATFORM_BACKEND_UNKNOWN};
backend MBackend{};
std::vector<DeviceImplUPtr> MRootDevices;
- struct DefaultContextEntry {
- uint32_t ContextGroupIndex;
- std::shared_ptr<ContextImpl> Context;
- };
- std::vector<DefaultContextEntry> MDefaultContexts;
+ std::shared_ptr<ContextImpl> MDefaultContext;
// 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 a184c49e497b3..df004a5cc0e1a 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(MDevice)) {
+ MContext(MDevice.getPlatformImpl().getDefaultContext()) {
assert(MContext.getOLHandleRef() &&
"Queue must be associated with a valid offload context");
callAndThrow(olCreateQueue, MContext.getOLHandleRef(), MDevice.getOLHandle(),
@@ -82,8 +82,14 @@ QueueImpl::~QueueImpl() {
backend QueueImpl::getBackend() const noexcept { return MDevice.getBackend(); }
static ol_device_handle_t getHostOLDevice() {
+ const auto &HostPlatformGroups =
+ getOffloadTopologies()[OL_PLATFORM_BACKEND_HOST].getPlatformGroups();
+ assert(!HostPlatformGroups.empty() &&
+ "Host platform must contain at least one context group");
+ assert(!HostPlatformGroups.front().Devices.empty() &&
+ "Host platform context group must contain at least one device");
static ol_device_handle_t HostDevice =
- *(getOffloadTopologies()[OL_PLATFORM_BACKEND_HOST].getDevices(0).begin());
+ HostPlatformGroups.front().Devices.front();
return HostDevice;
}
diff --git a/libsycl/unittests/common/unittests_helper.hpp b/libsycl/unittests/common/unittests_helper.hpp
index a04995225f0e5..c2cffac2375f5 100644
--- a/libsycl/unittests/common/unittests_helper.hpp
+++ b/libsycl/unittests/common/unittests_helper.hpp
@@ -29,15 +29,20 @@ namespace unittests {
// allows to call global state reset and platforms initialization methods to be
// able to set expectations on devices enumeration calls in a proper way.
struct UnittestsHelper {
- UnittestsHelper() { detail::PlatformImpl::rediscoverIfEmpty = true; }
+ UnittestsHelper() {
+ resetPlatformState();
+ detail::PlatformImpl::rediscoverIfEmpty = true;
+ }
+
+ ~UnittestsHelper() { resetPlatformState(); }
- ~UnittestsHelper() {
- if (!detail::getPlatformCache().empty()) {
- detail::getPlatformCache().clear();
- detail::getOffloadTopologies() = {};
- }
+private:
+ static void resetPlatformState() {
+ detail::getPlatformCache().clear();
+ detail::getOffloadTopologies() = {};
}
+public:
mock::MockWrapper Mock;
};
diff --git a/libsycl/unittests/platform/get_platforms.cpp b/libsycl/unittests/platform/get_platforms.cpp
index 16a701d481f24..1b0f5bb3f3ef0 100644
--- a/libsycl/unittests/platform/get_platforms.cpp
+++ b/libsycl/unittests/platform/get_platforms.cpp
@@ -6,13 +6,25 @@
//
//===----------------------------------------------------------------------===//
+#include <common/unittests_helper.hpp>
+
+#include <detail/platform_impl.hpp>
+#include <sycl/__impl/detail/obj_utils.hpp>
#include <sycl/sycl.hpp>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <array>
+#include <cstdint>
+#include <tuple>
+
using namespace sycl;
+using namespace ::testing;
TEST(PlatformTest, APIGetPlatformsDefaultMock) {
+ unittests::UnittestsHelper Helper;
+
auto Platforms = sycl::platform::get_platforms();
ASSERT_EQ(Platforms.size(), 1u);
EXPECT_EQ(Platforms[0].get_backend(), sycl::backend::level_zero);
@@ -36,3 +48,134 @@ TEST(PlatformTest, APIGetPlatformsDefaultMock) {
ASSERT_EQ(CtxDevices[0], Devices[0]);
#endif
}
+
+namespace {
+
+class PlatformContextGroupTest : public Test {
+protected:
+ void SetUp() override {
+ Platform = mock::createDummyHandle<ol_platform_handle_t>();
+ for (ol_device_handle_t &Device : Devices) {
+ Device = mock::createDummyHandleWithData<ol_device_handle_t>(
+ reinterpret_cast<unsigned char *>(&Platform), sizeof(Platform));
+ }
+
+ EXPECT_CALL(Helper.Mock.get(), olIterateDevices(_, _))
+ .WillRepeatedly([this](ol_device_iterate_cb_t Callback,
+ void *UserData) -> ol_result_t {
+ for (ol_device_handle_t Device : Devices)
+ std::ignore = Callback(Device, UserData);
+ return OL_SUCCESS;
+ });
+
+ ON_CALL(Helper.Mock.get(),
+ olGetDeviceInfo(_, OL_DEVICE_INFO_CONTEXT_GROUP_INDEX, _, _))
+ .WillByDefault([this](ol_device_handle_t Device,
+ ol_device_info_t /*PropName*/, size_t PropSize,
+ void *PropValue) -> ol_result_t {
+ EXPECT_EQ(PropSize, sizeof(uint32_t));
+ if (FailContextGroupQuery)
+ return Helper.Mock.get().makeEmptyStrError(OL_ERRC_UNIMPLEMENTED);
+
+ *static_cast<uint32_t *>(PropValue) = getContextGroup(Device);
+ return OL_SUCCESS;
+ });
+ }
+
+ void TearDown() override {
+ detail::getPlatformCache().clear();
+ detail::getOffloadTopologies() = {};
+ mock::releaseDummyHandles(Devices[0], Devices[1], Devices[2], Platform);
+ }
+
+ uint32_t getContextGroup(ol_device_handle_t Device) const {
+ if (Device == Devices[0] || Device == Devices[2])
+ return 0;
+ if (Device == Devices[1])
+ return 1;
+ ADD_FAILURE() << "Unexpected device";
+ return 0;
+ }
+
+ unittests::UnittestsHelper Helper;
+ ol_platform_handle_t Platform{};
+ std::array<ol_device_handle_t, 3> Devices{};
+ bool FailContextGroupQuery = false;
+};
+
+TEST_F(PlatformContextGroupTest, CreatesPlatformForEachContextGroup) {
+ EXPECT_CALL(Helper.Mock.get(), olCreateContext(_, _, _))
+ .Times(2)
+ .WillRepeatedly([this](size_t NumDevices,
+ ol_device_handle_t *ContextDevices,
+ ol_context_handle_t *Context) -> ol_result_t {
+ EXPECT_GT(NumDevices, 0u);
+ if (NumDevices == 0)
+ return Helper.Mock.get().makeEmptyStrError(OL_ERRC_INVALID_SIZE);
+ const uint32_t ContextGroup = getContextGroup(ContextDevices[0]);
+ for (size_t I = 1; I < NumDevices; ++I)
+ EXPECT_EQ(getContextGroup(ContextDevices[I]), ContextGroup);
+
+ *Context = mock::createDummyHandleWithData<ol_context_handle_t>(
+ reinterpret_cast<unsigned char *>(&ContextDevices[0]),
+ sizeof(ContextDevices[0]));
+ return OL_SUCCESS;
+ });
+
+ auto Platforms = sycl::platform::get_platforms();
+ ASSERT_EQ(Platforms.size(), 2u);
+ EXPECT_NE(Platforms[0], Platforms[1]);
+
+ auto Platform0Devices = Platforms[0].get_devices();
+ ASSERT_EQ(Platform0Devices.size(), 2u);
+ EXPECT_EQ(detail::getSyclObjImpl(Platform0Devices[0])->getOLHandle(),
+ Devices[0]);
+ EXPECT_EQ(detail::getSyclObjImpl(Platform0Devices[1])->getOLHandle(),
+ Devices[2]);
+
+ auto Platform1Devices = Platforms[1].get_devices();
+ ASSERT_EQ(Platform1Devices.size(), 1u);
+ EXPECT_EQ(detail::getSyclObjImpl(Platform1Devices[0])->getOLHandle(),
+ Devices[1]);
+
+ EXPECT_EQ(Platform0Devices[0].get_platform(), Platforms[0]);
+ EXPECT_EQ(Platform0Devices[1].get_platform(), Platforms[0]);
+ EXPECT_EQ(Platform1Devices[0].get_platform(), Platforms[1]);
+
+ EXPECT_EQ(detail::getSyclObjImpl(Platforms[0])->getOLHandleRef(), Platform);
+ EXPECT_EQ(detail::getSyclObjImpl(Platforms[1])->getOLHandleRef(), Platform);
+
+ context Context0 = Platforms[0].khr_get_default_context();
+ context Context1 = Platforms[1].khr_get_default_context();
+ EXPECT_EQ(Context0.get_platform(), Platforms[0]);
+ EXPECT_EQ(Context1.get_platform(), Platforms[1]);
+ EXPECT_EQ(Context0.get_devices(), Platform0Devices);
+ EXPECT_EQ(Context1.get_devices(), Platform1Devices);
+
+ queue Queue0{Platform0Devices[0]};
+ queue Queue1{Platform1Devices[0]};
+ EXPECT_EQ(Queue0.get_context(), Context0);
+ EXPECT_EQ(Queue1.get_context(), Context1);
+}
+
+TEST_F(PlatformContextGroupTest, ContextGroupQueryFailureUsesDefaultGroup) {
+ FailContextGroupQuery = true;
+
+ EXPECT_CALL(Helper.Mock.get(), olCreateContext(3, _, _)).Times(1);
+
+ auto Platforms = sycl::platform::get_platforms();
+ ASSERT_EQ(Platforms.size(), 1u);
+
+ auto PlatformDevices = Platforms[0].get_devices();
+ ASSERT_EQ(PlatformDevices.size(), Devices.size());
+ EXPECT_EQ(detail::getSyclObjImpl(PlatformDevices[0])->getOLHandle(),
+ Devices[0]);
+ EXPECT_EQ(detail::getSyclObjImpl(PlatformDevices[1])->getOLHandle(),
+ Devices[1]);
+ EXPECT_EQ(detail::getSyclObjImpl(PlatformDevices[2])->getOLHandle(),
+ Devices[2]);
+ EXPECT_EQ(Platforms[0].khr_get_default_context().get_devices(),
+ PlatformDevices);
+}
+
+} // namespace
>From ebad6f9f14f8d02294d91de85f563a376a064e15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Trusi=C5=82=C5=82o?= <jan.trusillo at intel.com>
Date: Mon, 24 Aug 2026 09:01:06 +0000
Subject: [PATCH 4/4] prettify olIterateDevices UserData access in
discoverOffloadDevices
---
libsycl/src/detail/offload/offload_topology.cpp | 5 +++--
libsycl/unittests/platform/get_platforms.cpp | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/libsycl/src/detail/offload/offload_topology.cpp b/libsycl/src/detail/offload/offload_topology.cpp
index 25e8e49a86758..e833cc5fbcf3f 100644
--- a/libsycl/src/detail/offload/offload_topology.cpp
+++ b/libsycl/src/detail/offload/offload_topology.cpp
@@ -80,8 +80,9 @@ void discoverOffloadDevices() {
if (Res != OL_SUCCESS)
ContextGroupIndex = 0;
- (*Data)[static_cast<size_t>(OlBackend)].push_back(
- {Platform, Dev, ContextGroupIndex});
+ auto BackendIndex = static_cast<size_t>(OlBackend);
+ auto &DeviceDescVec = (*Data)[BackendIndex];
+ DeviceDescVec.push_back({Platform, Dev, ContextGroupIndex});
return true;
},
&Mapping);
diff --git a/libsycl/unittests/platform/get_platforms.cpp b/libsycl/unittests/platform/get_platforms.cpp
index 1b0f5bb3f3ef0..ec362a6cc97e3 100644
--- a/libsycl/unittests/platform/get_platforms.cpp
+++ b/libsycl/unittests/platform/get_platforms.cpp
@@ -17,7 +17,7 @@
#include <array>
#include <cstdint>
-#include <tuple>
+// #include <tuple>
using namespace sycl;
using namespace ::testing;
More information about the llvm-commits
mailing list