[llvm] [libsycl] Add context to exception's throw. (PR #224668)
Kseniya Tikhomirova via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 24 09:52:07 PDT 2026
https://github.com/KseniyaTikhomirova updated https://github.com/llvm/llvm-project/pull/224668
>From 503fe1d6c08e95424b05b488e6a47d3dee0996ec Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Fri, 18 Sep 2026 07:18:00 -0700
Subject: [PATCH 1/4] [libsycl] Add context to exception's throw
Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
libsycl/include/sycl/__impl/exception.hpp | 4 +-
libsycl/src/detail/context_impl.cpp | 10 +-
libsycl/src/detail/device_image_wrapper.cpp | 17 +--
libsycl/src/detail/device_image_wrapper.hpp | 7 +-
libsycl/src/detail/global_objects.cpp | 7 ++
libsycl/src/detail/offload/offload_utils.hpp | 36 +++++++
libsycl/src/detail/queue_impl.cpp | 66 +++++++-----
libsycl/src/handler.cpp | 9 +-
libsycl/src/usm_functions.cpp | 18 ++--
libsycl/unittests/common/unittests_helper.hpp | 12 +--
libsycl/unittests/handler/semantics.cpp | 4 +
libsycl/unittests/mock/helpers.hpp | 11 +-
libsycl/unittests/queue/memcpy.cpp | 101 ++++++++++++++++++
libsycl/unittests/queue/prefetch.cpp | 18 ++++
libsycl/unittests/queue/queue.cpp | 44 ++++++++
libsycl/unittests/usm/alloc.cpp | 76 +++++++++++++
16 files changed, 381 insertions(+), 59 deletions(-)
diff --git a/libsycl/include/sycl/__impl/exception.hpp b/libsycl/include/sycl/__impl/exception.hpp
index c3c8ff32a1e05..588083a45a19b 100644
--- a/libsycl/include/sycl/__impl/exception.hpp
+++ b/libsycl/include/sycl/__impl/exception.hpp
@@ -165,9 +165,11 @@ class _LIBSYCL_EXPORT exception : public virtual std::exception {
private:
exception(std::error_code EC, std::shared_ptr<context> SharedPtrCtx,
const char *WhatArg);
+
// Exceptions must be noexcept copy constructible, so cannot use std::string
- // or context directly.
+ // directly.
std::shared_ptr<std::string> MMessage;
+
std::shared_ptr<context> MContext;
std::error_code MErrC = make_error_code(sycl::errc::invalid);
};
diff --git a/libsycl/src/detail/context_impl.cpp b/libsycl/src/detail/context_impl.cpp
index 0525cee4535c7..7469491281242 100644
--- a/libsycl/src/detail/context_impl.cpp
+++ b/libsycl/src/detail/context_impl.cpp
@@ -75,10 +75,10 @@ ContextImpl::getOrCreateKernel(const DeviceImageManager &DeviceImage,
// used rather than emplace: the latter would build a program even when one
// is already cached, only to destroy it again.
try {
- ProgramIt = ProgramsForImage
- .try_emplace(DeviceHandle, MOffloadContext, DeviceHandle,
- DeviceImage)
- .first;
+ ProgramIt =
+ ProgramsForImage
+ .try_emplace(DeviceHandle, *this, DeviceHandle, DeviceImage)
+ .first;
} catch (...) {
// Do not leave an empty entry behind if program creation failed.
if (ProgramsForImage.empty())
@@ -87,7 +87,7 @@ ContextImpl::getOrCreateKernel(const DeviceImageManager &DeviceImage,
}
}
- return ProgramIt->second.getOrCreateKernel(KernelName);
+ return ProgramIt->second.getOrCreateKernel(KernelName, *this);
}
void ContextImpl::releaseProgramsForImage(
diff --git a/libsycl/src/detail/device_image_wrapper.cpp b/libsycl/src/detail/device_image_wrapper.cpp
index 7f5582f3b680b..76b759eec11c7 100644
--- a/libsycl/src/detail/device_image_wrapper.cpp
+++ b/libsycl/src/detail/device_image_wrapper.cpp
@@ -8,20 +8,20 @@
#include <detail/device_image_wrapper.hpp>
+#include <detail/context_impl.hpp>
#include <detail/offload/offload_utils.hpp>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
-ProgramWrapper::ProgramWrapper(ol_context_handle_t Context,
- ol_device_handle_t Device,
+ProgramWrapper::ProgramWrapper(ContextImpl &Context, ol_device_handle_t Device,
const DeviceImageManager &DevImage) {
- assert(Context);
+ assert(Context.getOLHandleRef());
assert(Device);
llvm::StringRef Image = DevImage.getOffloadBinary().getImage();
- callAndThrow(olCreateProgram, Context, Device, Image.data(), Image.size(),
- &MProgram);
+ callAndThrow(Context, olCreateProgram, Context.getOLHandleRef(), Device,
+ Image.data(), Image.size(), &MProgram);
}
ProgramWrapper::~ProgramWrapper() {
@@ -31,14 +31,15 @@ ProgramWrapper::~ProgramWrapper() {
}
ol_symbol_handle_t
-ProgramWrapper::getOrCreateKernel(std::string_view KernelName) {
+ProgramWrapper::getOrCreateKernel(std::string_view KernelName,
+ ContextImpl &Context) {
auto It = MKernels.find(KernelName);
if (It != MKernels.end())
return It->second;
ol_symbol_handle_t Kernel{};
- callAndThrow(olGetSymbol, MProgram, KernelName.data(), OL_SYMBOL_KIND_KERNEL,
- &Kernel);
+ callAndThrow(Context, olGetSymbol, MProgram, KernelName.data(),
+ OL_SYMBOL_KIND_KERNEL, &Kernel);
MKernels.emplace(KernelName, Kernel);
return Kernel;
}
diff --git a/libsycl/src/detail/device_image_wrapper.hpp b/libsycl/src/detail/device_image_wrapper.hpp
index 5d639a2fe2970..e67ee77208d07 100644
--- a/libsycl/src/detail/device_image_wrapper.hpp
+++ b/libsycl/src/detail/device_image_wrapper.hpp
@@ -28,6 +28,7 @@
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
+class ContextImpl;
class DeviceImageManager;
/// A wrapper of liboffload program handle to manage its lifetime.
@@ -41,7 +42,7 @@ class ProgramWrapper {
/// \param DevImage is the device image to use for program creation.
/// \throw sycl::exception with sycl::errc::runtime when failed to create the
/// program.
- ProgramWrapper(ol_context_handle_t Context, ol_device_handle_t Device,
+ ProgramWrapper(ContextImpl &Context, ol_device_handle_t Device,
const DeviceImageManager &DevImage);
/// Releases the corresponding liboffload program handle by calling
@@ -65,10 +66,12 @@ class ProgramWrapper {
/// for a program it does not belong to.
///
/// \param KernelName the name of the kernel to look up.
+ /// \param Context is the context this program belongs to.
/// \throw sycl::exception with sycl::errc::runtime when the symbol lookup
/// fails.
/// \return the liboffload symbol handle of the kernel.
- ol_symbol_handle_t getOrCreateKernel(std::string_view KernelName);
+ ol_symbol_handle_t getOrCreateKernel(std::string_view KernelName,
+ ContextImpl &Context);
private:
ol_program_handle_t MProgram{};
diff --git a/libsycl/src/detail/global_objects.cpp b/libsycl/src/detail/global_objects.cpp
index aa45961a1bee0..9ebd306364b4c 100644
--- a/libsycl/src/detail/global_objects.cpp
+++ b/libsycl/src/detail/global_objects.cpp
@@ -35,6 +35,13 @@ struct StaticVarShutdownHandler {
operator=(const StaticVarShutdownHandler &) = delete;
~StaticVarShutdownHandler() {
ProgramAndKernelManager::getInstance().releaseResources();
+ {
+ auto &[AsyncExceptions, AsyncExceptionsMutex] = getAsyncExceptionList();
+ {
+ std::lock_guard<SpinLock> Lock(AsyncExceptionsMutex);
+ AsyncExceptions.clear();
+ }
+ }
// No error reporting in shutdown
std::ignore = olShutDown();
}
diff --git a/libsycl/src/detail/offload/offload_utils.hpp b/libsycl/src/detail/offload/offload_utils.hpp
index f565ca86aef4d..45c6274aa6732 100644
--- a/libsycl/src/detail/offload/offload_utils.hpp
+++ b/libsycl/src/detail/offload/offload_utils.hpp
@@ -16,6 +16,7 @@
#define _LIBSYCL_OFFLOAD_UTILS
#include <sycl/__impl/backend.hpp>
+#include <sycl/__impl/context.hpp>
#include <sycl/__impl/detail/config.hpp>
#include <sycl/__impl/detail/unified_range_view.hpp>
#include <sycl/__impl/exception.hpp>
@@ -28,6 +29,8 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
+class ContextImpl;
+
/// Converts liboffload error code to C-string.
///
/// \param Error liboffload error code.
@@ -64,6 +67,25 @@ void checkAndThrow(ol_result_t Result) {
}
}
+/// Checks liboffload API call result, attaches the context to the exception.
+///
+/// Used after calling the API without check.
+/// To be called when specific handling is needed and explicitly done by
+/// developer before throwing exception.
+///
+/// \param Context context the failed API call was made for.
+/// \param Result liboffload result of calling API.
+///
+/// \throw sycl::exception if the call was not successful.
+template <sycl::errc errc = sycl::errc::runtime>
+void checkAndThrow(ContextImpl &Context, ol_result_t Result) {
+ if (isFailed(Result)) {
+ throw sycl::exception(createSyclObjFromImpl<sycl::context>(Context),
+ sycl::make_error_code(errc),
+ detail::formatCodeString(Result));
+ }
+}
+
/// Calls the API, doesn't check result.
/// To be called when specific handling is needed and explicitly done by
/// developer after.
@@ -89,6 +111,20 @@ void callAndThrow(FunctionType &Function, ArgsT &&...Args) {
checkAndThrow(Err);
}
+/// Calls the API and checks result, attaches the context to the exception.
+///
+/// \param Context context the API call is made for.
+/// \param Function liboffload API function to be called.
+/// \param Args arguments to be passed to the liboffload API function.
+///
+/// \throw sycl::exception if the call was not successful.
+template <typename FunctionType, typename... ArgsT>
+void callAndThrow(ContextImpl &Context, FunctionType &Function,
+ ArgsT &&...Args) {
+ auto Err = callNoCheck(Function, std::forward<ArgsT>(Args)...);
+ checkAndThrow(Context, Err);
+}
+
/// Converts liboffload backend to SYCL backend.
///
/// \param Backend liboffload backend.
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index c6f3b2bd86cbc..31419254a0c01 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -24,9 +24,10 @@ namespace detail {
thread_local bool NestedCallsDetector = false;
class NestedCallsTracker {
public:
- NestedCallsTracker() {
+ NestedCallsTracker(ContextImpl &QueueContext) {
if (NestedCallsDetectorRef)
throw sycl::exception(
+ createSyclObjFromImpl<context>(QueueContext),
make_error_code(errc::invalid),
"Calls to sycl::queue::submit cannot be nested. Command group "
"function objects should use the sycl::handler API instead.");
@@ -52,9 +53,10 @@ QueueImpl::QueueImpl(const std::shared_ptr<ContextImpl> &contextImpl,
// liboffload guarantees OL_ERRC_INVALID_DEVICE when the device does not
// belong to the context.
if (isFailed(Err) && Err->Code == OL_ERRC_INVALID_DEVICE)
- throw sycl::exception(sycl::make_error_code(sycl::errc::invalid),
+ throw sycl::exception(createSyclObjFromImpl<context>(*MContext),
+ sycl::make_error_code(sycl::errc::invalid),
"The device is not associated with the context.");
- checkAndThrow(Err);
+ checkAndThrow(*MContext, Err);
}
QueueImpl::~QueueImpl() {
@@ -77,7 +79,10 @@ static ol_device_handle_t getHostOLDevice() {
return HostDevice;
}
-void QueueImpl::wait() { callAndThrow(olSyncQueue, MOffloadQueue); }
+void QueueImpl::wait() {
+ assert(MContext && "Context impl ptr can't be nullptr");
+ callAndThrow(*MContext, olSyncQueue, MOffloadQueue);
+}
void QueueImpl::waitAndThrow() {
wait();
@@ -87,7 +92,8 @@ void QueueImpl::waitAndThrow() {
void QueueImpl::throwAsynchronous() { flushAsyncExceptions(); }
static void checkEventsPlatformMatch(const std::vector<EventImplPtr> &Events,
- const PlatformImpl &QueuePlatform) {
+ const PlatformImpl &QueuePlatform,
+ ContextImpl &QueueContext) {
// liboffload limitation to olWaitEvents. We can't do any extra handling for
// cross context/platform events without host task support now.
// "The input events can be from any queue on any device provided by the
@@ -97,6 +103,7 @@ static void checkEventsPlatformMatch(const std::vector<EventImplPtr> &Events,
return &Event->getPlatformImpl() == &QueuePlatform;
})) {
throw sycl::exception(
+ createSyclObjFromImpl<context>(QueueContext),
sycl::make_error_code(sycl::errc::feature_not_supported),
"libsycl doesn't support cross-context/platform event dependencies "
"yet.");
@@ -111,13 +118,15 @@ void QueueImpl::setKernelLaunchParams(std::vector<EventImplPtr> &&Events,
void QueueImpl::setKernelLaunchParams(
std::vector<EventImplPtr> &&Events,
const ol_kernel_launch_size_args_t &Range) {
- checkEventsPlatformMatch(Events, MDevice.getPlatformImpl());
+ assert(MContext && "Context impl ptr can't be nullptr");
+ checkEventsPlatformMatch(Events, MDevice.getPlatformImpl(), *MContext);
MCurrentSubmitInfo.DepEvents = std::move(Events);
MCurrentSubmitInfo.Range = Range;
}
void QueueImpl::submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
size_t ArgSize) {
+ assert(MContext && "Context impl ptr can't be nullptr");
ol_symbol_handle_t Kernel =
detail::ProgramAndKernelManager::getInstance().getOrCreateKernel(
KernelInfo, MContext, MDevice);
@@ -135,7 +144,8 @@ void QueueImpl::submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
&MCurrentSubmitInfo.Range, NULL, 1, ArgPtrs, ArgSizes);
if (isFailed(Result))
- throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
+ throw sycl::exception(createSyclObjFromImpl<context>(*MContext),
+ sycl::make_error_code(sycl::errc::runtime),
std::string("Kernel submission (") +
KernelInfo.getName().data() + ") failed with " +
formatCodeString(Result));
@@ -144,13 +154,13 @@ void QueueImpl::submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
createEvent(std::move(MCurrentSubmitInfo.DepEvents));
}
-static ol_device_handle_t getAllocDevice(ol_context_handle_t Context,
+static ol_device_handle_t getAllocDevice(ContextImpl &Context,
const void *ptr) {
// TODO: consider caching this information to avoid querying it every time.
ol_device_handle_t Device{};
[[maybe_unused]] ol_result_t Result =
- callNoCheck(olGetMemInfo, Context, ptr, OL_MEM_INFO_DEVICE,
- sizeof(ol_device_handle_t), &Device);
+ callNoCheck(olGetMemInfo, Context.getOLHandleRef(), ptr,
+ OL_MEM_INFO_DEVICE, sizeof(ol_device_handle_t), &Device);
if (detail::isFailed(Result)) {
// NOT_FOUND: the pointer isn't a liboffload allocation at all (plain host
// malloc). INVALID_ARGUMENT: it's a liboffload host allocation, which has
@@ -159,7 +169,7 @@ static ol_device_handle_t getAllocDevice(ol_context_handle_t Context,
Result->Code == OL_ERRC_INVALID_ARGUMENT) {
return getHostOLDevice();
}
- checkAndThrow(Result);
+ checkAndThrow(Context, Result);
}
assert(Device);
@@ -169,37 +179,39 @@ static ol_device_handle_t getAllocDevice(ol_context_handle_t Context,
std::shared_ptr<EventImpl>
QueueImpl::memcpy(void *Dest, const void *Src, std::size_t NumBytes,
const std::vector<EventImplPtr> &DepEvents) {
- checkEventsPlatformMatch(DepEvents, MDevice.getPlatformImpl());
+ assert(MContext && "Context impl ptr can't be nullptr");
+ checkEventsPlatformMatch(DepEvents, MDevice.getPlatformImpl(), *MContext);
if (NumBytes == 0) {
return submitWait(DepEvents);
}
if (!Dest || !Src) {
- throw sycl::exception(sycl::make_error_code(sycl::errc::invalid),
+ throw sycl::exception(createSyclObjFromImpl<context>(*MContext),
+ sycl::make_error_code(sycl::errc::invalid),
"Nullptr argument in memcpy operation");
}
- ol_device_handle_t DestOLDevice =
- getAllocDevice(MContext->getOLHandleRef(), Dest);
- ol_device_handle_t SrcOLDevice =
- getAllocDevice(MContext->getOLHandleRef(), Src);
+ ol_device_handle_t DestOLDevice = getAllocDevice(*MContext, Dest);
+ ol_device_handle_t SrcOLDevice = getAllocDevice(*MContext, Src);
handleEventDependencies(DepEvents);
- callAndThrow(olMemcpy, MOffloadQueue, Dest, DestOLDevice, Src, SrcOLDevice,
- NumBytes);
+ callAndThrow(*MContext, olMemcpy, MOffloadQueue, Dest, DestOLDevice, Src,
+ SrcOLDevice, NumBytes);
return createEvent();
}
EventImplPtr QueueImpl::prefetch(void *Ptr, std::size_t NumBytes,
const std::vector<EventImplPtr> &DepEvents) {
- checkEventsPlatformMatch(DepEvents, MDevice.getPlatformImpl());
+ assert(MContext && "Context impl ptr can't be nullptr");
+ checkEventsPlatformMatch(DepEvents, MDevice.getPlatformImpl(), *MContext);
if (NumBytes == 0) {
handleEventDependencies(DepEvents);
return createEvent();
}
if (!Ptr) {
- throw sycl::exception(sycl::make_error_code(sycl::errc::invalid),
+ throw sycl::exception(createSyclObjFromImpl<context>(*MContext),
+ sycl::make_error_code(sycl::errc::invalid),
"Nullptr argument in prefetch operation");
}
@@ -211,12 +223,14 @@ EventImplPtr QueueImpl::prefetch(void *Ptr, std::size_t NumBytes,
OL_MEM_MIGRATION_FLAG_HOST_TO_DEVICE;
handleEventDependencies(DepEvents);
- callAndThrow(olMemPrefetch, MOffloadQueue, Count, Mems, Sizes, Flag);
+ callAndThrow(*MContext, olMemPrefetch, MOffloadQueue, Count, Mems, Sizes,
+ Flag);
return createEvent();
}
void QueueImpl::handleEventDependencies(const std::vector<EventImplPtr> &Deps) {
+ assert(MContext && "Context impl ptr can't be nullptr");
// TODO: liboffload supports only in-order queues and no cross context waiting
// is available now that means that this code is excessive but correct. I
// don't want to skip it and rely on default liboffload behaviour that is
@@ -225,24 +239,26 @@ void QueueImpl::handleEventDependencies(const std::vector<EventImplPtr> &Deps) {
// context dependencies should be enabled and checked as well.
if (!Deps.empty()) {
auto EventHandles = getSyclObjHandles(Deps);
- callAndThrow(olWaitEvents, MOffloadQueue, EventHandles.data(),
+ callAndThrow(*MContext, olWaitEvents, MOffloadQueue, EventHandles.data(),
EventHandles.size());
}
}
EventImplPtr QueueImpl::createEvent(std::vector<EventImplPtr> &&Deps) {
+ assert(MContext && "Context impl ptr can't be nullptr");
ol_event_handle_t NewEvent{};
ol_event_flags_t Flags{};
- callAndThrow(olCreateEvent, MOffloadQueue, Flags, &NewEvent);
+ callAndThrow(*MContext, olCreateEvent, MOffloadQueue, Flags, &NewEvent);
return EventImpl::createEventWithHandle(NewEvent, MDevice.getPlatformImpl(),
std::move(Deps));
}
EventImplPtr QueueImpl::submitWithHandler(const TypelessCGF &CGF) {
+ assert(MContext && "Context impl ptr can't be nullptr");
detail::HandlerImpl HandlerImplVal(*this);
handler Handler(HandlerImplVal);
{
- NestedCallsTracker tracker;
+ NestedCallsTracker tracker(*MContext);
CGF(Handler);
}
diff --git a/libsycl/src/handler.cpp b/libsycl/src/handler.cpp
index a8cc6004098fe..a73fafd398b11 100644
--- a/libsycl/src/handler.cpp
+++ b/libsycl/src/handler.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include <detail/context_impl.hpp>
#include <detail/handler_impl.hpp>
#include <detail/offload/offload_utils.hpp>
#include <detail/queue_impl.hpp>
@@ -14,9 +15,11 @@
_LIBSYCL_BEGIN_NAMESPACE_SYCL
static void checkCommandGroupFunction(
- const std::function<std::shared_ptr<detail::EventImpl>()> &CGF) {
+ const std::function<std::shared_ptr<detail::EventImpl>()> &CGF,
+ detail::ContextImpl &QueueContext) {
if (CGF) {
throw sycl::exception(
+ detail::createSyclObjFromImpl<context>(QueueContext),
sycl::make_error_code(sycl::errc::invalid),
"Attempt to set multiple actions for the command group");
}
@@ -24,7 +27,7 @@ static void checkCommandGroupFunction(
void handler::submitKernelImpl(detail::DeviceKernelInfo &KernelInfo,
void *ArgData, size_t ArgSize) {
- checkCommandGroupFunction(MImpl.MCGF);
+ checkCommandGroupFunction(MImpl.MCGF, MImpl.MQueue.getContext());
MImpl.MArgData.resize(ArgSize);
std::memcpy(MImpl.MArgData.data(), ArgData, ArgSize);
MImpl.MCGF = [this, &KernelInfo]() {
@@ -41,7 +44,7 @@ void handler::setKernelRange(const detail::UnifiedRangeView &Range) {
}
void handler::memcpy(void *dest, const void *src, std::size_t numBytes) {
- checkCommandGroupFunction(MImpl.MCGF);
+ checkCommandGroupFunction(MImpl.MCGF, MImpl.MQueue.getContext());
MImpl.MCGF = [this, dest, src, numBytes]() {
return MImpl.MQueue.memcpy(dest, src, numBytes,
detail::getSyclObjImpls(MDepEvents));
diff --git a/libsycl/src/usm_functions.cpp b/libsycl/src/usm_functions.cpp
index 699fdeec79e22..c513aca9e38db 100644
--- a/libsycl/src/usm_functions.cpp
+++ b/libsycl/src/usm_functions.cpp
@@ -57,7 +57,7 @@ static device getHostAllocDevice(const context &syclContext) {
if (It == ContextDevices.end()) {
throw sycl::exception(
- sycl::errc::feature_not_supported,
+ syclContext, sycl::errc::feature_not_supported,
"None of the context's devices support host USM allocations.");
}
return *It;
@@ -118,7 +118,8 @@ void *malloc_shared(std::size_t numBytes, const queue &syclQueue,
// SYCL 2020 4.8.3.5. Parameterized allocation functions.
-static aspect getAspectByAllocationKind(usm::alloc kind) {
+static aspect getAspectByAllocationKind(usm::alloc kind,
+ const context &syclContext) {
switch (kind) {
case usm::alloc::host:
return aspect::usm_host_allocations;
@@ -129,7 +130,7 @@ static aspect getAspectByAllocationKind(usm::alloc kind) {
case usm::alloc::unknown:
// usm::alloc::unknown can be returned to user from get_pointer_type but
// it can't be converted to a valid backend type.
- throw exception(sycl::make_error_code(sycl::errc::invalid),
+ throw exception(syclContext, sycl::make_error_code(sycl::errc::invalid),
"Invalid USM allocation kind requested");
}
}
@@ -141,12 +142,12 @@ void *aligned_alloc(std::size_t alignment, std::size_t numBytes,
auto ContextDevices = syclContext.get_devices();
if (std::none_of(ContextDevices.begin(), ContextDevices.end(),
[&syclDevice](device Dev) { return Dev == syclDevice; }))
- throw exception(make_error_code(errc::invalid),
+ throw exception(syclContext, make_error_code(errc::invalid),
"Specified device is not contained by specified context.");
- if (!syclDevice.has(getAspectByAllocationKind(kind)))
+ if (!syclDevice.has(getAspectByAllocationKind(kind, syclContext)))
throw sycl::exception(
- sycl::errc::feature_not_supported,
+ syclContext, sycl::errc::feature_not_supported,
"Device doesn't support requested kind of USM allocation");
if (!numBytes)
@@ -197,8 +198,9 @@ void *malloc(std::size_t numBytes, const queue &syclQueue, usm::alloc kind,
// SYCL 2020 4.8.3.6. Memory deallocation functions.
void free(void *ptr, const context &ctxt) {
- auto OLContext = detail::getSyclObjImpl(ctxt)->getOLHandleRef();
- detail::callAndThrow(olMemFree, OLContext, ptr);
+ detail::ContextImpl &ContextImplRef = *detail::getSyclObjImpl(ctxt);
+ detail::callAndThrow(ContextImplRef, olMemFree,
+ ContextImplRef.getOLHandleRef(), ptr);
}
void free(void *ptr, const queue &q) { return free(ptr, q.get_context()); }
diff --git a/libsycl/unittests/common/unittests_helper.hpp b/libsycl/unittests/common/unittests_helper.hpp
index f35684b36cdcd..8f1ba5bad0316 100644
--- a/libsycl/unittests/common/unittests_helper.hpp
+++ b/libsycl/unittests/common/unittests_helper.hpp
@@ -18,6 +18,8 @@
#include <detail/platform_impl.hpp>
#include <mock/helpers.hpp>
+#include <utility>
+
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace unittests {
@@ -29,17 +31,15 @@ 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(); }
+ UnittestsHelper() {
+ detail::PlatformImpl::rediscoverIfEmpty = true;
+ }
-private:
- static void resetPlatformState() {
+ ~UnittestsHelper() {
detail::getPlatformCache().clear();
detail::getOffloadTopologies() = {};
}
-public:
mock::MockWrapper Mock;
};
diff --git a/libsycl/unittests/handler/semantics.cpp b/libsycl/unittests/handler/semantics.cpp
index 3bd5d2bba4360..b4324a508554b 100644
--- a/libsycl/unittests/handler/semantics.cpp
+++ b/libsycl/unittests/handler/semantics.cpp
@@ -30,6 +30,8 @@ TEST(Handler, MultipleActionsRejected) {
Thrown = true;
EXPECT_NE(std::string(E.what()).find("multiple actions"),
std::string::npos);
+ EXPECT_TRUE(E.has_context());
+ EXPECT_EQ(E.get_context(), Q.get_context());
}
EXPECT_TRUE(Thrown);
@@ -122,6 +124,8 @@ TEST(Queue, SubmitCannotBeNested) {
Thrown = true;
EXPECT_NE(std::string(E.what()).find("cannot be nested"),
std::string::npos);
+ EXPECT_TRUE(E.has_context());
+ EXPECT_EQ(E.get_context(), Q.get_context());
}
EXPECT_TRUE(Thrown);
diff --git a/libsycl/unittests/mock/helpers.hpp b/libsycl/unittests/mock/helpers.hpp
index 46e9c7e70b4d5..c8b0a16f1dc64 100644
--- a/libsycl/unittests/mock/helpers.hpp
+++ b/libsycl/unittests/mock/helpers.hpp
@@ -157,8 +157,11 @@ class MockLiboffload {
ol_device_handle_t getHostOLDevice() { return HostDevice; }
private:
+ /// Installs the default single gpu-device configuration.
void initDefault();
+ friend class MockWrapper;
+
std::unordered_map<ol_errc_t, ol_error_struct_t> Errors;
ol_platform_handle_t DefaultPlatform{};
ol_device_handle_t DefaultDevice{};
@@ -179,7 +182,13 @@ _LIB_EXPORT MockLiboffload &getMockLiboffload();
class MockWrapper {
public:
MockWrapper() : Mock(getMockLiboffload()) {}
- ~MockWrapper() { ::testing::Mock::VerifyAndClearExpectations(&Mock); }
+
+ // VerifyAndClear verifies all expectations and drops the default actions.
+ ~MockWrapper() {
+ EXPECT_TRUE(::testing::Mock::VerifyAndClear(&Mock));
+ Mock.initDefault();
+ }
+
MockLiboffload &get() { return Mock; };
private:
diff --git a/libsycl/unittests/queue/memcpy.cpp b/libsycl/unittests/queue/memcpy.cpp
index 8184e5808e06e..9413c9cd5b63f 100644
--- a/libsycl/unittests/queue/memcpy.cpp
+++ b/libsycl/unittests/queue/memcpy.cpp
@@ -1,6 +1,8 @@
+#include <common/unittests_helper.hpp>
#include <mock/helpers.hpp>
#include <sycl/__impl/device.hpp>
+#include <sycl/__impl/platform.hpp>
#include <sycl/__impl/queue.hpp>
#include <detail/device_impl.hpp>
@@ -9,6 +11,9 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <array>
+#include <cstdint>
+
using namespace sycl;
using namespace ::testing;
@@ -80,3 +85,99 @@ TEST(Queue, MemcpyZeroBytes) {
event Event = Q.memcpy(nullptr, nullptr, 0);
Q.memcpy(nullptr, nullptr, 0, Event);
}
+
+TEST(Queue, MemcpyNullptrThrows) {
+ constexpr int NumBytes = 32;
+
+ mock::MockWrapper Mock;
+ queue Q;
+ int Src = 0;
+
+ EXPECT_CALL(Mock.get(), olGetMemInfo(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(Mock.get(), olMemcpy(_, _, _, _, _, _)).Times(0);
+
+ try {
+ Q.memcpy(nullptr, &Src, NumBytes);
+ FAIL() << "Expected sycl::exception";
+ } catch (const sycl::exception &E) {
+ EXPECT_EQ(E.code(), make_error_code(errc::invalid));
+ EXPECT_TRUE(E.has_context());
+ EXPECT_EQ(E.get_context(), Q.get_context());
+ }
+}
+
+namespace {
+
+// The default mock exposes a single platform, so device enumeration has to be
+// mocked to get events that belong to different platforms. Platforms are formed
+// per liboffload driver id.
+class QueueTwoPlatformsTest : 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_DRIVER_ID, _, _))
+ .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));
+ *static_cast<uint32_t *>(PropValue) = getDriverId(Device);
+ return OL_SUCCESS;
+ });
+ }
+
+ void TearDown() override {
+ mock::releaseDummyHandles(Devices[0], Devices[1], Platform);
+ }
+
+ uint32_t getDriverId(ol_device_handle_t Device) const {
+ if (Device == Devices[0])
+ 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, 2> Devices{};
+};
+
+} // namespace
+
+TEST_F(QueueTwoPlatformsTest, CrossPlatformDependencyThrows) {
+ std::vector<platform> Platforms = platform::get_platforms();
+ ASSERT_EQ(Platforms.size(), 2u);
+
+ std::vector<device> Devices0 = Platforms[0].get_devices();
+ std::vector<device> Devices1 = Platforms[1].get_devices();
+ ASSERT_EQ(Devices0.size(), 1u);
+ ASSERT_EQ(Devices1.size(), 1u);
+
+ queue Q0(Devices0.front());
+ queue Q1(Devices1.front());
+
+ event OtherPlatformEvent = Q1.memcpy(nullptr, nullptr, 0);
+
+ try {
+ Q0.memcpy(nullptr, nullptr, 0, OtherPlatformEvent);
+ FAIL() << "Expected sycl::exception";
+ } catch (const sycl::exception &E) {
+ EXPECT_EQ(E.code(), make_error_code(errc::feature_not_supported));
+ EXPECT_TRUE(E.has_context());
+ EXPECT_EQ(E.get_context(), Q0.get_context());
+ }
+}
diff --git a/libsycl/unittests/queue/prefetch.cpp b/libsycl/unittests/queue/prefetch.cpp
index 0abed1dd3f8f5..6bedd4ba8337b 100644
--- a/libsycl/unittests/queue/prefetch.cpp
+++ b/libsycl/unittests/queue/prefetch.cpp
@@ -50,3 +50,21 @@ TEST(Queue, PrefetchZeroBytes) {
event Event = Q.prefetch(nullptr, 0);
Q.prefetch(nullptr, 0, Event);
}
+
+TEST(Queue, PrefetchNullptrThrows) {
+ constexpr std::size_t NumBytes = 1024;
+
+ mock::MockWrapper Mock;
+ queue Q;
+
+ EXPECT_CALL(Mock.get(), olMemPrefetch(_, _, _, _, _)).Times(0);
+
+ try {
+ Q.prefetch(nullptr, NumBytes);
+ FAIL() << "Expected sycl::exception";
+ } catch (const sycl::exception &E) {
+ EXPECT_EQ(E.code(), make_error_code(errc::invalid));
+ EXPECT_TRUE(E.has_context());
+ EXPECT_EQ(E.get_context(), Q.get_context());
+ }
+}
diff --git a/libsycl/unittests/queue/queue.cpp b/libsycl/unittests/queue/queue.cpp
index 37174e571fd93..2182a588ffb30 100644
--- a/libsycl/unittests/queue/queue.cpp
+++ b/libsycl/unittests/queue/queue.cpp
@@ -52,3 +52,47 @@ TEST(Queue, ContextAndDeviceConstructor) {
EXPECT_EQ(AsyncSelectorQueue.get_context(), Context);
EXPECT_EQ(AsyncSelectorQueue.get_device(), Device);
}
+
+// The device does belong to the context here: the runtime relies on liboffload
+// to report OL_ERRC_INVALID_DEVICE, so the error is forced through the mock.
+TEST(Queue, CreateQueueInvalidDeviceErrorThrows) {
+ mock::MockWrapper Mock;
+
+ const device Device;
+ const context Context = Device.get_platform().khr_get_default_context();
+
+ EXPECT_CALL(Mock.get(), olCreateQueue(_, _, _))
+ .Times(1)
+ .WillOnce(Return(
+ mock::getMockLiboffload().makeEmptyStrError(OL_ERRC_INVALID_DEVICE)));
+
+ try {
+ queue Queue(Context, Device);
+ FAIL() << "Expected sycl::exception";
+ } catch (const sycl::exception &E) {
+ EXPECT_EQ(E.code(), make_error_code(errc::invalid));
+ EXPECT_TRUE(E.has_context());
+ EXPECT_EQ(E.get_context(), Context);
+ }
+}
+
+// Failures reported by liboffload and translated by checkAndThrow must carry
+// the context too.
+TEST(Queue, WaitFailureThrowsWithContext) {
+ mock::MockWrapper Mock;
+ queue Q;
+
+ EXPECT_CALL(Mock.get(), olSyncQueue(_))
+ .Times(1)
+ .WillOnce(Return(mock::getMockLiboffload().makeEmptyStrError(
+ OL_ERRC_OUT_OF_RESOURCES)));
+
+ try {
+ Q.wait();
+ FAIL() << "Expected sycl::exception";
+ } catch (const sycl::exception &E) {
+ EXPECT_EQ(E.code(), make_error_code(errc::runtime));
+ EXPECT_TRUE(E.has_context());
+ EXPECT_EQ(E.get_context(), Q.get_context());
+ }
+}
diff --git a/libsycl/unittests/usm/alloc.cpp b/libsycl/unittests/usm/alloc.cpp
index 1e80c336d18c6..b2c763ec17621 100644
--- a/libsycl/unittests/usm/alloc.cpp
+++ b/libsycl/unittests/usm/alloc.cpp
@@ -6,15 +6,18 @@
//
//===----------------------------------------------------------------------===//
+#include <common/unittests_helper.hpp>
#include <mock/helpers.hpp>
#include <sycl/__impl/device.hpp>
+#include <sycl/__impl/platform.hpp>
#include <sycl/__impl/queue.hpp>
#include <sycl/__impl/usm_functions.hpp>
#include <detail/device_impl.hpp>
#include <detail/queue_impl.hpp>
+#include <array>
#include <cstddef>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -188,6 +191,79 @@ TEST(USMFunctions, ZeroAlignmentSucceeds) {
free(Ptr3, Ctx);
}
+TEST(USMFunctions, UnknownAllocationKindThrows) {
+ mock::MockWrapper Mock;
+ queue Q;
+ device Dev = Q.get_device();
+ context Ctx = Q.get_context();
+
+ EXPECT_CALL(Mock.get(), olMemAlloc(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(Mock.get(), olMemAllocHost(_, _, _, _)).Times(0);
+
+ try {
+ sycl::malloc(NumBytes, Dev, Ctx, usm::alloc::unknown);
+ FAIL() << "Expected sycl::exception";
+ } catch (const sycl::exception &E) {
+ EXPECT_EQ(E.code(), make_error_code(errc::invalid));
+ EXPECT_TRUE(E.has_context());
+ EXPECT_EQ(E.get_context(), Ctx);
+ }
+}
+
+namespace {
+
+// The default mock exposes a single device, so device enumeration has to be
+// mocked to get a context that doesn't contain a device.
+class USMTwoDevicesTest : 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;
+ });
+ }
+
+ void TearDown() override {
+ mock::releaseDummyHandles(Devices[0], Devices[1], Platform);
+ }
+
+ unittests::UnittestsHelper Helper;
+ ol_platform_handle_t Platform{};
+ std::array<ol_device_handle_t, 2> Devices{};
+};
+
+} // namespace
+
+TEST_F(USMTwoDevicesTest, DeviceNotInContextThrows) {
+ std::vector<platform> Platforms = platform::get_platforms();
+ ASSERT_EQ(Platforms.size(), 1u);
+
+ std::vector<device> PlatformDevices = Platforms[0].get_devices();
+ ASSERT_EQ(PlatformDevices.size(), 2u);
+
+ context Ctx(PlatformDevices[0]);
+
+ EXPECT_CALL(Helper.Mock.get(), olMemAlloc(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(Helper.Mock.get(), olMemAllocAligned(_, _, _, _, _, _)).Times(0);
+
+ try {
+ malloc_device(NumBytes, PlatformDevices[1], Ctx);
+ FAIL() << "Expected sycl::exception";
+ } catch (const sycl::exception &E) {
+ EXPECT_EQ(E.code(), make_error_code(errc::invalid));
+ EXPECT_TRUE(E.has_context());
+ EXPECT_EQ(E.get_context(), Ctx);
+ }
+}
+
struct alignas(64) Over {
char c;
};
>From 5f3027410be9d72b15a6ab1466e64d3337c51e89 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Tue, 22 Sep 2026 09:19:18 -0700
Subject: [PATCH 2/4] fix comments
Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
libsycl/include/sycl/__impl/exception.hpp | 4 +---
libsycl/src/detail/global_objects.cpp | 7 -------
libsycl/unittests/common/unittests_helper.hpp | 4 +---
3 files changed, 2 insertions(+), 13 deletions(-)
diff --git a/libsycl/include/sycl/__impl/exception.hpp b/libsycl/include/sycl/__impl/exception.hpp
index 588083a45a19b..c3c8ff32a1e05 100644
--- a/libsycl/include/sycl/__impl/exception.hpp
+++ b/libsycl/include/sycl/__impl/exception.hpp
@@ -165,11 +165,9 @@ class _LIBSYCL_EXPORT exception : public virtual std::exception {
private:
exception(std::error_code EC, std::shared_ptr<context> SharedPtrCtx,
const char *WhatArg);
-
// Exceptions must be noexcept copy constructible, so cannot use std::string
- // directly.
+ // or context directly.
std::shared_ptr<std::string> MMessage;
-
std::shared_ptr<context> MContext;
std::error_code MErrC = make_error_code(sycl::errc::invalid);
};
diff --git a/libsycl/src/detail/global_objects.cpp b/libsycl/src/detail/global_objects.cpp
index b0f7559dc359d..0ef13295d513d 100644
--- a/libsycl/src/detail/global_objects.cpp
+++ b/libsycl/src/detail/global_objects.cpp
@@ -38,13 +38,6 @@ struct StaticVarShutdownHandler {
operator=(const StaticVarShutdownHandler &) = delete;
~StaticVarShutdownHandler() {
ProgramAndKernelManager::getInstance().releaseResources();
- {
- auto &[AsyncExceptions, AsyncExceptionsMutex] = getAsyncExceptionList();
- {
- std::lock_guard<SpinLock> Lock(AsyncExceptionsMutex);
- AsyncExceptions.clear();
- }
- }
// No error reporting in shutdown
std::ignore = olShutDown();
}
diff --git a/libsycl/unittests/common/unittests_helper.hpp b/libsycl/unittests/common/unittests_helper.hpp
index 8f1ba5bad0316..cdf2c863f0302 100644
--- a/libsycl/unittests/common/unittests_helper.hpp
+++ b/libsycl/unittests/common/unittests_helper.hpp
@@ -31,9 +31,7 @@ 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() { detail::PlatformImpl::rediscoverIfEmpty = true; }
~UnittestsHelper() {
detail::getPlatformCache().clear();
>From 50f3d5777d1579af79bbc043fd6b8191a1b2349d Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Thu, 24 Sep 2026 07:01:39 -0700
Subject: [PATCH 3/4] fix comments
Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
libsycl/src/detail/context_impl.cpp | 2 +-
libsycl/src/detail/device_image_wrapper.cpp | 14 +++++++-------
libsycl/src/detail/device_image_wrapper.hpp | 6 +++---
libsycl/src/detail/queue_impl.cpp | 10 +++++-----
libsycl/src/handler.cpp | 4 ++--
libsycl/src/usm_functions.cpp | 5 ++---
libsycl/unittests/common/unittests_helper.hpp | 2 --
libsycl/unittests/usm/alloc.cpp | 6 ++++--
8 files changed, 24 insertions(+), 25 deletions(-)
diff --git a/libsycl/src/detail/context_impl.cpp b/libsycl/src/detail/context_impl.cpp
index 7469491281242..0362c3d9241f0 100644
--- a/libsycl/src/detail/context_impl.cpp
+++ b/libsycl/src/detail/context_impl.cpp
@@ -87,7 +87,7 @@ ContextImpl::getOrCreateKernel(const DeviceImageManager &DeviceImage,
}
}
- return ProgramIt->second.getOrCreateKernel(KernelName, *this);
+ return ProgramIt->second.getOrCreateKernel(KernelName);
}
void ContextImpl::releaseProgramsForImage(
diff --git a/libsycl/src/detail/device_image_wrapper.cpp b/libsycl/src/detail/device_image_wrapper.cpp
index 76b759eec11c7..9133a28035524 100644
--- a/libsycl/src/detail/device_image_wrapper.cpp
+++ b/libsycl/src/detail/device_image_wrapper.cpp
@@ -15,12 +15,13 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
ProgramWrapper::ProgramWrapper(ContextImpl &Context, ol_device_handle_t Device,
- const DeviceImageManager &DevImage) {
- assert(Context.getOLHandleRef());
- assert(Device);
+ const DeviceImageManager &DevImage)
+ : MContext(Context) {
+ assert(MContext.getOLHandleRef() && "Context handle can't be nullptr");
+ assert(Device && "Device handle can't be nullptr");
llvm::StringRef Image = DevImage.getOffloadBinary().getImage();
- callAndThrow(Context, olCreateProgram, Context.getOLHandleRef(), Device,
+ callAndThrow(MContext, olCreateProgram, MContext.getOLHandleRef(), Device,
Image.data(), Image.size(), &MProgram);
}
@@ -31,14 +32,13 @@ ProgramWrapper::~ProgramWrapper() {
}
ol_symbol_handle_t
-ProgramWrapper::getOrCreateKernel(std::string_view KernelName,
- ContextImpl &Context) {
+ProgramWrapper::getOrCreateKernel(std::string_view KernelName) {
auto It = MKernels.find(KernelName);
if (It != MKernels.end())
return It->second;
ol_symbol_handle_t Kernel{};
- callAndThrow(Context, olGetSymbol, MProgram, KernelName.data(),
+ callAndThrow(MContext, olGetSymbol, MProgram, KernelName.data(),
OL_SYMBOL_KIND_KERNEL, &Kernel);
MKernels.emplace(KernelName, Kernel);
return Kernel;
diff --git a/libsycl/src/detail/device_image_wrapper.hpp b/libsycl/src/detail/device_image_wrapper.hpp
index e67ee77208d07..b7191e4484e28 100644
--- a/libsycl/src/detail/device_image_wrapper.hpp
+++ b/libsycl/src/detail/device_image_wrapper.hpp
@@ -66,14 +66,14 @@ class ProgramWrapper {
/// for a program it does not belong to.
///
/// \param KernelName the name of the kernel to look up.
- /// \param Context is the context this program belongs to.
/// \throw sycl::exception with sycl::errc::runtime when the symbol lookup
/// fails.
/// \return the liboffload symbol handle of the kernel.
- ol_symbol_handle_t getOrCreateKernel(std::string_view KernelName,
- ContextImpl &Context);
+ ol_symbol_handle_t getOrCreateKernel(std::string_view KernelName);
private:
+ // Programs are owned by their context, so the context outlives them.
+ ContextImpl &MContext;
ol_program_handle_t MProgram{};
// Kernel names are backed by the "symbols" string of the device image this
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index 0dcfa6223c09e..5163d8e795673 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -93,8 +93,8 @@ void QueueImpl::waitAndThrow() {
void QueueImpl::throwAsynchronous() { flushAsyncExceptions(); }
static void checkEventsPlatformMatch(const std::vector<EventImplPtr> &Events,
- const PlatformImpl &QueuePlatform,
ContextImpl &QueueContext) {
+ const PlatformImpl &QueuePlatform = QueueContext.getPlatformImpl();
// liboffload limitation to olWaitEvents. We can't do any extra handling for
// cross context/platform events without host task support now.
// "The input events can be from any queue on any device provided by the
@@ -120,7 +120,7 @@ void QueueImpl::setKernelLaunchParams(
std::vector<EventImplPtr> &&Events,
const ol_kernel_launch_size_args_t &Range) {
assert(MContext && "Context impl ptr can't be nullptr");
- checkEventsPlatformMatch(Events, MDevice.getPlatformImpl(), *MContext);
+ checkEventsPlatformMatch(Events, *MContext);
MCurrentSubmitInfo.DepEvents = std::move(Events);
MCurrentSubmitInfo.Range = Range;
}
@@ -181,7 +181,7 @@ std::shared_ptr<EventImpl>
QueueImpl::memcpy(void *Dest, const void *Src, std::size_t NumBytes,
const std::vector<EventImplPtr> &DepEvents) {
assert(MContext && "Context impl ptr can't be nullptr");
- checkEventsPlatformMatch(DepEvents, MDevice.getPlatformImpl(), *MContext);
+ checkEventsPlatformMatch(DepEvents, *MContext);
if (NumBytes == 0)
return submitWait(DepEvents);
@@ -204,7 +204,7 @@ EventImplPtr QueueImpl::fill(void *Ptr, const void *Pattern,
const std::vector<EventImplPtr> &DepEvents) {
assert(PatternSize > 0 && "Pattern size has to be greater than zero");
assert(MContext && "Context impl ptr can't be nullptr");
- checkEventsPlatformMatch(DepEvents, MDevice.getPlatformImpl(), *MContext);
+ checkEventsPlatformMatch(DepEvents, *MContext);
if (Count == 0)
return submitWait(DepEvents);
@@ -225,7 +225,7 @@ EventImplPtr QueueImpl::fill(void *Ptr, const void *Pattern,
EventImplPtr QueueImpl::prefetch(void *Ptr, std::size_t NumBytes,
const std::vector<EventImplPtr> &DepEvents) {
assert(MContext && "Context impl ptr can't be nullptr");
- checkEventsPlatformMatch(DepEvents, MDevice.getPlatformImpl(), *MContext);
+ checkEventsPlatformMatch(DepEvents, *MContext);
if (NumBytes == 0)
return submitWait(DepEvents);
diff --git a/libsycl/src/handler.cpp b/libsycl/src/handler.cpp
index a73fafd398b11..72f05f51ca661 100644
--- a/libsycl/src/handler.cpp
+++ b/libsycl/src/handler.cpp
@@ -16,10 +16,10 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
static void checkCommandGroupFunction(
const std::function<std::shared_ptr<detail::EventImpl>()> &CGF,
- detail::ContextImpl &QueueContext) {
+ detail::ContextImpl &Context) {
if (CGF) {
throw sycl::exception(
- detail::createSyclObjFromImpl<context>(QueueContext),
+ detail::createSyclObjFromImpl<context>(Context),
sycl::make_error_code(sycl::errc::invalid),
"Attempt to set multiple actions for the command group");
}
diff --git a/libsycl/src/usm_functions.cpp b/libsycl/src/usm_functions.cpp
index c513aca9e38db..f6278bd43df71 100644
--- a/libsycl/src/usm_functions.cpp
+++ b/libsycl/src/usm_functions.cpp
@@ -198,9 +198,8 @@ void *malloc(std::size_t numBytes, const queue &syclQueue, usm::alloc kind,
// SYCL 2020 4.8.3.6. Memory deallocation functions.
void free(void *ptr, const context &ctxt) {
- detail::ContextImpl &ContextImplRef = *detail::getSyclObjImpl(ctxt);
- detail::callAndThrow(ContextImplRef, olMemFree,
- ContextImplRef.getOLHandleRef(), ptr);
+ detail::ContextImpl &Context = *detail::getSyclObjImpl(ctxt);
+ detail::callAndThrow(Context, olMemFree, Context.getOLHandleRef(), ptr);
}
void free(void *ptr, const queue &q) { return free(ptr, q.get_context()); }
diff --git a/libsycl/unittests/common/unittests_helper.hpp b/libsycl/unittests/common/unittests_helper.hpp
index cdf2c863f0302..e47a3eddc50e1 100644
--- a/libsycl/unittests/common/unittests_helper.hpp
+++ b/libsycl/unittests/common/unittests_helper.hpp
@@ -18,8 +18,6 @@
#include <detail/platform_impl.hpp>
#include <mock/helpers.hpp>
-#include <utility>
-
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace unittests {
diff --git a/libsycl/unittests/usm/alloc.cpp b/libsycl/unittests/usm/alloc.cpp
index b2c763ec17621..28dd14b81c387 100644
--- a/libsycl/unittests/usm/alloc.cpp
+++ b/libsycl/unittests/usm/alloc.cpp
@@ -17,11 +17,13 @@
#include <detail/device_impl.hpp>
#include <detail/queue_impl.hpp>
-#include <array>
-#include <cstddef>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <array>
+#include <cstddef>
+#include <vector>
+
using namespace sycl;
using namespace ::testing;
>From b4b46b2320c620da59978c33a5cd3a198be3f534 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Thu, 24 Sep 2026 09:51:47 -0700
Subject: [PATCH 4/4] fix tests
Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
libsycl/unittests/common/unittests_helper.hpp | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/libsycl/unittests/common/unittests_helper.hpp b/libsycl/unittests/common/unittests_helper.hpp
index e47a3eddc50e1..49a16d5c5fb68 100644
--- a/libsycl/unittests/common/unittests_helper.hpp
+++ b/libsycl/unittests/common/unittests_helper.hpp
@@ -29,14 +29,22 @@ 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; }
+ // Platforms cached by earlier tests would hide the device enumeration mocked
+ // by the fixture, so the global state is reset on both ends.
+ UnittestsHelper() {
+ detail::PlatformImpl::rediscoverIfEmpty = true;
+ resetGlobalState();
+ }
+
+ ~UnittestsHelper() { resetGlobalState(); }
+
+ mock::MockWrapper Mock;
- ~UnittestsHelper() {
+private:
+ static void resetGlobalState() {
detail::getPlatformCache().clear();
detail::getOffloadTopologies() = {};
}
-
- mock::MockWrapper Mock;
};
} // namespace unittests
More information about the llvm-commits
mailing list