[llvm] [libsycl] Add liboffload context handler to default context (PR #213249)
Kseniya Tikhomirova via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 31 04:23:16 PDT 2026
https://github.com/KseniyaTikhomirova created https://github.com/llvm/llvm-project/pull/213249
None
>From b8c8d23eab43a45b59c3a962782ea96d9bcef984 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Fri, 31 Jul 2026 04:21:52 -0700
Subject: [PATCH] [libsycl] Add liboffload context handler to default context
Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
libsycl/src/detail/context_impl.cpp | 41 ++++++++++++++++++++++++----
libsycl/src/detail/context_impl.hpp | 41 ++++++++++++++++++++--------
libsycl/src/detail/platform_impl.cpp | 9 +++++-
libsycl/src/detail/queue_impl.cpp | 3 ++
libsycl/unittests/mock/helpers.cpp | 27 ++++++++++++++++++
libsycl/unittests/mock/helpers.hpp | 4 +++
libsycl/unittests/mock/mock.cpp | 10 +++++++
7 files changed, 118 insertions(+), 17 deletions(-)
diff --git a/libsycl/src/detail/context_impl.cpp b/libsycl/src/detail/context_impl.cpp
index 264716fbdfad4..58aa55187f339 100644
--- a/libsycl/src/detail/context_impl.cpp
+++ b/libsycl/src/detail/context_impl.cpp
@@ -13,15 +13,46 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
+ContextImpl::ContextImpl(std::vector<DeviceImpl *> &&DeviceList,
+ const async_handler &AsyncHandler,
+ const property_list &PropList, Private)
+ : MAsyncHandler(AsyncHandler), MDevices(DeviceList) {
+ (void)PropList;
+
+ assert(!MDevices.empty() && "Device list must not be empty");
+ const PlatformImpl &RefPlatform = MDevices[0]->getPlatformImpl();
+
+ std::vector<ol_device_handle_t> DeviceIds;
+ DeviceIds.reserve(MDevices.size());
+ for (DeviceImpl *D : MDevices) {
+ assert(D && "Device list must not contain null entries");
+ if (D->getPlatformImpl().getOLHandleRef() != RefPlatform.getOLHandleRef())
+ throw exception(
+ make_error_code(errc::invalid),
+ "Can't add devices across platforms to a single context.");
+ DeviceIds.push_back(D->getOLHandle());
+ }
+
+ callAndThrow(olCreateContext, DeviceIds.size(), DeviceIds.data(),
+ &MOffloadContext);
+}
+
+ContextImpl::~ContextImpl() {
+ assert(MOffloadContext && "Context must be created in ctor");
+ std::ignore = olDestroyContext(MOffloadContext);
+}
+
+PlatformImpl &ContextImpl::getPlatformImpl() const {
+ return MDevices[0]->getPlatformImpl();
+}
+
void ContextImpl::iterateDevices(
const std::function<void(DeviceImpl *)> &callback) const {
- // TODO: Intentionally don't store devices in context now. This class should
- // be reimplemented once liboffload adds context support. Treat context as
- // default context that is associated with all devices in the platform.
- return MPlatform.iterateDevices(info::device_type::all, callback);
+ for (DeviceImpl *Device : MDevices)
+ callback(Device);
}
-backend ContextImpl::getBackend() const { return MPlatform.getBackend(); }
+backend ContextImpl::getBackend() const { return MDevices[0]->getBackend(); }
} // namespace detail
_LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/detail/context_impl.hpp b/libsycl/src/detail/context_impl.hpp
index 4dcbda6c95f82..ec9c8d10aca7e 100644
--- a/libsycl/src/detail/context_impl.hpp
+++ b/libsycl/src/detail/context_impl.hpp
@@ -15,6 +15,7 @@
#ifndef _LIBSYCL_CONTEXT_IMPL
#define _LIBSYCL_CONTEXT_IMPL
+#include <sycl/__impl/async_handler.hpp>
#include <sycl/__impl/context.hpp>
#include <sycl/__impl/detail/config.hpp>
@@ -23,16 +24,14 @@
#include <functional>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+class property_list;
+
namespace detail {
class PlatformImpl;
class DeviceImpl;
-// TODO: Presence of context object is essential for many APIs. Current
-// implementation of this class is a way to support them in case of absence of
-// context support in liboffload. For backends where context exists and
-// participates in operations, liboffload plugins create and use default context
-// that represents all devices in that platform. Duplicating this logic here.
/// Context represents the runtime data structures and state required by a SYCL
/// backend API to interact with a group of devices associated with a platform.
class ContextImpl : public std::enable_shared_from_this<ContextImpl> {
@@ -41,12 +40,22 @@ class ContextImpl : public std::enable_shared_from_this<ContextImpl> {
};
public:
- /// Constructs a ContextImpl using a platform.
+ /// Constructs a context implementation for the provided devices.
///
- /// Newly created instance represents all devices in platform.
+ /// \param DeviceList is the list of devices associated with this context.
+ /// \param AsyncHandler is a SYCL asynchronous exception handler.
+ /// \param PropList is a list of context properties.
+ ContextImpl(std::vector<DeviceImpl *> &&DeviceList,
+ const async_handler &AsyncHandler, const property_list &PropList,
+ Private);
+
+ /// Releases the underlying offload context handle.
+ ~ContextImpl();
+
+ /// Gets asynchronous exception handler.
///
- /// \param Platform is a platform to associate this context with.
- ContextImpl(PlatformImpl &Platform, Private) : MPlatform(Platform) {}
+ /// \return an instance of SYCL async_handler.
+ const async_handler &get_async_handler() const { return MAsyncHandler; }
/// Constructs a ContextImpl with a provided arguments. Variadic helper.
/// Restrics ways of ContextImpl creation.
@@ -55,8 +64,16 @@ class ContextImpl : public std::enable_shared_from_this<ContextImpl> {
return std::make_shared<ContextImpl>(std::forward<Ts>(args)..., Private{});
}
+ /// Returns the raw underlying offload context handle.
+ ///
+ /// The caller is responsible for ensuring that the returned handle is only
+ /// used while this ContextImpl object is alive.
+ ///
+ /// \return the raw offload context handle.
+ const ol_context_handle_t &getOLHandleRef() const { return MOffloadContext; }
+
/// \return the platform this context is associated with.
- PlatformImpl &getPlatformImpl() const { return MPlatform; }
+ PlatformImpl &getPlatformImpl() const;
/// Calls "callback" with every device associated
/// with this context.
@@ -66,7 +83,9 @@ class ContextImpl : public std::enable_shared_from_this<ContextImpl> {
backend getBackend() const;
private:
- PlatformImpl &MPlatform;
+ const async_handler MAsyncHandler;
+ const std::vector<DeviceImpl *> MDevices;
+ ol_context_handle_t MOffloadContext{};
};
} // namespace detail
diff --git a/libsycl/src/detail/platform_impl.cpp b/libsycl/src/detail/platform_impl.cpp
index 96d52d1363a9b..1d81b848a21d3 100644
--- a/libsycl/src/detail/platform_impl.cpp
+++ b/libsycl/src/detail/platform_impl.cpp
@@ -8,6 +8,7 @@
#include <sycl/__impl/detail/config.hpp>
#include <sycl/__impl/detail/obj_utils.hpp>
+#include <sycl/__impl/property_list.hpp>
#include <detail/context_impl.hpp>
#include <detail/device_impl.hpp>
@@ -92,7 +93,13 @@ PlatformImpl::PlatformImpl(ol_platform_handle_t Platform, size_t PlatformIndex,
Device, *this, DeviceImpl::PrivateTag{}));
});
- MDefaultContext = ContextImpl::create(*this);
+ 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 {
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index 6116e2d787e6f..430c0eb302f72 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -8,6 +8,7 @@
#include <detail/queue_impl.hpp>
+#include <detail/context_impl.hpp>
#include <detail/device_impl.hpp>
#include <detail/event_impl.hpp>
#include <detail/global_objects.hpp>
@@ -66,6 +67,8 @@ QueueImpl::QueueImpl(DeviceImpl &deviceImpl, const async_handler &asyncHandler,
: MIsInorder(false), MAsyncHandler(asyncHandler), MPropList(propList),
MDevice(deviceImpl),
MContext(MDevice.getPlatformImpl().getDefaultContext()) {
+ assert(MContext.getOLHandleRef() &&
+ "Queue must be associated with a valid offload context");
callAndThrow(olCreateQueue, MDevice.getOLHandle(), &MOffloadQueue);
}
diff --git a/libsycl/unittests/mock/helpers.cpp b/libsycl/unittests/mock/helpers.cpp
index 6b4460cd66113..b430be7b35395 100644
--- a/libsycl/unittests/mock/helpers.cpp
+++ b/libsycl/unittests/mock/helpers.cpp
@@ -181,6 +181,33 @@ void mock::MockLiboffload::initDefault() {
return OL_SUCCESS;
});
+ ON_CALL(*this, olCreateContext)
+ .WillByDefault([this](size_t NumDevices, ol_device_handle_t *Devices,
+ ol_context_handle_t *Context) -> ol_result_t {
+ if (!Devices || !Context)
+ return makeEmptyStrError(OL_ERRC_INVALID_NULL_POINTER);
+ if (NumDevices == 0)
+ return makeEmptyStrError(OL_ERRC_INVALID_SIZE);
+ for (size_t I = 0; I < NumDevices; ++I) {
+ if (!Devices[I])
+ return makeEmptyStrError(OL_ERRC_INVALID_NULL_HANDLE);
+ }
+
+ // Preserve the first device in payload for tests that may need to
+ // inspect what device set the context was created from.
+ *Context = mock::createDummyHandleWithData<ol_context_handle_t>(
+ reinterpret_cast<unsigned char *>(&Devices[0]), sizeof(Devices[0]));
+ return OL_SUCCESS;
+ });
+
+ ON_CALL(*this, olDestroyContext)
+ .WillByDefault([this](ol_context_handle_t Context) -> ol_result_t {
+ if (!Context)
+ return makeEmptyStrError(OL_ERRC_INVALID_NULL_HANDLE);
+ mock::releaseDummyHandle(Context);
+ return OL_SUCCESS;
+ });
+
ON_CALL(*this, olCreateProgram)
.WillByDefault([this](ol_device_handle_t Device, const void *ProgData,
size_t ProgDataSize,
diff --git a/libsycl/unittests/mock/helpers.hpp b/libsycl/unittests/mock/helpers.hpp
index 0690970f1f99b..2435a49b22f29 100644
--- a/libsycl/unittests/mock/helpers.hpp
+++ b/libsycl/unittests/mock/helpers.hpp
@@ -89,6 +89,10 @@ class MockLiboffload {
MOCK_METHOD(ol_result_t, olIterateDevices,
(ol_device_iterate_cb_t Callback, void *UserData));
MOCK_METHOD(ol_result_t, olDestroyProgram, (ol_program_handle_t Program));
+ MOCK_METHOD(ol_result_t, olCreateContext,
+ (size_t NumDevices, ol_device_handle_t *Devices,
+ ol_context_handle_t *Context));
+ MOCK_METHOD(ol_result_t, olDestroyContext, (ol_context_handle_t Context));
MOCK_METHOD(ol_result_t, olCreateQueue,
(ol_device_handle_t Device, ol_queue_handle_t *Queue));
MOCK_METHOD(ol_result_t, olDestroyQueue, (ol_queue_handle_t Queue));
diff --git a/libsycl/unittests/mock/mock.cpp b/libsycl/unittests/mock/mock.cpp
index 8f5db4b5cb6ec..0a2eefb887c4b 100644
--- a/libsycl/unittests/mock/mock.cpp
+++ b/libsycl/unittests/mock/mock.cpp
@@ -50,6 +50,16 @@ ol_result_t olDestroyProgram(ol_program_handle_t Program) {
return mock::getMockLiboffload().olDestroyProgram(Program);
}
+ol_result_t olCreateContext(size_t NumDevices, ol_device_handle_t *Devices,
+ ol_context_handle_t *Context) {
+ return mock::getMockLiboffload().olCreateContext(NumDevices, Devices,
+ Context);
+}
+
+ol_result_t olDestroyContext(ol_context_handle_t Context) {
+ return mock::getMockLiboffload().olDestroyContext(Context);
+}
+
ol_result_t olCreateQueue(ol_device_handle_t Device, ol_queue_handle_t *Queue) {
return mock::getMockLiboffload().olCreateQueue(Device, Queue);
}
More information about the llvm-commits
mailing list