[llvm] [offload][sycl] add context parameter to olCreateProgram (PR #218387)

Ɓukasz Plewa via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 31 04:35:54 PDT 2026


https://github.com/lplewa updated https://github.com/llvm/llvm-project/pull/218387

>From 8289762f13657cb82b97cb15f7fa7cea2efea9ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Plewa?= <lukasz.plewa at intel.com>
Date: Wed, 19 Aug 2026 17:53:25 +0200
Subject: [PATCH 1/3] [offload][sycl] add context parameter to olCreateProgram

This patch is the 3rd patch in the context patch series.
This change is relatively simple compared to the others: we just introduce
context to the create program API and pass it down through the plugin
interface to the plugins.
---
 libsycl/src/detail/device_image_wrapper.cpp   | 12 +++--
 libsycl/src/detail/device_image_wrapper.hpp   |  9 +++-
 libsycl/src/detail/program_manager.cpp        |  3 +-
 libsycl/src/detail/program_manager.hpp        |  2 +
 libsycl/src/detail/queue_impl.cpp             |  2 +-
 libsycl/unittests/mock/helpers.cpp            |  5 +-
 libsycl/unittests/mock/helpers.hpp            |  5 +-
 libsycl/unittests/mock/mock.cpp               |  5 +-
 .../tools/llvm-gpu-loader/llvm-gpu-loader.cpp |  2 +-
 llvm/tools/llvm-gpu-loader/llvm-gpu-loader.h  |  3 +-
 .../kernel/src/LanguageRegistration.cpp       |  3 +-
 offload/liboffload/API/Program.td             |  8 ++-
 offload/liboffload/src/OffloadImpl.cpp        | 18 ++++---
 offload/plugins-nextgen/amdgpu/src/rtl.cpp    |  4 +-
 .../common/include/PluginInterface.h          |  9 ++--
 .../common/src/PluginInterface.cpp            | 10 ++--
 offload/plugins-nextgen/cuda/src/rtl.cpp      |  4 +-
 offload/plugins-nextgen/host/src/rtl.cpp      |  4 +-
 .../level_zero/include/L0Device.h             |  4 +-
 .../level_zero/include/L0Program.h            |  8 ++-
 .../level_zero/src/L0Device.cpp               |  6 ++-
 .../level_zero/src/L0Program.cpp              |  2 +-
 .../Conformance/lib/DeviceContext.cpp         |  2 +-
 .../unittests/OffloadAPI/common/Fixtures.hpp  |  5 +-
 .../event/olGetEventElapsedTime.cpp           |  2 +-
 .../OffloadAPI/program/olCreateProgram.cpp    | 50 ++++++++++++++-----
 .../OffloadAPI/symbol/olGetSymbol.cpp         |  2 +-
 27 files changed, 127 insertions(+), 62 deletions(-)

diff --git a/libsycl/src/detail/device_image_wrapper.cpp b/libsycl/src/detail/device_image_wrapper.cpp
index d5cb0135c2854..17a830614bd0a 100644
--- a/libsycl/src/detail/device_image_wrapper.cpp
+++ b/libsycl/src/detail/device_image_wrapper.cpp
@@ -13,12 +13,15 @@
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
 
-ProgramWrapper::ProgramWrapper(ol_device_handle_t Device,
+ProgramWrapper::ProgramWrapper(ol_context_handle_t Context,
+                               ol_device_handle_t Device,
                                DeviceImageManager &DevImage) {
+  assert(Context);
   assert(Device);
 
   llvm::StringRef Image = DevImage.getOffloadBinary().getImage();
-  callAndThrow(olCreateProgram, Device, Image.data(), Image.size(), &MProgram);
+  callAndThrow(olCreateProgram, Context, Device, Image.data(), Image.size(),
+               &MProgram);
 }
 
 ProgramWrapper::~ProgramWrapper() {
@@ -28,10 +31,11 @@ ProgramWrapper::~ProgramWrapper() {
 }
 
 ol_program_handle_t
-DeviceImageManager::getOrCreateProgram(ol_device_handle_t DeviceHandle) {
+DeviceImageManager::getOrCreateProgram(ol_context_handle_t ContextHandle,
+                                       ol_device_handle_t DeviceHandle) {
   const auto &[Iterator, Flag] = MPrograms.emplace(
       std::piecewise_construct, std::forward_as_tuple(DeviceHandle),
-      std::forward_as_tuple(DeviceHandle, *this));
+      std::forward_as_tuple(ContextHandle, DeviceHandle, *this));
   return Iterator->second.getOLHandle();
 }
 
diff --git a/libsycl/src/detail/device_image_wrapper.hpp b/libsycl/src/detail/device_image_wrapper.hpp
index bb0957002d251..5dfd7d05ee6b2 100644
--- a/libsycl/src/detail/device_image_wrapper.hpp
+++ b/libsycl/src/detail/device_image_wrapper.hpp
@@ -35,11 +35,13 @@ class ProgramWrapper {
   /// Constructs ProgramWrapper by creating a liboffload program with the
   /// provided arguments.
   ///
+  /// \param Context is the context to use for program creation.
   /// \param Device is the device to use for program creation.
   /// \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_device_handle_t Device, DeviceImageManager &DevImage);
+  ProgramWrapper(ol_context_handle_t Context, ol_device_handle_t Device,
+                 DeviceImageManager &DevImage);
 
   /// Releases the corresponding liboffload program handle by calling
   /// olDestroyProgram.
@@ -78,11 +80,14 @@ class DeviceImageManager {
   /// Returns a liboffload program which is compatible with the specified
   /// device. Searches among existing programs and creates a new one if no
   /// compatible image is found.
+  /// \param ContextHandle the liboffload handle of the context to create the
+  /// program in.
   /// \param DeviceHandle the liboffload handle of the device the program must
   /// be compatible with.
   /// \return the liboffload handle of the program compatible with the specified
   /// device.
-  ol_program_handle_t getOrCreateProgram(ol_device_handle_t DeviceHandle);
+  ol_program_handle_t getOrCreateProgram(ol_context_handle_t ContextHandle,
+                                         ol_device_handle_t DeviceHandle);
 
 protected:
   std::unordered_map<ol_device_handle_t, ProgramWrapper> MPrograms;
diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
index 688b49f847e10..d0ca9ba9dfce2 100644
--- a/libsycl/src/detail/program_manager.cpp
+++ b/libsycl/src/detail/program_manager.cpp
@@ -129,6 +129,7 @@ static bool isImageCompatible(const DeviceImageManager &Image,
 
 ol_symbol_handle_t
 ProgramAndKernelManager::getOrCreateKernel(DeviceKernelInfo &KernelInfo,
+                                           ol_context_handle_t Context,
                                            DeviceImpl &Device) {
 
   std::lock_guard<std::mutex> KernelGuard(MDataCollectionMutex);
@@ -144,7 +145,7 @@ ProgramAndKernelManager::getOrCreateKernel(DeviceKernelInfo &KernelInfo,
                         KernelInfo.getName().data() + " was found");
 
   auto DeviceHandle = Device.getOLHandle();
-  auto Program = DeviceImage.getOrCreateProgram(DeviceHandle);
+  auto Program = DeviceImage.getOrCreateProgram(Context, DeviceHandle);
 
   ol_symbol_handle_t Kernel{};
   callAndThrow(olGetSymbol, Program, KernelInfo.getName().data(),
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
index cf978796a0cfd..ebea47ddd55df 100644
--- a/libsycl/src/detail/program_manager.hpp
+++ b/libsycl/src/detail/program_manager.hpp
@@ -80,10 +80,12 @@ class ProgramAndKernelManager {
   /// This method is thread-safe.
   /// \param KernelInfo a set of kernel specific data: name, corresponding
   /// device image, etc.
+  /// \param Context the context in which the underlying program is created.
   /// \param Device the device for which this kernel must be compiled.
   /// \return a liboffload kernel handle that is ready to be passed to kernel
   /// execution methods.
   ol_symbol_handle_t getOrCreateKernel(DeviceKernelInfo &KernelInfo,
+                                       ol_context_handle_t Context,
                                        DeviceImpl &Device);
 
   /// \return kernel info for the kernel with the specified name.
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index e0700129c0fa5..78be58ec71e99 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -130,7 +130,7 @@ void QueueImpl::submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
                                  size_t ArgSize) {
   ol_symbol_handle_t Kernel =
       detail::ProgramAndKernelManager::getInstance().getOrCreateKernel(
-          KernelInfo, MDevice);
+          KernelInfo, MContext.getOLHandleRef(), MDevice);
   assert(Kernel);
 
   handleEventDependencies(MCurrentSubmitInfo.DepEvents);
diff --git a/libsycl/unittests/mock/helpers.cpp b/libsycl/unittests/mock/helpers.cpp
index 86e656a19a0ea..45fe4c7f3aa52 100644
--- a/libsycl/unittests/mock/helpers.cpp
+++ b/libsycl/unittests/mock/helpers.cpp
@@ -182,9 +182,10 @@ void mock::MockLiboffload::initDefault() {
       });
 
   ON_CALL(*this, olCreateProgram)
-      .WillByDefault([](ol_device_handle_t Device, const void *ProgData,
-                        size_t ProgDataSize,
+      .WillByDefault([](ol_context_handle_t Context, ol_device_handle_t Device,
+                        const void *ProgData, size_t ProgDataSize,
                         ol_program_handle_t *Program) -> ol_result_t {
+        std::ignore = Context;
         EXPECT_NE(Device, nullptr);
         EXPECT_NE(ProgData, nullptr);
         EXPECT_GT(ProgDataSize, 0);
diff --git a/libsycl/unittests/mock/helpers.hpp b/libsycl/unittests/mock/helpers.hpp
index 50ff09b2b02c8..64b3c2fc55f23 100644
--- a/libsycl/unittests/mock/helpers.hpp
+++ b/libsycl/unittests/mock/helpers.hpp
@@ -100,8 +100,9 @@ class MockLiboffload {
   MOCK_METHOD(ol_result_t, olSyncQueue, (ol_queue_handle_t Queue));
   MOCK_METHOD(ol_result_t, olDestroyEvent, (ol_event_handle_t Event));
   MOCK_METHOD(ol_result_t, olCreateProgram,
-              (ol_device_handle_t Device, const void *ProgData,
-               size_t ProgDataSize, ol_program_handle_t *Program));
+              (ol_context_handle_t Context, ol_device_handle_t Device,
+               const void *ProgData, size_t ProgDataSize,
+               ol_program_handle_t *Program));
 
   MOCK_METHOD(ol_result_t, olGetSymbol,
               (ol_program_handle_t Program, const char *Name,
diff --git a/libsycl/unittests/mock/mock.cpp b/libsycl/unittests/mock/mock.cpp
index 551c14e568701..74131c4692874 100644
--- a/libsycl/unittests/mock/mock.cpp
+++ b/libsycl/unittests/mock/mock.cpp
@@ -73,9 +73,10 @@ ol_result_t olSyncQueue(ol_queue_handle_t Queue) {
   return mock::getMockLiboffload().olSyncQueue(Queue);
 }
 
-ol_result_t olCreateProgram(ol_device_handle_t Device, const void *ProgData,
+ol_result_t olCreateProgram(ol_context_handle_t Context,
+                            ol_device_handle_t Device, const void *ProgData,
                             size_t ProgDataSize, ol_program_handle_t *Program) {
-  return mock::getMockLiboffload().olCreateProgram(Device, ProgData,
+  return mock::getMockLiboffload().olCreateProgram(Context, Device, ProgData,
                                                    ProgDataSize, Program);
 }
 
diff --git a/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp b/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp
index 92dc25e4a6d38..55b59faeefdb4 100644
--- a/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp
+++ b/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp
@@ -256,7 +256,7 @@ int main(int argc, const char **argv, const char **envp) {
   OFFLOAD_ERR(olCreateContext(1, &Device, &Context));
 
   ol_program_handle_t Program;
-  OFFLOAD_ERR(olCreateProgram(Device, Image.getBufferStart(),
+  OFFLOAD_ERR(olCreateProgram(Context, Device, Image.getBufferStart(),
                               Image.getBufferSize(), &Program));
 
   ol_queue_handle_t Queue;
diff --git a/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.h b/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.h
index c257f2ea391b2..c1dc25cc76cbc 100644
--- a/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.h
+++ b/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.h
@@ -123,7 +123,8 @@ ol_result_t (*olIterateDevices)(ol_device_iterate_cb_t Callback,
 ol_result_t (*olIsValidBinary)(ol_device_handle_t Device, const void *ProgData,
                                size_t ProgDataSize, bool *Valid);
 
-ol_result_t (*olCreateProgram)(ol_device_handle_t Device, const void *ProgData,
+ol_result_t (*olCreateProgram)(ol_context_handle_t Context,
+                               ol_device_handle_t Device, const void *ProgData,
                                size_t ProgDataSize,
                                ol_program_handle_t *Program);
 
diff --git a/offload/languages/kernel/src/LanguageRegistration.cpp b/offload/languages/kernel/src/LanguageRegistration.cpp
index 77aecdff21f23..47decd3f369d4 100644
--- a/offload/languages/kernel/src/LanguageRegistration.cpp
+++ b/offload/languages/kernel/src/LanguageRegistration.cpp
@@ -80,6 +80,7 @@ void __tgt_register_lib(__tgt_bin_desc *Desc) {
   RuntimeState &State = RuntimeState::get();
   ThreadState &Thread = ThreadState::get();
   ol_device_handle_t Device = Thread.getDefaultDevice();
+  ol_context_handle_t Context = State.getContext();
 
   for (int32_t I = 0, E = Desc->NumDeviceImages; I < E; ++I) {
     ol_program_handle_t Program = nullptr;
@@ -89,7 +90,7 @@ void __tgt_register_lib(__tgt_bin_desc *Desc) {
     size_t ProgramSize =
         (char *)DeviceImage.ImageEnd - (char *)DeviceImage.ImageStart;
     ol_result_t Result =
-        olCreateProgram(Device, ProgramData, ProgramSize, &Program);
+        olCreateProgram(Context, Device, ProgramData, ProgramSize, &Program);
 
     if (Result && Result->Code) {
       fprintf(stderr, "Failed to register device code (%i): %s\n", Result->Code,
diff --git a/offload/liboffload/API/Program.td b/offload/liboffload/API/Program.td
index 89b9dffbe0a48..ecc8fb73dad3f 100644
--- a/offload/liboffload/API/Program.td
+++ b/offload/liboffload/API/Program.td
@@ -11,11 +11,14 @@
 //===----------------------------------------------------------------------===//
 
 def olCreateProgram : Function {
-    let desc = "Create a program for the device from the binary image pointed to by `ProgData`.";
+    let desc = "Create a program for the device from the binary image pointed to by `ProgData` within the given context.";
     let details = [
         "The provided `ProgData` will be copied and need not outlive the returned handle",
+        "The program is scoped to `Context` and `Device` must belong to it.",
+        "The program can only be used with queues that were created in the same context."
     ];
     let params = [
+        Param<"ol_context_handle_t", "Context", "handle of the context", PARAM_IN>,
         Param<"ol_device_handle_t", "Device", "handle of the device", PARAM_IN>,
         Param<"const void*", "ProgData", "pointer to the program binary data", PARAM_IN>,
         Param<"size_t", "ProgDataSize", "size of the program binary in bytes", PARAM_IN>,
@@ -25,6 +28,9 @@ def olCreateProgram : Function {
         Return<"OL_ERRC_INVALID_BINARY", [
             "If the buffer described by `ProgData` and `ProgDataSize` is not a valid binary image for the platform."
         ]>,
+        Return<"OL_ERRC_INVALID_DEVICE", [
+            "Device does not belong to `Context`"
+        ]>,
     ];
 }
 
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index e59fed4b30c34..e666be7a5b287 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -133,9 +133,10 @@ struct ol_event_impl_t {
 };
 
 struct ol_program_impl_t {
-  ol_program_impl_t(plugin::DeviceImageTy *Image,
+  ol_program_impl_t(ol_context_handle_t Context, plugin::DeviceImageTy *Image,
                     llvm::MemoryBufferRef DeviceImage)
-      : Image(Image), DeviceImage(DeviceImage) {}
+      : Context(Context), Image(Image), DeviceImage(DeviceImage) {}
+  ol_context_handle_t Context;
   plugin::DeviceImageTy *Image;
   std::mutex SymbolListMutex;
   llvm::MemoryBufferRef DeviceImage;
@@ -1166,16 +1167,21 @@ Error olMemPrefetch_impl(ol_queue_handle_t Queue, size_t Count,
                                              Queue->AsyncInfo);
 }
 
-Error olCreateProgram_impl(ol_device_handle_t Device, const void *ProgData,
+Error olCreateProgram_impl(ol_context_handle_t Context,
+                           ol_device_handle_t Device, const void *ProgData,
                            size_t ProgDataSize, ol_program_handle_t *Program) {
+  if (!Context->contains(Device))
+    return createOffloadError(ErrorCode::INVALID_DEVICE,
+                              "device does not belong to the given context");
+
   StringRef Buffer(reinterpret_cast<const char *>(ProgData), ProgDataSize);
-  Expected<plugin::DeviceImageTy *> Res =
-      Device->Device->loadBinary(Device->Device->Plugin, Buffer);
+  Expected<plugin::DeviceImageTy *> Res = Device->Device->loadBinary(
+      Device->Device->Plugin, Buffer, Context->PluginCtx.get());
   if (!Res)
     return Res.takeError();
   assert(*Res && "loadBinary returned nullptr");
 
-  *Program = new ol_program_impl_t(*Res, (*Res)->getMemoryBuffer());
+  *Program = new ol_program_impl_t(Context, *Res, (*Res)->getMemoryBuffer());
   return Error::success();
 }
 
diff --git a/offload/plugins-nextgen/amdgpu/src/rtl.cpp b/offload/plugins-nextgen/amdgpu/src/rtl.cpp
index 2ba4d80978f4a..281b9e3795a54 100644
--- a/offload/plugins-nextgen/amdgpu/src/rtl.cpp
+++ b/offload/plugins-nextgen/amdgpu/src/rtl.cpp
@@ -2686,8 +2686,8 @@ struct AMDGPUDeviceTy : public GenericDeviceTy, AMDGenericDeviceTy {
 
   /// Load the binary image into the device and allocate an image object.
   Expected<DeviceImageTy *>
-  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId) override {
+  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId,
+                 PluginContextTy * /*Context*/) override {
     // Allocate and initialize the image object.
     AMDGPUDeviceImageTy *AMDImage = Plugin.allocate<AMDGPUDeviceImageTy>();
     new (AMDImage) AMDGPUDeviceImageTy(ImageId, *this, std::move(TgtImage));
diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index 2f8d52c830a64..7b7a62bc2d467 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -983,11 +983,14 @@ struct GenericDeviceTy : public DeviceAllocatorTy {
   Error deinit(GenericPluginTy &Plugin);
   virtual Error deinitImpl() = 0;
 
-  /// Load the binary image into the device and return the target table.
+  /// Load the binary image into the device and return the target table. When
+  /// \p Context is null the plugin's driver-scoped default context is used.
   Expected<DeviceImageTy *> loadBinary(GenericPluginTy &Plugin,
-                                       StringRef TgtImage);
+                                       StringRef TgtImage,
+                                       PluginContextTy *Context);
   virtual Expected<DeviceImageTy *>
-  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId) = 0;
+  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId,
+                 PluginContextTy *Context) = 0;
 
   /// Unload a previously loaded Image from the device
   Error unloadBinary(DeviceImageTy *Image);
diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp
index 9d6135da6d470..573369dc3ba57 100644
--- a/offload/plugins-nextgen/common/src/PluginInterface.cpp
+++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp
@@ -645,8 +645,9 @@ Error GenericDeviceTy::deinit(GenericPluginTy &Plugin) {
 
   return deinitImpl();
 }
-Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
-                                                      StringRef InputTgtImage) {
+Expected<DeviceImageTy *>
+GenericDeviceTy::loadBinary(GenericPluginTy &Plugin, StringRef InputTgtImage,
+                            PluginContextTy *Context) {
   ODBG(OLDT_Init) << "Load data from image "
                   << static_cast<const void *>(InputTgtImage.bytes_begin());
 
@@ -674,7 +675,8 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
 
   // Load the binary and allocate the image object. Use the next available id
   // for the image id, which is the number of previously loaded images.
-  auto ImageOrErr = loadBinaryImpl(std::move(Buffer), LoadedImages.size());
+  auto ImageOrErr =
+      loadBinaryImpl(std::move(Buffer), LoadedImages.size(), Context);
   if (!ImageOrErr)
     return ImageOrErr.takeError();
   DeviceImageTy *Image = *ImageOrErr;
@@ -1542,7 +1544,7 @@ int32_t GenericPluginTy::load_binary(int32_t DeviceId,
 
   StringRef Buffer(reinterpret_cast<const char *>(TgtImage->ImageStart),
                    utils::getPtrDiff(TgtImage->ImageEnd, TgtImage->ImageStart));
-  auto ImageOrErr = Device.loadBinary(*this, Buffer);
+  auto ImageOrErr = Device.loadBinary(*this, Buffer, /*Context=*/nullptr);
   if (!ImageOrErr) {
     auto Err = ImageOrErr.takeError();
     REPORT() << "Failure to load binary image " << TgtImage << " on device "
diff --git a/offload/plugins-nextgen/cuda/src/rtl.cpp b/offload/plugins-nextgen/cuda/src/rtl.cpp
index 0f666ffb65e8f..72e5dcf115fe9 100644
--- a/offload/plugins-nextgen/cuda/src/rtl.cpp
+++ b/offload/plugins-nextgen/cuda/src/rtl.cpp
@@ -561,8 +561,8 @@ struct CUDADeviceTy : public GenericDeviceTy {
 
   /// Load the binary image into the device and allocate an image object.
   Expected<DeviceImageTy *>
-  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId) override {
+  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId,
+                 PluginContextTy * /*Context*/) override {
     if (auto Err = setContext())
       return std::move(Err);
 
diff --git a/offload/plugins-nextgen/host/src/rtl.cpp b/offload/plugins-nextgen/host/src/rtl.cpp
index 3c5c29545d89d..55ada2f82c360 100644
--- a/offload/plugins-nextgen/host/src/rtl.cpp
+++ b/offload/plugins-nextgen/host/src/rtl.cpp
@@ -182,8 +182,8 @@ struct GenELF64DeviceTy : public GenericDeviceTy {
 
   /// Load the binary image into the device and allocate an image object.
   Expected<DeviceImageTy *>
-  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId) override {
+  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId,
+                 PluginContextTy * /*Context*/) override {
     // Allocate and initialize the image object.
     GenELF64DeviceImageTy *Image = Plugin.allocate<GenELF64DeviceImageTy>();
     new (Image) GenELF64DeviceImageTy(ImageId, *this, std::move(TgtImage));
diff --git a/offload/plugins-nextgen/level_zero/include/L0Device.h b/offload/plugins-nextgen/level_zero/include/L0Device.h
index 84df2a2140446..d535b8abb0fc0 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Device.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Device.h
@@ -510,8 +510,8 @@ class L0DeviceTy final : public GenericDeviceTy {
 
   // Generic device interface implementation.
   Expected<DeviceImageTy *>
-  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId) override;
+  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId,
+                 PluginContextTy *UserCtx) override;
   Error unloadBinaryImpl(DeviceImageTy *Image) override;
   Expected<void *> allocate(size_t Size, void *HstPtr, TargetAllocTy Kind,
                             size_t Alignment) override;
diff --git a/offload/plugins-nextgen/level_zero/include/L0Program.h b/offload/plugins-nextgen/level_zero/include/L0Program.h
index 68e901e984d22..e9fb2e778e0c6 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Program.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Program.h
@@ -21,6 +21,8 @@ class L0DeviceTy;
 
 class L0ProgramBuilderTy {
   L0DeviceTy &Device;
+  /// L0 context that owns the built modules.
+  ze_context_handle_t ZeContext;
   std::unique_ptr<MemoryBuffer> Image;
   /// Handle multiple modules within a single target image.
   llvm::SmallVector<ze_module_handle_t> Modules;
@@ -39,11 +41,13 @@ class L0ProgramBuilderTy {
   Error linkModules();
 
 public:
-  L0ProgramBuilderTy(L0DeviceTy &Device, std::unique_ptr<MemoryBuffer> &&Image)
-      : Device(Device), Image(std::move(Image)) {}
+  L0ProgramBuilderTy(L0DeviceTy &Device, ze_context_handle_t ZeContext,
+                     std::unique_ptr<MemoryBuffer> &&Image)
+      : Device(Device), ZeContext(ZeContext), Image(std::move(Image)) {}
   ~L0ProgramBuilderTy() = default;
 
   L0DeviceTy &getL0Device() const { return Device; }
+  ze_context_handle_t getZeContext() const { return ZeContext; }
   ze_module_handle_t getGlobalModule() const { return GlobalModule; }
   llvm::SmallVector<ze_module_handle_t> &getModules() { return Modules; }
 
diff --git a/offload/plugins-nextgen/level_zero/src/L0Device.cpp b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
index 08c45b534e16c..b07aa91ff44e8 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Device.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
@@ -213,7 +213,7 @@ Error L0DeviceTy::deinitImpl() {
 
 Expected<DeviceImageTy *>
 L0DeviceTy::loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                           int32_t ImageId) {
+                           int32_t ImageId, PluginContextTy *UserCtx) {
   auto *PGM = getProgramFromImage(TgtImage->getMemBufferRef());
   if (PGM) {
     // Program already exists.
@@ -234,7 +234,9 @@ L0DeviceTy::loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
   CompilationOptions += " ";
   CompilationOptions += Options.InternalCompilationOptions;
 
-  L0ProgramBuilderTy Builder(*this, std::move(TgtImage));
+  auto &Ctx = UserCtx ? static_cast<LevelZeroPluginContextTy &>(*UserCtx)
+                      : L0Context.getDefaultUserCtx();
+  L0ProgramBuilderTy Builder(*this, Ctx.getZeContext(), std::move(TgtImage));
   if (auto Err = Builder.buildModules(CompilationOptions))
     return std::move(Err);
 
diff --git a/offload/plugins-nextgen/level_zero/src/L0Program.cpp b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
index 6cadfe44d0813..7786164ae0040 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Program.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
@@ -94,7 +94,7 @@ Error L0ProgramBuilderTy::addModule(size_t Size, const uint8_t *Image,
   ModuleDesc.pBuildFlags = BuildOptions.c_str();
   ModuleDesc.pConstants = &SpecConstants;
   ze_result_t RC;
-  CALL_ZE(RC, zeModuleCreate, L0Device.getZeContext(), L0Device.getZeDevice(),
+  CALL_ZE(RC, zeModuleCreate, getZeContext(), L0Device.getZeDevice(),
           &ModuleDesc, &Module, &BuildLog);
   if (BuildLog)
     zeModuleBuildLogDestroy(BuildLog);
diff --git a/offload/unittests/Conformance/lib/DeviceContext.cpp b/offload/unittests/Conformance/lib/DeviceContext.cpp
index f3f0f8f394323..d81f5f0c5867a 100644
--- a/offload/unittests/Conformance/lib/DeviceContext.cpp
+++ b/offload/unittests/Conformance/lib/DeviceContext.cpp
@@ -252,7 +252,7 @@ DeviceContext::loadBinary(llvm::StringRef Directory,
 
   ol_program_handle_t ProgramHandle = nullptr;
   const ol_result_t OlResult =
-      olCreateProgram(DeviceHandle, BinaryData->getBufferStart(),
+      olCreateProgram(Context, DeviceHandle, BinaryData->getBufferStart(),
                       BinaryData->getBufferSize(), &ProgramHandle);
 
   if (OlResult != OL_SUCCESS) {
diff --git a/offload/unittests/OffloadAPI/common/Fixtures.hpp b/offload/unittests/OffloadAPI/common/Fixtures.hpp
index 9ccc3c75314ee..a05be01648ebc 100644
--- a/offload/unittests/OffloadAPI/common/Fixtures.hpp
+++ b/offload/unittests/OffloadAPI/common/Fixtures.hpp
@@ -261,7 +261,8 @@ struct OffloadProgramTestWithParam : OffloadDeviceTestWithParam<T> {
     ASSERT_TRUE(TestEnvironment::loadDeviceBinary(ProgramName, this->Device,
                                                   DeviceBin));
     ASSERT_GE(DeviceBin->getBufferSize(), 0lu);
-    ASSERT_SUCCESS(olCreateProgram(this->Device, DeviceBin->getBufferStart(),
+    ASSERT_SUCCESS(olCreateProgram(this->Context, this->Device,
+                                   DeviceBin->getBufferStart(),
                                    DeviceBin->getBufferSize(), &Program));
   }
 
@@ -346,7 +347,7 @@ struct LaunchKernelTestBase : OffloadQueueTest {
     RETURN_ON_FATAL_FAILURE(OffloadQueueTest::SetUp());
     ASSERT_TRUE(TestEnvironment::loadDeviceBinary(program, Device, DeviceBin));
     ASSERT_GE(DeviceBin->getBufferSize(), 0lu);
-    ASSERT_SUCCESS(olCreateProgram(Device, DeviceBin->getBufferStart(),
+    ASSERT_SUCCESS(olCreateProgram(Context, Device, DeviceBin->getBufferStart(),
                                    DeviceBin->getBufferSize(), &Program));
 
     LaunchArgs.Dimensions = 1;
diff --git a/offload/unittests/OffloadAPI/event/olGetEventElapsedTime.cpp b/offload/unittests/OffloadAPI/event/olGetEventElapsedTime.cpp
index 33a62921f4ae4..356f04bfd9bdb 100644
--- a/offload/unittests/OffloadAPI/event/olGetEventElapsedTime.cpp
+++ b/offload/unittests/OffloadAPI/event/olGetEventElapsedTime.cpp
@@ -19,7 +19,7 @@ struct olGetEventElapsedTimeTest : OffloadQueueTest {
     SKIP_KNOWN_FAILURE(LevelZero{"unsupported feature"});
 
     ASSERT_TRUE(TestEnvironment::loadDeviceBinary("foo", Device, DeviceBin));
-    ASSERT_SUCCESS(olCreateProgram(Device, DeviceBin->getBufferStart(),
+    ASSERT_SUCCESS(olCreateProgram(Context, Device, DeviceBin->getBufferStart(),
                                    DeviceBin->getBufferSize(), &Program));
     ASSERT_SUCCESS(olGetSymbol(Program, "foo", OL_SYMBOL_KIND_KERNEL, &Kernel));
 
diff --git a/offload/unittests/OffloadAPI/program/olCreateProgram.cpp b/offload/unittests/OffloadAPI/program/olCreateProgram.cpp
index 59629c79caff4..13811af32d479 100644
--- a/offload/unittests/OffloadAPI/program/olCreateProgram.cpp
+++ b/offload/unittests/OffloadAPI/program/olCreateProgram.cpp
@@ -20,7 +20,7 @@ TEST_P(olCreateProgramTest, Success) {
   ASSERT_GE(DeviceBin->getBufferSize(), 0lu);
 
   ol_program_handle_t Program;
-  ASSERT_SUCCESS(olCreateProgram(Device, DeviceBin->getBufferStart(),
+  ASSERT_SUCCESS(olCreateProgram(Context, Device, DeviceBin->getBufferStart(),
                                  DeviceBin->getBufferSize(), &Program));
   ASSERT_NE(Program, nullptr);
 
@@ -34,7 +34,7 @@ TEST_P(olCreateProgramTest, JITSuccess) {
   ASSERT_GE(DeviceBin->getBufferSize(), 0lu);
 
   ol_program_handle_t Program;
-  ASSERT_SUCCESS(olCreateProgram(Device, DeviceBin->getBufferStart(),
+  ASSERT_SUCCESS(olCreateProgram(Context, Device, DeviceBin->getBufferStart(),
                                  DeviceBin->getBufferSize(), &Program));
   ASSERT_NE(Program, nullptr);
 
@@ -46,6 +46,18 @@ TEST_P(olCreateProgramTest, JITSuccess) {
   ASSERT_SUCCESS(olDestroyProgram(Program));
 }
 
+TEST_P(olCreateProgramTest, NullContextHandle) {
+
+  std::unique_ptr<llvm::MemoryBuffer> DeviceBin;
+  ASSERT_TRUE(TestEnvironment::loadDeviceBinary("foo", Device, DeviceBin));
+  ASSERT_GE(DeviceBin->getBufferSize(), 0lu);
+
+  ol_program_handle_t Program;
+  ASSERT_ERROR(OL_ERRC_INVALID_NULL_HANDLE,
+               olCreateProgram(nullptr, Device, DeviceBin->getBufferStart(),
+                               DeviceBin->getBufferSize(), &Program));
+}
+
 TEST_P(olCreateProgramTest, NullDeviceHandle) {
 
   std::unique_ptr<llvm::MemoryBuffer> DeviceBin;
@@ -54,7 +66,7 @@ TEST_P(olCreateProgramTest, NullDeviceHandle) {
 
   ol_program_handle_t Program;
   ASSERT_ERROR(OL_ERRC_INVALID_NULL_HANDLE,
-               olCreateProgram(nullptr, DeviceBin->getBufferStart(),
+               olCreateProgram(Context, nullptr, DeviceBin->getBufferStart(),
                                DeviceBin->getBufferSize(), &Program));
 }
 
@@ -65,9 +77,9 @@ TEST_P(olCreateProgramTest, NullProgData) {
   ASSERT_GE(DeviceBin->getBufferSize(), 0lu);
 
   ol_program_handle_t Program;
-  ASSERT_ERROR(
-      OL_ERRC_INVALID_NULL_POINTER,
-      olCreateProgram(Device, nullptr, DeviceBin->getBufferSize(), &Program));
+  ASSERT_ERROR(OL_ERRC_INVALID_NULL_POINTER,
+               olCreateProgram(Context, Device, nullptr,
+                               DeviceBin->getBufferSize(), &Program));
 }
 
 TEST_P(olCreateProgramTest, NullOutputProgram) {
@@ -77,7 +89,7 @@ TEST_P(olCreateProgramTest, NullOutputProgram) {
   ASSERT_GE(DeviceBin->getBufferSize(), 0lu);
 
   ASSERT_ERROR(OL_ERRC_INVALID_NULL_POINTER,
-               olCreateProgram(Device, DeviceBin->getBufferStart(),
+               olCreateProgram(Context, Device, DeviceBin->getBufferStart(),
                                DeviceBin->getBufferSize(), nullptr));
 }
 
@@ -88,9 +100,9 @@ TEST_P(olCreateProgramTest, ZeroSizeBinary) {
 
   ol_program_handle_t Program = nullptr;
 
-  ASSERT_ERROR(
-      OL_ERRC_INVALID_BINARY,
-      olCreateProgram(Device, DeviceBin->getBufferStart(), 0, &Program));
+  ASSERT_ERROR(OL_ERRC_INVALID_BINARY,
+               olCreateProgram(Context, Device, DeviceBin->getBufferStart(), 0,
+                               &Program));
   ASSERT_EQ(Program, nullptr);
 }
 
@@ -99,8 +111,8 @@ TEST_P(olCreateProgramTest, InvalidBinary) {
 
   ol_program_handle_t Program = nullptr;
   ASSERT_ERROR(OL_ERRC_INVALID_BINARY,
-               olCreateProgram(Device, InvalidBinary, sizeof(InvalidBinary) - 1,
-                               &Program));
+               olCreateProgram(Context, Device, InvalidBinary,
+                               sizeof(InvalidBinary) - 1, &Program));
   ASSERT_EQ(Program, nullptr);
 }
 
@@ -120,7 +132,19 @@ TEST_P(olCreateProgramTest, WrongArchitecture) {
 
   ol_program_handle_t Program = nullptr;
   ASSERT_ERROR(OL_ERRC_INVALID_BINARY,
-               olCreateProgram(Device, ForeignBin->getBufferStart(),
+               olCreateProgram(Context, Device, ForeignBin->getBufferStart(),
                                ForeignBin->getBufferSize(), &Program));
   ASSERT_EQ(Program, nullptr);
 }
+
+TEST_P(olCreateProgramTest, InvalidDeviceNotInContext) {
+  std::unique_ptr<llvm::MemoryBuffer> DeviceBin;
+  ASSERT_TRUE(TestEnvironment::loadDeviceBinary("foo", Device, DeviceBin));
+  ASSERT_GE(DeviceBin->getBufferSize(), 0lu);
+
+  ol_program_handle_t Program = nullptr;
+  ASSERT_ERROR(OL_ERRC_INVALID_DEVICE,
+               olCreateProgram(Context, Host, DeviceBin->getBufferStart(),
+                               DeviceBin->getBufferSize(), &Program));
+  ASSERT_EQ(Program, nullptr);
+}
diff --git a/offload/unittests/OffloadAPI/symbol/olGetSymbol.cpp b/offload/unittests/OffloadAPI/symbol/olGetSymbol.cpp
index 1f496b9c6e1ae..021aa35b89bc6 100644
--- a/offload/unittests/OffloadAPI/symbol/olGetSymbol.cpp
+++ b/offload/unittests/OffloadAPI/symbol/olGetSymbol.cpp
@@ -18,7 +18,7 @@ struct olGetSymbolGlobalTest : OffloadQueueTest {
     RETURN_ON_FATAL_FAILURE(OffloadQueueTest::SetUp());
     ASSERT_TRUE(TestEnvironment::loadDeviceBinary("global", Device, DeviceBin));
     ASSERT_GE(DeviceBin->getBufferSize(), 0lu);
-    ASSERT_SUCCESS(olCreateProgram(Device, DeviceBin->getBufferStart(),
+    ASSERT_SUCCESS(olCreateProgram(Context, Device, DeviceBin->getBufferStart(),
                                    DeviceBin->getBufferSize(), &Program));
   }
 

>From 920c14d1581463ff8e7468ef0e83ae4d3425c495 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Plewa?= <lukasz.plewa at intel.com>
Date: Wed, 26 Aug 2026 17:49:20 +0200
Subject: [PATCH 2/3] review fixes

---
 libsycl/src/detail/program_manager.cpp | 10 +++++-----
 libsycl/src/detail/program_manager.hpp |  6 ++++--
 libsycl/src/detail/queue_impl.cpp      |  2 +-
 libsycl/unittests/mock/helpers.cpp     |  2 +-
 4 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
index d0ca9ba9dfce2..34e80435290e7 100644
--- a/libsycl/src/detail/program_manager.cpp
+++ b/libsycl/src/detail/program_manager.cpp
@@ -11,6 +11,7 @@
 #include <sycl/__impl/detail/get_device_kernel_info.hpp>
 #include <sycl/__impl/exception.hpp>
 
+#include <detail/context_impl.hpp>
 #include <detail/device_impl.hpp>
 #include <detail/offload/offload_utils.hpp>
 
@@ -127,10 +128,8 @@ static bool isImageCompatible(const DeviceImageManager &Image,
   return IsValid;
 }
 
-ol_symbol_handle_t
-ProgramAndKernelManager::getOrCreateKernel(DeviceKernelInfo &KernelInfo,
-                                           ol_context_handle_t Context,
-                                           DeviceImpl &Device) {
+ol_symbol_handle_t ProgramAndKernelManager::getOrCreateKernel(
+    DeviceKernelInfo &KernelInfo, ContextImpl &Context, DeviceImpl &Device) {
 
   std::lock_guard<std::mutex> KernelGuard(MDataCollectionMutex);
 
@@ -145,7 +144,8 @@ ProgramAndKernelManager::getOrCreateKernel(DeviceKernelInfo &KernelInfo,
                         KernelInfo.getName().data() + " was found");
 
   auto DeviceHandle = Device.getOLHandle();
-  auto Program = DeviceImage.getOrCreateProgram(Context, DeviceHandle);
+  auto Program =
+      DeviceImage.getOrCreateProgram(Context.getOLHandleRef(), DeviceHandle);
 
   ol_symbol_handle_t Kernel{};
   callAndThrow(olGetSymbol, Program, KernelInfo.getName().data(),
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
index ebea47ddd55df..59f77633debb1 100644
--- a/libsycl/src/detail/program_manager.hpp
+++ b/libsycl/src/detail/program_manager.hpp
@@ -54,6 +54,7 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 namespace detail {
 
+class ContextImpl;
 class DeviceImpl;
 
 /// A class to manage programs and kernels.
@@ -80,12 +81,13 @@ class ProgramAndKernelManager {
   /// This method is thread-safe.
   /// \param KernelInfo a set of kernel specific data: name, corresponding
   /// device image, etc.
-  /// \param Context the context in which the underlying program is created.
+  /// \param Context the context in which the underlying program must be
+  /// created.
   /// \param Device the device for which this kernel must be compiled.
   /// \return a liboffload kernel handle that is ready to be passed to kernel
   /// execution methods.
   ol_symbol_handle_t getOrCreateKernel(DeviceKernelInfo &KernelInfo,
-                                       ol_context_handle_t Context,
+                                       ContextImpl &Context,
                                        DeviceImpl &Device);
 
   /// \return kernel info for the kernel with the specified name.
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index 78be58ec71e99..881e4617b3a85 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -130,7 +130,7 @@ void QueueImpl::submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
                                  size_t ArgSize) {
   ol_symbol_handle_t Kernel =
       detail::ProgramAndKernelManager::getInstance().getOrCreateKernel(
-          KernelInfo, MContext.getOLHandleRef(), MDevice);
+          KernelInfo, MContext, MDevice);
   assert(Kernel);
 
   handleEventDependencies(MCurrentSubmitInfo.DepEvents);
diff --git a/libsycl/unittests/mock/helpers.cpp b/libsycl/unittests/mock/helpers.cpp
index 45fe4c7f3aa52..b3838daa00742 100644
--- a/libsycl/unittests/mock/helpers.cpp
+++ b/libsycl/unittests/mock/helpers.cpp
@@ -185,7 +185,7 @@ void mock::MockLiboffload::initDefault() {
       .WillByDefault([](ol_context_handle_t Context, ol_device_handle_t Device,
                         const void *ProgData, size_t ProgDataSize,
                         ol_program_handle_t *Program) -> ol_result_t {
-        std::ignore = Context;
+        EXPECT_NE(Context, nullptr);
         EXPECT_NE(Device, nullptr);
         EXPECT_NE(ProgData, nullptr);
         EXPECT_GT(ProgDataSize, 0);

>From d8e5f5f39ee5e6d8da34c13e39bcc6c5592ef922 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Plewa?= <lukasz.plewa at intel.com>
Date: Mon, 31 Aug 2026 12:11:43 +0200
Subject: [PATCH 3/3] review fixes - program cache

---
 .../plugins-nextgen/level_zero/include/L0Device.h    | 11 ++++++-----
 .../plugins-nextgen/level_zero/include/L0Program.h   | 12 ++++++++++--
 offload/plugins-nextgen/level_zero/src/L0Device.cpp  | 10 ++++++----
 3 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/offload/plugins-nextgen/level_zero/include/L0Device.h b/offload/plugins-nextgen/level_zero/include/L0Device.h
index d535b8abb0fc0..879c43acba596 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Device.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Device.h
@@ -213,9 +213,10 @@ class L0DeviceTy final : public GenericDeviceTy {
   }
   ze_module_handle_t *getGlobalModulesArray() { return GlobalModules.data(); }
 
-  L0ProgramTy *getProgramFromImage(MemoryBufferRef Image) {
+  L0ProgramTy *getProgramFromImage(MemoryBufferRef Image,
+                                   ze_context_handle_t ZeContext) {
     for (auto &PGM : Programs)
-      if (PGM.getMemoryBuffer() == Image)
+      if (PGM.getMemoryBuffer() == Image && PGM.getZeContext() == ZeContext)
         return &PGM;
     return nullptr;
   }
@@ -234,9 +235,9 @@ class L0DeviceTy final : public GenericDeviceTy {
     auto ImageOrErr = Builder.getELF();
     if (!ImageOrErr)
       return ImageOrErr.takeError();
-    Programs.emplace_back(ImageId, *this, std::move(*ImageOrErr),
-                          Builder.getGlobalModule(),
-                          std::move(Builder.getModules()));
+    Programs.emplace_back(
+        ImageId, *this, std::move(*ImageOrErr), Builder.getGlobalModule(),
+        std::move(Builder.getModules()), Builder.getZeContext());
     return Programs.back();
   }
 
diff --git a/offload/plugins-nextgen/level_zero/include/L0Program.h b/offload/plugins-nextgen/level_zero/include/L0Program.h
index e9fb2e778e0c6..5e29e4463e4db 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Program.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Program.h
@@ -74,6 +74,10 @@ class L0ProgramTy : public DeviceImageTy {
   /// Module that contains global data including device RTL.
   ze_module_handle_t GlobalModule = nullptr;
 
+  /// L0 context the modules were built against. Cached programs are only
+  /// reusable for lookups that share this context.
+  ze_context_handle_t ZeContext = nullptr;
+
   L0DeviceTy &getL0Device() const;
 
 public:
@@ -82,9 +86,11 @@ class L0ProgramTy : public DeviceImageTy {
   L0ProgramTy(int32_t ImageId, GenericDeviceTy &Device,
               std::unique_ptr<MemoryBuffer> Image,
               ze_module_handle_t GlobalModule,
-              llvm::SmallVector<ze_module_handle_t> &&Modules)
+              llvm::SmallVector<ze_module_handle_t> &&Modules,
+              ze_context_handle_t ZeContext)
       : DeviceImageTy(ImageId, Device, std::move(Image)),
-        Modules(std::move(Modules)), GlobalModule(GlobalModule) {}
+        Modules(std::move(Modules)), GlobalModule(GlobalModule),
+        ZeContext(ZeContext) {}
   ~L0ProgramTy() = default;
 
   L0ProgramTy(const L0ProgramTy &Other) = delete;
@@ -94,6 +100,8 @@ class L0ProgramTy : public DeviceImageTy {
 
   Error deinit();
 
+  ze_context_handle_t getZeContext() const { return ZeContext; }
+
   static L0ProgramTy &makeL0Program(DeviceImageTy &Device) {
     return static_cast<L0ProgramTy &>(Device);
   }
diff --git a/offload/plugins-nextgen/level_zero/src/L0Device.cpp b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
index b07aa91ff44e8..0919dfc4a279b 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Device.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
@@ -214,7 +214,11 @@ Error L0DeviceTy::deinitImpl() {
 Expected<DeviceImageTy *>
 L0DeviceTy::loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
                            int32_t ImageId, PluginContextTy *UserCtx) {
-  auto *PGM = getProgramFromImage(TgtImage->getMemBufferRef());
+  auto &Ctx = UserCtx ? static_cast<LevelZeroPluginContextTy &>(*UserCtx)
+                      : L0Context.getDefaultUserCtx();
+  auto ZeContext = Ctx.getZeContext();
+
+  auto *PGM = getProgramFromImage(TgtImage->getMemBufferRef(), ZeContext);
   if (PGM) {
     // Program already exists.
     return PGM;
@@ -234,9 +238,7 @@ L0DeviceTy::loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
   CompilationOptions += " ";
   CompilationOptions += Options.InternalCompilationOptions;
 
-  auto &Ctx = UserCtx ? static_cast<LevelZeroPluginContextTy &>(*UserCtx)
-                      : L0Context.getDefaultUserCtx();
-  L0ProgramBuilderTy Builder(*this, Ctx.getZeContext(), std::move(TgtImage));
+  L0ProgramBuilderTy Builder(*this, ZeContext, std::move(TgtImage));
   if (auto Err = Builder.buildModules(CompilationOptions))
     return std::move(Err);
 



More information about the llvm-commits mailing list