[llvm] [libsycl] Add parallel_for feature (PR #189068)

Kseniya Tikhomirova via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 1 05:01:00 PDT 2026


https://github.com/KseniyaTikhomirova updated https://github.com/llvm/llvm-project/pull/189068

>From 913cb645cd56e4bd9d56b28d76809d099091a643 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Thu, 19 Mar 2026 05:17:36 -0700
Subject: [PATCH 01/25] [libsycl] Add device image registration

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/docs/index.rst                        |   5 +
 libsycl/src/CMakeLists.txt                    |   1 +
 .../src/detail/device_binary_structures.hpp   | 129 +++++++++++++++
 libsycl/src/detail/device_image_wrapper.hpp   |  51 ++++++
 libsycl/src/detail/device_impl.hpp            |   2 +-
 libsycl/src/detail/global_objects.cpp         |   3 -
 libsycl/src/detail/global_objects.hpp         |  13 --
 libsycl/src/detail/kernel_id.hpp              |  77 +++++++++
 libsycl/src/detail/program_manager.cpp        | 154 ++++++++++++++++++
 libsycl/src/detail/program_manager.hpp        |  95 +++++++++++
 libsycl/src/usm_functions.cpp                 |   2 +-
 11 files changed, 514 insertions(+), 18 deletions(-)
 create mode 100644 libsycl/src/detail/device_binary_structures.hpp
 create mode 100644 libsycl/src/detail/device_image_wrapper.hpp
 create mode 100644 libsycl/src/detail/kernel_id.hpp
 create mode 100644 libsycl/src/detail/program_manager.cpp
 create mode 100644 libsycl/src/detail/program_manager.hpp

diff --git a/libsycl/docs/index.rst b/libsycl/docs/index.rst
index 0ec3a4d825075..03f7fb7c0876e 100644
--- a/libsycl/docs/index.rst
+++ b/libsycl/docs/index.rst
@@ -113,3 +113,8 @@ TODO for added SYCL classes
   * add aligned functions (blocked by liboffload support)
   * forward templated funcs to alignment methods (rewrite current impl)
   * handle sub devices once they are implemented (blocked by liboffload support)
+
+
+* general opens:
+
+  * define a way to report errors from object dtors.
\ No newline at end of file
diff --git a/libsycl/src/CMakeLists.txt b/libsycl/src/CMakeLists.txt
index 67ba7d28968de..bc39f9162f992 100644
--- a/libsycl/src/CMakeLists.txt
+++ b/libsycl/src/CMakeLists.txt
@@ -93,6 +93,7 @@ set(LIBSYCL_SOURCES
     "detail/device_impl.cpp"
     "detail/global_objects.cpp"
     "detail/platform_impl.cpp"
+    "detail/program_manager.cpp"
     "detail/queue_impl.cpp"
     "detail/offload/offload_utils.cpp"
     "detail/offload/offload_topology.cpp"
diff --git a/libsycl/src/detail/device_binary_structures.hpp b/libsycl/src/detail/device_binary_structures.hpp
new file mode 100644
index 0000000000000..d2abac6854e40
--- /dev/null
+++ b/libsycl/src/detail/device_binary_structures.hpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL_DEVICE_BINARY_STRUCTURES
+#define _LIBSYCL_DEVICE_BINARY_STRUCTURES
+
+#include <cstdint>
+
+/// Target identification strings.
+///
+/// A device type represented by a particular target
+/// triple requires specific binary images. We need
+/// to map the image type onto the device target triple.
+
+/// Unknown target.
+#define _LIBSYCL_DEVICE_BINARY_TARGET_TRIPLE_UNKNOWN "unknown-unknown-unknown"
+/// SPIR-V with 64-bit pointers.
+#define _LIBSYCL_DEVICE_BINARY_TARGET_SPIRV64 "spirv64-unknown-unknown"
+
+// This is a replica of the EntryTy data structure in
+// llvm/include/llvm/Frontend/Offloading/Utility.h.
+struct EntryTy {
+  /// Reserved bytes used to detect an older version of the struct, always zero.
+  uint64_t Reserved = 0x0;
+  /// The current version of the struct for runtime forward compatibility.
+  uint16_t Version = 0x1;
+  /// The expected consumer of this entry, e.g. SYCL or OpenMP.
+  /// llvm::object::OffloadKind
+  uint16_t Kind;
+  /// Flags associated with the global.
+  uint32_t Flags;
+  /// The address of the global to be registered by the runtime.
+  void *Address;
+  /// The name of the symbol in the device image.
+  char *SymbolName;
+  /// The number of bytes the symbol takes.
+  uint64_t Size;
+  /// Extra generic data used to register this entry.
+  uint64_t Data;
+  /// An extra pointer, usually null.
+  void *AuxAddr;
+};
+
+// TODO: would be nice to include it from llvm/Object. It doesn't work now since
+// I have to link LLVMObject to do that (linker error otherwise).
+// Copy of llvm::object::OffloadKind.
+/// The producer of the associated offloading image.
+enum OffloadKind : uint16_t {
+  OFK_None = 0,
+  OFK_OpenMP = (1 << 0),
+  OFK_Cuda = (1 << 1),
+  OFK_HIP = (1 << 2),
+  OFK_SYCL = (1 << 3),
+  OFK_LAST = (1 << 4),
+};
+
+// Copy of llvm::object::ImageKind.
+/// The type of contents the offloading image contains.
+enum ImageKind : uint16_t {
+  IMG_None = 0,
+  IMG_Object,
+  IMG_Bitcode,
+  IMG_Cubin,
+  IMG_Fatbinary,
+  IMG_PTX,
+  IMG_SPIRV,
+  IMG_LAST,
+};
+
+/// Device binary descriptor version supported by this library.
+static const uint16_t _LIBSYCL_SUPPORTED_DEVICE_BINARY_VERSION = 3;
+
+/// This struct is a record of the device binary information.
+///  It must match the __tgt_device_image structure generated by the
+///  clang-offload-wrapper tool when their `Version` field match.
+struct __sycl_tgt_device_image {
+  uint16_t Version;
+  /// The type of offload model the binary employs. See `OffloadKind`. Only
+  /// OFK_SYCL is supported by libsycl.
+  uint8_t OffloadKind;
+  /// Format of the binary data, see `ImageKind`.
+  uint8_t ImageFormat;
+  /// A null-terminated string representation of the device's target
+  /// architecture. Must hold one of _LIBSYCL_DEVICE_BINARY_TARGET_* values.
+  const char *TripleString;
+  /// A null-terminated string; target- and compiler-specific options
+  /// which are suggested to use to "compile" program at runtime.
+  const char *CompileOptions;
+  /// A null-terminated string; target- and compiler-specific options
+  /// which are suggested to use to "link" program at runtime.
+  const char *LinkOptions;
+  /// Pointer to the target code start.
+  const unsigned char *ImageStart;
+  /// Pointer to the target code end.
+  const unsigned char *ImageEnd;
+  /// The offload entry table
+  EntryTy *EntriesBegin;
+  EntryTy *EntriesEnd;
+  // TODO: properties are not supported now.
+  /// Array of preperty sets.
+  void *PropertiesBegin;
+  void *PropertiesEnd;
+};
+
+/// Version of offload binaries descriptor `__sycl_tgt_bin_desc` supported by
+/// libsycl.
+static constexpr uint16_t _LIBSYCL_SUPPORTED_OFFLOAD_BINARY_VERSION = 1;
+
+/// This struct is a record of all the device code that may be offloaded.
+/// It must match the `__tgt_bin_desc` structure generated by
+/// the clang-offload-wrapper tool when their `Version` field match.
+struct __sycl_tgt_bin_desc {
+  /// Version of the structure.
+  uint16_t Version;
+  /// Number of device binaries in this descriptor.
+  uint16_t NumDeviceBinaries;
+  /// Device binaries data.
+  __sycl_tgt_device_image *DeviceImages;
+  /// The offload entry table (not used, for compatibility with OpenMP).
+  EntryTy *HostEntriesBegin;
+  EntryTy *HostEntriesEnd;
+};
+
+#endif // _LIBSYCL_DEVICE_BINARY_STRUCTURES
diff --git a/libsycl/src/detail/device_image_wrapper.hpp b/libsycl/src/detail/device_image_wrapper.hpp
new file mode 100644
index 0000000000000..534a4be4fcf1e
--- /dev/null
+++ b/libsycl/src/detail/device_image_wrapper.hpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL_DEVICE_IMAGE_WRAPPER
+#define _LIBSYCL_DEVICE_IMAGE_WRAPPER
+
+#include <sycl/__impl/detail/config.hpp>
+
+#include <detail/device_binary_structures.hpp>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+namespace detail {
+
+/// A wrapper of __sycl_tgt_device_image structure.
+class DeviceImageWrapper {
+public:
+public:
+  DeviceImageWrapper(const __sycl_tgt_device_image &Bin) : MBin(&Bin) {}
+  // Explicitly delete copy constructor/operator= to avoid unintentional copies.
+  DeviceImageWrapper(const DeviceImageWrapper &) = delete;
+  DeviceImageWrapper &operator=(const DeviceImageWrapper &) = delete;
+
+  DeviceImageWrapper(DeviceImageWrapper &&) = default;
+  DeviceImageWrapper &operator=(DeviceImageWrapper &&) = default;
+
+  ~DeviceImageWrapper() {}
+
+  /// \return reference to corresponsing raw __sycl_tgt_device_image object.
+  const __sycl_tgt_device_image &getRawData() const { return *get(); }
+
+  /// \return size of corresponding device image data in bytes.
+  size_t getSize() const {
+    return static_cast<size_t>(MBin->ImageEnd - MBin->ImageStart);
+  }
+
+protected:
+  const __sycl_tgt_device_image *get() const { return MBin; }
+
+  __sycl_tgt_device_image const *MBin{};
+};
+
+} // namespace detail
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL_DEVICE_IMAGE_WRAPPER
diff --git a/libsycl/src/detail/device_impl.hpp b/libsycl/src/detail/device_impl.hpp
index c83b767aad02f..3c96166be2e8b 100644
--- a/libsycl/src/detail/device_impl.hpp
+++ b/libsycl/src/detail/device_impl.hpp
@@ -115,7 +115,7 @@ class DeviceImpl {
       static_assert(false && "Info descriptor is not properly supported");
   }
 
-  ol_device_handle_t getOLHandle() { return MOffloadDevice; }
+  ol_device_handle_t getHandle() { return MOffloadDevice; }
 
 private:
   ol_device_handle_t MOffloadDevice = {};
diff --git a/libsycl/src/detail/global_objects.cpp b/libsycl/src/detail/global_objects.cpp
index d80be710268f8..35e32985e7cbb 100644
--- a/libsycl/src/detail/global_objects.cpp
+++ b/libsycl/src/detail/global_objects.cpp
@@ -53,6 +53,3 @@ std::vector<PlatformImplUPtr> &getPlatformCache() {
 
 } // namespace detail
 _LIBSYCL_END_NAMESPACE_SYCL
-
-extern "C" void __sycl_register_lib(void *) {}
-extern "C" void __sycl_unregister_lib(void *) {}
diff --git a/libsycl/src/detail/global_objects.hpp b/libsycl/src/detail/global_objects.hpp
index 008cb01f4f355..4535a254c6609 100644
--- a/libsycl/src/detail/global_objects.hpp
+++ b/libsycl/src/detail/global_objects.hpp
@@ -16,19 +16,6 @@
 #include <mutex>
 #include <vector>
 
-// +++ Entry points referenced by the offload wrapper object {
-
-/// Executed as a part of current module's (.exe, .dll) static initialization.
-/// Registers device executable images with the runtime.
-extern "C" _LIBSYCL_EXPORT void __sycl_register_lib(void *);
-
-/// Executed as a part of current module's (.exe, .dll) static
-/// de-initialization.
-/// Unregisters device executable images with the runtime.
-extern "C" _LIBSYCL_EXPORT void __sycl_unregister_lib(void *);
-
-// +++ }
-
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 namespace detail {
diff --git a/libsycl/src/detail/kernel_id.hpp b/libsycl/src/detail/kernel_id.hpp
new file mode 100644
index 0000000000000..a1508042ed960
--- /dev/null
+++ b/libsycl/src/detail/kernel_id.hpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL_KERNEL_ID
+#define _LIBSYCL_KERNEL_ID
+
+#include <sycl/__impl/detail/config.hpp>
+#include <sycl/__impl/detail/obj_utils.hpp>
+
+#include <memory>
+#include <string>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+namespace detail {
+/// The class is impl counterpart for sycl::kernel_id which represent a kernel
+/// identificator.
+class KernelIdImpl {
+public:
+  KernelIdImpl(std::string_view Name) : MName(std::move(Name)) {}
+  KernelIdImpl() {}
+  /// \return a null-terminated string representing the name of kernel this id
+  /// stands for.
+  const char *get_name() { return MName.data(); }
+
+private:
+  std::string MName;
+};
+} // namespace detail
+
+// TODO: It is not exported now, but is a part of SYCL spec.
+/// Kernel identifier.
+class kernel_id {
+public:
+  kernel_id() = delete;
+
+  kernel_id(const kernel_id &rhs) = default;
+
+  kernel_id(kernel_id &&rhs) = default;
+
+  kernel_id &operator=(const kernel_id &rhs) = default;
+
+  kernel_id &operator=(kernel_id &&rhs) = default;
+
+  friend bool operator==(const kernel_id &lhs, const kernel_id &rhs) {
+    return lhs.impl == rhs.impl;
+  }
+
+  friend bool operator!=(const kernel_id &lhs, const kernel_id &rhs) {
+    return !(lhs == rhs);
+  }
+
+  /// \returns a null-terminated string which contains the kernel name.
+  const char *get_name() const noexcept;
+
+private:
+  kernel_id(const char *Name);
+
+  kernel_id(const std::shared_ptr<detail::KernelIdImpl> &Impl)
+      : impl(std::move(Impl)) {}
+
+  std::shared_ptr<detail::KernelIdImpl> impl;
+  friend sycl::detail::ImplUtils;
+};
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+template <>
+struct std::hash<sycl::kernel_id>
+    : public sycl::detail::HashBase<sycl::kernel_id> {};
+
+#endif // _LIBSYCL_KERNEL_ID
diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
new file mode 100644
index 0000000000000..ea8673e0f39e9
--- /dev/null
+++ b/libsycl/src/detail/program_manager.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <sycl/__impl/exception.hpp>
+
+#include <detail/device_impl.hpp>
+#include <detail/offload/offload_utils.hpp>
+#include <detail/program_manager.hpp>
+
+#include <cstring>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+namespace detail {
+
+static inline bool checkFatBinVersion(const __sycl_tgt_bin_desc &FatbinDesc) {
+  return FatbinDesc.Version == _LIBSYCL_SUPPORTED_OFFLOAD_BINARY_VERSION;
+}
+
+static inline bool
+checkDeviceImageValidness(const __sycl_tgt_device_image &DeviceImage) {
+  return (DeviceImage.Version == _LIBSYCL_SUPPORTED_DEVICE_BINARY_VERSION) &&
+         (DeviceImage.OffloadKind == OFK_SYCL) &&
+         (DeviceImage.ImageFormat == IMG_SPIRV);
+}
+
+void ProgramManager::addImages(__sycl_tgt_bin_desc *FatbinDesc) {
+  assert(FatbinDesc && "Device images descriptor can't be nullptr");
+
+  if (!checkFatBinVersion(*FatbinDesc))
+    throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
+                          "Incompatible version of device images descriptor.");
+  if (!FatbinDesc->NumDeviceBinaries)
+    return;
+
+  std::lock_guard<std::mutex> Guard(MImageCollectionMutex);
+  for (int I = 0; I < FatbinDesc->NumDeviceBinaries; I++) {
+    const auto &RawDeviceImage = FatbinDesc->DeviceImages[I];
+    if (!checkDeviceImageValidness(RawDeviceImage))
+      throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
+                            "Incompatible device image.");
+
+    const EntryTy *EntriesB = RawDeviceImage.EntriesBegin;
+    const EntryTy *EntriesE = RawDeviceImage.EntriesEnd;
+    // Ignore "empty" device image.
+    if (EntriesB == EntriesE)
+      continue;
+
+    std::unique_ptr<DeviceImageWrapper> NewImageWrapper =
+        std::make_unique<DeviceImageWrapper>(RawDeviceImage);
+
+    for (auto EntriesIt = EntriesB; EntriesIt != EntriesE; EntriesIt++) {
+      auto Name = EntriesIt->SymbolName;
+      auto KernelIDIt = MKernelNameToID.find(Name);
+      if (KernelIDIt == MKernelNameToID.end()) {
+        sycl::kernel_id KernelID =
+            detail::createSyclObjFromImpl<sycl::kernel_id>(
+                std::make_shared<detail::KernelIdImpl>(Name));
+        KernelIDIt = MKernelNameToID.insert(
+            MKernelNameToID.end(),
+            std::make_pair(std::string_view(Name), KernelID));
+      }
+
+      MKernelIDToDevImageJIT.insert(
+          std::make_pair(KernelIDIt->second, NewImageWrapper.get()));
+    }
+
+    MDeviceImageWrappers.insert(
+        std::make_pair(&RawDeviceImage, std::move(NewImageWrapper)));
+  }
+}
+
+void ProgramManager::removeImages(__sycl_tgt_bin_desc *FatbinDesc) {
+  assert(FatbinDesc && "Device images descriptor can't be nullptr");
+
+  if (!checkFatBinVersion(*FatbinDesc))
+    throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
+                          "Incompatible version of device images descriptor.");
+  if (FatbinDesc->NumDeviceBinaries == 0)
+    return;
+
+  std::lock_guard<std::mutex> Guard(MImageCollectionMutex);
+  for (int I = 0; I < FatbinDesc->NumDeviceBinaries; I++) {
+    const auto &RawDeviceImage = FatbinDesc->DeviceImages[I];
+
+    auto DevImageIt = MDeviceImageWrappers.find(&RawDeviceImage);
+    if (DevImageIt == MDeviceImageWrappers.end())
+      continue;
+
+    const EntryTy *EntriesB = RawDeviceImage.EntriesBegin;
+    const EntryTy *EntriesE = RawDeviceImage.EntriesEnd;
+    // Ignore "empty" device image
+    if (EntriesB == EntriesE)
+      continue;
+
+    for (auto EntriesIt = EntriesB; EntriesIt != EntriesE; EntriesIt++) {
+      if (auto KernelIDIt = MKernelNameToID.find(EntriesIt->SymbolName);
+          KernelIDIt != MKernelNameToID.end()) {
+        MKernelIDToDevImageJIT.erase(KernelIDIt->second);
+        MKernelNameToID.erase(KernelIDIt);
+      }
+    }
+
+    MDeviceImageWrappers.erase(DevImageIt);
+  }
+}
+
+static bool isImageTargetCompatible(const DeviceImageWrapper &Image,
+                                    const DeviceImpl &Device) {
+  sycl::backend BE = Device.getBackend();
+  const char *Target = Image.getRawData().TripleString;
+
+  return (strcmp(Target, _LIBSYCL_DEVICE_BINARY_TARGET_SPIRV64) == 0) &&
+         (BE == sycl::backend::level_zero);
+}
+
+DeviceImageWrapper *ProgramManager::getDeviceImage(const char *KernelName,
+                                                   kernel_id KernelID,
+                                                   DeviceImpl &Device) {
+  auto [Begin, End] = MKernelIDToDevImageJIT.equal_range(KernelID);
+  if (Begin != End) {
+    ol_result_t Result{};
+    bool IsValid{};
+    // TODO: with AOT (not implemented yet), we need to analize and check
+    // olIsValidBinary for AOT binaries first.
+    for (auto It = Begin; It != End; ++It) {
+      if (isImageTargetCompatible(*It->second, Device)) {
+        callAndThrow(olIsValidBinary, Device.getHandle(),
+                     It->second->getRawData().ImageStart, It->second->getSize(),
+                     &IsValid);
+        if (IsValid)
+          return It->second;
+      }
+    }
+  }
+
+  throw exception(make_error_code(errc::runtime),
+                  "No kernel named " + std::string(KernelName) + " was found");
+}
+
+} // namespace detail
+_LIBSYCL_END_NAMESPACE_SYCL
+
+extern "C" void __sycl_register_lib(__sycl_tgt_bin_desc *FatbinDesc) {
+  sycl::detail::ProgramManager::getInstance().addImages(FatbinDesc);
+}
+
+extern "C" void __sycl_unregister_lib(__sycl_tgt_bin_desc *FatbinDesc) {
+  sycl::detail::ProgramManager::getInstance().removeImages(FatbinDesc);
+}
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
new file mode 100644
index 0000000000000..3b1a2eb07d5ff
--- /dev/null
+++ b/libsycl/src/detail/program_manager.hpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL_PROGRAM_MANAGER
+#define _LIBSYCL_PROGRAM_MANAGER
+
+#include <sycl/__impl/detail/config.hpp>
+
+#include <detail/device_binary_structures.hpp>
+#include <detail/device_image_wrapper.hpp>
+#include <detail/kernel_id.hpp>
+
+#include <mutex>
+#include <unordered_map>
+
+// +++ Entry points referenced by the offload wrapper object {
+
+/// Executed as a part of current module's (.exe, .dll) static initialization.
+/// Registers device executable images with the runtime.
+extern "C" _LIBSYCL_EXPORT void __sycl_register_lib(__sycl_tgt_bin_desc *desc);
+
+/// Executed as a part of current module's (.exe, .dll) static
+/// de-initialization.
+/// Unregisters device executable images with the runtime.
+extern "C" _LIBSYCL_EXPORT void
+__sycl_unregister_lib(__sycl_tgt_bin_desc *desc);
+
+// +++ }
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+namespace detail {
+
+class DeviceImpl;
+
+/// A class to manage programs and kernels.
+class ProgramManager {
+
+public:
+  static ProgramManager &getInstance() {
+    static ProgramManager PM{};
+    return PM;
+  }
+
+  /// Parses raw device images data and prepare internal structures for
+  /// effective kernel/program creation.
+  /// \param FatbinDesc a record of all the device code that may be offloaded
+  /// generated by compiler and offloading tools.
+  /// \throw sycl::exception with sycl::errc::runtime if device image descriptor
+  /// has incompatible version or if device image has incompatible
+  /// version/target/kind.
+  void addImages(__sycl_tgt_bin_desc *FatbinDesc);
+
+  /// Removes all entries of the data in FatbinDesc in internal structures.
+  /// \param FatbinDesc a record of all the device code that may be offloaded
+  /// generated by compiler and offloading tools. Must match the pointer and
+  /// data passed to addImages.
+  void removeImages(__sycl_tgt_bin_desc *FatbinDesc);
+
+private:
+  ProgramManager() = default;
+  ~ProgramManager() = default;
+  ProgramManager(ProgramManager const &) = delete;
+  ProgramManager &operator=(ProgramManager const &) = delete;
+
+  /// Searches for device image that contains requested kernel and is compatible
+  /// with requested device.
+  /// \param KernelName a null-terminated string representing the name of kernel
+  /// to obtain device image for.
+  /// \param KernelID a kernel id matching KernelName.
+  /// \param DeviceImpl a device that device image must be compatible with.
+  /// \throw sycl::exception with sycl::errc::runtime if device image validness
+  /// check failed in liboffload or if no compatible image was found.
+  DeviceImageWrapper *getDeviceImage(const char *KernelName, kernel_id KernelID,
+                                     DeviceImpl &Device);
+
+  // Filled by addImages(...).
+  std::unordered_map<std::string_view, kernel_id> MKernelNameToID;
+  std::unordered_map<kernel_id, DeviceImageWrapper *> MKernelIDToDevImageJIT;
+  // Controls lifetime of device image ptr and wrapper.
+  std::unordered_map<const __sycl_tgt_device_image *,
+                     std::unique_ptr<DeviceImageWrapper>>
+      MDeviceImageWrappers;
+  std::mutex MImageCollectionMutex;
+};
+
+} // namespace detail
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL_PROGRAM_MANAGER
diff --git a/libsycl/src/usm_functions.cpp b/libsycl/src/usm_functions.cpp
index 24a099ea4cf2a..c94015b97d772 100644
--- a/libsycl/src/usm_functions.cpp
+++ b/libsycl/src/usm_functions.cpp
@@ -103,7 +103,7 @@ void *malloc(std::size_t numBytes, const device &syclDevice,
 
   void *Ptr{};
   auto Result = detail::callNoCheck(
-      olMemAlloc, detail::getSyclObjImpl(syclDevice)->getOLHandle(),
+      olMemAlloc, detail::getSyclObjImpl(syclDevice)->getHandle(),
       detail::getOlAllocType(kind), numBytes, &Ptr);
   return detail::isFailed(Result) ? nullptr : Ptr;
 }

>From 87725e76b89cb8f56e6ff5a3b9e81b5ff10b55f7 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Tue, 24 Mar 2026 10:42:48 -0700
Subject: [PATCH 02/25] fix comments

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/src/CMakeLists.txt                    |  3 +
 .../src/detail/device_binary_structures.hpp   | 92 ++++++-------------
 libsycl/src/detail/device_image_wrapper.hpp   | 11 ++-
 libsycl/src/detail/kernel_id.hpp              | 14 +--
 libsycl/src/detail/program_manager.cpp        | 52 +++++------
 libsycl/src/detail/program_manager.hpp        | 38 ++++----
 libsycl/src/detail/queue_impl.cpp             |  3 +-
 7 files changed, 91 insertions(+), 122 deletions(-)

diff --git a/libsycl/src/CMakeLists.txt b/libsycl/src/CMakeLists.txt
index bc39f9162f992..4501005e433e3 100644
--- a/libsycl/src/CMakeLists.txt
+++ b/libsycl/src/CMakeLists.txt
@@ -17,6 +17,7 @@ function(add_sycl_rt_library LIB_TARGET_NAME LIB_OBJ_NAME LIB_OUTPUT_NAME)
   add_dependencies(${LIB_OBJ_NAME}
     sycl-headers
     LLVMOffload
+    LLVMObject
   )
 
   target_include_directories(${LIB_OBJ_NAME}
@@ -24,6 +25,7 @@ function(add_sycl_rt_library LIB_TARGET_NAME LIB_OBJ_NAME LIB_OUTPUT_NAME)
       ${CMAKE_CURRENT_SOURCE_DIR}
       ${LIBSYCL_BUILD_INCLUDE_DIR}
       $<TARGET_PROPERTY:LLVMOffload,INTERFACE_INCLUDE_DIRECTORIES>
+      ${LLVM_MAIN_INCLUDE_DIR}
   )
 
   set_target_properties(${LIB_TARGET_NAME}
@@ -68,6 +70,7 @@ function(add_sycl_rt_library LIB_TARGET_NAME LIB_OBJ_NAME LIB_OUTPUT_NAME)
       ${CMAKE_DL_LIBS}
       ${CMAKE_THREAD_LIBS_INIT}
       LLVMOffload
+      LLVMObject
   )
 
   set_target_properties(${LIB_TARGET_NAME}
diff --git a/libsycl/src/detail/device_binary_structures.hpp b/libsycl/src/detail/device_binary_structures.hpp
index d2abac6854e40..be403da9fcbb4 100644
--- a/libsycl/src/detail/device_binary_structures.hpp
+++ b/libsycl/src/detail/device_binary_structures.hpp
@@ -9,8 +9,17 @@
 #ifndef _LIBSYCL_DEVICE_BINARY_STRUCTURES
 #define _LIBSYCL_DEVICE_BINARY_STRUCTURES
 
+#include <sycl/__impl/detail/config.hpp>
+
+#include <llvm/Frontend/Offloading/Utility.h>
+#include <llvm/Object/OffloadBinary.h>
+
 #include <cstdint>
 
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+namespace detail {
+
 /// Target identification strings.
 ///
 /// A device type represented by a particular target
@@ -18,66 +27,16 @@
 /// to map the image type onto the device target triple.
 
 /// Unknown target.
-#define _LIBSYCL_DEVICE_BINARY_TARGET_TRIPLE_UNKNOWN "unknown-unknown-unknown"
+static constexpr char DeviceBinaryTripleUnknown[] = "unknown-unknown-unknown";
 /// SPIR-V with 64-bit pointers.
-#define _LIBSYCL_DEVICE_BINARY_TARGET_SPIRV64 "spirv64-unknown-unknown"
-
-// This is a replica of the EntryTy data structure in
-// llvm/include/llvm/Frontend/Offloading/Utility.h.
-struct EntryTy {
-  /// Reserved bytes used to detect an older version of the struct, always zero.
-  uint64_t Reserved = 0x0;
-  /// The current version of the struct for runtime forward compatibility.
-  uint16_t Version = 0x1;
-  /// The expected consumer of this entry, e.g. SYCL or OpenMP.
-  /// llvm::object::OffloadKind
-  uint16_t Kind;
-  /// Flags associated with the global.
-  uint32_t Flags;
-  /// The address of the global to be registered by the runtime.
-  void *Address;
-  /// The name of the symbol in the device image.
-  char *SymbolName;
-  /// The number of bytes the symbol takes.
-  uint64_t Size;
-  /// Extra generic data used to register this entry.
-  uint64_t Data;
-  /// An extra pointer, usually null.
-  void *AuxAddr;
-};
-
-// TODO: would be nice to include it from llvm/Object. It doesn't work now since
-// I have to link LLVMObject to do that (linker error otherwise).
-// Copy of llvm::object::OffloadKind.
-/// The producer of the associated offloading image.
-enum OffloadKind : uint16_t {
-  OFK_None = 0,
-  OFK_OpenMP = (1 << 0),
-  OFK_Cuda = (1 << 1),
-  OFK_HIP = (1 << 2),
-  OFK_SYCL = (1 << 3),
-  OFK_LAST = (1 << 4),
-};
-
-// Copy of llvm::object::ImageKind.
-/// The type of contents the offloading image contains.
-enum ImageKind : uint16_t {
-  IMG_None = 0,
-  IMG_Object,
-  IMG_Bitcode,
-  IMG_Cubin,
-  IMG_Fatbinary,
-  IMG_PTX,
-  IMG_SPIRV,
-  IMG_LAST,
-};
+static constexpr char DeviceBinaryTripleSPIRV64[] = "spirv64-unknown-unknown";
 
 /// Device binary descriptor version supported by this library.
-static const uint16_t _LIBSYCL_SUPPORTED_DEVICE_BINARY_VERSION = 3;
+static constexpr uint16_t SupportedDevicyBinaryVersion = 3;
 
 /// This struct is a record of the device binary information.
 ///  It must match the __tgt_device_image structure generated by the
-///  clang-offload-wrapper tool when their `Version` field match.
+///  clang-offload-wrapper tool when their `Version` fields match.
 struct __sycl_tgt_device_image {
   uint16_t Version;
   /// The type of offload model the binary employs. See `OffloadKind`. Only
@@ -88,32 +47,32 @@ struct __sycl_tgt_device_image {
   /// A null-terminated string representation of the device's target
   /// architecture. Must hold one of _LIBSYCL_DEVICE_BINARY_TARGET_* values.
   const char *TripleString;
-  /// A null-terminated string; target- and compiler-specific options
-  /// which are suggested to use to "compile" program at runtime.
+  /// A null-terminated string of target- and compiler-specific options
+  /// that are suggested to use to "compile" program at runtime.
   const char *CompileOptions;
-  /// A null-terminated string; target- and compiler-specific options
-  /// which are suggested to use to "link" program at runtime.
+  /// A null-terminated string of target- and compiler-specific options
+  /// that are suggested to use to "link" program at runtime.
   const char *LinkOptions;
   /// Pointer to the target code start.
   const unsigned char *ImageStart;
   /// Pointer to the target code end.
   const unsigned char *ImageEnd;
   /// The offload entry table
-  EntryTy *EntriesBegin;
-  EntryTy *EntriesEnd;
+  llvm::offloading::EntryTy *EntriesBegin;
+  llvm::offloading::EntryTy *EntriesEnd;
   // TODO: properties are not supported now.
-  /// Array of preperty sets.
+  /// Array of property sets.
   void *PropertiesBegin;
   void *PropertiesEnd;
 };
 
 /// Version of offload binaries descriptor `__sycl_tgt_bin_desc` supported by
 /// libsycl.
-static constexpr uint16_t _LIBSYCL_SUPPORTED_OFFLOAD_BINARY_VERSION = 1;
+static constexpr uint16_t SupportedOffloadBinaryVersion = 1;
 
 /// This struct is a record of all the device code that may be offloaded.
 /// It must match the `__tgt_bin_desc` structure generated by
-/// the clang-offload-wrapper tool when their `Version` field match.
+/// the clang-offload-wrapper tool when their `Version` fields match.
 struct __sycl_tgt_bin_desc {
   /// Version of the structure.
   uint16_t Version;
@@ -122,8 +81,11 @@ struct __sycl_tgt_bin_desc {
   /// Device binaries data.
   __sycl_tgt_device_image *DeviceImages;
   /// The offload entry table (not used, for compatibility with OpenMP).
-  EntryTy *HostEntriesBegin;
-  EntryTy *HostEntriesEnd;
+  llvm::offloading::EntryTy *HostEntriesBegin;
+  llvm::offloading::EntryTy *HostEntriesEnd;
 };
 
+} // namespace detail
+_LIBSYCL_END_NAMESPACE_SYCL
+
 #endif // _LIBSYCL_DEVICE_BINARY_STRUCTURES
diff --git a/libsycl/src/detail/device_image_wrapper.hpp b/libsycl/src/detail/device_image_wrapper.hpp
index 534a4be4fcf1e..4b0be66eb97ae 100644
--- a/libsycl/src/detail/device_image_wrapper.hpp
+++ b/libsycl/src/detail/device_image_wrapper.hpp
@@ -16,9 +16,9 @@
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
 
-/// A wrapper of __sycl_tgt_device_image structure.
+/// A wrapper of __sycl_tgt_device_image structure to help with its fields
+/// parsing, iteration over data and data transformation.
 class DeviceImageWrapper {
-public:
 public:
   DeviceImageWrapper(const __sycl_tgt_device_image &Bin) : MBin(&Bin) {}
   // Explicitly delete copy constructor/operator= to avoid unintentional copies.
@@ -28,12 +28,13 @@ class DeviceImageWrapper {
   DeviceImageWrapper(DeviceImageWrapper &&) = default;
   DeviceImageWrapper &operator=(DeviceImageWrapper &&) = default;
 
-  ~DeviceImageWrapper() {}
+  ~DeviceImageWrapper() = default;
 
-  /// \return reference to corresponsing raw __sycl_tgt_device_image object.
+  /// \return a reference to the corresponding raw __sycl_tgt_device_image
+  /// object.
   const __sycl_tgt_device_image &getRawData() const { return *get(); }
 
-  /// \return size of corresponding device image data in bytes.
+  /// \return the size of the corresponding device image data in bytes.
   size_t getSize() const {
     return static_cast<size_t>(MBin->ImageEnd - MBin->ImageStart);
   }
diff --git a/libsycl/src/detail/kernel_id.hpp b/libsycl/src/detail/kernel_id.hpp
index a1508042ed960..a8009dace290d 100644
--- a/libsycl/src/detail/kernel_id.hpp
+++ b/libsycl/src/detail/kernel_id.hpp
@@ -18,14 +18,14 @@
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 namespace detail {
-/// The class is impl counterpart for sycl::kernel_id which represent a kernel
-/// identificator.
+/// The class is the implementation counterpart for sycl::kernel_id, which
+/// represents a kernel identificator.
 class KernelIdImpl {
 public:
-  KernelIdImpl(std::string_view Name) : MName(std::move(Name)) {}
+  KernelIdImpl(std::string_view Name) : MName(Name) {}
   KernelIdImpl() {}
-  /// \return a null-terminated string representing the name of kernel this id
-  /// stands for.
+  /// \return a null-terminated string representing the name of the kernel this
+  /// id stands for.
   const char *get_name() { return MName.data(); }
 
 private:
@@ -55,8 +55,8 @@ class kernel_id {
     return !(lhs == rhs);
   }
 
-  /// \returns a null-terminated string which contains the kernel name.
-  const char *get_name() const noexcept;
+  /// \returns a null-terminated string that contains the kernel name.
+  const char *get_name() const noexcept { return impl->get_name(); }
 
 private:
   kernel_id(const char *Name);
diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
index ea8673e0f39e9..f9b158a6e1918 100644
--- a/libsycl/src/detail/program_manager.cpp
+++ b/libsycl/src/detail/program_manager.cpp
@@ -6,11 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include <detail/program_manager.hpp>
+
 #include <sycl/__impl/exception.hpp>
 
 #include <detail/device_impl.hpp>
 #include <detail/offload/offload_utils.hpp>
-#include <detail/program_manager.hpp>
 
 #include <cstring>
 
@@ -18,14 +19,14 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
 
 static inline bool checkFatBinVersion(const __sycl_tgt_bin_desc &FatbinDesc) {
-  return FatbinDesc.Version == _LIBSYCL_SUPPORTED_OFFLOAD_BINARY_VERSION;
+  return FatbinDesc.Version == SupportedOffloadBinaryVersion;
 }
 
 static inline bool
-checkDeviceImageValidness(const __sycl_tgt_device_image &DeviceImage) {
-  return (DeviceImage.Version == _LIBSYCL_SUPPORTED_DEVICE_BINARY_VERSION) &&
-         (DeviceImage.OffloadKind == OFK_SYCL) &&
-         (DeviceImage.ImageFormat == IMG_SPIRV);
+checkDeviceImageValidity(const __sycl_tgt_device_image &DeviceImage) {
+  return (DeviceImage.Version == SupportedDevicyBinaryVersion) &&
+         (DeviceImage.OffloadKind == llvm::object::OFK_SYCL) &&
+         (DeviceImage.ImageFormat == llvm::object::IMG_SPIRV);
 }
 
 void ProgramManager::addImages(__sycl_tgt_bin_desc *FatbinDesc) {
@@ -38,14 +39,14 @@ void ProgramManager::addImages(__sycl_tgt_bin_desc *FatbinDesc) {
     return;
 
   std::lock_guard<std::mutex> Guard(MImageCollectionMutex);
-  for (int I = 0; I < FatbinDesc->NumDeviceBinaries; I++) {
+  for (int I = 0; I < FatbinDesc->NumDeviceBinaries; ++I) {
     const auto &RawDeviceImage = FatbinDesc->DeviceImages[I];
-    if (!checkDeviceImageValidness(RawDeviceImage))
+    if (!checkDeviceImageValidity(RawDeviceImage))
       throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
                             "Incompatible device image.");
 
-    const EntryTy *EntriesB = RawDeviceImage.EntriesBegin;
-    const EntryTy *EntriesE = RawDeviceImage.EntriesEnd;
+    const llvm::offloading::EntryTy *EntriesB = RawDeviceImage.EntriesBegin;
+    const llvm::offloading::EntryTy *EntriesE = RawDeviceImage.EntriesEnd;
     // Ignore "empty" device image.
     if (EntriesB == EntriesE)
       continue;
@@ -53,7 +54,7 @@ void ProgramManager::addImages(__sycl_tgt_bin_desc *FatbinDesc) {
     std::unique_ptr<DeviceImageWrapper> NewImageWrapper =
         std::make_unique<DeviceImageWrapper>(RawDeviceImage);
 
-    for (auto EntriesIt = EntriesB; EntriesIt != EntriesE; EntriesIt++) {
+    for (auto EntriesIt = EntriesB; EntriesIt != EntriesE; ++EntriesIt) {
       auto Name = EntriesIt->SymbolName;
       auto KernelIDIt = MKernelNameToID.find(Name);
       if (KernelIDIt == MKernelNameToID.end()) {
@@ -77,27 +78,24 @@ void ProgramManager::addImages(__sycl_tgt_bin_desc *FatbinDesc) {
 void ProgramManager::removeImages(__sycl_tgt_bin_desc *FatbinDesc) {
   assert(FatbinDesc && "Device images descriptor can't be nullptr");
 
-  if (!checkFatBinVersion(*FatbinDesc))
-    throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
-                          "Incompatible version of device images descriptor.");
-  if (FatbinDesc->NumDeviceBinaries == 0)
+  if (!checkFatBinVersion(*FatbinDesc) || FatbinDesc->NumDeviceBinaries == 0)
     return;
 
   std::lock_guard<std::mutex> Guard(MImageCollectionMutex);
-  for (int I = 0; I < FatbinDesc->NumDeviceBinaries; I++) {
+  for (int I = 0; I < FatbinDesc->NumDeviceBinaries; ++I) {
     const auto &RawDeviceImage = FatbinDesc->DeviceImages[I];
 
     auto DevImageIt = MDeviceImageWrappers.find(&RawDeviceImage);
     if (DevImageIt == MDeviceImageWrappers.end())
       continue;
 
-    const EntryTy *EntriesB = RawDeviceImage.EntriesBegin;
-    const EntryTy *EntriesE = RawDeviceImage.EntriesEnd;
+    const llvm::offloading::EntryTy *EntriesB = RawDeviceImage.EntriesBegin;
+    const llvm::offloading::EntryTy *EntriesE = RawDeviceImage.EntriesEnd;
     // Ignore "empty" device image
     if (EntriesB == EntriesE)
       continue;
 
-    for (auto EntriesIt = EntriesB; EntriesIt != EntriesE; EntriesIt++) {
+    for (auto EntriesIt = EntriesB; EntriesIt != EntriesE; ++EntriesIt) {
       if (auto KernelIDIt = MKernelNameToID.find(EntriesIt->SymbolName);
           KernelIDIt != MKernelNameToID.end()) {
         MKernelIDToDevImageJIT.erase(KernelIDIt->second);
@@ -114,18 +112,18 @@ static bool isImageTargetCompatible(const DeviceImageWrapper &Image,
   sycl::backend BE = Device.getBackend();
   const char *Target = Image.getRawData().TripleString;
 
-  return (strcmp(Target, _LIBSYCL_DEVICE_BINARY_TARGET_SPIRV64) == 0) &&
+  return (strcmp(Target, DeviceBinaryTripleSPIRV64) == 0) &&
          (BE == sycl::backend::level_zero);
 }
 
-DeviceImageWrapper *ProgramManager::getDeviceImage(const char *KernelName,
-                                                   kernel_id KernelID,
+DeviceImageWrapper *ProgramManager::getDeviceImage(std::string_view KernelName,
+                                                   const kernel_id &KernelID,
                                                    DeviceImpl &Device) {
+  std::lock_guard<std::mutex> Guard(MImageCollectionMutex);
   auto [Begin, End] = MKernelIDToDevImageJIT.equal_range(KernelID);
   if (Begin != End) {
-    ol_result_t Result{};
     bool IsValid{};
-    // TODO: with AOT (not implemented yet), we need to analize and check
+    // TODO: with AOT (not implemented yet), we need to analyze and check
     // olIsValidBinary for AOT binaries first.
     for (auto It = Begin; It != End; ++It) {
       if (isImageTargetCompatible(*It->second, Device)) {
@@ -145,10 +143,12 @@ DeviceImageWrapper *ProgramManager::getDeviceImage(const char *KernelName,
 } // namespace detail
 _LIBSYCL_END_NAMESPACE_SYCL
 
-extern "C" void __sycl_register_lib(__sycl_tgt_bin_desc *FatbinDesc) {
+extern "C" _LIBSYCL_EXPORT void
+__sycl_register_lib(sycl::detail::__sycl_tgt_bin_desc *FatbinDesc) {
   sycl::detail::ProgramManager::getInstance().addImages(FatbinDesc);
 }
 
-extern "C" void __sycl_unregister_lib(__sycl_tgt_bin_desc *FatbinDesc) {
+extern "C" _LIBSYCL_EXPORT void
+__sycl_unregister_lib(sycl::detail::__sycl_tgt_bin_desc *FatbinDesc) {
   sycl::detail::ProgramManager::getInstance().removeImages(FatbinDesc);
 }
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
index 3b1a2eb07d5ff..7d66602151d64 100644
--- a/libsycl/src/detail/program_manager.hpp
+++ b/libsycl/src/detail/program_manager.hpp
@@ -20,15 +20,16 @@
 
 // +++ Entry points referenced by the offload wrapper object {
 
-/// Executed as a part of current module's (.exe, .dll) static initialization.
+/// Executed as a part of a module's (.exe, .dll) static initialization.
 /// Registers device executable images with the runtime.
-extern "C" _LIBSYCL_EXPORT void __sycl_register_lib(__sycl_tgt_bin_desc *desc);
+extern "C" _LIBSYCL_EXPORT void
+__sycl_register_lib(sycl::detail::__sycl_tgt_bin_desc *FatbinDesc);
 
 /// Executed as a part of current module's (.exe, .dll) static
 /// de-initialization.
 /// Unregisters device executable images with the runtime.
 extern "C" _LIBSYCL_EXPORT void
-__sycl_unregister_lib(__sycl_tgt_bin_desc *desc);
+__sycl_unregister_lib(sycl::detail::__sycl_tgt_bin_desc *FatbinDesc);
 
 // +++ }
 
@@ -47,17 +48,17 @@ class ProgramManager {
     return PM;
   }
 
-  /// Parses raw device images data and prepare internal structures for
+  /// Parses raw device images data and prepares internal structures for
   /// effective kernel/program creation.
-  /// \param FatbinDesc a record of all the device code that may be offloaded
+  /// \param FatbinDesc a record of all the device code that may be offloaded,
   /// generated by compiler and offloading tools.
-  /// \throw sycl::exception with sycl::errc::runtime if device image descriptor
-  /// has incompatible version or if device image has incompatible
-  /// version/target/kind.
+  /// \throw sycl::exception with sycl::errc::runtime if a device image
+  /// descriptor has an incompatible version or if a device image has an
+  /// incompatible version, target or kind.
   void addImages(__sycl_tgt_bin_desc *FatbinDesc);
 
-  /// Removes all entries of the data in FatbinDesc in internal structures.
-  /// \param FatbinDesc a record of all the device code that may be offloaded
+  /// Removes all entries of the data in FatbinDesc from internal structures.
+  /// \param FatbinDesc a record of all the device code that may be offloaded,
   /// generated by compiler and offloading tools. Must match the pointer and
   /// data passed to addImages.
   void removeImages(__sycl_tgt_bin_desc *FatbinDesc);
@@ -68,15 +69,16 @@ class ProgramManager {
   ProgramManager(ProgramManager const &) = delete;
   ProgramManager &operator=(ProgramManager const &) = delete;
 
-  /// Searches for device image that contains requested kernel and is compatible
-  /// with requested device.
-  /// \param KernelName a null-terminated string representing the name of kernel
-  /// to obtain device image for.
+  /// Searches for a device image that contains the requested kernel and is
+  /// compatible with the requested device.
+  /// \param KernelName a null-terminated string representing the name of the
+  /// kernel to obtain a device image for.
   /// \param KernelID a kernel id matching KernelName.
-  /// \param DeviceImpl a device that device image must be compatible with.
-  /// \throw sycl::exception with sycl::errc::runtime if device image validness
-  /// check failed in liboffload or if no compatible image was found.
-  DeviceImageWrapper *getDeviceImage(const char *KernelName, kernel_id KernelID,
+  /// \param DeviceImpl a device with which device image must be compatible.
+  /// \throw sycl::exception with sycl::errc::runtime if the device image
+  /// validation failed in liboffload or if no compatible image was found.
+  DeviceImageWrapper *getDeviceImage(std::string_view KernelName,
+                                     const kernel_id &KernelID,
                                      DeviceImpl &Device);
 
   // Filled by addImages(...).
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index dec2d7d5507aa..9c93fe02de8a6 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -6,9 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include <detail/device_impl.hpp>
 #include <detail/queue_impl.hpp>
 
+#include <detail/device_impl.hpp>
+
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 namespace detail {

>From aaa76ae7598ee6af2aabdeb4dd7931c5f250cb5a Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Wed, 25 Mar 2026 02:50:37 -0700
Subject: [PATCH 03/25] remove clang-offload-wrapper mentioning

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/src/detail/device_binary_structures.hpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libsycl/src/detail/device_binary_structures.hpp b/libsycl/src/detail/device_binary_structures.hpp
index be403da9fcbb4..3b94ddca1f2d8 100644
--- a/libsycl/src/detail/device_binary_structures.hpp
+++ b/libsycl/src/detail/device_binary_structures.hpp
@@ -36,7 +36,7 @@ static constexpr uint16_t SupportedDevicyBinaryVersion = 3;
 
 /// This struct is a record of the device binary information.
 ///  It must match the __tgt_device_image structure generated by the
-///  clang-offload-wrapper tool when their `Version` fields match.
+///  compiler when their `Version` fields match.
 struct __sycl_tgt_device_image {
   uint16_t Version;
   /// The type of offload model the binary employs. See `OffloadKind`. Only
@@ -72,7 +72,7 @@ static constexpr uint16_t SupportedOffloadBinaryVersion = 1;
 
 /// This struct is a record of all the device code that may be offloaded.
 /// It must match the `__tgt_bin_desc` structure generated by
-/// the clang-offload-wrapper tool when their `Version` fields match.
+/// the compiler when their `Version` fields match.
 struct __sycl_tgt_bin_desc {
   /// Version of the structure.
   uint16_t Version;

>From 3683423bfa6e1de9fa2fba8bee44285d81b8587c Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Wed, 25 Mar 2026 04:02:52 -0700
Subject: [PATCH 04/25] [libsycl] add sycl::event and wait functionality to
 event  & queue

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/docs/index.rst                | 11 +++-
 libsycl/include/sycl/__impl/event.hpp | 90 +++++++++++++++++++++++++++
 libsycl/include/sycl/__impl/queue.hpp |  7 +++
 libsycl/include/sycl/sycl.hpp         |  1 +
 libsycl/src/CMakeLists.txt            |  2 +
 libsycl/src/detail/event_impl.cpp     | 39 ++++++++++++
 libsycl/src/detail/event_impl.hpp     | 68 ++++++++++++++++++++
 libsycl/src/detail/queue_impl.cpp     | 14 ++++-
 libsycl/src/detail/queue_impl.hpp     |  9 ++-
 libsycl/src/event.cpp                 | 25 ++++++++
 libsycl/src/queue.cpp                 |  2 +
 11 files changed, 264 insertions(+), 4 deletions(-)
 create mode 100644 libsycl/include/sycl/__impl/event.hpp
 create mode 100644 libsycl/src/detail/event_impl.cpp
 create mode 100644 libsycl/src/detail/event_impl.hpp
 create mode 100644 libsycl/src/event.cpp

diff --git a/libsycl/docs/index.rst b/libsycl/docs/index.rst
index 03f7fb7c0876e..9aa36b4a54c57 100644
--- a/libsycl/docs/index.rst
+++ b/libsycl/docs/index.rst
@@ -106,7 +106,14 @@ TODO for added SYCL classes
 
 * device selection: to add compatibility with old SYCL 1.2.1 device selectors, still part of SYCL 2020 specification
 * ``context``: to implement get_info, properties & public constructors once context support is added to liboffload
-* ``queue``: to implement USM methods, to implement synchronization methods, to implement submit & copy with accessors (low priority), get_info & properties, ctors that accepts context (blocked by lack of liboffload support)
+* ``queue``:
+
+  * to implement USM methods
+  * to implement synchronization methods
+  * to implement submit & copy with accessors (low priority)
+  * get_info & properties
+  * ctors that accepts context (blocked by lack of liboffload support)
+
 * ``property_list``: to fully implement and integrate with existing SYCL runtime classes supporting it
 * usm allocations:
 
@@ -114,7 +121,7 @@ TODO for added SYCL classes
   * forward templated funcs to alignment methods (rewrite current impl)
   * handle sub devices once they are implemented (blocked by liboffload support)
 
-
+* ``event``: get_wait_list, get_info, get_profiling_info, wait_and_throw & default ctor are not implemented
 * general opens:
 
   * define a way to report errors from object dtors.
\ No newline at end of file
diff --git a/libsycl/include/sycl/__impl/event.hpp b/libsycl/include/sycl/__impl/event.hpp
new file mode 100644
index 0000000000000..7df095c9a1fd1
--- /dev/null
+++ b/libsycl/include/sycl/__impl/event.hpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the declaration of the SYCL event class (SYCL
+/// 2020 4.6.6.), that represents the status of an operation that is being
+/// executed by the SYCL runtime.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL___IMPL_EVENT_HPP
+#define _LIBSYCL___IMPL_EVENT_HPP
+
+#include <sycl/__impl/backend.hpp>
+#include <sycl/__impl/detail/config.hpp>
+#include <sycl/__impl/detail/obj_utils.hpp>
+#include <sycl/__impl/info/desc_base.hpp>
+
+#include <memory>
+#include <vector>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+class event;
+
+namespace detail {
+class EventImpl;
+template <typename T>
+using is_event_info_desc_t = typename is_info_desc<T, event>::return_type;
+} // namespace detail
+
+/// SYCL 2020 4.6.6. Event class.
+class _LIBSYCL_EXPORT event {
+public:
+  event(const event &rhs) = default;
+
+  event(event &&rhs) = default;
+
+  event &operator=(const event &rhs) = default;
+
+  event &operator=(event &&rhs) = default;
+
+  friend bool operator==(const event &lhs, const event &rhs) {
+    return lhs.impl == rhs.impl;
+  }
+
+  friend bool operator!=(const event &lhs, const event &rhs) {
+    return !(lhs == rhs);
+  }
+
+  /// \return the backend associated with this platform.
+  backend get_backend() const noexcept;
+
+  /// Blocks until all commands associated with this event and any dependent
+  /// events have completed.
+  void wait();
+
+  /// Behaves as if calling event::wait on each event in eventList.
+  static void wait(const std::vector<event> &eventList);
+
+  /// Queries this SYCL event for information.
+  ///
+  /// \return depends on the information being requested.
+  template <typename Param>
+  detail::is_event_info_desc_t<Param> get_info() const;
+
+  /// Queries this SYCL event for SYCL backend-specific information.
+  ///
+  /// \return depends on information being queried.
+  template <typename Param>
+  typename Param::return_type get_backend_info() const;
+
+private:
+  event(std::shared_ptr<detail::EventImpl> Impl) : impl(Impl) {}
+  std::shared_ptr<detail::EventImpl> impl;
+
+  friend sycl::detail::ImplUtils;
+};
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+template <>
+struct std::hash<sycl::event> : public sycl::detail::HashBase<sycl::event> {};
+
+#endif // _LIBSYCL___IMPL_EVENT_HPP
diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index a440959c6311f..587f56a8eb245 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -17,6 +17,7 @@
 
 #include <sycl/__impl/async_handler.hpp>
 #include <sycl/__impl/device.hpp>
+#include <sycl/__impl/event.hpp>
 #include <sycl/__impl/property_list.hpp>
 
 #include <sycl/__impl/detail/config.hpp>
@@ -29,6 +30,7 @@ class context;
 
 namespace detail {
 class QueueImpl;
+
 } // namespace detail
 
 // SYCL 2020 4.6.5. Queue class.
@@ -136,6 +138,11 @@ class _LIBSYCL_EXPORT queue {
   template <typename Param>
   typename Param::return_type get_backend_info() const;
 
+  /// Blocks the calling thread until all commands previously submitted to this
+  /// queue have completed. Synchronous errors are reported through SYCL
+  /// exceptions.
+  void wait();
+
 private:
   queue(const std::shared_ptr<detail::QueueImpl> &Impl) : impl(Impl) {}
   std::shared_ptr<detail::QueueImpl> impl;
diff --git a/libsycl/include/sycl/sycl.hpp b/libsycl/include/sycl/sycl.hpp
index 3fcf088f45535..ce9fc8defd90b 100644
--- a/libsycl/include/sycl/sycl.hpp
+++ b/libsycl/include/sycl/sycl.hpp
@@ -17,6 +17,7 @@
 #include <sycl/__impl/context.hpp>
 #include <sycl/__impl/device.hpp>
 #include <sycl/__impl/device_selector.hpp>
+#include <sycl/__impl/event.hpp>
 #include <sycl/__impl/exception.hpp>
 #include <sycl/__impl/platform.hpp>
 #include <sycl/__impl/queue.hpp>
diff --git a/libsycl/src/CMakeLists.txt b/libsycl/src/CMakeLists.txt
index 4501005e433e3..7b9826fb8a3de 100644
--- a/libsycl/src/CMakeLists.txt
+++ b/libsycl/src/CMakeLists.txt
@@ -85,6 +85,7 @@ endfunction(add_sycl_rt_library)
 
 set(LIBSYCL_SOURCES
     "context.cpp"
+    "event.cpp"
     "exception.cpp"
     "exception_list.cpp"
     "device.cpp"
@@ -93,6 +94,7 @@ set(LIBSYCL_SOURCES
     "queue.cpp"
     "usm_functions.cpp"
     "detail/context_impl.cpp"
+    "detail/event_impl.cpp"
     "detail/device_impl.cpp"
     "detail/global_objects.cpp"
     "detail/platform_impl.cpp"
diff --git a/libsycl/src/detail/event_impl.cpp b/libsycl/src/detail/event_impl.cpp
new file mode 100644
index 0000000000000..895f8029d4c35
--- /dev/null
+++ b/libsycl/src/detail/event_impl.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <detail/event_impl.hpp>
+#include <detail/platform_impl.hpp>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+namespace detail {
+
+EventImpl::~EventImpl() {
+  if (MOffloadEvent)
+    std::ignore = olDestroyEvent(MOffloadEvent);
+}
+
+backend EventImpl::getBackend() const noexcept {
+  // TODO: to handle default cosntructed
+  //  The event is constructed as though it were created from a
+  //  default-constructed queue. Therefore, its backend is the same as the
+  //  backend of the device selected by default_selector_v.
+  return MPlatform.getBackend();
+}
+
+void EventImpl::wait() {
+  // MOffloadEvent == nullptr when event is default constructed. Default
+  // constructed event is immediately  ready.
+  if (!MOffloadEvent)
+    return;
+
+  callAndThrow(olSyncEvent, MOffloadEvent);
+}
+
+} // namespace detail
+_LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/detail/event_impl.hpp b/libsycl/src/detail/event_impl.hpp
new file mode 100644
index 0000000000000..f570538512def
--- /dev/null
+++ b/libsycl/src/detail/event_impl.hpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL_EVENT_IMPL
+#define _LIBSYCL_EVENT_IMPL
+
+#include <sycl/__impl/detail/config.hpp>
+#include <sycl/__impl/queue.hpp>
+
+#include <OffloadAPI.h>
+
+#include <memory>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+namespace detail {
+
+class PlatformImpl;
+
+class EventImpl {
+  // Helper to limit EventImpl creation.
+  struct PrivateTag {
+    explicit PrivateTag() = default;
+  };
+
+public:
+  /// Constructs a SYCL event  instance using the provided
+  /// offload event instance.
+  ///
+  /// \param Event is a raw offload library handle representing event.
+  /// \param Platform is a platform this event belongs to.
+  EventImpl(ol_event_handle_t Event, PlatformImpl &Platform, PrivateTag)
+      : MOffloadEvent(Event), MPlatform(Platform) {}
+
+  static std::shared_ptr<EventImpl>
+  createEventWithHandle(ol_event_handle_t Event, PlatformImpl &Queue) {
+    return std::make_shared<EventImpl>(Event, Queue, PrivateTag{});
+  }
+
+  /// Releases handle to the corresponding liboffload event.
+  ~EventImpl();
+
+  /// \return the sycl::backend associated with this event.
+  backend getBackend() const noexcept;
+
+  /// Waits for completion of the corresponding kernel and its dependencies.
+  void wait();
+
+  /// \return liboffload handle that this SYCL event represents.
+  ol_event_handle_t getHandle() { return MOffloadEvent; }
+
+  /// \return a platform implementation object this event belongs to.
+  const PlatformImpl &getPlatformImpl() const { return MPlatform; }
+
+private:
+  ol_event_handle_t MOffloadEvent{};
+  PlatformImpl &MPlatform;
+};
+
+} // namespace detail
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL_EVENT_IMPL
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index 9c93fe02de8a6..74ccc48877c09 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -9,6 +9,8 @@
 #include <detail/queue_impl.hpp>
 
 #include <detail/device_impl.hpp>
+#include <detail/event_impl.hpp>
+#include <detail/program_manager.hpp>
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
@@ -18,9 +20,19 @@ QueueImpl::QueueImpl(DeviceImpl &deviceImpl, const async_handler &asyncHandler,
                      const property_list &propList, PrivateTag)
     : MIsInorder(false), MAsyncHandler(asyncHandler), MPropList(propList),
       MDevice(deviceImpl),
-      MContext(MDevice.getPlatformImpl().getDefaultContext()) {}
+      MContext(MDevice.getPlatformImpl().getDefaultContext()) {
+  callAndThrow(olCreateQueue, MDevice.getHandle(), &MOffloadQueue);
+}
+
+QueueImpl::~QueueImpl() {
+  // TODO: consider where to report errors
+  if (MOffloadQueue)
+    std::ignore = olDestroyQueue(MOffloadQueue);
+}
 
 backend QueueImpl::getBackend() const noexcept { return MDevice.getBackend(); }
 
+void QueueImpl::wait() { callAndThrow(olSyncQueue, MOffloadQueue); }
+
 } // namespace detail
 _LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/detail/queue_impl.hpp b/libsycl/src/detail/queue_impl.hpp
index 6403099a19060..cdb7595e852ec 100644
--- a/libsycl/src/detail/queue_impl.hpp
+++ b/libsycl/src/detail/queue_impl.hpp
@@ -21,6 +21,9 @@ namespace detail {
 
 class ContextImpl;
 class DeviceImpl;
+class EventImpl;
+
+using EventImplPtr = std::shared_ptr<EventImpl>;
 
 class QueueImpl : public std::enable_shared_from_this<QueueImpl> {
   struct PrivateTag {
@@ -28,7 +31,7 @@ class QueueImpl : public std::enable_shared_from_this<QueueImpl> {
   };
 
 public:
-  ~QueueImpl() = default;
+  ~QueueImpl();
 
   /// Constructs a SYCL queue from a device using an asyncHandler and
   /// a propList.
@@ -59,7 +62,11 @@ class QueueImpl : public std::enable_shared_from_this<QueueImpl> {
   /// \return true if and only if the queue is in order.
   bool isInOrder() const { return MIsInorder; }
 
+  /// Waits for completion of all kernels submitted to this queue.
+  void wait();
+
 private:
+  ol_queue_handle_t MOffloadQueue = {};
   const bool MIsInorder;
   const async_handler MAsyncHandler;
   const property_list MPropList;
diff --git a/libsycl/src/event.cpp b/libsycl/src/event.cpp
new file mode 100644
index 0000000000000..68046211272f2
--- /dev/null
+++ b/libsycl/src/event.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <sycl/__impl/event.hpp>
+
+#include <detail/event_impl.hpp>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+backend event::get_backend() const noexcept { return impl->getBackend(); }
+
+void event::wait(const std::vector<event> &EventList) {
+  for (auto Event : EventList) {
+    Event.wait();
+  }
+}
+
+void event::wait() { return impl->wait(); }
+
+_LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/queue.cpp b/libsycl/src/queue.cpp
index faed274674447..9fe020eabf2cc 100644
--- a/libsycl/src/queue.cpp
+++ b/libsycl/src/queue.cpp
@@ -33,4 +33,6 @@ device queue::get_device() const {
 
 bool queue::is_in_order() const { return impl->isInOrder(); }
 
+void queue::wait() { return impl->wait(); }
+
 _LIBSYCL_END_NAMESPACE_SYCL

>From 1394cc9550ce4b875b84b3e622cc4fb84fef8155 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Wed, 25 Mar 2026 04:56:49 -0700
Subject: [PATCH 05/25] [libsycl] Add kernel creation

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/src/detail/program_manager.cpp | 100 ++++++++++++++++++++++++-
 libsycl/src/detail/program_manager.hpp |  87 ++++++++++++++++++++-
 2 files changed, 184 insertions(+), 3 deletions(-)

diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
index f9b158a6e1918..7d6523daeb6ee 100644
--- a/libsycl/src/detail/program_manager.cpp
+++ b/libsycl/src/detail/program_manager.cpp
@@ -18,6 +18,20 @@
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
 
+ProgramWrapper::ProgramWrapper(ol_device_handle_t Device,
+                               DeviceImageWrapper &DevImage) {
+  assert(Device);
+
+  callAndThrow(olCreateProgram, Device, DevImage.getRawData().ImageStart,
+               DevImage.getSize(), &MProgram);
+}
+
+ProgramWrapper::~ProgramWrapper() {
+  assert(MProgram);
+  std::ignore = olDestroyProgram(MProgram);
+  // TODO: define a way to report errors from dtors.
+}
+
 static inline bool checkFatBinVersion(const __sycl_tgt_bin_desc &FatbinDesc) {
   return FatbinDesc.Version == SupportedOffloadBinaryVersion;
 }
@@ -81,7 +95,7 @@ void ProgramManager::removeImages(__sycl_tgt_bin_desc *FatbinDesc) {
   if (!checkFatBinVersion(*FatbinDesc) || FatbinDesc->NumDeviceBinaries == 0)
     return;
 
-  std::lock_guard<std::mutex> Guard(MImageCollectionMutex);
+  std::scoped_lock Guard{MImageCollectionMutex, MKernelCollectionsMutex};
   for (int I = 0; I < FatbinDesc->NumDeviceBinaries; ++I) {
     const auto &RawDeviceImage = FatbinDesc->DeviceImages[I];
 
@@ -99,10 +113,18 @@ void ProgramManager::removeImages(__sycl_tgt_bin_desc *FatbinDesc) {
       if (auto KernelIDIt = MKernelNameToID.find(EntriesIt->SymbolName);
           KernelIDIt != MKernelNameToID.end()) {
         MKernelIDToDevImageJIT.erase(KernelIDIt->second);
+        MKernels.erase(KernelIDIt->second);
         MKernelNameToID.erase(KernelIDIt);
       }
     }
 
+    if (auto ProgramIt = MPrograms.find(DevImageIt->second.get());
+        ProgramIt != MPrograms.end()) {
+      for (auto &[Device, Program] : ProgramIt->second) {
+        MProgramWrappers.erase(Program);
+        MPrograms.erase(ProgramIt);
+      }
+    }
     MDeviceImageWrappers.erase(DevImageIt);
   }
 }
@@ -119,7 +141,6 @@ static bool isImageTargetCompatible(const DeviceImageWrapper &Image,
 DeviceImageWrapper *ProgramManager::getDeviceImage(std::string_view KernelName,
                                                    const kernel_id &KernelID,
                                                    DeviceImpl &Device) {
-  std::lock_guard<std::mutex> Guard(MImageCollectionMutex);
   auto [Begin, End] = MKernelIDToDevImageJIT.equal_range(KernelID);
   if (Begin != End) {
     bool IsValid{};
@@ -140,6 +161,81 @@ DeviceImageWrapper *ProgramManager::getDeviceImage(std::string_view KernelName,
                   "No kernel named " + std::string(KernelName) + " was found");
 }
 
+ol_symbol_handle_t ProgramManager::getOrCreateKernel(const char *KernelName,
+                                                     DeviceImpl &Device) {
+  std::lock_guard<std::mutex> ImageGuard(MImageCollectionMutex);
+
+  auto KernelIDIt = MKernelNameToID.find(KernelName);
+  if (KernelIDIt == MKernelNameToID.end())
+    throw exception(make_error_code(errc::runtime),
+                    "No kernel named " + std::string(KernelName) +
+                        " was found");
+
+  std::lock_guard<std::mutex> KernelGuard(MKernelCollectionsMutex);
+
+  auto Kernel = getKernel(KernelIDIt->second, Device);
+  if (Kernel)
+    return Kernel;
+
+  DeviceImageWrapper *DevImage =
+      getDeviceImage(KernelName, KernelIDIt->second, Device);
+  if (!DevImage)
+    throw;
+
+  ol_program_handle_t Program = getOrCreateProgram(Device, DevImage);
+  assert(Program);
+  Kernel = createKernel(Program, KernelIDIt->second, KernelName, Device);
+  assert(Kernel);
+  return Kernel;
+}
+
+ol_program_handle_t
+ProgramManager::getOrCreateProgram(DeviceImpl &Device,
+                                   DeviceImageWrapper *DevImage) {
+  if (auto DevToProgramIt = MPrograms.find(DevImage);
+      DevToProgramIt != MPrograms.end()) {
+    auto ProgramIt = DevToProgramIt->second.find(Device.getHandle());
+    if (ProgramIt != DevToProgramIt->second.end())
+      return ProgramIt->second;
+  }
+
+  std::unique_ptr<ProgramWrapper> NewProgramWrapper(
+      new ProgramWrapper(Device.getHandle(), *DevImage));
+  auto Program = NewProgramWrapper->getHandle();
+  {
+    MPrograms[DevImage].insert(std::make_pair(Device.getHandle(), Program));
+    MProgramWrappers.insert(std::make_pair(NewProgramWrapper->getHandle(),
+                                           std::move(NewProgramWrapper)));
+  }
+
+  return Program;
+}
+
+ol_symbol_handle_t ProgramManager::createKernel(ol_program_handle_t Program,
+                                                const kernel_id &KernelID,
+                                                const char *KernelName,
+                                                DeviceImpl &Device) {
+  ol_symbol_handle_t Kernel{};
+  callAndThrow(olGetSymbol, Program, KernelName, OL_SYMBOL_KIND_KERNEL,
+               &Kernel);
+  MKernels.insert(
+      std::make_pair(KernelID, std::make_pair(Device.getHandle(), Kernel)));
+  return Kernel;
+}
+
+ol_symbol_handle_t ProgramManager::getKernel(const kernel_id &KernelID,
+                                             DeviceImpl &Device) {
+  auto Range = MKernels.equal_range(KernelID);
+  for (auto Kernels = Range.first; Kernels != Range.second; ++Kernels) {
+    auto &[KernelDevice, KernelSymbol] = Kernels->second;
+    if (KernelDevice == Device.getHandle()) {
+      assert(KernelSymbol && "Built kernel symbol can't be null");
+      return KernelSymbol;
+    }
+  }
+  return nullptr;
+}
+
 } // namespace detail
 _LIBSYCL_END_NAMESPACE_SYCL
 
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
index 7d66602151d64..b017383a16b4c 100644
--- a/libsycl/src/detail/program_manager.hpp
+++ b/libsycl/src/detail/program_manager.hpp
@@ -15,6 +15,8 @@
 #include <detail/device_image_wrapper.hpp>
 #include <detail/kernel_id.hpp>
 
+#include <OffloadAPI.h>
+
 #include <mutex>
 #include <unordered_map>
 
@@ -39,6 +41,30 @@ namespace detail {
 
 class DeviceImpl;
 
+/// A wrapper of liboffload program handle to manage its lifetime.
+class ProgramWrapper {
+public:
+  /// Constructs ProgramWrapper by creating liboffload program with the provided
+  /// arguments.
+  ///
+  /// \param Device is a device to use for program creation.
+  /// \param DevImage is a device image (wrapped __sycl_tgt_device_image) to use
+  /// for program creation.
+  /// \throw sycl::exception with sycl::errc::runtime when failed to create
+  /// program.
+  ProgramWrapper(ol_device_handle_t Device, DeviceImageWrapper &DevImage);
+
+  /// Releases the corresponding liboffload program handle by calling
+  /// olDestroyProgram.
+  ~ProgramWrapper();
+
+  /// \return the corresponding liboffload program handle.
+  ol_program_handle_t getHandle() { return MProgram; }
+
+private:
+  ol_program_handle_t MProgram{};
+};
+
 /// A class to manage programs and kernels.
 class ProgramManager {
 
@@ -63,6 +89,16 @@ class ProgramManager {
   /// data passed to addImages.
   void removeImages(__sycl_tgt_bin_desc *FatbinDesc);
 
+  /// Creates liboffload kernel that is ready for execution.
+  /// Thread-safe.
+  /// \param KernelName a null-terminated string representing a name of kernel
+  /// to be created.
+  /// \param Device a device for which this kernel must be compiled.
+  /// \return liboffload kernel handle that is ready to be passed to kernel
+  /// execution methods.
+  ol_symbol_handle_t getOrCreateKernel(const char *KernelName,
+                                       DeviceImpl &Device);
+
 private:
   ProgramManager() = default;
   ~ProgramManager() = default;
@@ -74,13 +110,43 @@ class ProgramManager {
   /// \param KernelName a null-terminated string representing the name of the
   /// kernel to obtain a device image for.
   /// \param KernelID a kernel id matching KernelName.
-  /// \param DeviceImpl a device with which device image must be compatible.
+  /// \param Device a device with which device image must be compatible.
   /// \throw sycl::exception with sycl::errc::runtime if the device image
   /// validation failed in liboffload or if no compatible image was found.
   DeviceImageWrapper *getDeviceImage(std::string_view KernelName,
                                      const kernel_id &KernelID,
                                      DeviceImpl &Device);
 
+  /// Searches for or creates a program.
+  /// This call must be protected with mutex since it updates MPrograms and
+  /// MProgramWrappers collections.
+  /// \param Device a device that program must be created with.
+  /// \param DevImage a device image to get or create program with.
+  /// \return liboffload program for the requested configuration.
+  ol_program_handle_t getOrCreateProgram(DeviceImpl &Device,
+                                         DeviceImageWrapper *DevImage);
+
+  /// Creates kernel from program.
+  /// This call must be protected with mutex since it updates MKernels
+  /// collection.
+  /// \param Program a program to create kernel with.
+  /// \param KernelID an id of kernel to create.
+  /// \param KernelName a null-terminated string representing the name of kernel
+  /// to create.
+  /// \param Device a device that kernel must be created with.
+  /// \return liboffload kernel for the requested configuration.
+  ol_symbol_handle_t createKernel(ol_program_handle_t Program,
+                                  const kernel_id &KernelID,
+                                  const char *KernelName, DeviceImpl &Device);
+
+  /// Searches for kernel.
+  /// This call must be protected with mutex since it reads MKernels collection.
+  /// \param KernelID an id of kernel to look for.
+  /// \param Device a device that kernel must be created with.
+  /// \return liboffload kernel for the requested configuration or nullptr if
+  /// such kernel is not found.
+  ol_symbol_handle_t getKernel(const kernel_id &KernelID, DeviceImpl &Device);
+
   // Filled by addImages(...).
   std::unordered_map<std::string_view, kernel_id> MKernelNameToID;
   std::unordered_map<kernel_id, DeviceImageWrapper *> MKernelIDToDevImageJIT;
@@ -88,7 +154,26 @@ class ProgramManager {
   std::unordered_map<const __sycl_tgt_device_image *,
                      std::unique_ptr<DeviceImageWrapper>>
       MDeviceImageWrappers;
+  // All data collections, created from data in __sycl_register_lib, must be
+  // protected with this mutex. Protects data that can be modified only by
+  // modules load/unload.
   std::mutex MImageCollectionMutex;
+
+  // Filled by getOrCreateKernel and everything it calls inside.
+  std::unordered_map<
+      DeviceImageWrapper *,
+      std::unordered_map<ol_device_handle_t, ol_program_handle_t>>
+      MPrograms;
+  std::unordered_multimap<kernel_id,
+                          std::pair<ol_device_handle_t, ol_symbol_handle_t>>
+      MKernels;
+
+  // Controls lifetime of programs.
+  std::unordered_map<ol_program_handle_t, std::unique_ptr<ProgramWrapper>>
+      MProgramWrappers;
+  // All data collections, used and modified by kernel submissions, must be
+  // protected with this mutex.
+  std::mutex MKernelCollectionsMutex;
 };
 
 } // namespace detail

>From 03a1c675484bf83746ac9cb9b9580e2f3bed238f Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Wed, 25 Mar 2026 05:18:49 -0700
Subject: [PATCH 06/25] [libsycl] add single_task

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>

addition to single task

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/docs/index.rst                        |   4 +
 .../sycl/__impl/detail/arg_wrapper.hpp        | 135 ++++++++++++++++++
 .../sycl/__impl/detail/unified_range_view.hpp |  52 +++++++
 libsycl/include/sycl/__impl/queue.hpp         |  96 +++++++++++++
 libsycl/src/detail/queue_impl.cpp             | 112 +++++++++++++++
 libsycl/src/detail/queue_impl.hpp             |  37 +++++
 libsycl/src/queue.cpp                         |  19 +++
 libsycl/test/basic/get_backend.cpp            |  54 +++++++
 libsycl/test/basic/submit_fn_ptr.cpp          |  18 +++
 9 files changed, 527 insertions(+)
 create mode 100644 libsycl/include/sycl/__impl/detail/arg_wrapper.hpp
 create mode 100644 libsycl/include/sycl/__impl/detail/unified_range_view.hpp
 create mode 100644 libsycl/test/basic/get_backend.cpp
 create mode 100644 libsycl/test/basic/submit_fn_ptr.cpp

diff --git a/libsycl/docs/index.rst b/libsycl/docs/index.rst
index 9aa36b4a54c57..5961eeeedcedb 100644
--- a/libsycl/docs/index.rst
+++ b/libsycl/docs/index.rst
@@ -113,6 +113,10 @@ TODO for added SYCL classes
   * to implement submit & copy with accessors (low priority)
   * get_info & properties
   * ctors that accepts context (blocked by lack of liboffload support)
+  * nd_range kernel submissions
+  * cross-context events wait (host tasks are needed)
+  * implement check if lambda arguments are device copyable (requires clang support of corresponding builtins)
+  * kernel instantiating on host (debugging purposes)
 
 * ``property_list``: to fully implement and integrate with existing SYCL runtime classes supporting it
 * usm allocations:
diff --git a/libsycl/include/sycl/__impl/detail/arg_wrapper.hpp b/libsycl/include/sycl/__impl/detail/arg_wrapper.hpp
new file mode 100644
index 0000000000000..96f60a3121787
--- /dev/null
+++ b/libsycl/include/sycl/__impl/detail/arg_wrapper.hpp
@@ -0,0 +1,135 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains helper functions used to wrap kernel arguments to
+/// typeless collection.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL___IMPL_DETAIL_ARG_WRAPPER_HPP
+#define _LIBSYCL___IMPL_DETAIL_ARG_WRAPPER_HPP
+
+#include <sycl/__impl/detail/config.hpp>
+#include <sycl/__impl/exception.hpp>
+
+#include <cassert>
+#include <memory>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+namespace detail {
+
+/// Base class is needed for unification, we pass arguments through ABI
+/// boundary.
+class ArgWrapperBase {
+public:
+  ArgWrapperBase(const ArgWrapperBase &) = delete;
+  ArgWrapperBase &operator=(const ArgWrapperBase &) = delete;
+  virtual ~ArgWrapperBase() = default;
+
+  virtual void deepCopy() = 0;
+  virtual size_t getSize() const = 0;
+  virtual const void *getPtr() const = 0;
+
+protected:
+  ArgWrapperBase() = default;
+};
+
+/// Helps to manage arguments in a typeless way.
+template <typename Type> class ArgWrapper : public ArgWrapperBase {
+public:
+  ArgWrapper(Type &Arg) { Ptr = &Arg; }
+  ArgWrapper(const ArgWrapper &) = delete;
+  ArgWrapper &operator=(const ArgWrapper &) = delete;
+
+  /// \return size of argument in bytes.
+  size_t getSize() const override { return sizeof(Type); }
+
+  /// Returns raw pointer to the corresponding argument.
+  /// No copy is done by this method. It works with pointer to the memory whose
+  /// existence must be guaranteed by class user or with copy that must be
+  /// explicitly requested by class user via deepCopy method.
+  /// \return pointer to the argument.
+  const void *getPtr() const override {
+    assert((!DeepCopy || (DeepCopy.get()) == Ptr) &&
+           "Incorrect state of copied argument");
+    return Ptr;
+  }
+
+  /// Copies agrument to RT owned storage.
+  void deepCopy() override {
+    if (DeepCopy)
+      return;
+
+    DeepCopy.reset(new Type(*Ptr));
+    Ptr = DeepCopy.get();
+  }
+
+private:
+  Type *Ptr;
+  std::unique_ptr<Type> DeepCopy;
+};
+
+/// Collection of arguments. Provides functionality to accumulate all arguments
+/// data to pass through ABI boundary.
+class ArgCollection {
+public:
+  /// Adds argument to the collection. Don't own the memory. Argument lifetime
+  /// must be guaranteed by class user. If extended lifetime is needed (copy),
+  /// deepCopy must be called.
+  template <typename Type> void addArg(Type &Arg) {
+    MArgs.emplace_back(new ArgWrapper(Arg));
+  }
+
+  /// \return array of argument pointers.
+  const void **getArgPtrArray() {
+    if (MPtrs.size() != MArgs.size()) {
+      MPtrs.clear();
+      MPtrs.reserve(MArgs.size());
+      auto it = MArgs.cbegin();
+      while (it != MArgs.cend()) {
+        MPtrs.push_back((*it++)->getPtr());
+      }
+    }
+    return MPtrs.data();
+  }
+
+  /// \return array of argument sizes.
+  int64_t *getSizesArray() {
+    if (MSizes.size() != MArgs.size()) {
+      MSizes.clear();
+      MSizes.reserve(MArgs.size());
+      auto it = MArgs.cbegin();
+      while (it != MArgs.cend()) {
+        MSizes.push_back(static_cast<int64_t>((*it++)->getSize()));
+      }
+    }
+    return MSizes.data();
+  }
+
+  /// \return count of arguments in collection.
+  size_t getArgCount() { return MArgs.size(); }
+
+  /// Extends arguments lifetime by doing copy of all arguments.
+  void deepCopy() {
+    for (auto &Arg : MArgs)
+      Arg->deepCopy();
+  }
+
+private:
+  std::vector<std::unique_ptr<ArgWrapperBase>> MArgs;
+  std::vector<int64_t> MSizes;
+  std::vector<const void *> MPtrs;
+};
+
+} // namespace detail
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL___IMPL_DETAIL_ARG_WRAPPER_HPP
diff --git a/libsycl/include/sycl/__impl/detail/unified_range_view.hpp b/libsycl/include/sycl/__impl/detail/unified_range_view.hpp
new file mode 100644
index 0000000000000..afa613fc8627b
--- /dev/null
+++ b/libsycl/include/sycl/__impl/detail/unified_range_view.hpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains helper function class to unify ABI for different kernel
+/// ranges.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL___IMPL_DETAIL_UNIFIED_RANGE_VIEW_HPP
+#define _LIBSYCL___IMPL_DETAIL_UNIFIED_RANGE_VIEW_HPP
+
+#include <sycl/__impl/detail/config.hpp>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+namespace detail {
+
+/// The structure to keep dimension and references to ranges unified for
+/// all dimensions.
+class UnifiedRangeView {
+
+public:
+  /// Default contructed view matches single task execution range.
+  UnifiedRangeView() = default;
+  UnifiedRangeView(const UnifiedRangeView &Desc) = default;
+  UnifiedRangeView(UnifiedRangeView &&Desc) = default;
+  UnifiedRangeView &operator=(const UnifiedRangeView &Desc) = default;
+  UnifiedRangeView &operator=(UnifiedRangeView &&Desc) = default;
+
+  // TODO: ctors with sycl::range and nd::range will be added later.
+
+  UnifiedRangeView(const size_t *GlobalSize, const size_t *LocalSize,
+                   const size_t *Offset, size_t Dims)
+      : MGlobalSize(GlobalSize), MLocalSize(LocalSize), MOffset(Offset),
+        MDims(Dims) {}
+
+  const size_t *MGlobalSize = nullptr;
+  const size_t *MLocalSize = nullptr;
+  const size_t *MOffset = nullptr;
+  size_t MDims = 1;
+};
+} // namespace detail
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL___IMPL_DETAIL_UNIFIED_RANGE_VIEW_HPP
diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index 587f56a8eb245..d1ac320433c38 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -20,9 +20,11 @@
 #include <sycl/__impl/event.hpp>
 #include <sycl/__impl/property_list.hpp>
 
+#include <sycl/__impl/detail/arg_wrapper.hpp>
 #include <sycl/__impl/detail/config.hpp>
 #include <sycl/__impl/detail/default_async_handler.hpp>
 #include <sycl/__impl/detail/obj_utils.hpp>
+#include <sycl/__impl/detail/unified_range_view.hpp>
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
@@ -31,6 +33,27 @@ class context;
 namespace detail {
 class QueueImpl;
 
+template <typename, typename T> struct CheckFunctionSignature {
+  static_assert(std::integral_constant<T, false>::value,
+                "Second template parameter is required to be of function type");
+};
+
+template <typename F, typename RetT, typename... Args>
+struct CheckFunctionSignature<F, RetT(Args...)> {
+private:
+  template <typename T>
+  static constexpr auto check(T *) -> typename std::is_same<
+      decltype(std::declval<T>().operator()(std::declval<Args>()...)),
+      RetT>::type;
+
+  template <typename> static constexpr std::false_type check(...);
+
+  using type = decltype(check<F>(0));
+
+public:
+  static constexpr bool value = type::value;
+};
+
 } // namespace detail
 
 // SYCL 2020 4.6.5. Queue class.
@@ -138,12 +161,85 @@ class _LIBSYCL_EXPORT queue {
   template <typename Param>
   typename Param::return_type get_backend_info() const;
 
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type.
+  ///
+  /// \param kernelFunc is the kernel functor or lambda.
+  /// \return an event that represents the status of the submitted kernel.
+  template <typename KernelName, typename KernelType>
+  event single_task(const KernelType &kernelFunc) {
+    return single_task<KernelName, KernelType>({}, kernelFunc);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type.
+  ///
+  /// \param depEvent is an event that specifies the kernel dependency.
+  /// \param kernelFunc is the kernel functor or lambda.
+  /// \return an event that represents the status of the submitted kernel.
+  template <typename KernelName, typename KernelType>
+  event single_task(event depEvent, const KernelType &kernelFunc) {
+    return single_task<KernelName, KernelType>({depEvent}, kernelFunc);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type.
+  ///
+  /// \param depEvents is a collection of events which specify the kernel
+  /// dependencies.
+  /// \param kernelFunc is the kernel functor or lambda.
+  /// \return an event that represents the status of the submitted kernel.
+  template <typename KernelName, typename KernelType>
+  event single_task(const std::vector<event> &depEvents,
+                    const KernelType &kernelFunc) {
+    static_assert(
+        (detail::CheckFunctionSignature<std::remove_reference_t<KernelType>,
+                                        void()>::value),
+        "sycl::queue::single_task() requires a kernel instead of command "
+        "group. ");
+
+    setKernelParameters(depEvents);
+    submitSingleTask<KernelName, KernelType>(kernelFunc);
+    return getLastEvent();
+  }
+
   /// Blocks the calling thread until all commands previously submitted to this
   /// queue have completed. Synchronous errors are reported through SYCL
   /// exceptions.
   void wait();
 
 private:
+  // Name of this function is defined by compiler. It generates call to this
+  // function in the host implementation of KernelFunc in submitSingleTask.
+  template <typename, typename... Args>
+  void sycl_kernel_launch(const char *KernelName, Args &&...args) {
+    static_assert((sizeof...(args) == 1) &&
+                  "Only 2 arguments are expected in sycl_kernel_launch.");
+    detail::ArgCollection TypelessArgs;
+    (TypelessArgs.addArg(args), ...);
+
+    submitKernelImpl(KernelName, TypelessArgs);
+  }
+
+#ifdef SYCL_LANGUAGE_VERSION
+#  define _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)                              \
+    [[clang::sycl_kernel_entry_point(KernelName)]]
+#else
+#  define _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
+#endif // SYCL_LANGUAGE_VERSION
+
+  template <typename KernelName, typename KernelType>
+  _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
+  void submitSingleTask(const KernelType KernelFunc) {
+    KernelFunc();
+  }
+
+  event getLastEvent();
+  void submitKernelImpl(const char *KernelName,
+                        detail::ArgCollection &TypelessArgs);
+  void setKernelParameters(const std::vector<event> &Events,
+                           const detail::UnifiedRangeView &Range = {});
+
   queue(const std::shared_ptr<detail::QueueImpl> &Impl) : impl(Impl) {}
   std::shared_ptr<detail::QueueImpl> impl;
 
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index 74ccc48877c09..243f38612e74c 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -16,6 +16,32 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 namespace detail {
 
+static void setKernelLaunchArgs(const detail::UnifiedRangeView &Range,
+                                ol_kernel_launch_size_args_t &ArgsToSet) {
+  size_t GlobalSize[3] = {1, 1, 1};
+  if (Range.MGlobalSize) {
+    for (uint32_t I = 0; I < Range.MDims; I++) {
+      GlobalSize[I] = Range.MGlobalSize[I];
+    }
+  }
+
+  size_t GroupSize[3] = {1, 1, 1};
+  if (Range.MLocalSize) {
+    for (uint32_t I = 0; I < Range.MDims; I++) {
+      GroupSize[I] = Range.MLocalSize[I];
+    }
+  }
+
+  ArgsToSet.Dimensions = Range.MDims;
+  ArgsToSet.NumGroups.x = GlobalSize[0] / GroupSize[0];
+  ArgsToSet.NumGroups.y = GlobalSize[1] / GroupSize[1];
+  ArgsToSet.NumGroups.z = GlobalSize[2] / GroupSize[2];
+  ArgsToSet.GroupSize.x = GroupSize[0];
+  ArgsToSet.GroupSize.y = GroupSize[1];
+  ArgsToSet.GroupSize.z = GroupSize[2];
+  ArgsToSet.DynSharedMemory = 0;
+}
+
 QueueImpl::QueueImpl(DeviceImpl &deviceImpl, const async_handler &asyncHandler,
                      const property_list &propList, PrivateTag)
     : MIsInorder(false), MAsyncHandler(asyncHandler), MPropList(propList),
@@ -34,5 +60,91 @@ backend QueueImpl::getBackend() const noexcept { return MDevice.getBackend(); }
 
 void QueueImpl::wait() { callAndThrow(olSyncQueue, MOffloadQueue); }
 
+static bool checkEventsPlatformMatch(std::vector<EventImplPtr> &Events,
+                                     const PlatformImpl &QueuePlatform) {
+  // 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
+  //   same platform as `Queue`."
+  return std::all_of(Events.cbegin(), Events.cend(),
+                     [&QueuePlatform](const EventImplPtr &Event) {
+                       return &Event->getPlatformImpl() == &QueuePlatform;
+                     });
+}
+
+void QueueImpl::setKernelParameters(std::vector<EventImplPtr> &&Events,
+                                    const detail::UnifiedRangeView &Range) {
+  if (!checkEventsPlatformMatch(Events, MDevice.getPlatformImpl()))
+    throw sycl::exception(
+        sycl::make_error_code(sycl::errc::feature_not_supported),
+        "libsycl doesn't support cross-context/platform event dependencies "
+        "now.");
+
+  // TODO: this convertion and storing only offload events is possible only
+  // while we don't have host tasks (and features based on host tasks, like
+  // streams). With them - it is very likely we should copy EventImplPtr
+  // (shared_ptr) and keep it here. Although it may differ if host tasks will be
+  // implemented on offload level (no data now).
+  assert(MCurrentSubmitInfo.DepEvents.empty() &&
+         "Kernel submission must clean up dependencies.");
+  MCurrentSubmitInfo.DepEvents.reserve(Events.size());
+  for (auto &Event : Events) {
+    assert(Event && "Event impl object can't be nullptr");
+    MCurrentSubmitInfo.DepEvents.push_back(Event->getHandle());
+  }
+  setKernelLaunchArgs(Range, MCurrentSubmitInfo.Range);
+}
+
+void QueueImpl::submitKernelImpl(const char *KernelName,
+                                 detail::ArgCollection &TypelessArgs) {
+  ol_symbol_handle_t Kernel =
+      detail::ProgramManager::getInstance().getOrCreateKernel(KernelName,
+                                                              MDevice);
+  assert(Kernel);
+
+  ol_event_handle_t NewEvent{};
+  if (!MCurrentSubmitInfo.DepEvents.empty()) {
+    callAndThrow(olWaitEvents, MOffloadQueue,
+                 MCurrentSubmitInfo.DepEvents.data(),
+                 MCurrentSubmitInfo.DepEvents.size());
+  }
+
+  const void *Arguments = nullptr;
+  int64_t ArgumentsSize = 0;
+  if (TypelessArgs.getArgCount()) {
+    // without decomposition and free functions extension we always expect 1
+    // argument to the kernel - lambda capture.
+    assert(TypelessArgs.getArgCount() == 1 &&
+           "No arg decomposition or extensions are supported now.");
+    // TODO: liboffload doesn't support more than 1 argument without copy now.
+    // It doesn't expect array of arguments, it requires a contiguous memory
+    // with args. While we have only 1 argument we don't need extra handling
+    // here, we just pass the first argument directly.
+    Arguments = TypelessArgs.getArgPtrArray()[0];
+    ArgumentsSize = TypelessArgs.getSizesArray()[0];
+  }
+
+  // ol_kernel_launch_prop_t Props[2];
+  // Props[0].type = OL_KERNEL_LAUNCH_PROP_TYPE_SIZE;
+  // Props[0].data = &ArgumentsSize;
+  // Props[1] = OL_KERNEL_LAUNCH_PROP_END;
+  auto Result =
+      olLaunchKernel(MOffloadQueue, MDevice.getHandle(), Kernel, Arguments,
+                     ArgumentsSize, &MCurrentSubmitInfo.Range /*, Props*/);
+  // Clean up current kernel submit data to prepare structures for next
+  // submission.
+  MCurrentSubmitInfo.DepEvents.clear();
+  MCurrentSubmitInfo.Range = {};
+  if (isFailed(Result))
+    throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
+                          std::string("Kernel submission (") + KernelName +
+                              ") failed with " + formatCodeString(Result));
+
+  callAndThrow(olCreateEvent, MOffloadQueue, &NewEvent);
+
+  MCurrentSubmitInfo.LastEvent =
+      EventImpl::createEventWithHandle(NewEvent, MDevice.getPlatformImpl());
+}
+
 } // namespace detail
 _LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/detail/queue_impl.hpp b/libsycl/src/detail/queue_impl.hpp
index cdb7595e852ec..6edb40471826a 100644
--- a/libsycl/src/detail/queue_impl.hpp
+++ b/libsycl/src/detail/queue_impl.hpp
@@ -15,6 +15,7 @@
 #include <OffloadAPI.h>
 
 #include <memory>
+#include <mutex>
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
@@ -62,16 +63,52 @@ class QueueImpl : public std::enable_shared_from_this<QueueImpl> {
   /// \return true if and only if the queue is in order.
   bool isInOrder() const { return MIsInorder; }
 
+  /// Enqueues kernel to liboffload.
+  /// Kernel parameters like dependencies and range must be passed in advance by
+  /// calling setKernelParameters.
+  /// \param KernelName a name of kernel to be enqueued.
+  /// \param TypelessArgs data about kernel arguments to be used for enqueue.
+  void submitKernelImpl(const char *KernelName,
+                        detail::ArgCollection &TypelessArgs);
+
+  /// \return an event impl object that corresponds to the last kernel
+  /// submission in the calling thread.
+  EventImplPtr getLastEvent() {
+    assert(MCurrentSubmitInfo.LastEvent &&
+           "getLastEvent must be called after enqueue");
+    return MCurrentSubmitInfo.LastEvent;
+  }
+
+  /// Sets kernel parameters to be used in the next submitKernelImpl call.
+  /// Must be called prior to submitKernelImpl call.
+  /// \param Events a collection of events that kernal depends on.
+  /// \param Range a unified range view of execution range.
+  void setKernelParameters(std::vector<EventImplPtr> &&Events,
+                           const detail::UnifiedRangeView &Range);
+
   /// Waits for completion of all kernels submitted to this queue.
   void wait();
 
 private:
+  // Queue features.
   ol_queue_handle_t MOffloadQueue = {};
   const bool MIsInorder;
   const async_handler MAsyncHandler;
   const property_list MPropList;
   DeviceImpl &MDevice;
   ContextImpl &MContext;
+
+  // Submit data.
+  struct KernelSubmitInfo {
+    EventImplPtr LastEvent;
+    ol_kernel_launch_size_args_t Range;
+    // TODO: consider storing EventImplPtr here, it will work with plain handle
+    // only because submission is done within queue::submit call. Otherwise we
+    // need to ensure that event handle is still alive by keeping our own copy
+    // of EventImpl.
+    std::vector<ol_event_handle_t> DepEvents;
+  };
+  inline static thread_local KernelSubmitInfo MCurrentSubmitInfo = {};
 };
 
 } // namespace detail
diff --git a/libsycl/src/queue.cpp b/libsycl/src/queue.cpp
index 9fe020eabf2cc..f9d867e9567d7 100644
--- a/libsycl/src/queue.cpp
+++ b/libsycl/src/queue.cpp
@@ -33,6 +33,25 @@ device queue::get_device() const {
 
 bool queue::is_in_order() const { return impl->isInOrder(); }
 
+event queue::getLastEvent() {
+  return detail::createSyclObjFromImpl<event>(impl->getLastEvent());
+}
+
+void queue::setKernelParameters(const std::vector<event> &Events,
+                                const detail::UnifiedRangeView &Range) {
+  std::vector<detail::EventImplPtr> DepEventImplRefs;
+  DepEventImplRefs.reserve(Events.size());
+  for (const auto &Event : Events) {
+    DepEventImplRefs.push_back(detail::getSyclObjImpl(Event));
+  }
+  return impl->setKernelParameters(std::move(DepEventImplRefs), Range);
+}
+
+void queue::submitKernelImpl(const char *KernelName,
+                             detail::ArgCollection &TypelessArgs) {
+  impl->submitKernelImpl(KernelName, TypelessArgs);
+}
+
 void queue::wait() { return impl->wait(); }
 
 _LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/test/basic/get_backend.cpp b/libsycl/test/basic/get_backend.cpp
new file mode 100644
index 0000000000000..064149a0c67e8
--- /dev/null
+++ b/libsycl/test/basic/get_backend.cpp
@@ -0,0 +1,54 @@
+// REQUIRES: any-device
+// RUN: %clangxx -fsycl %s -o %t.out
+// RUN: %t.out
+
+#include <iostream>
+
+#include <sycl/sycl.hpp>
+
+using namespace sycl;
+
+class Kernel1;
+
+bool check(backend be) {
+  switch (be) {
+  case backend::opencl:
+  case backend::level_zero:
+  case backend::cuda:
+  case backend::hip:
+    return true;
+  default:
+    return false;
+  }
+  return false;
+}
+
+inline void return_fail() {
+  std::cout << "Failed" << std::endl;
+  exit(1);
+}
+
+int main() {
+  for (const auto &plt : platform::get_platforms()) {
+    if (check(plt.get_backend()) == false) {
+      return_fail();
+    }
+
+    auto device = device::get_devices()[0];
+    if (device.get_backend() != plt.get_backend()) {
+      return_fail();
+    }
+
+    queue q(device);
+    if (q.get_backend() != plt.get_backend()) {
+      return_fail();
+    }
+
+    event e = q.single_task<Kernel1>([]() {});
+    if (e.get_backend() != plt.get_backend()) {
+      return_fail();
+    }
+  }
+  std::cout << "Passed" << std::endl;
+  return 0;
+}
diff --git a/libsycl/test/basic/submit_fn_ptr.cpp b/libsycl/test/basic/submit_fn_ptr.cpp
new file mode 100644
index 0000000000000..2a5ce832d4db2
--- /dev/null
+++ b/libsycl/test/basic/submit_fn_ptr.cpp
@@ -0,0 +1,18 @@
+// REQUIRES: any-device
+// RUN: %clangxx -fsycl  %s -o %t.out
+// RUN: %t.out
+
+#include <sycl/sycl.hpp>
+
+class Test;
+
+int main() {
+  sycl::queue q;
+  int *p = sycl::malloc_shared<int>(1, q);
+  *p = 0;
+  q.single_task<Test>([=]() { *p = 42; });
+  q.wait();
+  assert(*p == 42);
+  sycl::free(p, q);
+  return 0;
+}

>From 104ccef02b0d36581c7a60bcf6d7459284e8db64 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Thu, 26 Mar 2026 07:05:33 -0700
Subject: [PATCH 07/25] draft

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>

add tests for parallel_for

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>

remove operators from index space classes

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/docs/index.rst                        |   1 +
 .../sycl/__impl/detail/kernel_arg_helpers.hpp | 187 ++++++++
 .../sycl/__impl/detail/unified_range_view.hpp |   6 +-
 .../sycl/__impl/index_space_classes.hpp       | 413 ++++++++++++++++++
 libsycl/include/sycl/__impl/queue.hpp         | 223 ++++++++--
 libsycl/include/sycl/__spirv/spirv_vars.hpp   |  75 ++++
 .../test/basic/queue_parallel_for_generic.cpp |  47 ++
 libsycl/test/basic/wrapped_usm_pointers.cpp   | 111 +++++
 8 files changed, 1031 insertions(+), 32 deletions(-)
 create mode 100644 libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
 create mode 100644 libsycl/include/sycl/__impl/index_space_classes.hpp
 create mode 100644 libsycl/include/sycl/__spirv/spirv_vars.hpp
 create mode 100644 libsycl/test/basic/queue_parallel_for_generic.cpp
 create mode 100644 libsycl/test/basic/wrapped_usm_pointers.cpp

diff --git a/libsycl/docs/index.rst b/libsycl/docs/index.rst
index 5961eeeedcedb..585d05a78987d 100644
--- a/libsycl/docs/index.rst
+++ b/libsycl/docs/index.rst
@@ -126,6 +126,7 @@ TODO for added SYCL classes
   * handle sub devices once they are implemented (blocked by liboffload support)
 
 * ``event``: get_wait_list, get_info, get_profiling_info, wait_and_throw & default ctor are not implemented
+* ``range``, ``id`` - to add operators
 * general opens:
 
   * define a way to report errors from object dtors.
\ No newline at end of file
diff --git a/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp b/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
new file mode 100644
index 0000000000000..d4a0ea9f63ff2
--- /dev/null
+++ b/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
@@ -0,0 +1,187 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// to add
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL___IMPL_DETAIL_KERNEL_ARG_HELPERS
+#define _LIBSYCL___IMPL_DETAIL_KERNEL_ARG_HELPERS
+
+#include <sycl/__impl/index_space_classes.hpp>
+
+#include <sycl/__impl/detail/config.hpp>
+
+#ifdef __SYCL_DEVICE_ONLY__
+#  include <sycl/__spirv/spirv_vars.hpp>
+#endif
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+namespace detail {
+
+/// \name  Helpers for the unnamed lambda extension.
+/// @{
+/// This class is the default kernel name template parameter type for kernel
+/// invocation APIs such as single_task.
+class AutoName {};
+
+/// Helper struct to get a kernel name type based on given Name and Type
+/// types: if Name is undefined (is a AutoName) then Type becomes
+/// the Name.
+template <typename Name, typename Type> struct get_kernel_name_t {
+  using name = Name;
+};
+
+/// Specialization for the case when Name is undefined.
+/// This is only legal with our compiler with the unnamed lambda extension or if
+/// the kernel is a functor object.
+template <typename Type> struct get_kernel_name_t<detail::AutoName, Type> {
+  using name = Type;
+};
+/// @}
+
+/// \name  Helpers to verify kernel lambda type.
+/// \brief Checks that the function is callable with operator().
+/// @{
+template <typename, typename T> struct CheckFunctionSignature {
+  static_assert(std::integral_constant<T, false>::value,
+                "Second template parameter is required to be of function type");
+};
+
+template <typename F, typename RetT, typename... Args>
+struct CheckFunctionSignature<F, RetT(Args...)> {
+private:
+  template <typename T>
+  static constexpr auto check(T *) -> typename std::is_same<
+      decltype(std::declval<T>().operator()(std::declval<Args>()...)),
+      RetT>::type;
+
+  template <typename> static constexpr std::false_type check(...);
+
+  using type = decltype(check<F>(0));
+
+public:
+  static constexpr bool value = type::value;
+};
+/// @}
+
+/// \name  Helpers to extract types of lambda arguments.
+/// @{
+template <typename RetType, typename Func, typename Arg>
+static Arg member_ptr_helper(RetType (Func::*)(Arg) const);
+
+// Non-const version of the above template to match functors whose
+// 'operator()' is declared w/o the 'const' qualifier.
+template <typename RetType, typename Func, typename Arg>
+static Arg member_ptr_helper(RetType (Func::*)(Arg));
+
+template <typename F, typename SuggestedArgType>
+decltype(member_ptr_helper(&F::operator())) argument_helper(int);
+
+template <typename F, typename SuggestedArgType>
+SuggestedArgType argument_helper(...);
+
+template <typename F, typename SuggestedArgType>
+using lambda_arg_type = decltype(argument_helper<F, SuggestedArgType>(0));
+
+#if __has_builtin(__type_pack_element)
+template <int N, typename... Ts>
+using nth_type_t = __type_pack_element<N, Ts...>;
+#else
+template <int N, typename T, typename... Ts> struct nth_type {
+  using type = typename nth_type<N - 1, Ts...>::type;
+};
+
+template <typename T, typename... Ts> struct nth_type<0, T, Ts...> {
+  using type = T;
+};
+
+template <int N, typename... Ts>
+using nth_type_t = typename nth_type<N, Ts...>::type;
+#endif
+/// @}
+
+template <typename T> T *declptr() { return static_cast<T *>(nullptr); }
+
+template <int N>
+static inline constexpr bool isValidDimensions = (N > 0) && (N < 4);
+
+/// Class provides helper functions for iteration space coordinates in kernel
+/// invocation on device.
+class Builder {
+public:
+  Builder() = delete;
+
+#ifdef __SYCL_DEVICE_ONLY__
+  /// \return a global index of work item currently being operated on by device.
+  template <int Dims> static const id<Dims> getElement(id<Dims> *) {
+    static_assert(isValidDimensions<Dims>, "invalid dimensions");
+    return __spirv::initBuiltInGlobalInvocationId<Dims, id<Dims>>();
+  }
+
+  /// Constructs item with the given data.
+  /// \param Extent a range representing the dimensions of the range of possible
+  /// values of the item.
+  /// \param Index a constituent id representing the work-item’s position in the
+  /// iteration space.
+  /// \param Offset an id representing the n-dimensional offset that should be
+  /// added to the global-ID of each work-item, if this item represents a global
+  /// range. Deprecated in SYCL 2020.
+  template <int Dims, bool WithOffset>
+  static std::enable_if_t<WithOffset, item<Dims, WithOffset>>
+  createItem(const range<Dims> &Extent, const id<Dims> &Index,
+             const id<Dims> &Offset) {
+    return item<Dims, WithOffset>(Extent, Index, Offset);
+  }
+
+  /// Constructs item with the given data.
+  /// \param Extent a range representing the dimensions of the range of possible
+  /// values of the item.
+  /// \param Index a constituent id representing the work-item’s position in the
+  /// iteration space.
+  template <int Dims, bool WithOffset>
+  static std::enable_if_t<!WithOffset, item<Dims, WithOffset>>
+  createItem(const range<Dims> &Extent, const id<Dims> &Index) {
+    return item<Dims, WithOffset>(Extent, Index);
+  }
+
+  /// Creates sycl::item instance for work item that is currently being operated
+  /// on.
+  template <int Dims, bool WithOffset>
+  static std::enable_if_t<WithOffset, const item<Dims, WithOffset>> getItem() {
+    static_assert(isValidDimensions<Dims>, "invalid dimensions");
+    id<Dims> GlobalId{__spirv::initBuiltInGlobalInvocationId<Dims, id<Dims>>()};
+    range<Dims> GlobalSize{__spirv::initBuiltInGlobalSize<Dims, range<Dims>>()};
+    id<Dims> GlobalOffset{__spirv::initBuiltInGlobalOffset<Dims, id<Dims>>()};
+    return createItem<Dims, true>(GlobalSize, GlobalId, GlobalOffset);
+  }
+
+  /// Creates sycl::item instance for work item that is currently being operated
+  /// on.
+  template <int Dims, bool WithOffset>
+  static std::enable_if_t<!WithOffset, const item<Dims, WithOffset>> getItem() {
+    static_assert(isValidDimensions<Dims>, "invalid dimensions");
+    id<Dims> GlobalId{__spirv::initBuiltInGlobalInvocationId<Dims, id<Dims>>()};
+    range<Dims> GlobalSize{__spirv::initBuiltInGlobalSize<Dims, range<Dims>>()};
+    return createItem<Dims, false>(GlobalSize, GlobalId);
+  }
+
+  /// \return a work item currently being operated on by device.
+  template <int Dims, bool WithOffset>
+  static auto getElement(item<Dims, WithOffset> *)
+      -> decltype(getItem<Dims, WithOffset>()) {
+    return getItem<Dims, WithOffset>();
+  }
+
+#endif // __SYCL_DEVICE_ONLY__
+};
+
+} // namespace detail
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL___IMPL_DETAIL_KERNEL_ARG_HELPERS
diff --git a/libsycl/include/sycl/__impl/detail/unified_range_view.hpp b/libsycl/include/sycl/__impl/detail/unified_range_view.hpp
index afa613fc8627b..8f321349d4c2e 100644
--- a/libsycl/include/sycl/__impl/detail/unified_range_view.hpp
+++ b/libsycl/include/sycl/__impl/detail/unified_range_view.hpp
@@ -17,6 +17,8 @@
 
 #include <sycl/__impl/detail/config.hpp>
 
+#include <sycl/__impl/index_space_classes.hpp>
+
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 namespace detail {
@@ -33,7 +35,9 @@ class UnifiedRangeView {
   UnifiedRangeView &operator=(const UnifiedRangeView &Desc) = default;
   UnifiedRangeView &operator=(UnifiedRangeView &&Desc) = default;
 
-  // TODO: ctors with sycl::range and nd::range will be added later.
+  template <int Dims>
+  UnifiedRangeView(sycl::range<Dims> &N)
+      : MGlobalSize(&(N[0])), MDims(size_t(Dims)) {}
 
   UnifiedRangeView(const size_t *GlobalSize, const size_t *LocalSize,
                    const size_t *Offset, size_t Dims)
diff --git a/libsycl/include/sycl/__impl/index_space_classes.hpp b/libsycl/include/sycl/__impl/index_space_classes.hpp
new file mode 100644
index 0000000000000..ef2897cee5307
--- /dev/null
+++ b/libsycl/include/sycl/__impl/index_space_classes.hpp
@@ -0,0 +1,413 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the declaration of the SYCL 2020 ranges and index space
+/// identifiers (4.9.1.).
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL___IMPL_INDEX_SPACE_CLASSES_HPP
+#define _LIBSYCL___IMPL_INDEX_SPACE_CLASSES_HPP
+
+#include <sycl/__impl/detail/config.hpp>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+namespace detail {
+
+class Builder;
+
+/// Helper class for dimensions data management.
+template <int Dimensions = 1> class RawArray {
+  static_assert(Dimensions >= 1 && Dimensions <= 3,
+                "RawArray can only be 1, 2, or 3 Dimensional.");
+
+public:
+  /// Constructs one-dimensional instance and assign corresponding data to Dim0
+  /// value. Available only if Dimensions = 1.
+  template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
+  RawArray(size_t Dim0 = 0) : MArray{Dim0} {}
+
+  /// Constructs two-dimensional instance and assign corresponding data.
+  /// Available only if Dimensions = 2.
+  template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
+  RawArray(size_t Dim0, size_t Dim1) : MArray{Dim0, Dim1} {}
+
+  /// Constructs two-dimensional instance with zero-initialized corresponding
+  /// data. Available only if Dimensions = 2.
+  template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
+  RawArray() : RawArray(0, 0) {}
+
+  /// Constructs three-dimensional instance and assign corresponding data.
+  /// Available only if Dimensions = 3.
+  template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
+  RawArray(size_t Dim0, size_t Dim1, size_t Dim2) : MArray{Dim0, Dim1, Dim2} {}
+
+  /// Constructs three-dimensional instance with zero-initialized corresponding
+  /// data. Available only if Dimensions = 3.
+  template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
+  RawArray() : RawArray(0, 0, 0) {}
+
+  /// Returns value for the specified dimension.
+  /// Results in undefined behavior if dimension is not in the range [0,
+  /// Dimensions).
+  /// \param Dimension a dimension to query data for.
+  /// \return value in array matching requested dimension.
+  std::size_t get(int Dimension) const noexcept { return MArray[Dimension]; }
+
+  /// Returns value for the specified dimension.
+  /// Results in undefined behavior if dimension is not in the range [0,
+  /// Dimensions).
+  /// \param Dimension a dimension to query data for.
+  /// \return value in array matching requested dimension.
+  std::size_t &operator[](int Dimension) noexcept { return MArray[Dimension]; }
+
+  /// Returns value for the specified dimension.
+  /// Results in undefined behavior if dimension is not in the range [0,
+  /// Dimensions).
+  /// \param Dimension a dimension to query data for.
+  /// \return value in array matching requested dimension.
+  std::size_t operator[](int Dimension) const noexcept {
+    return MArray[Dimension];
+  }
+
+  RawArray(const RawArray<Dimensions> &rhs) = default;
+  RawArray(RawArray<Dimensions> &&rhs) = default;
+  RawArray<Dimensions> &operator=(const RawArray<Dimensions> &rhs) = default;
+  RawArray<Dimensions> &operator=(RawArray<Dimensions> &&rhs) = default;
+  ~RawArray() = default;
+
+  friend bool operator==(const RawArray<Dimensions> &lhs,
+                         const RawArray<Dimensions> &rhs) {
+    for (int i = 0; i < Dimensions; ++i) {
+      if (lhs.MArray[i] != rhs.MArray[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  friend bool operator!=(const RawArray<Dimensions> &lhs,
+                         const RawArray<Dimensions> &rhs) {
+    for (int i = 0; i < Dimensions; ++i) {
+      if (lhs.MArray[i] != rhs.MArray[i]) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+protected:
+  size_t MArray[Dimensions];
+};
+} // namespace detail
+
+/// SYCL 2020 4.9.1.1. range class.
+/// range<int Dimensions> is a 1D, 2D or 3D vector that defines the iteration
+/// domain of either a single work-group in a parallel dispatch, or the overall
+/// Dimensions of the dispatch.
+template <int Dimensions = 1>
+class range : public detail::RawArray<Dimensions> {
+  static_assert(Dimensions >= 1 && Dimensions <= 3,
+                "range can only be 1, 2, or 3 Dimensional.");
+  using Base = detail::RawArray<Dimensions>;
+
+public:
+  static constexpr int dimensions = Dimensions;
+  range() noexcept = default;
+  range(const range<Dimensions> &rhs) = default;
+  range(range<Dimensions> &&rhs) = default;
+  range<Dimensions> &operator=(const range<Dimensions> &rhs) = default;
+  range<Dimensions> &operator=(range<Dimensions> &&rhs) = default;
+
+  /// Construct a 1D range with value dim0.
+  ///  Only valid when the template parameter Dimensions is equal to 1.
+  template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
+  range(std::size_t dim0) noexcept : Base(dim0) {}
+
+  /// Construct a 2D range with values dim0 and dim1.
+  /// Only valid when the template parameter Dimensions is equal to 2.
+  template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
+  range(std::size_t dim0, std::size_t dim1) noexcept : Base(dim0, dim1) {}
+
+  /// Construct a 3D range with values dim0, dim1 and dim2.
+  /// Only valid when the template parameter Dimensions is equal to 3.
+  template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
+  range(std::size_t dim0, std::size_t dim1, std::size_t dim2) noexcept
+      : Base(dim0, dim1, dim2) {}
+
+  /*
+  Declared and implemented in detail::RawArray:
+      std::size_t get(int dimension) const noexcept;
+      std::size_t& operator[](int dimension) noexcept;
+      std::size_t operator[](int dimension) const noexcept;
+  */
+
+  /// \return the size of the range computed as dimension0*…​*dimensionN.
+  std::size_t size() const noexcept {
+    std::size_t size = 1;
+    for (int i = 0; i < Dimensions; ++i) {
+      size *= Base::MArray[i];
+    }
+    return size;
+  }
+
+  // TODO: operators to be added
+};
+
+/// c++ deduction guides.
+#ifdef __cpp_deduction_guides
+range(std::size_t) -> range<1>;
+range(std::size_t, std::size_t) -> range<2>;
+range(std::size_t, std::size_t, std::size_t) -> range<3>;
+#endif
+
+template <int Dimensions = 1, bool WithOffset = true> class item;
+
+/// SYCL 2020 4.9.1.3. id class.
+/// id<int Dimensions> is a vector of Dimensions that is used to represent an id
+/// into a global or local range. It can be used as an index in an accessor of
+/// the same rank.
+template <int Dimensions = 1> class id : public detail::RawArray<Dimensions> {
+  static_assert(Dimensions >= 1 && Dimensions <= 3,
+                "id can only be 1, 2, or 3 Dimensional.");
+  using Base = detail::RawArray<Dimensions>;
+
+  // Helper class for conversion operator. Void type is not suitable. User
+  // cannot even try to get address of the operator PrivateTag(). User
+  // may try to get an address of operator void() and will get the
+  // compile-time error
+  class PrivateTag;
+  template <bool Condition, typename T>
+  using EnableIfT = std::conditional_t<Condition, T, PrivateTag>;
+
+public:
+  static constexpr int dimensions = Dimensions;
+
+  id() noexcept = default;
+  id(const id<Dimensions> &rhs) = default;
+  id(id<Dimensions> &&rhs) = default;
+  id<Dimensions> &operator=(const id<Dimensions> &rhs) = default;
+  id<Dimensions> &operator=(id<Dimensions> &&rhs) = default;
+
+  /// Construct a 1D id with value dim0.
+  /// Only valid when the template parameter Dimensions is equal to 1.
+  template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
+  id(std::size_t dim0) noexcept : Base(dim0) {}
+
+  /// Construct a 2D id with values dim0, dim1.
+  /// Only valid when the template parameter Dimensions is equal to 2.
+  template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
+  id(std::size_t dim0, std::size_t dim1) noexcept : Base(dim0, dim1) {}
+
+  /// Construct a 3D id with values dim0, dim1, dim2.
+  /// Only valid when the template parameter Dimensions is equal to 3.
+  template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
+  id(std::size_t dim0, std::size_t dim1, std::size_t dim2) noexcept
+      : Base(dim0, dim1, dim2) {}
+
+  /// Construct an id from the dimensions of range.
+  /// Only valid when the template parameter Dimensions is equal to 1.
+  template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
+  id(const range<Dimensions> &range) noexcept : Base(range.get(0)) {}
+
+  /// Construct an id from the dimensions of range.
+  /// Only valid when the template parameter Dimensions is equal to 2.
+  template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
+  id(const range<Dimensions> &range) noexcept
+      : Base(range.get(0), range.get(1)) {}
+
+  /// Construct an id from the dimensions of range.
+  /// Only valid when the template parameter Dimensions is equal to 3.
+  template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
+  id(const range<Dimensions> &range) noexcept
+      : Base(range.get(0), range.get(1), range.get(2)) {}
+
+  /// Construct an id from item.get_id().
+  /// Only valid when the template parameter Dimensions is equal to 1.
+  template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
+  id(const item<Dimensions> &item) noexcept : Base(item.get_id(0)) {}
+
+  /// Construct an id from item.get_id().
+  /// Only valid when the template parameter Dimensions is equal to 2.
+  template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
+  id(const item<Dimensions> &item) noexcept
+      : Base(item.get_id(0), item.get_id(1)) {}
+
+  /// Construct an id from item.get_id().
+  /// Only valid when the template parameter Dimensions is equal to 3.
+  template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
+  id(const item<Dimensions> &item) noexcept
+      : Base(item.get_id(0), item.get_id(1), item.get_id(2)) {}
+
+  /*
+    Declared and implemented in detail::RawArray:
+        std::size_t get(int dimension) const noexcept;
+        std::size_t& operator[](int dimension) noexcept;
+        std::size_t operator[](int dimension) const noexcept;
+    */
+
+  // Template operator is not allowed because it disables further type
+  //   conversion. For example, the next code will not work in case of template
+  //   conversion:
+  //   int a = id<1>(value);
+  /// Returns the same value as get(0).
+  ///  Available only when: Dimensions == 1.
+  operator EnableIfT<(Dimensions == 1), std::size_t>() const noexcept {
+    return Base::get(0);
+  }
+
+  // TODO: operators to be added
+};
+
+/// c++ deduction guides.
+#ifdef __cpp_deduction_guides
+id(std::size_t) -> id<1>;
+id(std::size_t, std::size_t) -> id<2>;
+id(std::size_t, std::size_t, std::size_t) -> id<3>;
+#endif
+
+/// SYCL 2020 4.9.1.4. item class.
+/// item identifies an instance of the function object executing at each point
+/// in a range.
+template <int Dimensions /* = 1*/, bool WithOffset /* = true*/> class item {
+  /* Helper class for conversion operator. Void type is not suitable. User
+   * cannot even try to get address of the operator PrivateTag(). User
+   * may try to get an address of operator void() and will get the
+   * compile-time error */
+  class PrivateTag;
+  template <bool Condition, typename T>
+  using EnableIfT = std::conditional_t<Condition, T, PrivateTag>;
+
+public:
+  static constexpr int dimensions = Dimensions;
+
+  item() = delete;
+
+  item(const item &rhs) = default;
+
+  item(item<Dimensions, WithOffset> &&rhs) = default;
+
+  item &operator=(const item &rhs) = default;
+
+  item &operator=(item &&rhs) = default;
+
+  friend bool operator==(const item<Dimensions, WithOffset> &lhs,
+                         const item<Dimensions, WithOffset> &rhs) {
+    if constexpr (WithOffset)
+      return (lhs.MId == rhs.MId) && (lhs.MRange == rhs.MRange) &&
+             (lhs.MOffset == rhs.MOffset);
+    else
+      return (lhs.MId == rhs.MId) && (lhs.MRange == rhs.MRange);
+  }
+
+  friend bool operator!=(const item<Dimensions, WithOffset> &lhs,
+                         const item<Dimensions, WithOffset> &rhs) {
+    return !(lhs == rhs);
+  }
+
+  /// \return the constituent id representing the work-item’s position in the
+  /// iteration space.
+  id<Dimensions> get_id() const noexcept { return MId; }
+
+  /// Equivalent to return get_id()[dimension].
+  std::size_t get_id(int dimension) const noexcept {
+    return MId.get(dimension);
+  }
+
+  /// Equivalent to return get_id(dimension).
+  std::size_t operator[](int dimension) const noexcept {
+    return MId[dimension];
+  }
+
+  /// \return a range representing the dimensions of the range of possible
+  /// values of the item.
+  range<Dimensions> get_range() const noexcept { return MRange; }
+
+  /// Equivalent to return get_range().get(dimension).
+  std::size_t get_range(int dimension) const noexcept {
+    return MRange[dimension];
+  }
+
+  /// Deprecated in SYCL 2020.
+  /// For an item converted from an item with no offset this will always return
+  /// an id of all 0 values. This member function is only available if
+  /// WithOffset is true.
+  /// \return an id representing the n-dimensional offset provided to the
+  /// parallel_for and that is added by the runtime to the global-ID of each
+  /// work-item, if this item represents a global range.
+  template <bool HasOffset = WithOffset,
+            std::enable_if_t<HasOffset == true, bool> = true>
+  id<Dimensions> get_offset() const noexcept {
+    return MOffset;
+  }
+
+  /// Deprecated in SYCL 2020.
+  /// This conversion allow users to seamlessly write code that assumes an
+  /// offset and still provides an offset-less item. Available only when:
+  /// WithOffset == false.
+  /// \return an item representing the same information as the object holds but
+  /// also includes the offset set to 0.
+  template <bool HasOffset = WithOffset,
+            std::enable_if_t<HasOffset == false, bool> = true>
+  operator item<Dimensions, true>() const noexcept {
+    return item<Dimensions, true>(MRange, MId, id<Dimensions>{});
+  }
+
+  /// Equivalent to get_id(0).
+  /// Available only when: Dimensions == 1.
+  operator EnableIfT<(Dimensions == 1), std::size_t>() const noexcept {
+    return get_id(0);
+  }
+
+  /// \return Return the id as a linear index value.
+  std::size_t get_linear_id() const noexcept {
+    if constexpr (WithOffset) {
+      if constexpr (1 == Dimensions) {
+        return MId;
+      }
+      if constexpr (2 == Dimensions) {
+        return (MId[0] - MOffset[0]) * MRange[1] + (MId[1] - MOffset[1]);
+      }
+      return ((MId[0] - MOffset[0]) * MRange[1] * MRange[2]) +
+             ((MId[1] - MOffset[1]) * MRange[2]) + (MId[2] - MOffset[2]);
+    } else {
+      if constexpr (1 == Dimensions) {
+        return MId[0];
+      }
+      if constexpr (2 == Dimensions) {
+        return MId[0] * MRange[1] + MId[1];
+      }
+      return (MId[0] * MRange[1] * MRange[2]) + (MId[1] * MRange[2]) + MId[2];
+    }
+  }
+
+protected:
+  template <bool HasOffset = WithOffset,
+            std::enable_if_t<HasOffset == true, bool> = true>
+  item(const sycl::range<Dimensions> &range, const sycl::id<Dimensions> &id,
+       const sycl::id<Dimensions> &offset)
+      : MRange(range), MId(id), MOffset(offset) {}
+
+  template <bool HasOffset = WithOffset,
+            std::enable_if_t<HasOffset == false, bool> = true>
+  item(const range<Dimensions> &range, const id<Dimensions> &id)
+      : MRange(range), MId(id), MOffset() {}
+
+private:
+  range<Dimensions> MRange;
+  id<Dimensions> MId;
+  id<Dimensions> MOffset;
+
+  friend class detail::Builder;
+};
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL___IMPL_INDEX_SPACE_CLASSES_HPP
diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index d1ac320433c38..95653ab0c34ff 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -23,6 +23,7 @@
 #include <sycl/__impl/detail/arg_wrapper.hpp>
 #include <sycl/__impl/detail/config.hpp>
 #include <sycl/__impl/detail/default_async_handler.hpp>
+#include <sycl/__impl/detail/kernel_arg_helpers.hpp>
 #include <sycl/__impl/detail/obj_utils.hpp>
 #include <sycl/__impl/detail/unified_range_view.hpp>
 
@@ -32,28 +33,6 @@ class context;
 
 namespace detail {
 class QueueImpl;
-
-template <typename, typename T> struct CheckFunctionSignature {
-  static_assert(std::integral_constant<T, false>::value,
-                "Second template parameter is required to be of function type");
-};
-
-template <typename F, typename RetT, typename... Args>
-struct CheckFunctionSignature<F, RetT(Args...)> {
-private:
-  template <typename T>
-  static constexpr auto check(T *) -> typename std::is_same<
-      decltype(std::declval<T>().operator()(std::declval<Args>()...)),
-      RetT>::type;
-
-  template <typename> static constexpr std::false_type check(...);
-
-  using type = decltype(check<F>(0));
-
-public:
-  static constexpr bool value = type::value;
-};
-
 } // namespace detail
 
 // SYCL 2020 4.6.5. Queue class.
@@ -166,7 +145,7 @@ class _LIBSYCL_EXPORT queue {
   ///
   /// \param kernelFunc is the kernel functor or lambda.
   /// \return an event that represents the status of the submitted kernel.
-  template <typename KernelName, typename KernelType>
+  template <typename KernelName = detail::AutoName, typename KernelType>
   event single_task(const KernelType &kernelFunc) {
     return single_task<KernelName, KernelType>({}, kernelFunc);
   }
@@ -177,7 +156,7 @@ class _LIBSYCL_EXPORT queue {
   /// \param depEvent is an event that specifies the kernel dependency.
   /// \param kernelFunc is the kernel functor or lambda.
   /// \return an event that represents the status of the submitted kernel.
-  template <typename KernelName, typename KernelType>
+  template <typename KernelName = detail::AutoName, typename KernelType>
   event single_task(event depEvent, const KernelType &kernelFunc) {
     return single_task<KernelName, KernelType>({depEvent}, kernelFunc);
   }
@@ -189,7 +168,7 @@ class _LIBSYCL_EXPORT queue {
   /// dependencies.
   /// \param kernelFunc is the kernel functor or lambda.
   /// \return an event that represents the status of the submitted kernel.
-  template <typename KernelName, typename KernelType>
+  template <typename KernelName = detail::AutoName, typename KernelType>
   event single_task(const std::vector<event> &depEvents,
                     const KernelType &kernelFunc) {
     static_assert(
@@ -199,18 +178,169 @@ class _LIBSYCL_EXPORT queue {
         "group. ");
 
     setKernelParameters(depEvents);
-    submitSingleTask<KernelName, KernelType>(kernelFunc);
+    using NameT =
+        typename detail::get_kernel_name_t<KernelName, KernelType>::name;
+    submitSingleTask<NameT, KernelType>(kernelFunc);
     return getLastEvent();
   }
 
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type, for the specified range.
+  ///
+  /// \param numWorkItems specifies the global work space of the kernel.
+  /// \param rest acts as-if: const KernelType &KernelFunc".
+  // TODO: Rest will represent reduction types once it is supported.
+  template <typename KernelName = detail::AutoName, typename... Rest>
+  event parallel_for(range<1> numWorkItems, Rest &&...rest) {
+    return parallel_for<KernelName>(numWorkItems, {},
+                                    std::forward<Rest>(rest)...);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type, for the specified range.
+  ///
+  /// \param numWorkItems specifies the global work space of the kernel.
+  /// \param rest acts as-if: const KernelType &KernelFunc".
+  // TODO: Rest will represent reduction types once it is supported.
+  template <typename KernelName = detail::AutoName, typename... Rest>
+  event parallel_for(range<2> numWorkItems, Rest &&...rest) {
+    return parallel_for<KernelName>(numWorkItems, {},
+                                    std::forward<Rest>(rest)...);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type, for the specified range.
+  ///
+  /// \param numWorkItems specifies the global work space of the kernel.
+  /// \param rest acts as-if: const KernelType &KernelFunc".
+  // TODO: Rest will represent reduction types once it is supported.
+  template <typename KernelName = detail::AutoName, typename... Rest>
+  event parallel_for(range<3> numWorkItems, Rest &&...rest) {
+    return parallel_for<KernelName>(numWorkItems, {},
+                                    std::forward<Rest>(rest)...);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type, for the specified range.
+  ///
+  /// \param numWorkItems specifies the global work space of the kernel.
+  /// \param depEvent adds a requirement that the action represented by depEvent
+  /// must complete before executing this kernel.
+  /// \param rest acts as-if: const KernelType &KernelFunc".
+  // TODO: Rest will represent reduction types once it is supported.
+  template <typename KernelName = detail::AutoName, typename... Rest>
+  event parallel_for(range<1> numWorkItems, event depEvent, Rest &&...rest) {
+    return parallel_for<KernelName>(numWorkItems, {depEvent},
+                                    std::forward<Rest>(rest)...);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type, for the specified range.
+  ///
+  /// \param numWorkItems specifies the global work space of the kernel.
+  /// \param depEvent adds a requirement that the action represented by depEvent
+  /// must complete before executing this kernel.
+  /// \param rest acts as-if: const KernelType &KernelFunc".
+  // TODO: Rest will represent reduction types once it is supported.
+  template <typename KernelName = detail::AutoName, typename... Rest>
+  event parallel_for(range<2> numWorkItems, event depEvent, Rest &&...rest) {
+    return parallel_for<KernelName>(numWorkItems, {depEvent},
+                                    std::forward<Rest>(rest)...);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type, for the specified range.
+  ///
+  /// \param numWorkItems specifies the global work space of the kernel.
+  /// \param depEvent adds a requirement that the action represented by depEvent
+  /// must complete before executing this kernel.
+  /// \param rest acts as-if: const KernelType &KernelFunc".
+  // TODO: Rest will represent reduction types once it is supported.
+  template <typename KernelName = detail::AutoName, typename... Rest>
+  event parallel_for(range<3> numWorkItems, event depEvent, Rest &&...rest) {
+    return parallel_for<KernelName>(numWorkItems, {depEvent},
+                                    std::forward<Rest>(rest)...);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type, for the specified range.
+  ///
+  /// \param numWorkItems specifies the global work space of the kernel
+  /// \param depEvents is a vector of events that specifies the kernel
+  /// dependencies.
+  /// \param rest acts as-if: const KernelType &KernelFunc".
+  // TODO: Rest will represent reduction types once it is supported.
+  template <typename KernelName = detail::AutoName, typename... Rest>
+  event parallel_for(range<1> numWorkItems, const std::vector<event> &depEvents,
+                     Rest &&...rest) {
+    return parallelForImpl<KernelName>(numWorkItems, depEvents,
+                                       std::forward<Rest>(rest)...);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type, for the specified range.
+  ///
+  /// \param numWorkItems specifies the global work space of the kernel
+  /// \param depEvents is a vector of events that specifies the kernel
+  /// dependencies.
+  /// \param rest acts as-if: const KernelType &KernelFunc".
+  // TODO: Rest will represent reduction types once it is supported.
+  template <typename KernelName = detail::AutoName, typename... Rest>
+  event parallel_for(range<2> numWorkItems, const std::vector<event> &depEvents,
+                     Rest &&...rest) {
+    return parallelForImpl<KernelName>(numWorkItems, depEvents,
+                                       std::forward<Rest>(rest)...);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type, for the specified range.
+  ///
+  /// \param numWorkItems specifies the global work space of the kernel
+  /// \param depEvents is a vector of events that specifies the kernel
+  /// dependencies.
+  /// \param rest acts as-if: const KernelType &KernelFunc".
+  // TODO: Rest will represent reduction types once it is supported.
+  template <typename KernelName = detail::AutoName, typename... Rest>
+  event parallel_for(range<3> numWorkItems, const std::vector<event> &depEvents,
+                     Rest &&...rest) {
+    return parallelForImpl<KernelName>(numWorkItems, depEvents,
+                                       std::forward<Rest>(rest)...);
+  }
+
   /// Blocks the calling thread until all commands previously submitted to this
   /// queue have completed. Synchronous errors are reported through SYCL
   /// exceptions.
   void wait();
 
 private:
-  // Name of this function is defined by compiler. It generates call to this
-  // function in the host implementation of KernelFunc in submitSingleTask.
+  template <typename KernelName, int Dims, typename... Rest>
+  event parallelForImpl(range<Dims> numWorkItems,
+                        const std::vector<event> &depEvents, Rest &&...rest) {
+    if constexpr (sizeof...(Rest) != 1)
+      throw sycl::exception(errc::feature_not_supported,
+                            "Reductions are not supported.");
+    setKernelParameters(depEvents, numWorkItems);
+
+    using KernelType =
+        std::decay_t<detail::nth_type_t<sizeof...(Rest) - 1, Rest...>>;
+    using LambdaArgType = sycl::detail::lambda_arg_type<KernelType, item<Dims>>;
+    static_assert(
+        std::is_convertible_v<sycl::item<Dims>, LambdaArgType>,
+        "Kernel argument of a sycl::parallel_for with sycl::range "
+        "must be either sycl::item or be convertible from sycl::item");
+
+    using NameT =
+        typename detail::get_kernel_name_t<KernelName, KernelType>::name;
+    submitParallelFor<NameT, item<Dims>, KernelType>(rest...);
+    return getLastEvent();
+  }
+
+  /// Name of this function is defined by compiler. It generates call to this
+  /// function in the host implementation of KernelFunc in submitSingleTask or
+  /// submitParallelFor.
+  /// \param KernelName a name of the kernel being invoked.
+  /// \param args kernel arguments for kernel invocation.
+  // TODO: now `args` always represents  single argument - lambda capture.
   template <typename, typename... Args>
   void sycl_kernel_launch(const char *KernelName, Args &&...args) {
     static_assert((sizeof...(args) == 1) &&
@@ -221,6 +351,10 @@ class _LIBSYCL_EXPORT queue {
     submitKernelImpl(KernelName, TypelessArgs);
   }
 
+  /// The sycl_kernel_entry_point attribute facilitates the generation of an
+  /// offload kernel entry point function with parameters corresponding to the
+  /// (potentially decomposed) kernel arguments and a body that (potentially
+  /// reconstructs the arguments and) executes the kernel.
 #ifdef SYCL_LANGUAGE_VERSION
 #  define _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)                              \
     [[clang::sycl_kernel_entry_point(KernelName)]]
@@ -228,18 +362,45 @@ class _LIBSYCL_EXPORT queue {
 #  define _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
 #endif // SYCL_LANGUAGE_VERSION
 
+  /// Specifies the parameters and body of the generated offload kernel entry
+  /// point for single_task invocations. On host compiler generates call to
+  /// sycl_kernel_launch instead of KernelFunc invocation.
   template <typename KernelName, typename KernelType>
   _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
   void submitSingleTask(const KernelType KernelFunc) {
     KernelFunc();
   }
 
-  event getLastEvent();
-  void submitKernelImpl(const char *KernelName,
-                        detail::ArgCollection &TypelessArgs);
+  /// Specifies the parameters and body of the generated offload kernel entry
+  /// point for parallel_for invocations. On host compiler generates call to
+  /// sycl_kernel_launch instead of KernelFunc invocation.
+  template <typename KernelName, typename ElementType, typename KernelType>
+  _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
+  void submitParallelFor(const KernelType KernelFunc) {
+#ifdef __SYCL_DEVICE_ONLY__
+    KernelFunc(detail::Builder::getElement(detail::declptr<ElementType>()));
+#endif
+    (void)KernelFunc;
+  }
+
+  /// Passes kernel parameters to runtime.
+  /// \param Events a collection of events representing dependencies of the
+  /// kernel to submit.
+  /// \param Range a unified view of range for kernel execution.
   void setKernelParameters(const std::vector<event> &Events,
                            const detail::UnifiedRangeView &Range = {});
 
+  /// Passes kernel arguments to runtime.
+  /// If all dependencies are met and kernel can be submitted to backend - it is
+  /// done in this call.
+  /// \param KernelName a name of the kernel being invoked.
+  /// \param TypelessArgs a unified arguments collection.
+  void submitKernelImpl(const char *KernelName,
+                        detail::ArgCollection &TypelessArgs);
+
+  /// \return an event representing last kernel invocation.
+  event getLastEvent();
+
   queue(const std::shared_ptr<detail::QueueImpl> &Impl) : impl(Impl) {}
   std::shared_ptr<detail::QueueImpl> impl;
 
diff --git a/libsycl/include/sycl/__spirv/spirv_vars.hpp b/libsycl/include/sycl/__spirv/spirv_vars.hpp
new file mode 100644
index 0000000000000..ec8c691b35e92
--- /dev/null
+++ b/libsycl/include/sycl/__spirv/spirv_vars.hpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains SPIRV builtins needed for kernel invocations
+/// (parallel_for).
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL___SPIRV_SPIRV_VARS
+#define _LIBSYCL___SPIRV_SPIRV_VARS
+
+#ifdef __SYCL_DEVICE_ONLY__
+
+#  include <cstddef>
+#  include <cstdint>
+
+// SPIR-V built-in variables mapped to function call.
+#  define _LIBSYCL_SYCL_DEVICE_ATTR __attribute__((sycl_external))
+
+_LIBSYCL_SYCL_DEVICE_ATTR __attribute__((const)) size_t
+__spirv_BuiltInGlobalInvocationId(int);
+_LIBSYCL_SYCL_DEVICE_ATTR __attribute__((const)) size_t
+__spirv_BuiltInGlobalSize(int);
+_LIBSYCL_SYCL_DEVICE_ATTR __attribute__((const)) size_t
+__spirv_BuiltInGlobalOffset(int);
+
+namespace __spirv {
+
+// Helper function templates to initialize and get vector component from SPIR-V
+// built-in variables
+#  define __SPIRV_DEFINE_INIT_AND_GET_HELPERS(POSTFIX)                         \
+    template <int ID> size_t get##POSTFIX();                                   \
+    template <> size_t get##POSTFIX<0>() { return __spirv_##POSTFIX(0); }      \
+    template <> size_t get##POSTFIX<1>() { return __spirv_##POSTFIX(1); }      \
+    template <> size_t get##POSTFIX<2>() { return __spirv_##POSTFIX(2); }      \
+                                                                               \
+    template <int Dim, class DstT> struct InitSizesST##POSTFIX;                \
+                                                                               \
+    template <class DstT> struct InitSizesST##POSTFIX<1, DstT> {               \
+      static DstT initSize() { return {get##POSTFIX<0>()}; }                   \
+    };                                                                         \
+                                                                               \
+    template <class DstT> struct InitSizesST##POSTFIX<2, DstT> {               \
+      static DstT initSize() {                                                 \
+        return {get##POSTFIX<1>(), get##POSTFIX<0>()};                         \
+      }                                                                        \
+    };                                                                         \
+                                                                               \
+    template <class DstT> struct InitSizesST##POSTFIX<3, DstT> {               \
+      static DstT initSize() {                                                 \
+        return {get##POSTFIX<2>(), get##POSTFIX<1>(), get##POSTFIX<0>()};      \
+      }                                                                        \
+    };                                                                         \
+                                                                               \
+    template <int Dims, class DstT> DstT init##POSTFIX() {                     \
+      return InitSizesST##POSTFIX<Dims, DstT>::initSize();                     \
+    }
+
+__SPIRV_DEFINE_INIT_AND_GET_HELPERS(BuiltInGlobalSize);
+__SPIRV_DEFINE_INIT_AND_GET_HELPERS(BuiltInGlobalInvocationId)
+__SPIRV_DEFINE_INIT_AND_GET_HELPERS(BuiltInGlobalOffset)
+
+#  undef __SPIRV_DEFINE_INIT_AND_GET_HELPERS
+
+} // namespace __spirv
+
+#endif //__SYCL_DEVICE_ONLY__
+
+#endif // _LIBSYCL___SPIRV_SPIRV_VARS
diff --git a/libsycl/test/basic/queue_parallel_for_generic.cpp b/libsycl/test/basic/queue_parallel_for_generic.cpp
new file mode 100644
index 0000000000000..cac423b85f218
--- /dev/null
+++ b/libsycl/test/basic/queue_parallel_for_generic.cpp
@@ -0,0 +1,47 @@
+// REQUIRES: any-device
+// RUN: %clangxx -fsycl %s -o %t.out
+// RUN: %t.out
+
+#include <sycl/sycl.hpp>
+
+#include <cassert>
+#include <iostream>
+#include <type_traits>
+
+int main() {
+  // TODO: uncomment property once it is implemented. now all sycl::queue
+  // objects are in-order due to liboffload limitation. Test is intended to
+  // check in-order execution.
+  sycl::queue q{/*sycl::property::queue::in_order()*/};
+  auto dev = q.get_device();
+  auto ctx = q.get_context();
+  constexpr int N = 8;
+
+  auto A = static_cast<int *>(sycl::malloc_shared(N * sizeof(int), dev, ctx));
+
+  for (int i = 0; i < N; i++) {
+    A[i] = 1;
+  }
+
+  q.parallel_for<class Bar>(N, [=](auto i) {
+    static_assert(std::is_same<decltype(i), sycl::item<1>>::value,
+                  "lambda arg type is unexpected");
+    A[i]++;
+  });
+
+  q.parallel_for<class Foo>({N}, [=](auto i) {
+    static_assert(std::is_same<decltype(i), sycl::item<1>>::value,
+                  "lambda arg type is unexpected");
+    A[i]++;
+  });
+
+  // TODO: add kernel with offset and kernel with nd_range once they
+  // are implemented.
+
+  q.wait();
+
+  for (int i = 0; i < N; i++) {
+    assert(A[i] == 3);
+  }
+  sycl::free(A, ctx);
+}
diff --git a/libsycl/test/basic/wrapped_usm_pointers.cpp b/libsycl/test/basic/wrapped_usm_pointers.cpp
new file mode 100644
index 0000000000000..16a86963cc976
--- /dev/null
+++ b/libsycl/test/basic/wrapped_usm_pointers.cpp
@@ -0,0 +1,111 @@
+// REQUIRES: any-device
+// RUN: %clangxx -fsycl %s -o %t.out
+// RUN: %t.out
+
+#include <sycl/sycl.hpp>
+
+#include <iostream>
+
+struct Simple {
+  int *Data;
+  int Addition;
+};
+
+struct WrapperOfSimple {
+  int Addition;
+  Simple Obj;
+};
+
+struct NonTrivial {
+  int Addition;
+  int *Data;
+
+  NonTrivial(int *D, int A) : Data(D), Addition(A) {}
+};
+
+struct NonTrivialDerived : NonTrivial {
+  int AA = 0;
+  NonTrivialDerived(int *D, int A) : NonTrivial(D, A) {}
+};
+
+using namespace sycl;
+
+int main() {
+  constexpr int NumOfElements = 7;
+
+  queue Q;
+
+  NonTrivial NonTrivialObj(sycl::malloc_shared<int>(NumOfElements, Q), 38);
+  NonTrivialDerived NonTrivialDerivedObj(
+      sycl::malloc_shared<int>(NumOfElements, Q), 39);
+  Simple SimpleObj = {sycl::malloc_shared<int>(NumOfElements, Q), 42};
+  WrapperOfSimple WrapperOfSimpleObj = {
+      300, {sycl::malloc_shared<int>(NumOfElements, Q), 100500}};
+
+  // Test simple struct containing pointer.
+  Q.parallel_for(NumOfElements, [=](id<1> Idx) {
+    SimpleObj.Data[Idx] = Idx + SimpleObj.Addition;
+  });
+
+  // Test simple non-trivial struct containing pointer.
+  Q.parallel_for(NumOfElements, [=](id<1> Idx) {
+    NonTrivialObj.Data[Idx] = Idx + NonTrivialObj.Addition;
+  });
+
+  // Test simple non-trivial derived struct containing pointer.
+  Q.parallel_for(NumOfElements, [=](id<1> Idx) {
+    NonTrivialDerivedObj.Data[Idx] = Idx + NonTrivialDerivedObj.Addition;
+  });
+
+  // Test nested struct containing pointer.
+  Q.parallel_for(NumOfElements, [=](id<1> Idx) {
+    WrapperOfSimpleObj.Obj.Data[Idx] = Idx + WrapperOfSimpleObj.Obj.Addition;
+  });
+
+  // Test array of structs containing pointers.
+  Simple SimpleArr[NumOfElements];
+  for (int i = 0; i < NumOfElements; ++i) {
+    SimpleArr[i].Data = sycl::malloc_shared<int>(NumOfElements, Q);
+    SimpleArr[i].Addition = 38 + i;
+  }
+
+  Q.parallel_for(range<2>(NumOfElements, NumOfElements), [=](item<2> Idx) {
+    SimpleArr[Idx.get_id(0)].Data[Idx.get_id(1)] =
+        Idx.get_id(1) + SimpleArr[Idx.get_id(0)].Addition;
+  });
+
+  Q.wait();
+
+  auto Checker = [](auto Obj) {
+    for (int i = 0; i < NumOfElements; ++i) {
+      if (Obj.Data[i] != (i + Obj.Addition)) {
+        std::cout << "line: " << __LINE__ << " result[" << i << "] is "
+                  << Obj.Data[i] << " expected " << i + Obj.Addition
+                  << std::endl;
+        return true; // true if fail
+      }
+    }
+
+    return false;
+  };
+
+  bool Fail = false;
+  Fail = Checker(SimpleObj);
+  Fail = Checker(NonTrivialObj);
+  Fail = Checker(NonTrivialDerivedObj);
+  Fail = Checker(WrapperOfSimpleObj.Obj);
+
+  for (int i = 0; i < NumOfElements; ++i)
+    Fail = Checker(SimpleArr[i]);
+
+  // Free allocated memory.
+  sycl::free(NonTrivialObj.Data, Q);
+  sycl::free(NonTrivialDerivedObj.Data, Q);
+  sycl::free(SimpleObj.Data, Q);
+  sycl::free(WrapperOfSimpleObj.Obj.Data, Q);
+
+  for (int i = 0; i < NumOfElements; ++i)
+    sycl::free(SimpleArr[i].Data, Q);
+
+  return Fail;
+}

>From a5b6a4c40d367b6f80d7ec43915d94a0707aa911 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Fri, 27 Mar 2026 10:37:44 -0700
Subject: [PATCH 08/25] removed invalid comment

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp b/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
index d4a0ea9f63ff2..a7478e1300e21 100644
--- a/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
+++ b/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
@@ -5,8 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// to add
-//===----------------------------------------------------------------------===//
 
 #ifndef _LIBSYCL___IMPL_DETAIL_KERNEL_ARG_HELPERS
 #define _LIBSYCL___IMPL_DETAIL_KERNEL_ARG_HELPERS

>From 67a4c90a5b912b25107e0b49e88785033f997d57 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Wed, 1 Apr 2026 03:36:56 -0700
Subject: [PATCH 09/25] fix comments

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/src/detail/program_manager.cpp | 18 +++++++++---------
 libsycl/src/detail/program_manager.hpp |  5 +++++
 2 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
index 7d6523daeb6ee..18b892d272432 100644
--- a/libsycl/src/detail/program_manager.cpp
+++ b/libsycl/src/detail/program_manager.cpp
@@ -122,8 +122,8 @@ void ProgramManager::removeImages(__sycl_tgt_bin_desc *FatbinDesc) {
         ProgramIt != MPrograms.end()) {
       for (auto &[Device, Program] : ProgramIt->second) {
         MProgramWrappers.erase(Program);
-        MPrograms.erase(ProgramIt);
       }
+      MPrograms.erase(ProgramIt);
     }
     MDeviceImageWrappers.erase(DevImageIt);
   }
@@ -163,7 +163,7 @@ DeviceImageWrapper *ProgramManager::getDeviceImage(std::string_view KernelName,
 
 ol_symbol_handle_t ProgramManager::getOrCreateKernel(const char *KernelName,
                                                      DeviceImpl &Device) {
-  std::lock_guard<std::mutex> ImageGuard(MImageCollectionMutex);
+  std::unique_lock<std::mutex> ImageGuard(MImageCollectionMutex);
 
   auto KernelIDIt = MKernelNameToID.find(KernelName);
   if (KernelIDIt == MKernelNameToID.end())
@@ -179,8 +179,8 @@ ol_symbol_handle_t ProgramManager::getOrCreateKernel(const char *KernelName,
 
   DeviceImageWrapper *DevImage =
       getDeviceImage(KernelName, KernelIDIt->second, Device);
-  if (!DevImage)
-    throw;
+
+  ImageGuard.unlock();
 
   ol_program_handle_t Program = getOrCreateProgram(Device, DevImage);
   assert(Program);
@@ -202,11 +202,9 @@ ProgramManager::getOrCreateProgram(DeviceImpl &Device,
   std::unique_ptr<ProgramWrapper> NewProgramWrapper(
       new ProgramWrapper(Device.getHandle(), *DevImage));
   auto Program = NewProgramWrapper->getHandle();
-  {
-    MPrograms[DevImage].insert(std::make_pair(Device.getHandle(), Program));
-    MProgramWrappers.insert(std::make_pair(NewProgramWrapper->getHandle(),
-                                           std::move(NewProgramWrapper)));
-  }
+  MPrograms[DevImage].insert(std::make_pair(Device.getHandle(), Program));
+  MProgramWrappers.insert(std::make_pair(NewProgramWrapper->getHandle(),
+                                         std::move(NewProgramWrapper)));
 
   return Program;
 }
@@ -215,6 +213,8 @@ ol_symbol_handle_t ProgramManager::createKernel(ol_program_handle_t Program,
                                                 const kernel_id &KernelID,
                                                 const char *KernelName,
                                                 DeviceImpl &Device) {
+  assert((getKernel(KernelID, Device) == nullptr) &&
+         "Attempt to create kernel that already exists.");
   ol_symbol_handle_t Kernel{};
   callAndThrow(olGetSymbol, Program, KernelName, OL_SYMBOL_KIND_KERNEL,
                &Kernel);
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
index b017383a16b4c..22e380334befd 100644
--- a/libsycl/src/detail/program_manager.hpp
+++ b/libsycl/src/detail/program_manager.hpp
@@ -58,6 +58,11 @@ class ProgramWrapper {
   /// olDestroyProgram.
   ~ProgramWrapper();
 
+  ProgramWrapper(const ProgramWrapper &) = delete;
+  ProgramWrapper &operator=(const ProgramWrapper &) = delete;
+  ProgramWrapper(ProgramWrapper &&) = delete;
+  ProgramWrapper &operator=(ProgramWrapper &&) = delete;
+
   /// \return the corresponding liboffload program handle.
   ol_program_handle_t getHandle() { return MProgram; }
 

>From 470543acf33600f82041bfb688d1efc77b6ade7d Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Fri, 10 Apr 2026 04:28:54 -0700
Subject: [PATCH 10/25] remove extra mutex

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/include/sycl/__impl/queue.hpp  |  2 --
 libsycl/src/detail/program_manager.cpp | 13 ++++++-------
 libsycl/src/detail/program_manager.hpp | 20 ++++++++------------
 3 files changed, 14 insertions(+), 21 deletions(-)

diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index 36f0b9ad57d4a..41b018b681b8e 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -17,7 +17,6 @@
 
 #include <sycl/__impl/async_handler.hpp>
 #include <sycl/__impl/device.hpp>
-#include <sycl/__impl/event.hpp>
 #include <sycl/__impl/property_list.hpp>
 
 #include <sycl/__impl/detail/config.hpp>
@@ -30,7 +29,6 @@ class context;
 
 namespace detail {
 class QueueImpl;
-
 } // namespace detail
 
 // SYCL 2020 4.6.5. Queue class.
diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
index aacd33b405806..f461c0bcdfec1 100644
--- a/libsycl/src/detail/program_manager.cpp
+++ b/libsycl/src/detail/program_manager.cpp
@@ -52,7 +52,7 @@ void ProgramAndKernelManager::registerFatBin(__sycl_tgt_bin_desc *FatbinDesc) {
   if (!FatbinDesc->NumDeviceBinaries)
     return;
 
-  std::lock_guard<std::mutex> Guard(MImageCollectionMutex);
+  std::lock_guard<std::mutex> Guard(MDataCollectionMutex);
   for (uint16_t I = 0; I < FatbinDesc->NumDeviceBinaries; ++I) {
     const auto &RawDeviceImage = FatbinDesc->DeviceImages[I];
     if (!checkDeviceImageValidity(RawDeviceImage))
@@ -96,7 +96,7 @@ void ProgramAndKernelManager::unregisterFatBin(
   if (!checkFatBinVersion(*FatbinDesc) || FatbinDesc->NumDeviceBinaries == 0)
     return;
 
-  std::scoped_lock Guard{MImageCollectionMutex, MKernelCollectionsMutex};
+  std::lock_guard<std::mutex> Guard(MDataCollectionMutex);
   for (uint16_t I = 0; I < FatbinDesc->NumDeviceBinaries; ++I) {
     const auto &RawDeviceImage = FatbinDesc->DeviceImages[I];
 
@@ -144,7 +144,7 @@ DeviceImageWrapper *
 ProgramAndKernelManager::getDeviceImage(std::string_view KernelName,
                                         const kernel_id &KernelID,
                                         DeviceImpl &Device) {
-  std::lock_guard<std::mutex> Guard(MImageCollectionMutex);
+  std::lock_guard<std::mutex> Guard(MDataCollectionMutex);
   auto [Begin, End] = MKernelIDToDevImageJIT.equal_range(KernelID);
   if (Begin != End) {
     bool IsValid{};
@@ -167,7 +167,7 @@ ProgramAndKernelManager::getDeviceImage(std::string_view KernelName,
 
 ol_symbol_handle_t ProgramAndKernelManager::getOrCreateKernel(const char *KernelName,
                                                      DeviceImpl &Device) {
-  std::unique_lock<std::mutex> ImageGuard(MImageCollectionMutex);
+  std::unique_lock<std::mutex> ImageGuard(MDataCollectionMutex);
 
   auto KernelIDIt = MKernelNameToID.find(KernelName);
   if (KernelIDIt == MKernelNameToID.end())
@@ -175,7 +175,7 @@ ol_symbol_handle_t ProgramAndKernelManager::getOrCreateKernel(const char *Kernel
                     "No kernel named " + std::string(KernelName) +
                         " was found");
 
-  std::lock_guard<std::mutex> KernelGuard(MKernelCollectionsMutex);
+  std::lock_guard<std::mutex> KernelGuard(MDataCollectionMutex);
 
   auto Kernel = getKernel(KernelIDIt->second, Device);
   if (Kernel)
@@ -184,10 +184,9 @@ ol_symbol_handle_t ProgramAndKernelManager::getOrCreateKernel(const char *Kernel
   DeviceImageWrapper *DevImage =
       getDeviceImage(KernelName, KernelIDIt->second, Device);
 
-  ImageGuard.unlock();
-
   ol_program_handle_t Program = getOrCreateProgram(Device, DevImage);
   assert(Program);
+
   Kernel = createKernel(Program, KernelIDIt->second, KernelName, Device);
   assert(Kernel);
   return Kernel;
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
index bcf8be30f2407..5ab828b25a470 100644
--- a/libsycl/src/detail/program_manager.hpp
+++ b/libsycl/src/detail/program_manager.hpp
@@ -118,6 +118,7 @@ class ProgramAndKernelManager {
 
   /// Searches for a device image that contains the requested kernel and is
   /// compatible with the requested device.
+  /// This call must be protected with MDataCollectionMutex.
   /// \param KernelName a null-terminated string representing the name of the
   /// kernel to obtain a device image for.
   /// \param KernelID a kernel id matching KernelName.
@@ -129,8 +130,8 @@ class ProgramAndKernelManager {
                                      DeviceImpl &Device);
 
   /// Searches for or creates a program.
-  /// This call must be protected with mutex since it updates MPrograms and
-  /// MProgramWrappers collections.
+  /// This call must be protected with MDataCollectionMutex since it updates
+  /// MPrograms and MProgramWrappers collections.
   /// \param Device a device that program must be created with.
   /// \param DevImage a device image to get or create program with.
   /// \return liboffload program for the requested configuration.
@@ -138,8 +139,8 @@ class ProgramAndKernelManager {
                                          DeviceImageWrapper *DevImage);
 
   /// Creates kernel from program.
-  /// This call must be protected with mutex since it updates MKernels
-  /// collection.
+  /// This call must be protected with MDataCollectionMutex since it updates
+  /// MKernels collection.
   /// \param Program a program to create kernel with.
   /// \param KernelID an id of kernel to create.
   /// \param KernelName a null-terminated string representing the name of kernel
@@ -151,7 +152,8 @@ class ProgramAndKernelManager {
                                   const char *KernelName, DeviceImpl &Device);
 
   /// Searches for kernel.
-  /// This call must be protected with mutex since it reads MKernels collection.
+  /// This call must be protected with MDataCollectionMutex since it reads
+  /// MKernels collection.
   /// \param KernelID an id of kernel to look for.
   /// \param Device a device that kernel must be created with.
   /// \return liboffload kernel for the requested configuration or nullptr if
@@ -166,10 +168,7 @@ class ProgramAndKernelManager {
                      std::unique_ptr<DeviceImageWrapper>>
       MDeviceImageWrappers;
 
-  // All data collections, created from data in __sycl_register_lib, must be
-  // protected with this mutex. Protects data that can be modified only by
-  // modules load/unload.
-  std::mutex MImageCollectionMutex;
+  std::mutex MDataCollectionMutex;
 
   // Filled by getOrCreateKernel and everything it calls inside.
   std::unordered_map<
@@ -183,9 +182,6 @@ class ProgramAndKernelManager {
   // Controls lifetime of programs.
   std::unordered_map<ol_program_handle_t, std::unique_ptr<ProgramWrapper>>
       MProgramWrappers;
-  // All data collections, used and modified by kernel submissions, must be
-  // protected with this mutex.
-  std::mutex MKernelCollectionsMutex;
 };
 
 } // namespace detail

>From 49ee3f34a182d171afce6e0331107578184539be Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Fri, 10 Apr 2026 05:11:55 -0700
Subject: [PATCH 11/25] fix format

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/src/detail/program_manager.cpp | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
index f461c0bcdfec1..47755bbd69a02 100644
--- a/libsycl/src/detail/program_manager.cpp
+++ b/libsycl/src/detail/program_manager.cpp
@@ -165,8 +165,9 @@ ProgramAndKernelManager::getDeviceImage(std::string_view KernelName,
                   "No kernel named " + std::string(KernelName) + " was found");
 }
 
-ol_symbol_handle_t ProgramAndKernelManager::getOrCreateKernel(const char *KernelName,
-                                                     DeviceImpl &Device) {
+ol_symbol_handle_t
+ProgramAndKernelManager::getOrCreateKernel(const char *KernelName,
+                                           DeviceImpl &Device) {
   std::unique_lock<std::mutex> ImageGuard(MDataCollectionMutex);
 
   auto KernelIDIt = MKernelNameToID.find(KernelName);
@@ -194,7 +195,7 @@ ol_symbol_handle_t ProgramAndKernelManager::getOrCreateKernel(const char *Kernel
 
 ol_program_handle_t
 ProgramAndKernelManager::getOrCreateProgram(DeviceImpl &Device,
-                                   DeviceImageWrapper *DevImage) {
+                                            DeviceImageWrapper *DevImage) {
   if (auto DevToProgramIt = MPrograms.find(DevImage);
       DevToProgramIt != MPrograms.end()) {
     auto ProgramIt = DevToProgramIt->second.find(Device.getOLHandle());
@@ -212,10 +213,9 @@ ProgramAndKernelManager::getOrCreateProgram(DeviceImpl &Device,
   return Program;
 }
 
-ol_symbol_handle_t ProgramAndKernelManager::createKernel(ol_program_handle_t Program,
-                                                const kernel_id &KernelID,
-                                                const char *KernelName,
-                                                DeviceImpl &Device) {
+ol_symbol_handle_t ProgramAndKernelManager::createKernel(
+    ol_program_handle_t Program, const kernel_id &KernelID,
+    const char *KernelName, DeviceImpl &Device) {
   assert((getKernel(KernelID, Device) == nullptr) &&
          "Attempt to create kernel that already exists.");
   ol_symbol_handle_t Kernel{};
@@ -227,7 +227,7 @@ ol_symbol_handle_t ProgramAndKernelManager::createKernel(ol_program_handle_t Pro
 }
 
 ol_symbol_handle_t ProgramAndKernelManager::getKernel(const kernel_id &KernelID,
-                                             DeviceImpl &Device) {
+                                                      DeviceImpl &Device) {
   auto Range = MKernels.equal_range(KernelID);
   for (auto Kernels = Range.first; Kernels != Range.second; ++Kernels) {
     auto &[KernelDevice, KernelSymbol] = Kernels->second;

>From 3d40e17f3e8f2f25d2bcd1d5e25c0e547f31688a Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Mon, 13 Apr 2026 06:03:54 -0700
Subject: [PATCH 12/25] redesigned kernel creation

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/src/detail/device_image_wrapper.hpp |  67 ++++++--
 libsycl/src/detail/device_impl.hpp          |   2 +-
 libsycl/src/detail/device_kernel_info.hpp   |  59 ++++++++
 libsycl/src/detail/kernel_id.hpp            |  83 ----------
 libsycl/src/detail/program_manager.cpp      | 160 +++++---------------
 libsycl/src/detail/program_manager.hpp      | 106 ++-----------
 6 files changed, 172 insertions(+), 305 deletions(-)
 create mode 100644 libsycl/src/detail/device_kernel_info.hpp
 delete mode 100644 libsycl/src/detail/kernel_id.hpp

diff --git a/libsycl/src/detail/device_image_wrapper.hpp b/libsycl/src/detail/device_image_wrapper.hpp
index 45dbb84a5a14a..11b0d67c4471e 100644
--- a/libsycl/src/detail/device_image_wrapper.hpp
+++ b/libsycl/src/detail/device_image_wrapper.hpp
@@ -19,22 +19,57 @@
 
 #include <detail/device_binary_structures.hpp>
 
+#include <OffloadAPI.h>
+
+#include <map>
+
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
 
-/// A wrapper of __sycl_tgt_device_image structure to help with its fields
-/// parsing, iteration over data and data transformation.
-class DeviceImageWrapper {
+class DeviceImageManager;
+
+/// A wrapper of liboffload program handle to manage its lifetime.
+class ProgramWrapper {
+public:
+  /// Constructs ProgramWrapper by creating liboffload program with the provided
+  /// arguments.
+  ///
+  /// \param Device is a device to use for program creation.
+  /// \param DevImage is a device image (wrapped __sycl_tgt_device_image) to use
+  /// for program creation.
+  /// \throw sycl::exception with sycl::errc::runtime when failed to create
+  /// program.
+  ProgramWrapper(ol_device_handle_t Device, DeviceImageManager &DevImage);
+
+  /// Releases the corresponding liboffload program handle by calling
+  /// olDestroyProgram.
+  ~ProgramWrapper();
+
+  ProgramWrapper(const ProgramWrapper &) = delete;
+  ProgramWrapper &operator=(const ProgramWrapper &) = delete;
+  ProgramWrapper(ProgramWrapper &&) = delete;
+  ProgramWrapper &operator=(ProgramWrapper &&) = delete;
+
+  /// \return the corresponding liboffload program handle.
+  ol_program_handle_t getHandle() { return MProgram; }
+
+private:
+  ol_program_handle_t MProgram{};
+};
+
+/// This class manages all work with device images: from data parsing to program
+/// creation.
+class DeviceImageManager {
 public:
-  DeviceImageWrapper(const __sycl_tgt_device_image &Bin) : MBin(&Bin) {}
+  DeviceImageManager(const __sycl_tgt_device_image &Bin) : MBin(&Bin) {}
   // Explicitly delete copy constructor/operator= to avoid unintentional copies.
-  DeviceImageWrapper(const DeviceImageWrapper &) = delete;
-  DeviceImageWrapper &operator=(const DeviceImageWrapper &) = delete;
+  DeviceImageManager(const DeviceImageManager &) = delete;
+  DeviceImageManager &operator=(const DeviceImageManager &) = delete;
 
-  DeviceImageWrapper(DeviceImageWrapper &&) = default;
-  DeviceImageWrapper &operator=(DeviceImageWrapper &&) = default;
+  DeviceImageManager(DeviceImageManager &&) = default;
+  DeviceImageManager &operator=(DeviceImageManager &&) = default;
 
-  ~DeviceImageWrapper() = default;
+  ~DeviceImageManager() = default;
 
   /// \return a reference to the corresponding raw __sycl_tgt_device_image
   /// object.
@@ -45,7 +80,21 @@ class DeviceImageWrapper {
     return static_cast<size_t>(MBin->ImageEnd - MBin->ImageStart);
   }
 
+  ol_program_handle_t getOrCreateProgram(ol_device_handle_t DeviceHandle) {
+    auto ProgramIt = MPrograms.find(DeviceHandle);
+    if (ProgramIt == MPrograms.end()) {
+      ProgramIt =
+          MPrograms.emplace_hint(ProgramIt, std::piecewise_construct,
+                                 std::forward_as_tuple(DeviceHandle),
+                                 std::forward_as_tuple(DeviceHandle, *this));
+    }
+
+    return ProgramIt->second.getHandle();
+  }
+
 protected:
+  std::unordered_map<ol_device_handle_t, ProgramWrapper> MPrograms;
+
   const __sycl_tgt_device_image *get() const { return MBin; }
 
   __sycl_tgt_device_image const *MBin{};
diff --git a/libsycl/src/detail/device_impl.hpp b/libsycl/src/detail/device_impl.hpp
index b7c127390afeb..345047b57f891 100644
--- a/libsycl/src/detail/device_impl.hpp
+++ b/libsycl/src/detail/device_impl.hpp
@@ -121,7 +121,7 @@ class DeviceImpl {
       static_assert(false && "Info descriptor is not properly supported");
   }
 
-  ol_device_handle_t getOLHandle() { return MOffloadDevice; }
+  ol_device_handle_t getOLHandle() const { return MOffloadDevice; }
 
 private:
   ol_device_handle_t MOffloadDevice = {};
diff --git a/libsycl/src/detail/device_kernel_info.hpp b/libsycl/src/detail/device_kernel_info.hpp
new file mode 100644
index 0000000000000..89d33431a95f4
--- /dev/null
+++ b/libsycl/src/detail/device_kernel_info.hpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// to add
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL_DEVICE_KERNEL_INFO
+#define _LIBSYCL_DEVICE_KERNEL_INFO
+
+#include <sycl/__impl/detail/config.hpp>
+
+#include <OffloadAPI.h>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+namespace detail {
+
+// This class aggregates information specific to device kernels (i.e.
+// information that is uniform between different submissions of the same
+// kernel). Pointers to instances of this class are stored in header function
+// templates as a static variable to avoid repeated runtime lookup overhead.
+class DeviceKernelInfo {
+public:
+  DeviceKernelInfo(std::string_view KernelName, DeviceImageManager &DeviceImage)
+      : MName(KernelName), MDeviceImage(DeviceImage) {}
+
+  ol_symbol_handle_t getKernel(ol_device_handle_t Device) {
+    if (auto KernelIt = MBuiltKernels.find(Device);
+        KernelIt != MBuiltKernels.end())
+      return KernelIt->second;
+    return nullptr;
+  }
+
+  DeviceImageManager &getDeviceImage() { return MDeviceImage; }
+  std::string_view getName() { return MName; }
+
+  void addKernel(ol_device_handle_t Device, ol_symbol_handle_t Kernel) {
+    assert(MBuiltKernels.find(Device) != MBuiltKernels.end());
+    MBuiltKernels.insert({Device, Kernel});
+  }
+
+private:
+  std::unordered_map<ol_device_handle_t, ol_symbol_handle_t> MBuiltKernels;
+
+  std::string_view MName;
+  DeviceImageManager &MDeviceImage;
+};
+
+} // namespace detail
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL_DEVICE_KERNEL_INFO
diff --git a/libsycl/src/detail/kernel_id.hpp b/libsycl/src/detail/kernel_id.hpp
deleted file mode 100644
index 421e4c6fe17d6..0000000000000
--- a/libsycl/src/detail/kernel_id.hpp
+++ /dev/null
@@ -1,83 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This file contains the declaration of sycl::kernel_id and its implementation
-/// counterpart, which represent a kernel identificator.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBSYCL_KERNEL_ID
-#define _LIBSYCL_KERNEL_ID
-
-#include <sycl/__impl/detail/config.hpp>
-#include <sycl/__impl/detail/obj_utils.hpp>
-
-#include <memory>
-#include <string>
-
-_LIBSYCL_BEGIN_NAMESPACE_SYCL
-
-namespace detail {
-/// The class is the implementation counterpart for sycl::kernel_id, which
-/// represents a kernel identificator.
-class KernelIdImpl {
-public:
-  KernelIdImpl(std::string_view Name) : MName(Name) {}
-  KernelIdImpl() {}
-  /// \return a null-terminated string representing the name of the kernel this
-  /// id stands for.
-  const char *get_name() { return MName.data(); }
-
-private:
-  std::string MName;
-};
-} // namespace detail
-
-// TODO: It is not exported now, but is a part of SYCL spec.
-/// Kernel identifier.
-class kernel_id {
-public:
-  kernel_id() = delete;
-
-  kernel_id(const kernel_id &rhs) = default;
-
-  kernel_id(kernel_id &&rhs) = default;
-
-  kernel_id &operator=(const kernel_id &rhs) = default;
-
-  kernel_id &operator=(kernel_id &&rhs) = default;
-
-  friend bool operator==(const kernel_id &lhs, const kernel_id &rhs) {
-    return lhs.impl == rhs.impl;
-  }
-
-  friend bool operator!=(const kernel_id &lhs, const kernel_id &rhs) {
-    return !(lhs == rhs);
-  }
-
-  /// \returns a null-terminated string that contains the kernel name.
-  const char *get_name() const noexcept { return impl->get_name(); }
-
-private:
-  kernel_id(const char *Name);
-
-  kernel_id(const std::shared_ptr<detail::KernelIdImpl> &Impl)
-      : impl(std::move(Impl)) {}
-
-  std::shared_ptr<detail::KernelIdImpl> impl;
-  friend sycl::detail::ImplUtils;
-};
-
-_LIBSYCL_END_NAMESPACE_SYCL
-
-template <>
-struct std::hash<sycl::kernel_id>
-    : public sycl::detail::HashBase<sycl::kernel_id> {};
-
-#endif // _LIBSYCL_KERNEL_ID
diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
index 47755bbd69a02..19e9017cc3a09 100644
--- a/libsycl/src/detail/program_manager.cpp
+++ b/libsycl/src/detail/program_manager.cpp
@@ -19,7 +19,7 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
 
 ProgramWrapper::ProgramWrapper(ol_device_handle_t Device,
-                               DeviceImageWrapper &DevImage) {
+                               DeviceImageManager &DevImage) {
   assert(Device);
 
   callAndThrow(olCreateProgram, Device, DevImage.getRawData().ImageStart,
@@ -65,26 +65,22 @@ void ProgramAndKernelManager::registerFatBin(__sycl_tgt_bin_desc *FatbinDesc) {
     if (EntriesB == EntriesE)
       continue;
 
-    std::unique_ptr<DeviceImageWrapper> NewImageWrapper =
-        std::make_unique<DeviceImageWrapper>(RawDeviceImage);
+    std::unique_ptr<DeviceImageManager> NewImageWrapper =
+        std::make_unique<DeviceImageManager>(RawDeviceImage);
 
     for (auto EntriesIt = EntriesB; EntriesIt != EntriesE; ++EntriesIt) {
       auto Name = EntriesIt->SymbolName;
-      auto KernelIDIt = MKernelNameToID.find(Name);
-      if (KernelIDIt == MKernelNameToID.end()) {
-        sycl::kernel_id KernelID =
-            detail::createSyclObjFromImpl<sycl::kernel_id>(
-                std::make_shared<detail::KernelIdImpl>(Name));
-        KernelIDIt = MKernelNameToID.insert(
-            MKernelNameToID.end(),
-            std::make_pair(std::string_view(Name), KernelID));
-      }
 
-      MKernelIDToDevImageJIT.insert(
-          std::make_pair(KernelIDIt->second, NewImageWrapper.get()));
+      auto It = MDeviceKernelInfoMap.find(std::string_view(Name));
+      if (It == MDeviceKernelInfoMap.end()) {
+
+        std::ignore = MDeviceKernelInfoMap.emplace_hint(
+            It, std::piecewise_construct, std::forward_as_tuple(Name),
+            std::forward_as_tuple(Name, *NewImageWrapper));
+      }
     }
 
-    MDeviceImageWrappers.insert(
+    MDeviceImageManagers.insert(
         std::make_pair(&RawDeviceImage, std::move(NewImageWrapper)));
   }
 }
@@ -100,8 +96,8 @@ void ProgramAndKernelManager::unregisterFatBin(
   for (uint16_t I = 0; I < FatbinDesc->NumDeviceBinaries; ++I) {
     const auto &RawDeviceImage = FatbinDesc->DeviceImages[I];
 
-    auto DevImageIt = MDeviceImageWrappers.find(&RawDeviceImage);
-    if (DevImageIt == MDeviceImageWrappers.end())
+    auto DevImageIt = MDeviceImageManagers.find(&RawDeviceImage);
+    if (DevImageIt == MDeviceImageManagers.end())
       continue;
 
     const llvm::offloading::EntryTy *EntriesB = RawDeviceImage.EntriesBegin;
@@ -111,134 +107,60 @@ void ProgramAndKernelManager::unregisterFatBin(
       continue;
 
     for (auto EntriesIt = EntriesB; EntriesIt != EntriesE; ++EntriesIt) {
-      if (auto KernelIDIt = MKernelNameToID.find(EntriesIt->SymbolName);
-          KernelIDIt != MKernelNameToID.end()) {
-        MKernelIDToDevImageJIT.erase(KernelIDIt->second);
-        MKernels.erase(KernelIDIt->second);
-        MKernelNameToID.erase(KernelIDIt);
+      if (auto KernelIt = MDeviceKernelInfoMap.find(EntriesIt->SymbolName);
+          KernelIt != MDeviceKernelInfoMap.end()) {
+        // Programs are attached to image and will be release with image
+        // destruction. Clear only kernel specific data by destroying its kernel
+        // info object.
+        MDeviceKernelInfoMap.erase(KernelIt);
       }
     }
 
-    if (auto ProgramIt = MPrograms.find(DevImageIt->second.get());
-        ProgramIt != MPrograms.end()) {
-      for (auto &[Device, Program] : ProgramIt->second) {
-        MProgramWrappers.erase(Program);
-      }
-      MPrograms.erase(ProgramIt);
-    }
-
-    MDeviceImageWrappers.erase(DevImageIt);
+    MDeviceImageManagers.erase(DevImageIt);
   }
 }
 
-static bool isImageTargetCompatible(const DeviceImageWrapper &Image,
-                                    const DeviceImpl &Device) {
+static bool isImageCompatible(const DeviceImageManager &Image,
+                              const DeviceImpl &Device) {
   sycl::backend BE = Device.getBackend();
   const char *Target = Image.getRawData().TripleString;
 
-  return (strcmp(Target, DeviceBinaryTripleSPIRV64) == 0) &&
-         (BE == sycl::backend::level_zero);
-}
+  if (!(strcmp(Target, DeviceBinaryTripleSPIRV64) == 0) &&
+      (BE == sycl::backend::level_zero))
+    return false;
 
-DeviceImageWrapper *
-ProgramAndKernelManager::getDeviceImage(std::string_view KernelName,
-                                        const kernel_id &KernelID,
-                                        DeviceImpl &Device) {
-  std::lock_guard<std::mutex> Guard(MDataCollectionMutex);
-  auto [Begin, End] = MKernelIDToDevImageJIT.equal_range(KernelID);
-  if (Begin != End) {
-    bool IsValid{};
-    // TODO: with AOT (not implemented yet), we need to analyze and check
-    // olIsValidBinary for AOT binaries first.
-    for (auto It = Begin; It != End; ++It) {
-      if (isImageTargetCompatible(*It->second, Device)) {
-        callAndThrow(olIsValidBinary, Device.getOLHandle(),
-                     It->second->getRawData().ImageStart, It->second->getSize(),
-                     &IsValid);
-        if (IsValid)
-          return It->second;
-      }
-    }
-  }
-
-  throw exception(make_error_code(errc::runtime),
-                  "No kernel named " + std::string(KernelName) + " was found");
+  bool IsValid{};
+  callAndThrow(olIsValidBinary, Device.getOLHandle(),
+               Image.getRawData().ImageStart, Image.getSize(), &IsValid);
+  return IsValid;
 }
 
 ol_symbol_handle_t
-ProgramAndKernelManager::getOrCreateKernel(const char *KernelName,
+ProgramAndKernelManager::getOrCreateKernel(DeviceKernelInfo &KernelInfo,
                                            DeviceImpl &Device) {
-  std::unique_lock<std::mutex> ImageGuard(MDataCollectionMutex);
-
-  auto KernelIDIt = MKernelNameToID.find(KernelName);
-  if (KernelIDIt == MKernelNameToID.end())
-    throw exception(make_error_code(errc::runtime),
-                    "No kernel named " + std::string(KernelName) +
-                        " was found");
 
   std::lock_guard<std::mutex> KernelGuard(MDataCollectionMutex);
 
-  auto Kernel = getKernel(KernelIDIt->second, Device);
-  if (Kernel)
+  if (auto Kernel = KernelInfo.getKernel(Device.getOLHandle()))
     return Kernel;
 
-  DeviceImageWrapper *DevImage =
-      getDeviceImage(KernelName, KernelIDIt->second, Device);
-
-  ol_program_handle_t Program = getOrCreateProgram(Device, DevImage);
-  assert(Program);
-
-  Kernel = createKernel(Program, KernelIDIt->second, KernelName, Device);
-  assert(Kernel);
-  return Kernel;
-}
-
-ol_program_handle_t
-ProgramAndKernelManager::getOrCreateProgram(DeviceImpl &Device,
-                                            DeviceImageWrapper *DevImage) {
-  if (auto DevToProgramIt = MPrograms.find(DevImage);
-      DevToProgramIt != MPrograms.end()) {
-    auto ProgramIt = DevToProgramIt->second.find(Device.getOLHandle());
-    if (ProgramIt != DevToProgramIt->second.end())
-      return ProgramIt->second;
-  }
+  auto &DeviceImage = KernelInfo.getDeviceImage();
 
-  std::unique_ptr<ProgramWrapper> NewProgramWrapper(
-      new ProgramWrapper(Device.getOLHandle(), *DevImage));
-  auto Program = NewProgramWrapper->getHandle();
-  MPrograms[DevImage].insert(std::make_pair(Device.getOLHandle(), Program));
-  MProgramWrappers.insert(std::make_pair(NewProgramWrapper->getHandle(),
-                                         std::move(NewProgramWrapper)));
+  if (!isImageCompatible(DeviceImage, Device))
+    throw exception(make_error_code(errc::runtime),
+                    std::string("No compatible image for ") +
+                        KernelInfo.getName().data() + " was found");
 
-  return Program;
-}
+  auto DeviceHandle = Device.getOLHandle();
+  auto Program = DeviceImage.getOrCreateProgram(DeviceHandle);
 
-ol_symbol_handle_t ProgramAndKernelManager::createKernel(
-    ol_program_handle_t Program, const kernel_id &KernelID,
-    const char *KernelName, DeviceImpl &Device) {
-  assert((getKernel(KernelID, Device) == nullptr) &&
-         "Attempt to create kernel that already exists.");
   ol_symbol_handle_t Kernel{};
-  callAndThrow(olGetSymbol, Program, KernelName, OL_SYMBOL_KIND_KERNEL,
-               &Kernel);
-  MKernels.insert(
-      std::make_pair(KernelID, std::make_pair(Device.getOLHandle(), Kernel)));
+  callAndThrow(olGetSymbol, Program, KernelInfo.getName().data(),
+               OL_SYMBOL_KIND_KERNEL, &Kernel);
+  KernelInfo.addKernel(DeviceHandle, Kernel);
   return Kernel;
 }
 
-ol_symbol_handle_t ProgramAndKernelManager::getKernel(const kernel_id &KernelID,
-                                                      DeviceImpl &Device) {
-  auto Range = MKernels.equal_range(KernelID);
-  for (auto Kernels = Range.first; Kernels != Range.second; ++Kernels) {
-    auto &[KernelDevice, KernelSymbol] = Kernels->second;
-    if (KernelDevice == Device.getOLHandle()) {
-      assert(KernelSymbol && "Built kernel symbol can't be null");
-      return KernelSymbol;
-    }
-  }
-  return nullptr;
-}
-
 } // namespace detail
 _LIBSYCL_END_NAMESPACE_SYCL
 
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
index 5ab828b25a470..7cee3b999531d 100644
--- a/libsycl/src/detail/program_manager.hpp
+++ b/libsycl/src/detail/program_manager.hpp
@@ -19,7 +19,7 @@
 
 #include <detail/device_binary_structures.hpp>
 #include <detail/device_image_wrapper.hpp>
-#include <detail/kernel_id.hpp>
+#include <detail/device_kernel_info.hpp>
 
 #include <OffloadAPI.h>
 
@@ -47,35 +47,6 @@ namespace detail {
 
 class DeviceImpl;
 
-/// A wrapper of liboffload program handle to manage its lifetime.
-class ProgramWrapper {
-public:
-  /// Constructs ProgramWrapper by creating liboffload program with the provided
-  /// arguments.
-  ///
-  /// \param Device is a device to use for program creation.
-  /// \param DevImage is a device image (wrapped __sycl_tgt_device_image) to use
-  /// for program creation.
-  /// \throw sycl::exception with sycl::errc::runtime when failed to create
-  /// program.
-  ProgramWrapper(ol_device_handle_t Device, DeviceImageWrapper &DevImage);
-
-  /// Releases the corresponding liboffload program handle by calling
-  /// olDestroyProgram.
-  ~ProgramWrapper();
-
-  ProgramWrapper(const ProgramWrapper &) = delete;
-  ProgramWrapper &operator=(const ProgramWrapper &) = delete;
-  ProgramWrapper(ProgramWrapper &&) = delete;
-  ProgramWrapper &operator=(ProgramWrapper &&) = delete;
-
-  /// \return the corresponding liboffload program handle.
-  ol_program_handle_t getHandle() { return MProgram; }
-
-private:
-  ol_program_handle_t MProgram{};
-};
-
 /// A class to manage programs and kernels.
 class ProgramAndKernelManager {
 
@@ -102,12 +73,12 @@ class ProgramAndKernelManager {
 
   /// Creates liboffload kernel that is ready for execution.
   /// Thread-safe.
-  /// \param KernelName a null-terminated string representing a name of kernel
-  /// to be created.
+  /// \param KernelInfo a set of kernel specific data: name, corresponding
+  /// device image, etc.
   /// \param Device a device for which this kernel must be compiled.
   /// \return liboffload kernel handle that is ready to be passed to kernel
   /// execution methods.
-  ol_symbol_handle_t getOrCreateKernel(const char *KernelName,
+  ol_symbol_handle_t getOrCreateKernel(DeviceKernelInfo &KernelInfo,
                                        DeviceImpl &Device);
 
 private:
@@ -116,72 +87,21 @@ class ProgramAndKernelManager {
   ProgramAndKernelManager(ProgramAndKernelManager const &) = delete;
   ProgramAndKernelManager &operator=(ProgramAndKernelManager const &) = delete;
 
-  /// Searches for a device image that contains the requested kernel and is
-  /// compatible with the requested device.
-  /// This call must be protected with MDataCollectionMutex.
-  /// \param KernelName a null-terminated string representing the name of the
-  /// kernel to obtain a device image for.
-  /// \param KernelID a kernel id matching KernelName.
-  /// \param Device a device with which device image must be compatible.
-  /// \throw sycl::exception with sycl::errc::runtime if the device image
-  /// validation failed in liboffload or if no compatible image was found.
-  DeviceImageWrapper *getDeviceImage(std::string_view KernelName,
-                                     const kernel_id &KernelID,
-                                     DeviceImpl &Device);
-
-  /// Searches for or creates a program.
-  /// This call must be protected with MDataCollectionMutex since it updates
-  /// MPrograms and MProgramWrappers collections.
-  /// \param Device a device that program must be created with.
-  /// \param DevImage a device image to get or create program with.
-  /// \return liboffload program for the requested configuration.
-  ol_program_handle_t getOrCreateProgram(DeviceImpl &Device,
-                                         DeviceImageWrapper *DevImage);
-
-  /// Creates kernel from program.
-  /// This call must be protected with MDataCollectionMutex since it updates
-  /// MKernels collection.
-  /// \param Program a program to create kernel with.
-  /// \param KernelID an id of kernel to create.
-  /// \param KernelName a null-terminated string representing the name of kernel
-  /// to create.
-  /// \param Device a device that kernel must be created with.
-  /// \return liboffload kernel for the requested configuration.
-  ol_symbol_handle_t createKernel(ol_program_handle_t Program,
-                                  const kernel_id &KernelID,
-                                  const char *KernelName, DeviceImpl &Device);
-
-  /// Searches for kernel.
-  /// This call must be protected with MDataCollectionMutex since it reads
-  /// MKernels collection.
-  /// \param KernelID an id of kernel to look for.
-  /// \param Device a device that kernel must be created with.
-  /// \return liboffload kernel for the requested configuration or nullptr if
-  /// such kernel is not found.
-  ol_symbol_handle_t getKernel(const kernel_id &KernelID, DeviceImpl &Device);
-
   // Filled by registerFatBin(...).
-  std::unordered_map<std::string_view, kernel_id> MKernelNameToID;
-  std::unordered_map<kernel_id, DeviceImageWrapper *> MKernelIDToDevImageJIT;
-  // Controls lifetime of device image ptr and wrapper.
-  std::unordered_map<const __sycl_tgt_device_image *,
-                     std::unique_ptr<DeviceImageWrapper>>
-      MDeviceImageWrappers;
-
-  std::mutex MDataCollectionMutex;
+  // Map for storing device kernel information. Runtime lookup should be avoided
+  // by caching the pointers when possible.
+  std::unordered_map<std::string_view, DeviceKernelInfo> MDeviceKernelInfoMap;
 
-  // Filled by getOrCreateKernel and everything it calls inside.
-  std::unordered_map<
-      DeviceImageWrapper *,
-      std::unordered_map<ol_device_handle_t, ol_program_handle_t>>
-      MPrograms;
-  std::unordered_multimap<kernel_id,
-                          std::pair<ol_device_handle_t, ol_symbol_handle_t>>
-      MKernels;
+  // Controls lifetime of device images.
+  std::unordered_map<const __sycl_tgt_device_image *,
+                     std::unique_ptr<DeviceImageManager>>
+      MDeviceImageManagers;
 
   // Controls lifetime of programs.
   std::unordered_map<ol_program_handle_t, std::unique_ptr<ProgramWrapper>>
       MProgramWrappers;
+
+  std::mutex MDataCollectionMutex;
 };
 
 } // namespace detail

>From 32c3481ddf8956d0ee43f430b93ad352b9821412 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Tue, 14 Apr 2026 02:56:59 -0700
Subject: [PATCH 13/25] code cleanup

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/src/detail/device_image_wrapper.hpp | 13 ++++--
 libsycl/src/detail/device_impl.hpp          |  1 +
 libsycl/src/detail/device_kernel_info.hpp   | 49 +++++++++++++++------
 libsycl/src/detail/program_manager.cpp      |  2 +-
 libsycl/src/detail/program_manager.hpp      |  6 +--
 5 files changed, 48 insertions(+), 23 deletions(-)

diff --git a/libsycl/src/detail/device_image_wrapper.hpp b/libsycl/src/detail/device_image_wrapper.hpp
index 11b0d67c4471e..d2b952acb39ed 100644
--- a/libsycl/src/detail/device_image_wrapper.hpp
+++ b/libsycl/src/detail/device_image_wrapper.hpp
@@ -7,8 +7,8 @@
 //===----------------------------------------------------------------------===//
 ///
 /// \file
-/// This file contains the declaration of the helper for raw device image
-/// parsing and iteration.
+/// This file contains the declaration of the helpers for device images and
+/// programs.
 ///
 //===----------------------------------------------------------------------===//
 
@@ -51,7 +51,7 @@ class ProgramWrapper {
   ProgramWrapper &operator=(ProgramWrapper &&) = delete;
 
   /// \return the corresponding liboffload program handle.
-  ol_program_handle_t getHandle() { return MProgram; }
+  ol_program_handle_t getOLHandle() { return MProgram; }
 
 private:
   ol_program_handle_t MProgram{};
@@ -80,6 +80,11 @@ class DeviceImageManager {
     return static_cast<size_t>(MBin->ImageEnd - MBin->ImageStart);
   }
 
+  ///  Returns liboffload program handle by lookup of existing programs or by
+  ///  creation of a new one from this image.
+  /// \param DeviceHandle liboffload handle of device the program must be
+  /// compatible with.
+  /// \return liboffload handle of the program compatible with specified device.
   ol_program_handle_t getOrCreateProgram(ol_device_handle_t DeviceHandle) {
     auto ProgramIt = MPrograms.find(DeviceHandle);
     if (ProgramIt == MPrograms.end()) {
@@ -89,7 +94,7 @@ class DeviceImageManager {
                                  std::forward_as_tuple(DeviceHandle, *this));
     }
 
-    return ProgramIt->second.getHandle();
+    return ProgramIt->second.getOLHandle();
   }
 
 protected:
diff --git a/libsycl/src/detail/device_impl.hpp b/libsycl/src/detail/device_impl.hpp
index 345047b57f891..f5012fe84c069 100644
--- a/libsycl/src/detail/device_impl.hpp
+++ b/libsycl/src/detail/device_impl.hpp
@@ -121,6 +121,7 @@ class DeviceImpl {
       static_assert(false && "Info descriptor is not properly supported");
   }
 
+  /// \return the corresponding liboffload device handle.
   ol_device_handle_t getOLHandle() const { return MOffloadDevice; }
 
 private:
diff --git a/libsycl/src/detail/device_kernel_info.hpp b/libsycl/src/detail/device_kernel_info.hpp
index 89d33431a95f4..6c9928f711ee2 100644
--- a/libsycl/src/detail/device_kernel_info.hpp
+++ b/libsycl/src/detail/device_kernel_info.hpp
@@ -7,7 +7,9 @@
 //===----------------------------------------------------------------------===//
 ///
 /// \file
-/// to add
+/// This file contains the declaration of the class that aggregates information
+/// specific to device kernels (i.e. information that is uniform between
+/// different submissions of the same kernel).
 ///
 //===----------------------------------------------------------------------===//
 
@@ -21,35 +23,54 @@
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
 
-// This class aggregates information specific to device kernels (i.e.
-// information that is uniform between different submissions of the same
-// kernel). Pointers to instances of this class are stored in header function
-// templates as a static variable to avoid repeated runtime lookup overhead.
+class ProgramAndKernelManager;
+
+// Pointers to instances of this class are stored in header function templates
+// as a static variable to avoid repeated runtime lookup overhead.
 class DeviceKernelInfo {
 public:
+  /// Constructs device kernel info instance.
+  ///
+  /// \param KernelName a name of kernel.
+  /// \param DeviceImage a device image containing device code of this kernel.
   DeviceKernelInfo(std::string_view KernelName, DeviceImageManager &DeviceImage)
       : MName(KernelName), MDeviceImage(DeviceImage) {}
 
-  ol_symbol_handle_t getKernel(ol_device_handle_t Device) {
+  /// \return the name of this kernel.
+  std::string_view getName() { return MName; }
+
+private:
+  std::unordered_map<ol_device_handle_t, ol_symbol_handle_t> MBuiltKernels;
+
+  std::string_view MName;
+  DeviceImageManager &MDeviceImage;
+
+  /// Searches for the existing kernel handle compatible with the specified
+  /// device.
+  /// \param Device a device the kernel must be compatible with.
+  /// \return a liboffload kernel handle if and only if built kernel was found,
+  /// otherwise returns nullptr.
+  ol_symbol_handle_t getKernel(ol_device_handle_t Device) const {
     if (auto KernelIt = MBuiltKernels.find(Device);
         KernelIt != MBuiltKernels.end())
       return KernelIt->second;
     return nullptr;
   }
 
-  DeviceImageManager &getDeviceImage() { return MDeviceImage; }
-  std::string_view getName() { return MName; }
+  /// \return device image which contains device code of this kernel.
+  DeviceImageManager &getDeviceImage() const { return MDeviceImage; }
 
+  /// Attaches liboffload kernel handle to this device kernel info object.
+  /// \param Device the device the kernel symbol was created for.
+  /// \param Kernel the liboffload kernel symbol to attach..
   void addKernel(ol_device_handle_t Device, ol_symbol_handle_t Kernel) {
-    assert(MBuiltKernels.find(Device) != MBuiltKernels.end());
+    assert(Kernel && Device &&
+           MBuiltKernels.find(Device) == MBuiltKernels.end());
     MBuiltKernels.insert({Device, Kernel});
   }
 
-private:
-  std::unordered_map<ol_device_handle_t, ol_symbol_handle_t> MBuiltKernels;
-
-  std::string_view MName;
-  DeviceImageManager &MDeviceImage;
+  /// Kernel info update is intended to be done only by ProgramAndKernelManager.
+  friend class ProgramAndKernelManager;
 };
 
 } // namespace detail
diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
index 19e9017cc3a09..7cec508921279 100644
--- a/libsycl/src/detail/program_manager.cpp
+++ b/libsycl/src/detail/program_manager.cpp
@@ -109,7 +109,7 @@ void ProgramAndKernelManager::unregisterFatBin(
     for (auto EntriesIt = EntriesB; EntriesIt != EntriesE; ++EntriesIt) {
       if (auto KernelIt = MDeviceKernelInfoMap.find(EntriesIt->SymbolName);
           KernelIt != MDeviceKernelInfoMap.end()) {
-        // Programs are attached to image and will be release with image
+        // Programs are attached to image and will be released with image
         // destruction. Clear only kernel specific data by destroying its kernel
         // info object.
         MDeviceKernelInfoMap.erase(KernelIt);
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
index 7cee3b999531d..d3b64c154fd2f 100644
--- a/libsycl/src/detail/program_manager.hpp
+++ b/libsycl/src/detail/program_manager.hpp
@@ -97,10 +97,8 @@ class ProgramAndKernelManager {
                      std::unique_ptr<DeviceImageManager>>
       MDeviceImageManagers;
 
-  // Controls lifetime of programs.
-  std::unordered_map<ol_program_handle_t, std::unique_ptr<ProgramWrapper>>
-      MProgramWrappers;
-
+  // All work with device images and data related to it must be wrapped with a
+  // lock of this mutex.
   std::mutex MDataCollectionMutex;
 };
 

>From 22cf0f4a8725f66c076cd20a7301f11d1af72fef Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Wed, 15 Apr 2026 09:34:11 -0700
Subject: [PATCH 14/25] fix comments

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/src/CMakeLists.txt                  |  1 +
 libsycl/src/detail/device_image_wrapper.cpp | 39 +++++++++++++++++++++
 libsycl/src/detail/device_image_wrapper.hpp | 37 ++++++++-----------
 libsycl/src/detail/device_kernel_info.hpp   | 23 ++++++------
 libsycl/src/detail/program_manager.cpp      | 26 ++++----------
 libsycl/src/detail/program_manager.hpp      |  6 ++--
 6 files changed, 76 insertions(+), 56 deletions(-)
 create mode 100644 libsycl/src/detail/device_image_wrapper.cpp

diff --git a/libsycl/src/CMakeLists.txt b/libsycl/src/CMakeLists.txt
index 7b9826fb8a3de..5b75db713353a 100644
--- a/libsycl/src/CMakeLists.txt
+++ b/libsycl/src/CMakeLists.txt
@@ -95,6 +95,7 @@ set(LIBSYCL_SOURCES
     "usm_functions.cpp"
     "detail/context_impl.cpp"
     "detail/event_impl.cpp"
+    "detail/device_image_wrapper.cpp"
     "detail/device_impl.cpp"
     "detail/global_objects.cpp"
     "detail/platform_impl.cpp"
diff --git a/libsycl/src/detail/device_image_wrapper.cpp b/libsycl/src/detail/device_image_wrapper.cpp
new file mode 100644
index 0000000000000..a150feb6516e6
--- /dev/null
+++ b/libsycl/src/detail/device_image_wrapper.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <detail/device_image_wrapper.hpp>
+
+#include <detail/offload/offload_utils.hpp>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+namespace detail {
+
+ProgramWrapper::ProgramWrapper(ol_device_handle_t Device,
+                               DeviceImageManager &DevImage) {
+  assert(Device);
+
+  callAndThrow(olCreateProgram, Device, DevImage.getRawData().ImageStart,
+               DevImage.getSize(), &MProgram);
+}
+
+ProgramWrapper::~ProgramWrapper() {
+  assert(MProgram);
+  std::ignore = olDestroyProgram(MProgram);
+  // TODO: define a way to report errors from dtors.
+}
+
+ol_program_handle_t
+DeviceImageManager::getOrCreateProgram(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));
+  return Iterator->second.getOLHandle();
+}
+
+} // namespace detail
+_LIBSYCL_END_NAMESPACE_SYCL
\ No newline at end of file
diff --git a/libsycl/src/detail/device_image_wrapper.hpp b/libsycl/src/detail/device_image_wrapper.hpp
index d2b952acb39ed..2695264f89d2b 100644
--- a/libsycl/src/detail/device_image_wrapper.hpp
+++ b/libsycl/src/detail/device_image_wrapper.hpp
@@ -21,7 +21,7 @@
 
 #include <OffloadAPI.h>
 
-#include <map>
+#include <unordered_map>
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
@@ -31,13 +31,13 @@ class DeviceImageManager;
 /// A wrapper of liboffload program handle to manage its lifetime.
 class ProgramWrapper {
 public:
-  /// Constructs ProgramWrapper by creating liboffload program with the provided
-  /// arguments.
+  /// Constructs ProgramWrapper by creating a liboffload program with the
+  /// provided arguments.
   ///
-  /// \param Device is a device to use for program creation.
-  /// \param DevImage is a device image (wrapped __sycl_tgt_device_image) to use
-  /// for program creation.
-  /// \throw sycl::exception with sycl::errc::runtime when failed to create
+  /// \param Device is the device to use for program creation.
+  /// \param DevImage is the device image (wrapped __sycl_tgt_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);
 
@@ -80,22 +80,13 @@ class DeviceImageManager {
     return static_cast<size_t>(MBin->ImageEnd - MBin->ImageStart);
   }
 
-  ///  Returns liboffload program handle by lookup of existing programs or by
-  ///  creation of a new one from this image.
-  /// \param DeviceHandle liboffload handle of device the program must be
-  /// compatible with.
-  /// \return liboffload handle of the program compatible with specified device.
-  ol_program_handle_t getOrCreateProgram(ol_device_handle_t DeviceHandle) {
-    auto ProgramIt = MPrograms.find(DeviceHandle);
-    if (ProgramIt == MPrograms.end()) {
-      ProgramIt =
-          MPrograms.emplace_hint(ProgramIt, std::piecewise_construct,
-                                 std::forward_as_tuple(DeviceHandle),
-                                 std::forward_as_tuple(DeviceHandle, *this));
-    }
-
-    return ProgramIt->second.getOLHandle();
-  }
+  /// Returns a liboffload program handle by looking up existing programs or
+  /// creating a new one from this image.
+  /// \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);
 
 protected:
   std::unordered_map<ol_device_handle_t, ProgramWrapper> MPrograms;
diff --git a/libsycl/src/detail/device_kernel_info.hpp b/libsycl/src/detail/device_kernel_info.hpp
index 6c9928f711ee2..2d5fdf9fe9ee9 100644
--- a/libsycl/src/detail/device_kernel_info.hpp
+++ b/libsycl/src/detail/device_kernel_info.hpp
@@ -25,14 +25,15 @@ namespace detail {
 
 class ProgramAndKernelManager;
 
-// Pointers to instances of this class are stored in header function templates
-// as a static variable to avoid repeated runtime lookup overhead.
+// TODO: Pointers to instances of this class are supported to be stored in
+// header function templates as a static variable to avoid repeated runtime
+// lookup overhead.
 class DeviceKernelInfo {
 public:
-  /// Constructs device kernel info instance.
+  /// Constructs a device kernel info instance.
   ///
-  /// \param KernelName a name of kernel.
-  /// \param DeviceImage a device image containing device code of this kernel.
+  /// \param KernelName the name of the kernel.
+  /// \param DeviceImage the device image containing device code of this kernel.
   DeviceKernelInfo(std::string_view KernelName, DeviceImageManager &DeviceImage)
       : MName(KernelName), MDeviceImage(DeviceImage) {}
 
@@ -47,9 +48,9 @@ class DeviceKernelInfo {
 
   /// Searches for the existing kernel handle compatible with the specified
   /// device.
-  /// \param Device a device the kernel must be compatible with.
-  /// \return a liboffload kernel handle if and only if built kernel was found,
-  /// otherwise returns nullptr.
+  /// \param Device the device the kernel must be compatible with.
+  /// \return a liboffload kernel handle if a built kernel was found; otherwise
+  /// returns nullptr.
   ol_symbol_handle_t getKernel(ol_device_handle_t Device) const {
     if (auto KernelIt = MBuiltKernels.find(Device);
         KernelIt != MBuiltKernels.end())
@@ -57,12 +58,12 @@ class DeviceKernelInfo {
     return nullptr;
   }
 
-  /// \return device image which contains device code of this kernel.
+  /// \return the device image containing the device code of this kernel.
   DeviceImageManager &getDeviceImage() const { return MDeviceImage; }
 
-  /// Attaches liboffload kernel handle to this device kernel info object.
+  /// Attaches a liboffload kernel handle to this device kernel info object.
   /// \param Device the device the kernel symbol was created for.
-  /// \param Kernel the liboffload kernel symbol to attach..
+  /// \param Kernel the liboffload kernel symbol to attach.
   void addKernel(ol_device_handle_t Device, ol_symbol_handle_t Kernel) {
     assert(Kernel && Device &&
            MBuiltKernels.find(Device) == MBuiltKernels.end());
diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
index 7cec508921279..90d7c48d3d1c7 100644
--- a/libsycl/src/detail/program_manager.cpp
+++ b/libsycl/src/detail/program_manager.cpp
@@ -18,20 +18,6 @@
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
 
-ProgramWrapper::ProgramWrapper(ol_device_handle_t Device,
-                               DeviceImageManager &DevImage) {
-  assert(Device);
-
-  callAndThrow(olCreateProgram, Device, DevImage.getRawData().ImageStart,
-               DevImage.getSize(), &MProgram);
-}
-
-ProgramWrapper::~ProgramWrapper() {
-  assert(MProgram);
-  std::ignore = olDestroyProgram(MProgram);
-  // TODO: define a way to report errors from dtors.
-}
-
 static inline bool checkFatBinVersion(const __sycl_tgt_bin_desc &FatbinDesc) {
   return FatbinDesc.Version == SupportedOffloadBinaryVersion;
 }
@@ -74,9 +60,11 @@ void ProgramAndKernelManager::registerFatBin(__sycl_tgt_bin_desc *FatbinDesc) {
       auto It = MDeviceKernelInfoMap.find(std::string_view(Name));
       if (It == MDeviceKernelInfoMap.end()) {
 
-        std::ignore = MDeviceKernelInfoMap.emplace_hint(
-            It, std::piecewise_construct, std::forward_as_tuple(Name),
-            std::forward_as_tuple(Name, *NewImageWrapper));
+        [[maybe_unused]] auto [Iterator, EmplaceSucceeded] =
+            MDeviceKernelInfoMap.emplace(
+                std::piecewise_construct, std::forward_as_tuple(Name),
+                std::forward_as_tuple(Name, *NewImageWrapper));
+        assert(EmplaceSucceeded && "Kernel name found in multiple images");
       }
     }
 
@@ -125,8 +113,8 @@ static bool isImageCompatible(const DeviceImageManager &Image,
   sycl::backend BE = Device.getBackend();
   const char *Target = Image.getRawData().TripleString;
 
-  if (!(strcmp(Target, DeviceBinaryTripleSPIRV64) == 0) &&
-      (BE == sycl::backend::level_zero))
+  if (!(strcmp(Target, DeviceBinaryTripleSPIRV64) == 0 &&
+        BE == sycl::backend::level_zero))
     return false;
 
   bool IsValid{};
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
index d3b64c154fd2f..60b781e3b66e5 100644
--- a/libsycl/src/detail/program_manager.hpp
+++ b/libsycl/src/detail/program_manager.hpp
@@ -71,12 +71,12 @@ class ProgramAndKernelManager {
   /// data passed to registerFatBin.
   void unregisterFatBin(__sycl_tgt_bin_desc *FatbinDesc);
 
-  /// Creates liboffload kernel that is ready for execution.
+  /// Creates a liboffload kernel that is ready for execution.
   /// Thread-safe.
   /// \param KernelInfo a set of kernel specific data: name, corresponding
   /// device image, etc.
-  /// \param Device a device for which this kernel must be compiled.
-  /// \return liboffload kernel handle that is ready to be passed to kernel
+  /// \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,
                                        DeviceImpl &Device);

>From dd46e5f2dfb639300f72fc0de1dba60a36c6779e Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Thu, 16 Apr 2026 05:12:00 -0700
Subject: [PATCH 15/25] fix comments

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/src/detail/device_image_wrapper.cpp |  2 +-
 libsycl/src/detail/device_image_wrapper.hpp |  5 +++--
 libsycl/src/detail/device_kernel_info.hpp   | 14 ++++++++------
 libsycl/src/detail/program_manager.hpp      |  2 +-
 4 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/libsycl/src/detail/device_image_wrapper.cpp b/libsycl/src/detail/device_image_wrapper.cpp
index a150feb6516e6..36a7be0e67f87 100644
--- a/libsycl/src/detail/device_image_wrapper.cpp
+++ b/libsycl/src/detail/device_image_wrapper.cpp
@@ -36,4 +36,4 @@ DeviceImageManager::getOrCreateProgram(ol_device_handle_t DeviceHandle) {
 }
 
 } // namespace detail
-_LIBSYCL_END_NAMESPACE_SYCL
\ No newline at end of file
+_LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/detail/device_image_wrapper.hpp b/libsycl/src/detail/device_image_wrapper.hpp
index 2695264f89d2b..73bdd8e7f7df1 100644
--- a/libsycl/src/detail/device_image_wrapper.hpp
+++ b/libsycl/src/detail/device_image_wrapper.hpp
@@ -80,8 +80,9 @@ class DeviceImageManager {
     return static_cast<size_t>(MBin->ImageEnd - MBin->ImageStart);
   }
 
-  /// Returns a liboffload program handle by looking up existing programs or
-  /// creating a new one from this image.
+  /// 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 DeviceHandle the liboffload handle of the device the program must
   /// be compatible with.
   /// \return the liboffload handle of the program compatible with the specified
diff --git a/libsycl/src/detail/device_kernel_info.hpp b/libsycl/src/detail/device_kernel_info.hpp
index 2d5fdf9fe9ee9..148e2e366777d 100644
--- a/libsycl/src/detail/device_kernel_info.hpp
+++ b/libsycl/src/detail/device_kernel_info.hpp
@@ -52,10 +52,10 @@ class DeviceKernelInfo {
   /// \return a liboffload kernel handle if a built kernel was found; otherwise
   /// returns nullptr.
   ol_symbol_handle_t getKernel(ol_device_handle_t Device) const {
-    if (auto KernelIt = MBuiltKernels.find(Device);
-        KernelIt != MBuiltKernels.end())
-      return KernelIt->second;
-    return nullptr;
+    auto KernelIt = MBuiltKernels.find(Device);
+    if (KernelIt == MBuiltKernels.end())
+      return nullptr;
+    return KernelIt->second;
   }
 
   /// \return the device image containing the device code of this kernel.
@@ -65,8 +65,10 @@ class DeviceKernelInfo {
   /// \param Device the device the kernel symbol was created for.
   /// \param Kernel the liboffload kernel symbol to attach.
   void addKernel(ol_device_handle_t Device, ol_symbol_handle_t Kernel) {
-    assert(Kernel && Device &&
-           MBuiltKernels.find(Device) == MBuiltKernels.end());
+    assert(Kernel && "Invalid liboffload kernel handle");
+    assert(Device && "Invalid liboffload device handle");
+    assert((MBuiltKernels.find(Device) == MBuiltKernels.end()) &&
+           "Kernel is being managed already");
     MBuiltKernels.insert({Device, Kernel});
   }
 
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
index 60b781e3b66e5..f5d3a1c8dc6dd 100644
--- a/libsycl/src/detail/program_manager.hpp
+++ b/libsycl/src/detail/program_manager.hpp
@@ -72,7 +72,7 @@ class ProgramAndKernelManager {
   void unregisterFatBin(__sycl_tgt_bin_desc *FatbinDesc);
 
   /// Creates a liboffload kernel that is ready for execution.
-  /// Thread-safe.
+  /// This method is thread-safe (protected with MDataCollectionMutex).
   /// \param KernelInfo a set of kernel specific data: name, corresponding
   /// device image, etc.
   /// \param Device the device for which this kernel must be compiled.

>From 79372977bc5f059e5d7602d6ca1cccc52798b702 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Wed, 15 Apr 2026 05:55:42 -0700
Subject: [PATCH 16/25] single_task on top of getKernelInfo

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/docs/index.rst                        |   4 +
 .../__impl/detail/get_device_kernel_info.hpp  |  43 +++++++
 .../sycl/__impl/detail/unified_range_view.hpp |  51 ++++++++
 libsycl/include/sycl/__impl/queue.hpp         |  99 ++++++++++++++++
 libsycl/src/detail/global_objects.cpp         |   2 +
 libsycl/src/detail/program_manager.cpp        |  17 +++
 libsycl/src/detail/program_manager.hpp        |   6 +
 libsycl/src/detail/queue_impl.cpp             | 111 ++++++++++++++++++
 libsycl/src/detail/queue_impl.hpp             |  39 ++++++
 libsycl/src/queue.cpp                         |  19 +++
 10 files changed, 391 insertions(+)
 create mode 100644 libsycl/include/sycl/__impl/detail/get_device_kernel_info.hpp
 create mode 100644 libsycl/include/sycl/__impl/detail/unified_range_view.hpp

diff --git a/libsycl/docs/index.rst b/libsycl/docs/index.rst
index 9aa36b4a54c57..04691a96a188a 100644
--- a/libsycl/docs/index.rst
+++ b/libsycl/docs/index.rst
@@ -113,6 +113,10 @@ TODO for added SYCL classes
   * to implement submit & copy with accessors (low priority)
   * get_info & properties
   * ctors that accepts context (blocked by lack of liboffload support)
+  * nd_range kernel submissions
+  * cross-context events wait (host tasks are needed)
+  * implement check if lambda arguments are device copyable (requires clang support of corresponding builtins) unless FE will fully cover it
+  * kernel instantiating on host (debugging purposes)
 
 * ``property_list``: to fully implement and integrate with existing SYCL runtime classes supporting it
 * usm allocations:
diff --git a/libsycl/include/sycl/__impl/detail/get_device_kernel_info.hpp b/libsycl/include/sycl/__impl/detail/get_device_kernel_info.hpp
new file mode 100644
index 0000000000000..292755037410e
--- /dev/null
+++ b/libsycl/include/sycl/__impl/detail/get_device_kernel_info.hpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains helper function to query kernel info that is uniform
+/// between different submissions of the same kernel.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL_GET_DEV_INFO
+#define _LIBSYCL_GET_DEV_INFO
+
+#include <sycl/__impl/detail/config.hpp>
+
+#include <string_view>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+namespace detail {
+
+class DeviceKernelInfo;
+// Lifetime of the underlying `DeviceKernelInfo` is tied to the availability of
+// the `sycl_device_binaries` corresponding to this kernel. In other words, once
+// user library is unloaded (see __sycl_unregister_lib), program manager
+// destroys this `DeviceKernelInfo` object and the reference returned from here
+// becomes stale.
+_LIBSYCL_EXPORT DeviceKernelInfo &getDeviceKernelInfo(std::string_view);
+
+template <class KernelName>
+DeviceKernelInfo &getDeviceKernelInfo(std::string_view KernelNameStr) {
+  static DeviceKernelInfo &Info = getDeviceKernelInfo(KernelNameStr);
+  return Info;
+}
+
+} // namespace detail
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL_GET_DEV_INFO
diff --git a/libsycl/include/sycl/__impl/detail/unified_range_view.hpp b/libsycl/include/sycl/__impl/detail/unified_range_view.hpp
new file mode 100644
index 0000000000000..4bcaa48eec757
--- /dev/null
+++ b/libsycl/include/sycl/__impl/detail/unified_range_view.hpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains helper function class to unify ABI for different kernel
+/// ranges.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBSYCL___IMPL_DETAIL_UNIFIED_RANGE_VIEW_HPP
+#define _LIBSYCL___IMPL_DETAIL_UNIFIED_RANGE_VIEW_HPP
+
+#include <sycl/__impl/detail/config.hpp>
+
+_LIBSYCL_BEGIN_NAMESPACE_SYCL
+
+namespace detail {
+
+/// The structure to keep dimension and references to ranges unified for
+/// all dimensions.
+struct UnifiedRangeView {
+  /// Default contructed view matches the single task execution range.
+  UnifiedRangeView() = default;
+  UnifiedRangeView(const UnifiedRangeView &Desc) = default;
+  UnifiedRangeView(UnifiedRangeView &&Desc) = default;
+  UnifiedRangeView &operator=(const UnifiedRangeView &Desc) = default;
+  UnifiedRangeView &operator=(UnifiedRangeView &&Desc) = default;
+  ~UnifiedRangeView() = default;
+
+  // TODO: ctors with sycl::range and nd::range will be added later.
+
+  UnifiedRangeView(const size_t *GlobalSize, const size_t *LocalSize,
+                   const size_t *Offset, size_t Dims)
+      : MGlobalSize(GlobalSize), MLocalSize(LocalSize), MOffset(Offset),
+        MDims(Dims) {}
+
+  const size_t *MGlobalSize = nullptr;
+  const size_t *MLocalSize = nullptr;
+  const size_t *MOffset = nullptr;
+  size_t MDims = 1;
+};
+} // namespace detail
+
+_LIBSYCL_END_NAMESPACE_SYCL
+
+#endif // _LIBSYCL___IMPL_DETAIL_UNIFIED_RANGE_VIEW_HPP
diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index 41b018b681b8e..991ebe7f2a666 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -17,11 +17,14 @@
 
 #include <sycl/__impl/async_handler.hpp>
 #include <sycl/__impl/device.hpp>
+#include <sycl/__impl/event.hpp>
 #include <sycl/__impl/property_list.hpp>
 
 #include <sycl/__impl/detail/config.hpp>
 #include <sycl/__impl/detail/default_async_handler.hpp>
+#include <sycl/__impl/detail/get_device_kernel_info.hpp>
 #include <sycl/__impl/detail/obj_utils.hpp>
+#include <sycl/__impl/detail/unified_range_view.hpp>
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
@@ -29,6 +32,27 @@ class context;
 
 namespace detail {
 class QueueImpl;
+
+template <typename, typename T> struct CheckFunctionSignature {
+  static_assert(std::integral_constant<T, false>::value,
+                "Second template parameter is required to be of function type");
+};
+
+template <typename F, typename RetT, typename... Args>
+struct CheckFunctionSignature<F, RetT(Args...)> {
+private:
+  template <typename T>
+  static constexpr auto check(T *) -> typename std::is_same<
+      decltype(std::declval<T>().operator()(std::declval<Args>()...)),
+      RetT>::type;
+
+  template <typename> static constexpr std::false_type check(...);
+
+  using type = decltype(check<F>(0));
+
+public:
+  static constexpr bool value = type::value;
+};
 } // namespace detail
 
 // SYCL 2020 4.6.5. Queue class.
@@ -139,7 +163,82 @@ class _LIBSYCL_EXPORT queue {
   /// exceptions.
   void wait();
 
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type.
+  ///
+  /// \param kernelFunc is the kernel functor or lambda.
+  /// \return an event that represents the status of the submitted kernel.
+  template <typename KernelName, typename KernelType>
+  event single_task(const KernelType &kernelFunc) {
+    return single_task<KernelName, KernelType>({}, kernelFunc);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type.
+  ///
+  /// \param depEvent is an event that specifies the kernel dependency.
+  /// \param kernelFunc is the kernel functor or lambda.
+  /// \return an event that represents the status of the submitted kernel.
+  template <typename KernelName, typename KernelType>
+  event single_task(event depEvent, const KernelType &kernelFunc) {
+    return single_task<KernelName, KernelType>({depEvent}, kernelFunc);
+  }
+
+  /// Defines and invokes a SYCL kernel function as a lambda expression or a
+  /// named function object type.
+  ///
+  /// \param depEvents is a collection of events that specify the kernel
+  /// dependencies.
+  /// \param kernelFunc is the kernel functor or lambda.
+  /// \return an event that represents the status of the submitted kernel.
+  template <typename KernelName, typename KernelType>
+  event single_task(const std::vector<event> &depEvents,
+                    const KernelType &kernelFunc) {
+    static_assert(
+        detail::CheckFunctionSignature<std::remove_reference_t<KernelType>,
+                                       void()>::value,
+        "sycl::queue::single_task() requires a kernel instead of a command "
+        "group");
+
+    setKernelParameters(depEvents);
+    submitSingleTask<KernelName, KernelType>(kernelFunc);
+    return getLastEvent();
+  }
+
 private:
+  // Name of this function is defined by compiler. It generates call to this
+  // function in the host implementation of KernelFunc in submitSingleTask.
+  template <typename KN, typename... Args>
+  void sycl_kernel_launch(const char *KernelName, Args &&...args) {
+    static_assert(
+        sizeof...(args) == 1,
+        "sycl_kernel_launch expects only 2 arguments now: name of kernel and "
+        "callable object passed to kernel invocation by the user.");
+
+    auto FirstArg = std::get<0>(std::tie(args...));
+    submitKernelImpl(detail::getDeviceKernelInfo<KN>(KernelName), &FirstArg,
+                     sizeof(FirstArg));
+  }
+
+#ifdef SYCL_LANGUAGE_VERSION
+#  define _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)                              \
+    [[clang::sycl_kernel_entry_point(KernelName)]]
+#else
+#  define _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
+#endif // SYCL_LANGUAGE_VERSION
+
+  template <typename KernelName, typename KernelType>
+  _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
+  void submitSingleTask(const KernelType &KernelFunc) {
+    KernelFunc();
+  }
+
+  event getLastEvent();
+  void submitKernelImpl(detail::DeviceKernelInfo &KernelInfo, void *ArgData,
+                        size_t ArgSize);
+  void setKernelParameters(const std::vector<event> &Events,
+                           const detail::UnifiedRangeView &Range = {});
+
   queue(const std::shared_ptr<detail::QueueImpl> &Impl) : impl(Impl) {}
   std::shared_ptr<detail::QueueImpl> impl;
 
diff --git a/libsycl/src/detail/global_objects.cpp b/libsycl/src/detail/global_objects.cpp
index 35e32985e7cbb..fd94d772337d6 100644
--- a/libsycl/src/detail/global_objects.cpp
+++ b/libsycl/src/detail/global_objects.cpp
@@ -8,6 +8,7 @@
 
 #include <detail/global_objects.hpp>
 #include <detail/platform_impl.hpp>
+#include <detail/program_manager.hpp>
 
 #ifdef _WIN32
 #  include <windows.h>
@@ -31,6 +32,7 @@ struct StaticVarShutdownHandler {
   StaticVarShutdownHandler &
   operator=(const StaticVarShutdownHandler &) = delete;
   ~StaticVarShutdownHandler() {
+    ProgramAndKernelManager::getInstance().releaseResources();
     // No error reporting in shutdown
     std::ignore = olShutDown();
   }
diff --git a/libsycl/src/detail/program_manager.cpp b/libsycl/src/detail/program_manager.cpp
index 90d7c48d3d1c7..870d55198181b 100644
--- a/libsycl/src/detail/program_manager.cpp
+++ b/libsycl/src/detail/program_manager.cpp
@@ -18,6 +18,23 @@
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
 
+DeviceKernelInfo &_LIBSYCL_EXPORT
+getDeviceKernelInfo(std::string_view KernelName) {
+  return ProgramAndKernelManager::getInstance().getDeviceKernelInfo(KernelName);
+}
+
+DeviceKernelInfo &
+ProgramAndKernelManager::getDeviceKernelInfo(std::string_view KernelName) {
+  auto It = MDeviceKernelInfoMap.find(KernelName);
+  assert(It != MDeviceKernelInfoMap.end());
+  return It->second;
+}
+
+void ProgramAndKernelManager::releaseResources() {
+  MDeviceKernelInfoMap.clear();
+  MDeviceImageManagers.clear();
+}
+
 static inline bool checkFatBinVersion(const __sycl_tgt_bin_desc &FatbinDesc) {
   return FatbinDesc.Version == SupportedOffloadBinaryVersion;
 }
diff --git a/libsycl/src/detail/program_manager.hpp b/libsycl/src/detail/program_manager.hpp
index f5d3a1c8dc6dd..da56f6ec9706d 100644
--- a/libsycl/src/detail/program_manager.hpp
+++ b/libsycl/src/detail/program_manager.hpp
@@ -81,6 +81,12 @@ class ProgramAndKernelManager {
   ol_symbol_handle_t getOrCreateKernel(DeviceKernelInfo &KernelInfo,
                                        DeviceImpl &Device);
 
+  /// \return kernel info for the kernel with the specified name.
+  DeviceKernelInfo &getDeviceKernelInfo(std::string_view KernelName);
+
+  /// Release device image managers and corresponding resources.
+  void releaseResources();
+
 private:
   ProgramAndKernelManager() = default;
   ~ProgramAndKernelManager() = default;
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index 1d378f0ab5ef9..ff194f4d4152b 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -10,11 +10,41 @@
 
 #include <detail/device_impl.hpp>
 #include <detail/event_impl.hpp>
+#include <detail/program_manager.hpp>
+
+#include <algorithm>
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 namespace detail {
 
+static void setKernelLaunchArgs(const detail::UnifiedRangeView &Range,
+                                ol_kernel_launch_size_args_t &ArgsToSet) {
+  assert(Range.MDims < 4 && "Invalid dimensions.");
+  uint32_t GlobalSize[3] = {1, 1, 1};
+  if (Range.MGlobalSize) {
+    for (auto I = 0; I < Range.MDims; I++) {
+      GlobalSize[I] = static_cast<uint32_t>(Range.MGlobalSize[I]);
+    }
+  }
+
+  uint32_t GroupSize[3] = {1, 1, 1};
+  if (Range.MLocalSize) {
+    for (auto I = 0; I < Range.MDims; I++) {
+      GroupSize[I] = static_cast<uint32_t>(Range.MLocalSize[I]);
+    }
+  }
+
+  ArgsToSet.Dimensions = Range.MDims;
+  ArgsToSet.NumGroups.x = GlobalSize[0] / GroupSize[0];
+  ArgsToSet.NumGroups.y = GlobalSize[1] / GroupSize[1];
+  ArgsToSet.NumGroups.z = GlobalSize[2] / GroupSize[2];
+  ArgsToSet.GroupSize.x = GroupSize[0];
+  ArgsToSet.GroupSize.y = GroupSize[1];
+  ArgsToSet.GroupSize.z = GroupSize[2];
+  ArgsToSet.DynSharedMemory = 0;
+}
+
 QueueImpl::QueueImpl(DeviceImpl &deviceImpl, const async_handler &asyncHandler,
                      const property_list &propList, PrivateTag)
     : MIsInorder(false), MAsyncHandler(asyncHandler), MPropList(propList),
@@ -33,5 +63,86 @@ backend QueueImpl::getBackend() const noexcept { return MDevice.getBackend(); }
 
 void QueueImpl::wait() { callAndThrow(olSyncQueue, MOffloadQueue); }
 
+static bool checkEventsPlatformMatch(std::vector<EventImplPtr> &Events,
+                                     const PlatformImpl &QueuePlatform) {
+  // 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
+  //   same platform as `Queue`."
+  return std::all_of(Events.cbegin(), Events.cend(),
+                     [&QueuePlatform](const EventImplPtr &Event) {
+                       return &Event->getPlatformImpl() == &QueuePlatform;
+                     });
+}
+
+void QueueImpl::setKernelParameters(std::vector<EventImplPtr> &&Events,
+                                    const detail::UnifiedRangeView &Range) {
+  if (!checkEventsPlatformMatch(Events, MDevice.getPlatformImpl()))
+    throw sycl::exception(
+        sycl::make_error_code(sycl::errc::feature_not_supported),
+        "libsycl doesn't support cross-context/platform event dependencies "
+        "now.");
+
+  // TODO: this conversion and storing of only offload events is possible only
+  // while we don't have host tasks (or features based on host tasks, like
+  // streams). With them - it is very likely we should copy EventImplPtr
+  // (shared_ptr) and keep it here. Although it may differ if host tasks will be
+  // implemented on offload level (no data now).
+  assert(MCurrentSubmitInfo.DepEvents.empty() &&
+         "Kernel submission must clean up dependencies.");
+  MCurrentSubmitInfo.DepEvents.reserve(Events.size());
+  for (auto &Event : Events) {
+    assert(Event && "Event impl object can't be nullptr");
+    MCurrentSubmitInfo.DepEvents.push_back(Event->getHandle());
+  }
+  setKernelLaunchArgs(Range, MCurrentSubmitInfo.Range);
+}
+
+void QueueImpl::submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
+                                 size_t ArgSize) {
+  ol_symbol_handle_t Kernel =
+      detail::ProgramAndKernelManager::getInstance().getOrCreateKernel(
+          KernelInfo, MDevice);
+  assert(Kernel);
+
+  // 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
+  // applicable for in-order queue only. Once OOO queues are added this waiting
+  // must be disabled for in-order queues. Once host tasks are added - cross
+  // context dependencies should be enabled and checked as well.
+  if (!MCurrentSubmitInfo.DepEvents.empty()) {
+    callAndThrow(olWaitEvents, MOffloadQueue,
+                 MCurrentSubmitInfo.DepEvents.data(),
+                 MCurrentSubmitInfo.DepEvents.size());
+  }
+
+  assert(ArgData && "At least one argument must exist");
+  assert(ArgSize && "Arguments size must be greater than 0");
+
+  // ol_kernel_launch_prop_t Props[2];
+  // Props[0].type = OL_KERNEL_LAUNCH_PROP_TYPE_SIZE;
+  // Props[0].data = &ArgSize;
+  // Props[1] = OL_KERNEL_LAUNCH_PROP_END;
+  auto Result =
+      olLaunchKernel(MOffloadQueue, MDevice.getOLHandle(), Kernel, &ArgData,
+                     ArgSize, &MCurrentSubmitInfo.Range /*, Props*/);
+  // Clean up current kernel submit data to prepare structures for next
+  // submission.
+  MCurrentSubmitInfo.DepEvents.clear();
+  MCurrentSubmitInfo.Range = {};
+  if (isFailed(Result))
+    throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
+                          std::string("Kernel submission (") +
+                              KernelInfo.getName().data() + ") failed with " +
+                              formatCodeString(Result));
+
+  ol_event_handle_t NewEvent{};
+  callAndThrow(olCreateEvent, MOffloadQueue, &NewEvent);
+
+  MCurrentSubmitInfo.LastEvent =
+      EventImpl::createEventWithHandle(NewEvent, MDevice.getPlatformImpl());
+}
+
 } // namespace detail
 _LIBSYCL_END_NAMESPACE_SYCL
diff --git a/libsycl/src/detail/queue_impl.hpp b/libsycl/src/detail/queue_impl.hpp
index 047cb121150f3..cda6ae2961c19 100644
--- a/libsycl/src/detail/queue_impl.hpp
+++ b/libsycl/src/detail/queue_impl.hpp
@@ -29,6 +29,8 @@ class ContextImpl;
 class DeviceImpl;
 class EventImpl;
 
+using EventImplPtr = std::shared_ptr<EventImpl>;
+
 class QueueImpl : public std::enable_shared_from_this<QueueImpl> {
   struct PrivateTag {
     explicit PrivateTag() = default;
@@ -69,13 +71,50 @@ class QueueImpl : public std::enable_shared_from_this<QueueImpl> {
   /// Waits for completion of all commands submitted to this queue.
   void wait();
 
+  /// Enqueues a kernel to liboffload.
+  /// Kernel parameters like dependencies and range must be passed in advance by
+  /// calling setKernelParameters.
+  /// \param KernelInfo a kernel info that is uniform between different
+  /// submissions of the same kernel.
+  /// \param TypelessArgs data about kernel arguments to be used for enqueue.
+  void submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
+                        size_t ArgSize);
+
+  /// \return an event impl object that corresponds to the last kernel
+  /// submission in the calling thread.
+  EventImplPtr getLastEvent() {
+    assert(MCurrentSubmitInfo.LastEvent &&
+           "getLastEvent must be called after enqueue");
+    return MCurrentSubmitInfo.LastEvent;
+  }
+
+  /// Sets kernel parameters to be used in the next submitKernelImpl call.
+  /// Must be called prior to a submitKernelImpl call.
+  /// \param Events a collection of events that the kernal depends on.
+  /// \param Range a unified range view of the execution range.
+  void setKernelParameters(std::vector<EventImplPtr> &&Events,
+                           const detail::UnifiedRangeView &Range);
+
 private:
+  // Queue features.
   ol_queue_handle_t MOffloadQueue = {};
   const bool MIsInorder;
   const async_handler MAsyncHandler;
   const property_list MPropList;
   DeviceImpl &MDevice;
   ContextImpl &MContext;
+
+  // Submit data.
+  struct KernelSubmitInfo {
+    EventImplPtr LastEvent;
+    ol_kernel_launch_size_args_t Range;
+    // TODO: consider storing EventImplPtr here, it will work with plain handle
+    // only because submission is done within queue::submit call. Otherwise we
+    // need to ensure that event handle is still alive by keeping our own copy
+    // of EventImpl.
+    std::vector<ol_event_handle_t> DepEvents;
+  };
+  inline static thread_local KernelSubmitInfo MCurrentSubmitInfo = {};
 };
 
 } // namespace detail
diff --git a/libsycl/src/queue.cpp b/libsycl/src/queue.cpp
index 6584a6e080ec3..b57324219e46b 100644
--- a/libsycl/src/queue.cpp
+++ b/libsycl/src/queue.cpp
@@ -35,4 +35,23 @@ bool queue::is_in_order() const { return impl->isInOrder(); }
 
 void queue::wait() { impl->wait(); }
 
+event queue::getLastEvent() {
+  return detail::createSyclObjFromImpl<event>(impl->getLastEvent());
+}
+
+void queue::setKernelParameters(const std::vector<event> &Events,
+                                const detail::UnifiedRangeView &Range) {
+  std::vector<detail::EventImplPtr> DepEventImplRefs;
+  DepEventImplRefs.reserve(Events.size());
+  for (const auto &Event : Events) {
+    DepEventImplRefs.push_back(detail::getSyclObjImpl(Event));
+  }
+  return impl->setKernelParameters(std::move(DepEventImplRefs), Range);
+}
+
+void queue::submitKernelImpl(detail::DeviceKernelInfo &KernelInfo,
+                             void *ArgData, size_t ArgSize) {
+  impl->submitKernelImpl(KernelInfo, ArgData, ArgSize);
+}
+
 _LIBSYCL_END_NAMESPACE_SYCL

>From bfd2999b494c2337d6ab708f641716e320abde66 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Tue, 21 Apr 2026 02:37:24 -0700
Subject: [PATCH 17/25] fix comments

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 .../include/sycl/__impl/detail/get_device_kernel_info.hpp   | 6 +++---
 libsycl/include/sycl/__impl/queue.hpp                       | 1 +
 libsycl/src/detail/queue_impl.cpp                           | 5 +++--
 libsycl/src/detail/queue_impl.hpp                           | 2 +-
 4 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/libsycl/include/sycl/__impl/detail/get_device_kernel_info.hpp b/libsycl/include/sycl/__impl/detail/get_device_kernel_info.hpp
index 292755037410e..8d3ce6c0028eb 100644
--- a/libsycl/include/sycl/__impl/detail/get_device_kernel_info.hpp
+++ b/libsycl/include/sycl/__impl/detail/get_device_kernel_info.hpp
@@ -12,8 +12,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBSYCL_GET_DEV_INFO
-#define _LIBSYCL_GET_DEV_INFO
+#ifndef _LIBSYCL___IMPL_DETAIL_GET_DEVICE_KERNEL_INFO_HPP
+#define _LIBSYCL___IMPL_DETAIL_GET_DEVICE_KERNEL_INFO_HPP
 
 #include <sycl/__impl/detail/config.hpp>
 
@@ -40,4 +40,4 @@ DeviceKernelInfo &getDeviceKernelInfo(std::string_view KernelNameStr) {
 } // namespace detail
 _LIBSYCL_END_NAMESPACE_SYCL
 
-#endif // _LIBSYCL_GET_DEV_INFO
+#endif // _LIBSYCL___IMPL_DETAIL_GET_DEVICE_KERNEL_INFO_HPP
diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index 991ebe7f2a666..254512fecd862 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -232,6 +232,7 @@ class _LIBSYCL_EXPORT queue {
   void submitSingleTask(const KernelType &KernelFunc) {
     KernelFunc();
   }
+#undef _LIBSYCL_ENTRY_POINT_ATTR__
 
   event getLastEvent();
   void submitKernelImpl(detail::DeviceKernelInfo &KernelInfo, void *ArgData,
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index ff194f4d4152b..623b326637932 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -23,14 +23,15 @@ static void setKernelLaunchArgs(const detail::UnifiedRangeView &Range,
   assert(Range.MDims < 4 && "Invalid dimensions.");
   uint32_t GlobalSize[3] = {1, 1, 1};
   if (Range.MGlobalSize) {
-    for (auto I = 0; I < Range.MDims; I++) {
+    for (size_t I = 0; I < Range.MDims; ++I) {
+      assert(Range.MGlobalSize[I] <= std::numeric_limits<uint32_t>::max());
       GlobalSize[I] = static_cast<uint32_t>(Range.MGlobalSize[I]);
     }
   }
 
   uint32_t GroupSize[3] = {1, 1, 1};
   if (Range.MLocalSize) {
-    for (auto I = 0; I < Range.MDims; I++) {
+    for (size_t I = 0; I < Range.MDims; ++I) {
       GroupSize[I] = static_cast<uint32_t>(Range.MLocalSize[I]);
     }
   }
diff --git a/libsycl/src/detail/queue_impl.hpp b/libsycl/src/detail/queue_impl.hpp
index cda6ae2961c19..8800464e96612 100644
--- a/libsycl/src/detail/queue_impl.hpp
+++ b/libsycl/src/detail/queue_impl.hpp
@@ -90,7 +90,7 @@ class QueueImpl : public std::enable_shared_from_this<QueueImpl> {
 
   /// Sets kernel parameters to be used in the next submitKernelImpl call.
   /// Must be called prior to a submitKernelImpl call.
-  /// \param Events a collection of events that the kernal depends on.
+  /// \param Events a collection of events that the kernel depends on.
   /// \param Range a unified range view of the execution range.
   void setKernelParameters(std::vector<EventImplPtr> &&Events,
                            const detail::UnifiedRangeView &Range);

>From f8ddbb2a05b73049d8770b303d01d1a80c7e10e8 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Tue, 21 Apr 2026 02:43:53 -0700
Subject: [PATCH 18/25] add tests

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/test/basic/get_backend.cpp   | 53 ++++++++++++++++++++++++++++
 libsycl/test/basic/submit_fn_ptr.cpp | 20 +++++++++++
 2 files changed, 73 insertions(+)
 create mode 100644 libsycl/test/basic/get_backend.cpp
 create mode 100644 libsycl/test/basic/submit_fn_ptr.cpp

diff --git a/libsycl/test/basic/get_backend.cpp b/libsycl/test/basic/get_backend.cpp
new file mode 100644
index 0000000000000..e54ef2d8c1a7b
--- /dev/null
+++ b/libsycl/test/basic/get_backend.cpp
@@ -0,0 +1,53 @@
+// REQUIRES: any-device
+// RUN: %clangxx -fsycl %s -o %t.out
+// RUN: %t.out
+
+#include <iostream>
+
+#include <sycl/sycl.hpp>
+
+using namespace sycl;
+
+class Kernel1;
+
+bool check(backend be) {
+  switch (be) {
+  case backend::opencl:
+  case backend::level_zero:
+  case backend::cuda:
+  case backend::hip:
+    return true;
+  default:
+    return false;
+  }
+}
+
+void return_fail() {
+  std::cout << "Failed" << std::endl;
+  exit(1);
+}
+
+int main() {
+  for (const auto &plt : platform::get_platforms()) {
+    if (!check(plt.get_backend())) {
+      return_fail();
+    }
+
+    auto device = plt.get_devices()[0];
+    if (device.get_backend() != plt.get_backend()) {
+      return_fail();
+    }
+
+    queue q(device);
+    if (q.get_backend() != plt.get_backend()) {
+      return_fail();
+    }
+
+    event e = q.single_task<Kernel1>([]() {});
+    if (e.get_backend() != plt.get_backend()) {
+      return_fail();
+    }
+  }
+  std::cout << "Passed" << std::endl;
+  return 0;
+}
diff --git a/libsycl/test/basic/submit_fn_ptr.cpp b/libsycl/test/basic/submit_fn_ptr.cpp
new file mode 100644
index 0000000000000..b933c87e4ad15
--- /dev/null
+++ b/libsycl/test/basic/submit_fn_ptr.cpp
@@ -0,0 +1,20 @@
+// REQUIRES: any-device
+// RUN: %clangxx -fsycl  %s -o %t.out
+// RUN: %t.out
+
+#include <sycl/sycl.hpp>
+
+class Test;
+
+int main() {
+  sycl::queue q;
+  int *p = sycl::malloc_shared<int>(1, q);
+  *p = 0;
+  q.single_task<Test>([=]() { *p = 42; });
+  q.wait();
+
+  bool Failed = *p != 42;
+
+  sycl::free(p, q);
+  return Failed;
+}

>From 60af5a987839fac6b250e7519a74fe33ef8ff3b4 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Tue, 21 Apr 2026 04:40:29 -0700
Subject: [PATCH 19/25] fix merge errors

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 .../sycl/__impl/detail/arg_wrapper.hpp        | 135 ------------------
 libsycl/include/sycl/__impl/queue.hpp         |  43 ------
 2 files changed, 178 deletions(-)
 delete mode 100644 libsycl/include/sycl/__impl/detail/arg_wrapper.hpp

diff --git a/libsycl/include/sycl/__impl/detail/arg_wrapper.hpp b/libsycl/include/sycl/__impl/detail/arg_wrapper.hpp
deleted file mode 100644
index 96f60a3121787..0000000000000
--- a/libsycl/include/sycl/__impl/detail/arg_wrapper.hpp
+++ /dev/null
@@ -1,135 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This file contains helper functions used to wrap kernel arguments to
-/// typeless collection.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBSYCL___IMPL_DETAIL_ARG_WRAPPER_HPP
-#define _LIBSYCL___IMPL_DETAIL_ARG_WRAPPER_HPP
-
-#include <sycl/__impl/detail/config.hpp>
-#include <sycl/__impl/exception.hpp>
-
-#include <cassert>
-#include <memory>
-
-_LIBSYCL_BEGIN_NAMESPACE_SYCL
-
-namespace detail {
-
-/// Base class is needed for unification, we pass arguments through ABI
-/// boundary.
-class ArgWrapperBase {
-public:
-  ArgWrapperBase(const ArgWrapperBase &) = delete;
-  ArgWrapperBase &operator=(const ArgWrapperBase &) = delete;
-  virtual ~ArgWrapperBase() = default;
-
-  virtual void deepCopy() = 0;
-  virtual size_t getSize() const = 0;
-  virtual const void *getPtr() const = 0;
-
-protected:
-  ArgWrapperBase() = default;
-};
-
-/// Helps to manage arguments in a typeless way.
-template <typename Type> class ArgWrapper : public ArgWrapperBase {
-public:
-  ArgWrapper(Type &Arg) { Ptr = &Arg; }
-  ArgWrapper(const ArgWrapper &) = delete;
-  ArgWrapper &operator=(const ArgWrapper &) = delete;
-
-  /// \return size of argument in bytes.
-  size_t getSize() const override { return sizeof(Type); }
-
-  /// Returns raw pointer to the corresponding argument.
-  /// No copy is done by this method. It works with pointer to the memory whose
-  /// existence must be guaranteed by class user or with copy that must be
-  /// explicitly requested by class user via deepCopy method.
-  /// \return pointer to the argument.
-  const void *getPtr() const override {
-    assert((!DeepCopy || (DeepCopy.get()) == Ptr) &&
-           "Incorrect state of copied argument");
-    return Ptr;
-  }
-
-  /// Copies agrument to RT owned storage.
-  void deepCopy() override {
-    if (DeepCopy)
-      return;
-
-    DeepCopy.reset(new Type(*Ptr));
-    Ptr = DeepCopy.get();
-  }
-
-private:
-  Type *Ptr;
-  std::unique_ptr<Type> DeepCopy;
-};
-
-/// Collection of arguments. Provides functionality to accumulate all arguments
-/// data to pass through ABI boundary.
-class ArgCollection {
-public:
-  /// Adds argument to the collection. Don't own the memory. Argument lifetime
-  /// must be guaranteed by class user. If extended lifetime is needed (copy),
-  /// deepCopy must be called.
-  template <typename Type> void addArg(Type &Arg) {
-    MArgs.emplace_back(new ArgWrapper(Arg));
-  }
-
-  /// \return array of argument pointers.
-  const void **getArgPtrArray() {
-    if (MPtrs.size() != MArgs.size()) {
-      MPtrs.clear();
-      MPtrs.reserve(MArgs.size());
-      auto it = MArgs.cbegin();
-      while (it != MArgs.cend()) {
-        MPtrs.push_back((*it++)->getPtr());
-      }
-    }
-    return MPtrs.data();
-  }
-
-  /// \return array of argument sizes.
-  int64_t *getSizesArray() {
-    if (MSizes.size() != MArgs.size()) {
-      MSizes.clear();
-      MSizes.reserve(MArgs.size());
-      auto it = MArgs.cbegin();
-      while (it != MArgs.cend()) {
-        MSizes.push_back(static_cast<int64_t>((*it++)->getSize()));
-      }
-    }
-    return MSizes.data();
-  }
-
-  /// \return count of arguments in collection.
-  size_t getArgCount() { return MArgs.size(); }
-
-  /// Extends arguments lifetime by doing copy of all arguments.
-  void deepCopy() {
-    for (auto &Arg : MArgs)
-      Arg->deepCopy();
-  }
-
-private:
-  std::vector<std::unique_ptr<ArgWrapperBase>> MArgs;
-  std::vector<int64_t> MSizes;
-  std::vector<const void *> MPtrs;
-};
-
-} // namespace detail
-
-_LIBSYCL_END_NAMESPACE_SYCL
-
-#endif // _LIBSYCL___IMPL_DETAIL_ARG_WRAPPER_HPP
diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index 5f31777c09cf7..e3856d2f5b4b6 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -20,7 +20,6 @@
 #include <sycl/__impl/event.hpp>
 #include <sycl/__impl/property_list.hpp>
 
-#include <sycl/__impl/detail/arg_wrapper.hpp>
 #include <sycl/__impl/detail/config.hpp>
 #include <sycl/__impl/detail/default_async_handler.hpp>
 #include <sycl/__impl/detail/get_device_kernel_info.hpp>
@@ -311,48 +310,6 @@ class _LIBSYCL_EXPORT queue {
   /// exceptions.
   void wait();
 
-  /// Defines and invokes a SYCL kernel function as a lambda expression or a
-  /// named function object type.
-  ///
-  /// \param kernelFunc is the kernel functor or lambda.
-  /// \return an event that represents the status of the submitted kernel.
-  template <typename KernelName, typename KernelType>
-  event single_task(const KernelType &kernelFunc) {
-    return single_task<KernelName, KernelType>({}, kernelFunc);
-  }
-
-  /// Defines and invokes a SYCL kernel function as a lambda expression or a
-  /// named function object type.
-  ///
-  /// \param depEvent is an event that specifies the kernel dependency.
-  /// \param kernelFunc is the kernel functor or lambda.
-  /// \return an event that represents the status of the submitted kernel.
-  template <typename KernelName, typename KernelType>
-  event single_task(event depEvent, const KernelType &kernelFunc) {
-    return single_task<KernelName, KernelType>({depEvent}, kernelFunc);
-  }
-
-  /// Defines and invokes a SYCL kernel function as a lambda expression or a
-  /// named function object type.
-  ///
-  /// \param depEvents is a collection of events that specify the kernel
-  /// dependencies.
-  /// \param kernelFunc is the kernel functor or lambda.
-  /// \return an event that represents the status of the submitted kernel.
-  template <typename KernelName, typename KernelType>
-  event single_task(const std::vector<event> &depEvents,
-                    const KernelType &kernelFunc) {
-    static_assert(
-        detail::CheckFunctionSignature<std::remove_reference_t<KernelType>,
-                                       void()>::value,
-        "sycl::queue::single_task() requires a kernel instead of a command "
-        "group");
-
-    setKernelParameters(depEvents);
-    submitSingleTask<KernelName, KernelType>(kernelFunc);
-    return getLastEvent();
-  }
-
 private:
   template <typename KernelName, int Dims, typename... Rest>
   event parallelForImpl(range<Dims> numWorkItems,

>From 2861b1031d9c712c8c43c360b1fc88ab3ba388ec Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Tue, 21 Apr 2026 08:46:55 -0700
Subject: [PATCH 20/25] fix more comments and revert some merge issues

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 .../sycl/__impl/detail/kernel_arg_helpers.hpp | 11 ++++++++--
 .../sycl/__impl/index_space_classes.hpp       | 20 +++++++++----------
 libsycl/include/sycl/__impl/queue.hpp         | 20 +++++++++----------
 libsycl/include/sycl/__spirv/spirv_vars.hpp   | 10 +++-------
 libsycl/include/sycl/sycl.hpp                 |  1 +
 libsycl/src/detail/queue_impl.cpp             |  1 +
 libsycl/src/detail/queue_impl.hpp             |  1 -
 libsycl/test/basic/wrapped_usm_pointers.cpp   | 10 +++++-----
 8 files changed, 38 insertions(+), 36 deletions(-)

diff --git a/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp b/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
index a7478e1300e21..f3d733981922a 100644
--- a/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
+++ b/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
@@ -5,6 +5,11 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains helpers for kernel invocation.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef _LIBSYCL___IMPL_DETAIL_KERNEL_ARG_HELPERS
 #define _LIBSYCL___IMPL_DETAIL_KERNEL_ARG_HELPERS
@@ -17,11 +22,13 @@
 #  include <sycl/__spirv/spirv_vars.hpp>
 #endif
 
+#include <type_traits>
+
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 namespace detail {
 
-/// \name  Helpers for the unnamed lambda extension.
+/// \name  Helpers for the unnamed lambda.
 /// @{
 /// This class is the default kernel name template parameter type for kernel
 /// invocation APIs such as single_task.
@@ -35,7 +42,7 @@ template <typename Name, typename Type> struct get_kernel_name_t {
 };
 
 /// Specialization for the case when Name is undefined.
-/// This is only legal with our compiler with the unnamed lambda extension or if
+/// This is only legal with our compiler with the unnamed lambda support or if
 /// the kernel is a functor object.
 template <typename Type> struct get_kernel_name_t<detail::AutoName, Type> {
   using name = Type;
diff --git a/libsycl/include/sycl/__impl/index_space_classes.hpp b/libsycl/include/sycl/__impl/index_space_classes.hpp
index ef2897cee5307..0dc8e90decc3d 100644
--- a/libsycl/include/sycl/__impl/index_space_classes.hpp
+++ b/libsycl/include/sycl/__impl/index_space_classes.hpp
@@ -17,6 +17,9 @@
 
 #include <sycl/__impl/detail/config.hpp>
 
+#include <cstddef>
+#include <type_traits>
+
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 namespace detail {
@@ -95,12 +98,7 @@ template <int Dimensions = 1> class RawArray {
 
   friend bool operator!=(const RawArray<Dimensions> &lhs,
                          const RawArray<Dimensions> &rhs) {
-    for (int i = 0; i < Dimensions; ++i) {
-      if (lhs.MArray[i] != rhs.MArray[i]) {
-        return true;
-      }
-    }
-    return false;
+    return !(lhs == rhs);
   }
 
 protected:
@@ -370,13 +368,13 @@ template <int Dimensions /* = 1*/, bool WithOffset /* = true*/> class item {
   std::size_t get_linear_id() const noexcept {
     if constexpr (WithOffset) {
       if constexpr (1 == Dimensions) {
-        return MId;
+        return MId[0] - MOffset[0];
       }
       if constexpr (2 == Dimensions) {
-        return (MId[0] - MOffset[0]) * MRange[1] + (MId[1] - MOffset[1]);
+        return (MId[0] - MOffset[0]) * MRange[1] + MId[1] - MOffset[1];
       }
-      return ((MId[0] - MOffset[0]) * MRange[1] * MRange[2]) +
-             ((MId[1] - MOffset[1]) * MRange[2]) + (MId[2] - MOffset[2]);
+      return (MId[0] - MOffset[0]) * MRange[1] * MRange[2] +
+             (MId[1] - MOffset[1]) * MRange[2] + MId[2] - MOffset[2];
     } else {
       if constexpr (1 == Dimensions) {
         return MId[0];
@@ -384,7 +382,7 @@ template <int Dimensions /* = 1*/, bool WithOffset /* = true*/> class item {
       if constexpr (2 == Dimensions) {
         return MId[0] * MRange[1] + MId[1];
       }
-      return (MId[0] * MRange[1] * MRange[2]) + (MId[1] * MRange[2]) + MId[2];
+      return MId[0] * MRange[1] * MRange[2] + MId[1] * MRange[2] + MId[2];
     }
   }
 
diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index e3856d2f5b4b6..ea96ee03da5ee 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -138,6 +138,11 @@ class _LIBSYCL_EXPORT queue {
   template <typename Param>
   typename Param::return_type get_backend_info() const;
 
+  /// Blocks the calling thread until all commands previously submitted to this
+  /// queue have completed. Synchronous errors are reported through SYCL
+  /// exceptions.
+  void wait();
+
   /// Defines and invokes a SYCL kernel function as a lambda expression or a
   /// named function object type.
   ///
@@ -172,8 +177,8 @@ class _LIBSYCL_EXPORT queue {
     static_assert(
         (detail::CheckFunctionSignature<std::remove_reference_t<KernelType>,
                                         void()>::value),
-        "sycl::queue::single_task() requires a kernel instead of command "
-        "group. ");
+        "sycl::queue::single_task() requires a kernel instead of a command "
+        "group");
 
     setKernelParameters(depEvents);
     using NameT =
@@ -305,18 +310,13 @@ class _LIBSYCL_EXPORT queue {
                                        std::forward<Rest>(rest)...);
   }
 
-  /// Blocks the calling thread until all commands previously submitted to this
-  /// queue have completed. Synchronous errors are reported through SYCL
-  /// exceptions.
-  void wait();
-
 private:
   template <typename KernelName, int Dims, typename... Rest>
   event parallelForImpl(range<Dims> numWorkItems,
                         const std::vector<event> &depEvents, Rest &&...rest) {
     if constexpr (sizeof...(Rest) != 1)
       throw sycl::exception(errc::feature_not_supported,
-                            "Reductions are not supported.");
+                            "Reductions are not supported");
     setKernelParameters(depEvents, numWorkItems);
 
     using KernelType =
@@ -366,7 +366,7 @@ class _LIBSYCL_EXPORT queue {
   /// sycl_kernel_launch instead of KernelFunc invocation.
   template <typename KernelName, typename KernelType>
   _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
-  void submitSingleTask(const KernelType KernelFunc) {
+  void submitSingleTask(const KernelType &KernelFunc) {
     KernelFunc();
   }
 
@@ -375,7 +375,7 @@ class _LIBSYCL_EXPORT queue {
   /// sycl_kernel_launch instead of KernelFunc invocation.
   template <typename KernelName, typename ElementType, typename KernelType>
   _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
-  void submitParallelFor(const KernelType KernelFunc) {
+  void submitParallelFor(const KernelType &KernelFunc) {
 #ifdef __SYCL_DEVICE_ONLY__
     KernelFunc(detail::Builder::getElement(detail::declptr<ElementType>()));
 #endif
diff --git a/libsycl/include/sycl/__spirv/spirv_vars.hpp b/libsycl/include/sycl/__spirv/spirv_vars.hpp
index ec8c691b35e92..2c93e510565b3 100644
--- a/libsycl/include/sycl/__spirv/spirv_vars.hpp
+++ b/libsycl/include/sycl/__spirv/spirv_vars.hpp
@@ -21,14 +21,10 @@
 #  include <cstdint>
 
 // SPIR-V built-in variables mapped to function call.
-#  define _LIBSYCL_SYCL_DEVICE_ATTR __attribute__((sycl_external))
 
-_LIBSYCL_SYCL_DEVICE_ATTR __attribute__((const)) size_t
-__spirv_BuiltInGlobalInvocationId(int);
-_LIBSYCL_SYCL_DEVICE_ATTR __attribute__((const)) size_t
-__spirv_BuiltInGlobalSize(int);
-_LIBSYCL_SYCL_DEVICE_ATTR __attribute__((const)) size_t
-__spirv_BuiltInGlobalOffset(int);
+__attribute__((const)) size_t __spirv_BuiltInGlobalInvocationId(int);
+__attribute__((const)) size_t __spirv_BuiltInGlobalSize(int);
+__attribute__((const)) size_t __spirv_BuiltInGlobalOffset(int);
 
 namespace __spirv {
 
diff --git a/libsycl/include/sycl/sycl.hpp b/libsycl/include/sycl/sycl.hpp
index ce9fc8defd90b..7e81d952bd41c 100644
--- a/libsycl/include/sycl/sycl.hpp
+++ b/libsycl/include/sycl/sycl.hpp
@@ -19,6 +19,7 @@
 #include <sycl/__impl/device_selector.hpp>
 #include <sycl/__impl/event.hpp>
 #include <sycl/__impl/exception.hpp>
+#include <sycl/__impl/index_space_classes.hpp>
 #include <sycl/__impl/platform.hpp>
 #include <sycl/__impl/queue.hpp>
 #include <sycl/__impl/usm_functions.hpp>
diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index 93a1f43d25bf6..623b326637932 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -20,6 +20,7 @@ namespace detail {
 
 static void setKernelLaunchArgs(const detail::UnifiedRangeView &Range,
                                 ol_kernel_launch_size_args_t &ArgsToSet) {
+  assert(Range.MDims < 4 && "Invalid dimensions.");
   uint32_t GlobalSize[3] = {1, 1, 1};
   if (Range.MGlobalSize) {
     for (size_t I = 0; I < Range.MDims; ++I) {
diff --git a/libsycl/src/detail/queue_impl.hpp b/libsycl/src/detail/queue_impl.hpp
index a504c467e3927..8800464e96612 100644
--- a/libsycl/src/detail/queue_impl.hpp
+++ b/libsycl/src/detail/queue_impl.hpp
@@ -21,7 +21,6 @@
 #include <OffloadAPI.h>
 
 #include <memory>
-#include <mutex>
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 namespace detail {
diff --git a/libsycl/test/basic/wrapped_usm_pointers.cpp b/libsycl/test/basic/wrapped_usm_pointers.cpp
index 16a86963cc976..c936dcada4a6b 100644
--- a/libsycl/test/basic/wrapped_usm_pointers.cpp
+++ b/libsycl/test/basic/wrapped_usm_pointers.cpp
@@ -90,13 +90,13 @@ int main() {
   };
 
   bool Fail = false;
-  Fail = Checker(SimpleObj);
-  Fail = Checker(NonTrivialObj);
-  Fail = Checker(NonTrivialDerivedObj);
-  Fail = Checker(WrapperOfSimpleObj.Obj);
+  Fail |= Checker(SimpleObj);
+  Fail |= Checker(NonTrivialObj);
+  Fail |= Checker(NonTrivialDerivedObj);
+  Fail |= Checker(WrapperOfSimpleObj.Obj);
 
   for (int i = 0; i < NumOfElements; ++i)
-    Fail = Checker(SimpleArr[i]);
+    Fail |= Checker(SimpleArr[i]);
 
   // Free allocated memory.
   sycl::free(NonTrivialObj.Data, Q);

>From b05c6d1017482699ef865187d0c679a870777676 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Wed, 22 Apr 2026 09:12:44 -0700
Subject: [PATCH 21/25] fix comments

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 .../sycl/__impl/detail/kernel_arg_helpers.hpp |  7 +-
 libsycl/include/sycl/__impl/queue.hpp         | 13 ++-
 libsycl/include/sycl/__spirv/spirv_vars.hpp   | 46 ++++-----
 libsycl/test/basic/parallel_for_indexers.cpp  | 98 +++++++++++++++++++
 4 files changed, 127 insertions(+), 37 deletions(-)
 create mode 100644 libsycl/test/basic/parallel_for_indexers.cpp

diff --git a/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp b/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
index f3d733981922a..d58df91f19465 100644
--- a/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
+++ b/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
@@ -18,9 +18,7 @@
 
 #include <sycl/__impl/detail/config.hpp>
 
-#ifdef __SYCL_DEVICE_ONLY__
-#  include <sycl/__spirv/spirv_vars.hpp>
-#endif
+#include <sycl/__spirv/spirv_vars.hpp>
 
 #include <type_traits>
 
@@ -121,7 +119,6 @@ class Builder {
 public:
   Builder() = delete;
 
-#ifdef __SYCL_DEVICE_ONLY__
   /// \return a global index of work item currently being operated on by device.
   template <int Dims> static const id<Dims> getElement(id<Dims> *) {
     static_assert(isValidDimensions<Dims>, "invalid dimensions");
@@ -181,8 +178,6 @@ class Builder {
       -> decltype(getItem<Dims, WithOffset>()) {
     return getItem<Dims, WithOffset>();
   }
-
-#endif // __SYCL_DEVICE_ONLY__
 };
 
 } // namespace detail
diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index ea96ee03da5ee..776cbabfe9c8e 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -323,13 +323,19 @@ class _LIBSYCL_EXPORT queue {
         std::decay_t<detail::nth_type_t<sizeof...(Rest) - 1, Rest...>>;
     using LambdaArgType = sycl::detail::lambda_arg_type<KernelType, item<Dims>>;
     static_assert(
-        std::is_convertible_v<sycl::item<Dims>, LambdaArgType>,
+        std::is_convertible_v<sycl::item<Dims>, LambdaArgType> ||
+            std::is_convertible_v<sycl::item<Dims, false>, LambdaArgType>,
         "Kernel argument of a sycl::parallel_for with sycl::range "
         "must be either sycl::item or be convertible from sycl::item");
+    using TranformedLambdaArgType = std::conditional_t<
+        std::is_convertible_v<item<Dims>, LambdaArgType>, item<Dims>,
+        std::conditional_t<
+            std::is_convertible_v<item<Dims, false>, LambdaArgType>,
+            item<Dims, false>, LambdaArgType>>;
 
     using NameT =
         typename detail::get_kernel_name_t<KernelName, KernelType>::name;
-    submitParallelFor<NameT, item<Dims>, KernelType>(rest...);
+    submitParallelFor<NameT, TranformedLambdaArgType, KernelType>(rest...);
     return getLastEvent();
   }
 
@@ -376,10 +382,7 @@ class _LIBSYCL_EXPORT queue {
   template <typename KernelName, typename ElementType, typename KernelType>
   _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
   void submitParallelFor(const KernelType &KernelFunc) {
-#ifdef __SYCL_DEVICE_ONLY__
     KernelFunc(detail::Builder::getElement(detail::declptr<ElementType>()));
-#endif
-    (void)KernelFunc;
   }
 #undef _LIBSYCL_ENTRY_POINT_ATTR__
 
diff --git a/libsycl/include/sycl/__spirv/spirv_vars.hpp b/libsycl/include/sycl/__spirv/spirv_vars.hpp
index 2c93e510565b3..c8d2c990d76c3 100644
--- a/libsycl/include/sycl/__spirv/spirv_vars.hpp
+++ b/libsycl/include/sycl/__spirv/spirv_vars.hpp
@@ -15,8 +15,6 @@
 #ifndef _LIBSYCL___SPIRV_SPIRV_VARS
 #define _LIBSYCL___SPIRV_SPIRV_VARS
 
-#ifdef __SYCL_DEVICE_ONLY__
-
 #  include <cstddef>
 #  include <cstdint>
 
@@ -30,33 +28,31 @@ namespace __spirv {
 
 // Helper function templates to initialize and get vector component from SPIR-V
 // built-in variables
-#  define __SPIRV_DEFINE_INIT_AND_GET_HELPERS(POSTFIX)                         \
-    template <int ID> size_t get##POSTFIX();                                   \
-    template <> size_t get##POSTFIX<0>() { return __spirv_##POSTFIX(0); }      \
-    template <> size_t get##POSTFIX<1>() { return __spirv_##POSTFIX(1); }      \
-    template <> size_t get##POSTFIX<2>() { return __spirv_##POSTFIX(2); }      \
+#define __SPIRV_DEFINE_INIT_AND_GET_HELPERS(POSTFIX)                           \
+  template <int ID> size_t get##POSTFIX();                                     \
+  template <> inline size_t get##POSTFIX<0>() { return __spirv_##POSTFIX(0); } \
+  template <> inline size_t get##POSTFIX<1>() { return __spirv_##POSTFIX(1); } \
+  template <> inline size_t get##POSTFIX<2>() { return __spirv_##POSTFIX(2); } \
                                                                                \
-    template <int Dim, class DstT> struct InitSizesST##POSTFIX;                \
+  template <int Dim, class DstT> struct InitSizesST##POSTFIX;                  \
                                                                                \
-    template <class DstT> struct InitSizesST##POSTFIX<1, DstT> {               \
-      static DstT initSize() { return {get##POSTFIX<0>()}; }                   \
-    };                                                                         \
+  template <class DstT> struct InitSizesST##POSTFIX<1, DstT> {                 \
+    static DstT initSize() { return {get##POSTFIX<0>()}; }                     \
+  };                                                                           \
                                                                                \
-    template <class DstT> struct InitSizesST##POSTFIX<2, DstT> {               \
-      static DstT initSize() {                                                 \
-        return {get##POSTFIX<1>(), get##POSTFIX<0>()};                         \
-      }                                                                        \
-    };                                                                         \
+  template <class DstT> struct InitSizesST##POSTFIX<2, DstT> {                 \
+    static DstT initSize() { return {get##POSTFIX<1>(), get##POSTFIX<0>()}; }  \
+  };                                                                           \
                                                                                \
-    template <class DstT> struct InitSizesST##POSTFIX<3, DstT> {               \
-      static DstT initSize() {                                                 \
-        return {get##POSTFIX<2>(), get##POSTFIX<1>(), get##POSTFIX<0>()};      \
-      }                                                                        \
-    };                                                                         \
+  template <class DstT> struct InitSizesST##POSTFIX<3, DstT> {                 \
+    static DstT initSize() {                                                   \
+      return {get##POSTFIX<2>(), get##POSTFIX<1>(), get##POSTFIX<0>()};        \
+    }                                                                          \
+  };                                                                           \
                                                                                \
-    template <int Dims, class DstT> DstT init##POSTFIX() {                     \
-      return InitSizesST##POSTFIX<Dims, DstT>::initSize();                     \
-    }
+  template <int Dims, class DstT> DstT init##POSTFIX() {                       \
+    return InitSizesST##POSTFIX<Dims, DstT>::initSize();                       \
+  }
 
 __SPIRV_DEFINE_INIT_AND_GET_HELPERS(BuiltInGlobalSize);
 __SPIRV_DEFINE_INIT_AND_GET_HELPERS(BuiltInGlobalInvocationId)
@@ -66,6 +62,4 @@ __SPIRV_DEFINE_INIT_AND_GET_HELPERS(BuiltInGlobalOffset)
 
 } // namespace __spirv
 
-#endif //__SYCL_DEVICE_ONLY__
-
 #endif // _LIBSYCL___SPIRV_SPIRV_VARS
diff --git a/libsycl/test/basic/parallel_for_indexers.cpp b/libsycl/test/basic/parallel_for_indexers.cpp
new file mode 100644
index 0000000000000..e9cef87e7472b
--- /dev/null
+++ b/libsycl/test/basic/parallel_for_indexers.cpp
@@ -0,0 +1,98 @@
+// REQUIRES: any-device
+// RUN: %clangxx -fsycl -Wno-error=deprecated-declarations %s -o %t.out
+// RUN: %t.out
+
+#include <sycl/sycl.hpp>
+
+#include <cassert>
+#include <memory>
+
+using namespace sycl;
+
+// TODO: original test works with buffers, revert changes to USM once they are
+// implemented.
+// TODO add cases with dimensions more than 1
+int main() {
+  bool Fail{};
+
+  constexpr size_t DataSize = 10;
+  const range<1> globalRange(6);
+  // Id indexer
+  {
+    queue Q;
+    int *Data = sycl::malloc_shared<int>(DataSize, Q);
+    for (size_t i = 0; i < DataSize; ++i)
+      Data[i] = -1;
+
+    Q.parallel_for<class id1>(globalRange,
+                              [=](id<1> index) { Data[index] = index[0]; });
+    Q.wait();
+
+    for (size_t i = 0; i < DataSize; i++) {
+      const int id = Data[i];
+      if (i < globalRange[0]) {
+        Fail |= !(id == i);
+      } else {
+        Fail |= !(id == -1);
+      }
+    }
+
+    free(Data, Q);
+  }
+  // print and return;
+
+  // Item indexer without offset
+  {
+    // TODO: replace strcut with sycl::int2 once implemented.
+    struct DoubleInt {
+      int First;
+      int Second;
+    };
+    queue Q;
+    DoubleInt *Data = sycl::malloc_shared<DoubleInt>(DataSize, Q);
+    for (size_t i = 0; i < DataSize; ++i)
+      Data[i] = {-1, -1};
+
+    Q.parallel_for<class item1_nooffset>(
+        globalRange, [=](item<1, false> index) {
+          Data[index.get_id()] = {int(index.get_id()[0]),
+                                  int(index.get_range()[0])};
+        });
+    Q.wait();
+    for (size_t i = 0; i < DataSize; ++i) {
+      const int id = Data[i].First;
+      const int range = Data[i].Second;
+      if (i < globalRange[0]) {
+        Fail |= !(id == i);
+        Fail |= !(range == globalRange[0]);
+      } else {
+        Fail |= !(id == -1);
+        Fail |= !(range == -1);
+      }
+    }
+    free(Data, Q);
+  }
+
+  // get_linear_id()
+  {
+    queue Q;
+    size_t DataSize3D = DataSize * DataSize * DataSize;
+    int *Data = sycl::malloc_shared<int>(DataSize3D, Q);
+    Q.parallel_for(range<3>(DataSize, DataSize, DataSize), [=](item<3> Idx) {
+      auto Id = Idx.get_linear_id();
+      Data[Id] = Id;
+    });
+    Q.wait();
+    for (size_t i = 0; i < DataSize3D; ++i) {
+      Fail |= !(Data[i] == i);
+    }
+    free(Data, Q);
+  }
+
+  // TODO:  Item indexer with offset
+  // blocked by liboffload support
+  // blocked by absence of sycl::handler implementation
+
+  // TODO: add nd_item check
+  return Fail;
+}

>From 51cd8902bba241421f4ee188e3f944c7f5c0aa94 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Thu, 23 Apr 2026 04:05:03 -0700
Subject: [PATCH 22/25] fix tests

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 .../sycl/__impl/index_space_classes.hpp       |  3 +-
 libsycl/include/sycl/__spirv/spirv_vars.hpp   |  6 +-
 libsycl/test/basic/parallel_for_indexers.cpp  | 75 ++++++++-----------
 .../test/basic/queue_parallel_for_generic.cpp | 34 ++++++---
 4 files changed, 61 insertions(+), 57 deletions(-)

diff --git a/libsycl/include/sycl/__impl/index_space_classes.hpp b/libsycl/include/sycl/__impl/index_space_classes.hpp
index 0dc8e90decc3d..d47f803886235 100644
--- a/libsycl/include/sycl/__impl/index_space_classes.hpp
+++ b/libsycl/include/sycl/__impl/index_space_classes.hpp
@@ -19,6 +19,7 @@
 
 #include <cstddef>
 #include <type_traits>
+#include <variant>
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
@@ -401,7 +402,7 @@ template <int Dimensions /* = 1*/, bool WithOffset /* = true*/> class item {
 private:
   range<Dimensions> MRange;
   id<Dimensions> MId;
-  id<Dimensions> MOffset;
+  std::conditional_t<WithOffset, id<Dimensions>, std::monostate> MOffset;
 
   friend class detail::Builder;
 };
diff --git a/libsycl/include/sycl/__spirv/spirv_vars.hpp b/libsycl/include/sycl/__spirv/spirv_vars.hpp
index c8d2c990d76c3..450f581d9506d 100644
--- a/libsycl/include/sycl/__spirv/spirv_vars.hpp
+++ b/libsycl/include/sycl/__spirv/spirv_vars.hpp
@@ -15,8 +15,8 @@
 #ifndef _LIBSYCL___SPIRV_SPIRV_VARS
 #define _LIBSYCL___SPIRV_SPIRV_VARS
 
-#  include <cstddef>
-#  include <cstdint>
+#include <cstddef>
+#include <cstdint>
 
 // SPIR-V built-in variables mapped to function call.
 
@@ -58,7 +58,7 @@ __SPIRV_DEFINE_INIT_AND_GET_HELPERS(BuiltInGlobalSize);
 __SPIRV_DEFINE_INIT_AND_GET_HELPERS(BuiltInGlobalInvocationId)
 __SPIRV_DEFINE_INIT_AND_GET_HELPERS(BuiltInGlobalOffset)
 
-#  undef __SPIRV_DEFINE_INIT_AND_GET_HELPERS
+#undef __SPIRV_DEFINE_INIT_AND_GET_HELPERS
 
 } // namespace __spirv
 
diff --git a/libsycl/test/basic/parallel_for_indexers.cpp b/libsycl/test/basic/parallel_for_indexers.cpp
index e9cef87e7472b..078508f53fff5 100644
--- a/libsycl/test/basic/parallel_for_indexers.cpp
+++ b/libsycl/test/basic/parallel_for_indexers.cpp
@@ -11,12 +11,11 @@ using namespace sycl;
 
 // TODO: original test works with buffers, revert changes to USM once they are
 // implemented.
-// TODO add cases with dimensions more than 1
 int main() {
   bool Fail{};
 
   constexpr size_t DataSize = 10;
-  const range<1> globalRange(6);
+  const range<1> GlobalRange(6);
   // Id indexer
   {
     queue Q;
@@ -24,29 +23,31 @@ int main() {
     for (size_t i = 0; i < DataSize; ++i)
       Data[i] = -1;
 
-    Q.parallel_for<class id1>(globalRange,
-                              [=](id<1> index) { Data[index] = index[0]; });
+    Q.parallel_for<class id1>(GlobalRange,
+                              [=](id<1> Index) { Data[Index] = Index[0]; });
     Q.wait();
 
-    for (size_t i = 0; i < DataSize; i++) {
-      const int id = Data[i];
-      if (i < globalRange[0]) {
-        Fail |= !(id == i);
-      } else {
-        Fail |= !(id == -1);
+    Fail |= [&]() {
+      for (size_t i = 0; i < DataSize; ++i) {
+        const int ExpectedVal = i < GlobalRange[0] ? i : -1;
+        if (Data[i] != ExpectedVal) {
+          std::cout << "line: " << __LINE__ << " Data[" << i << "] is "
+                    << Data[i] << " expected " << ExpectedVal << std::endl;
+          return true;
+        }
       }
-    }
+      return false;
+    }();
 
     free(Data, Q);
   }
-  // print and return;
 
   // Item indexer without offset
   {
     // TODO: replace strcut with sycl::int2 once implemented.
     struct DoubleInt {
-      int First;
-      int Second;
+      int Id;
+      int Range;
     };
     queue Q;
     DoubleInt *Data = sycl::malloc_shared<DoubleInt>(DataSize, Q);
@@ -54,38 +55,26 @@ int main() {
       Data[i] = {-1, -1};
 
     Q.parallel_for<class item1_nooffset>(
-        globalRange, [=](item<1, false> index) {
-          Data[index.get_id()] = {int(index.get_id()[0]),
-                                  int(index.get_range()[0])};
+        GlobalRange, [=](item<1, false> Index) {
+          Data[Index.get_id()] = {int(Index.get_id()[0]),
+                                  int(Index.get_range()[0])};
         });
     Q.wait();
-    for (size_t i = 0; i < DataSize; ++i) {
-      const int id = Data[i].First;
-      const int range = Data[i].Second;
-      if (i < globalRange[0]) {
-        Fail |= !(id == i);
-        Fail |= !(range == globalRange[0]);
-      } else {
-        Fail |= !(id == -1);
-        Fail |= !(range == -1);
-      }
-    }
-    free(Data, Q);
-  }
 
-  // get_linear_id()
-  {
-    queue Q;
-    size_t DataSize3D = DataSize * DataSize * DataSize;
-    int *Data = sycl::malloc_shared<int>(DataSize3D, Q);
-    Q.parallel_for(range<3>(DataSize, DataSize, DataSize), [=](item<3> Idx) {
-      auto Id = Idx.get_linear_id();
-      Data[Id] = Id;
-    });
-    Q.wait();
-    for (size_t i = 0; i < DataSize3D; ++i) {
-      Fail |= !(Data[i] == i);
-    }
+    Fail |= [&]() {
+      for (size_t i = 0; i < DataSize; ++i) {
+        const int ExpectedValID = i < GlobalRange[0] ? i : -1;
+        const int ExpectedValRange = i < GlobalRange[0] ? GlobalRange[0] : -1;
+        if (Data[i].Id != ExpectedValID || Data[i].Range != ExpectedValRange) {
+          std::cout << "line: " << __LINE__ << " Data[" << i << "] is {"
+                    << Data[i].Id << ", " << Data[i].Range << "} expected {"
+                    << ExpectedValID << ", " << ExpectedValRange << "}"
+                    << std::endl;
+          return true;
+        }
+      }
+      return false;
+    }();
     free(Data, Q);
   }
 
diff --git a/libsycl/test/basic/queue_parallel_for_generic.cpp b/libsycl/test/basic/queue_parallel_for_generic.cpp
index cac423b85f218..70a191e6ab220 100644
--- a/libsycl/test/basic/queue_parallel_for_generic.cpp
+++ b/libsycl/test/basic/queue_parallel_for_generic.cpp
@@ -12,36 +12,50 @@ int main() {
   // TODO: uncomment property once it is implemented. now all sycl::queue
   // objects are in-order due to liboffload limitation. Test is intended to
   // check in-order execution.
-  sycl::queue q{/*sycl::property::queue::in_order()*/};
-  auto dev = q.get_device();
-  auto ctx = q.get_context();
+  sycl::queue Q{/*sycl::property::queue::in_order()*/};
+  auto Dev = Q.get_device();
+  auto Ctx = Q.get_context();
   constexpr int N = 8;
 
-  auto A = static_cast<int *>(sycl::malloc_shared(N * sizeof(int), dev, ctx));
+  auto A = static_cast<int *>(sycl::malloc_shared(N * sizeof(int), Dev, Ctx));
 
-  for (int i = 0; i < N; i++) {
+  for (int i = 0; i < N; ++i) {
     A[i] = 1;
   }
 
-  q.parallel_for<class Bar>(N, [=](auto i) {
+  Q.parallel_for<class IntRange>(N, [=](auto i) {
     static_assert(std::is_same<decltype(i), sycl::item<1>>::value,
                   "lambda arg type is unexpected");
     A[i]++;
   });
 
-  q.parallel_for<class Foo>({N}, [=](auto i) {
+  Q.parallel_for<class InitRange>({N}, [=](auto i) {
     static_assert(std::is_same<decltype(i), sycl::item<1>>::value,
                   "lambda arg type is unexpected");
     A[i]++;
   });
 
+  Q.parallel_for<class InitRange2D>({4, 2}, [=](auto i) {
+    static_assert(std::is_same<decltype(i), sycl::item<2>>::value,
+                  "lambda arg type is unexpected");
+    A[i.get_linear_id()]++;
+  });
+
+  Q.parallel_for<class InitRange3D>({2, 2, 2}, [=](auto i) {
+    static_assert(std::is_same<decltype(i), sycl::item<3>>::value,
+                  "lambda arg type is unexpected");
+    A[i.get_linear_id()]++;
+  });
+
   // TODO: add kernel with offset and kernel with nd_range once they
   // are implemented.
 
-  q.wait();
+  Q.wait();
 
+  bool Fail{};
   for (int i = 0; i < N; i++) {
-    assert(A[i] == 3);
+    Fail |= !(A[i] == 5);
   }
-  sycl::free(A, ctx);
+  sycl::free(A, Ctx);
+  return Fail;
 }

>From 2befbcde11508d3eeb5db5ce31eb64e68c1184b0 Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Mon, 27 Apr 2026 04:13:24 -0700
Subject: [PATCH 23/25] fix comments

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 .../sycl/__impl/detail/kernel_arg_helpers.hpp | 21 +++---
 .../sycl/__impl/index_space_classes.hpp       | 70 +++++++++----------
 libsycl/include/sycl/__impl/queue.hpp         | 48 ++++++-------
 3 files changed, 70 insertions(+), 69 deletions(-)

diff --git a/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp b/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
index d58df91f19465..31a50e835cc80 100644
--- a/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
+++ b/libsycl/include/sycl/__impl/detail/kernel_arg_helpers.hpp
@@ -26,14 +26,14 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 namespace detail {
 
-/// \name  Helpers for the unnamed lambda.
+/// \name  Helpers for unnamed lambdas.
 /// @{
 /// This class is the default kernel name template parameter type for kernel
 /// invocation APIs such as single_task.
 class AutoName {};
 
 /// Helper struct to get a kernel name type based on given Name and Type
-/// types: if Name is undefined (is a AutoName) then Type becomes
+/// types: if Name is undefined (is AutoName) then Type becomes
 /// the Name.
 template <typename Name, typename Type> struct get_kernel_name_t {
   using name = Name;
@@ -41,13 +41,13 @@ template <typename Name, typename Type> struct get_kernel_name_t {
 
 /// Specialization for the case when Name is undefined.
 /// This is only legal with our compiler with the unnamed lambda support or if
-/// the kernel is a functor object.
+/// the kernel is a functor.
 template <typename Type> struct get_kernel_name_t<detail::AutoName, Type> {
   using name = Type;
 };
 /// @}
 
-/// \name  Helpers to verify kernel lambda type.
+/// \name  Helpers to verify kernel lambda types.
 /// \brief Checks that the function is callable with operator().
 /// @{
 template <typename, typename T> struct CheckFunctionSignature {
@@ -119,7 +119,8 @@ class Builder {
 public:
   Builder() = delete;
 
-  /// \return a global index of work item currently being operated on by device.
+  /// \return the global index of the work item currently being operated on by
+  /// the device.
   template <int Dims> static const id<Dims> getElement(id<Dims> *) {
     static_assert(isValidDimensions<Dims>, "invalid dimensions");
     return __spirv::initBuiltInGlobalInvocationId<Dims, id<Dims>>();
@@ -151,8 +152,8 @@ class Builder {
     return item<Dims, WithOffset>(Extent, Index);
   }
 
-  /// Creates sycl::item instance for work item that is currently being operated
-  /// on.
+  /// Creates a sycl::item instance for the work item that is currently being
+  /// operated on.
   template <int Dims, bool WithOffset>
   static std::enable_if_t<WithOffset, const item<Dims, WithOffset>> getItem() {
     static_assert(isValidDimensions<Dims>, "invalid dimensions");
@@ -162,8 +163,8 @@ class Builder {
     return createItem<Dims, true>(GlobalSize, GlobalId, GlobalOffset);
   }
 
-  /// Creates sycl::item instance for work item that is currently being operated
-  /// on.
+  /// Creates a sycl::item instance for the work item that is currently being
+  /// operated on.
   template <int Dims, bool WithOffset>
   static std::enable_if_t<!WithOffset, const item<Dims, WithOffset>> getItem() {
     static_assert(isValidDimensions<Dims>, "invalid dimensions");
@@ -172,7 +173,7 @@ class Builder {
     return createItem<Dims, false>(GlobalSize, GlobalId);
   }
 
-  /// \return a work item currently being operated on by device.
+  /// \return the work item currently being operated on by the device.
   template <int Dims, bool WithOffset>
   static auto getElement(item<Dims, WithOffset> *)
       -> decltype(getItem<Dims, WithOffset>()) {
diff --git a/libsycl/include/sycl/__impl/index_space_classes.hpp b/libsycl/include/sycl/__impl/index_space_classes.hpp
index d47f803886235..823dcea062d15 100644
--- a/libsycl/include/sycl/__impl/index_space_classes.hpp
+++ b/libsycl/include/sycl/__impl/index_space_classes.hpp
@@ -33,50 +33,50 @@ template <int Dimensions = 1> class RawArray {
                 "RawArray can only be 1, 2, or 3 Dimensional.");
 
 public:
-  /// Constructs one-dimensional instance and assign corresponding data to Dim0
-  /// value. Available only if Dimensions = 1.
+  /// Constructs a one-dimensional instance and assigns the corresponding data
+  /// to Dim0 value. Available only if Dimensions = 1.
   template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
   RawArray(size_t Dim0 = 0) : MArray{Dim0} {}
 
-  /// Constructs two-dimensional instance and assign corresponding data.
+  /// Constructs a two-dimensional instance and assigns the corresponding data.
   /// Available only if Dimensions = 2.
   template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
   RawArray(size_t Dim0, size_t Dim1) : MArray{Dim0, Dim1} {}
 
-  /// Constructs two-dimensional instance with zero-initialized corresponding
-  /// data. Available only if Dimensions = 2.
+  /// Constructs a two-dimensional instance with the zero-initialized
+  /// corresponding data. Available only if Dimensions = 2.
   template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
   RawArray() : RawArray(0, 0) {}
 
-  /// Constructs three-dimensional instance and assign corresponding data.
-  /// Available only if Dimensions = 3.
+  /// Constructs a three-dimensional instance and assigns the corresponding
+  /// data. Available only if Dimensions = 3.
   template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
   RawArray(size_t Dim0, size_t Dim1, size_t Dim2) : MArray{Dim0, Dim1, Dim2} {}
 
-  /// Constructs three-dimensional instance with zero-initialized corresponding
-  /// data. Available only if Dimensions = 3.
+  /// Constructs a three-dimensional instance with the zero-initialized
+  /// corresponding data. Available only if Dimensions = 3.
   template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
   RawArray() : RawArray(0, 0, 0) {}
 
-  /// Returns value for the specified dimension.
+  /// Returns the value for the specified dimension.
   /// Results in undefined behavior if dimension is not in the range [0,
   /// Dimensions).
-  /// \param Dimension a dimension to query data for.
-  /// \return value in array matching requested dimension.
+  /// \param Dimension the dimension to return the value for.
+  /// \return the value matching the requested dimension.
   std::size_t get(int Dimension) const noexcept { return MArray[Dimension]; }
 
-  /// Returns value for the specified dimension.
+  /// Returns the value for the specified dimension.
   /// Results in undefined behavior if dimension is not in the range [0,
   /// Dimensions).
-  /// \param Dimension a dimension to query data for.
-  /// \return value in array matching requested dimension.
+  /// \param Dimension the dimension to return the value for.
+  /// \return the value matching the requested dimension.
   std::size_t &operator[](int Dimension) noexcept { return MArray[Dimension]; }
 
-  /// Returns value for the specified dimension.
+  /// Returns the value for the specified dimension.
   /// Results in undefined behavior if dimension is not in the range [0,
   /// Dimensions).
-  /// \param Dimension a dimension to query data for.
-  /// \return value in array matching requested dimension.
+  /// \param Dimension the dimension to return the value for.
+  /// \return the value matching the requested dimension.
   std::size_t operator[](int Dimension) const noexcept {
     return MArray[Dimension];
   }
@@ -114,7 +114,7 @@ template <int Dimensions = 1> class RawArray {
 template <int Dimensions = 1>
 class range : public detail::RawArray<Dimensions> {
   static_assert(Dimensions >= 1 && Dimensions <= 3,
-                "range can only be 1, 2, or 3 Dimensional.");
+                "range can only be 1-, 2-, or 3-dimensional.");
   using Base = detail::RawArray<Dimensions>;
 
 public:
@@ -125,17 +125,17 @@ class range : public detail::RawArray<Dimensions> {
   range<Dimensions> &operator=(const range<Dimensions> &rhs) = default;
   range<Dimensions> &operator=(range<Dimensions> &&rhs) = default;
 
-  /// Construct a 1D range with value dim0.
+  /// Constructs a 1D range with value dim0.
   ///  Only valid when the template parameter Dimensions is equal to 1.
   template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
   range(std::size_t dim0) noexcept : Base(dim0) {}
 
-  /// Construct a 2D range with values dim0 and dim1.
+  /// Constructs a 2D range with values dim0 and dim1.
   /// Only valid when the template parameter Dimensions is equal to 2.
   template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
   range(std::size_t dim0, std::size_t dim1) noexcept : Base(dim0, dim1) {}
 
-  /// Construct a 3D range with values dim0, dim1 and dim2.
+  /// Constructs a 3D range with values dim0, dim1 and dim2.
   /// Only valid when the template parameter Dimensions is equal to 3.
   template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
   range(std::size_t dim0, std::size_t dim1, std::size_t dim2) noexcept
@@ -175,7 +175,7 @@ template <int Dimensions = 1, bool WithOffset = true> class item;
 /// the same rank.
 template <int Dimensions = 1> class id : public detail::RawArray<Dimensions> {
   static_assert(Dimensions >= 1 && Dimensions <= 3,
-                "id can only be 1, 2, or 3 Dimensional.");
+                "id can only be 1-, 2-, or 3-dimensional.");
   using Base = detail::RawArray<Dimensions>;
 
   // Helper class for conversion operator. Void type is not suitable. User
@@ -195,51 +195,51 @@ template <int Dimensions = 1> class id : public detail::RawArray<Dimensions> {
   id<Dimensions> &operator=(const id<Dimensions> &rhs) = default;
   id<Dimensions> &operator=(id<Dimensions> &&rhs) = default;
 
-  /// Construct a 1D id with value dim0.
+  /// Constructs a 1D id with value dim0.
   /// Only valid when the template parameter Dimensions is equal to 1.
   template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
   id(std::size_t dim0) noexcept : Base(dim0) {}
 
-  /// Construct a 2D id with values dim0, dim1.
+  /// Constructs a 2D id with values dim0, dim1.
   /// Only valid when the template parameter Dimensions is equal to 2.
   template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
   id(std::size_t dim0, std::size_t dim1) noexcept : Base(dim0, dim1) {}
 
-  /// Construct a 3D id with values dim0, dim1, dim2.
+  /// Constructs a 3D id with values dim0, dim1, dim2.
   /// Only valid when the template parameter Dimensions is equal to 3.
   template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
   id(std::size_t dim0, std::size_t dim1, std::size_t dim2) noexcept
       : Base(dim0, dim1, dim2) {}
 
-  /// Construct an id from the dimensions of range.
+  /// Constructs an id from the dimensions of range.
   /// Only valid when the template parameter Dimensions is equal to 1.
   template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
   id(const range<Dimensions> &range) noexcept : Base(range.get(0)) {}
 
-  /// Construct an id from the dimensions of range.
+  /// Constructs an id from the dimensions of range.
   /// Only valid when the template parameter Dimensions is equal to 2.
   template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
   id(const range<Dimensions> &range) noexcept
       : Base(range.get(0), range.get(1)) {}
 
-  /// Construct an id from the dimensions of range.
+  /// Constructs an id from the dimensions of range.
   /// Only valid when the template parameter Dimensions is equal to 3.
   template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
   id(const range<Dimensions> &range) noexcept
       : Base(range.get(0), range.get(1), range.get(2)) {}
 
-  /// Construct an id from item.get_id().
+  /// Constructs an id from item.get_id().
   /// Only valid when the template parameter Dimensions is equal to 1.
   template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
   id(const item<Dimensions> &item) noexcept : Base(item.get_id(0)) {}
 
-  /// Construct an id from item.get_id().
+  /// Constructs an id from item.get_id().
   /// Only valid when the template parameter Dimensions is equal to 2.
   template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
   id(const item<Dimensions> &item) noexcept
       : Base(item.get_id(0), item.get_id(1)) {}
 
-  /// Construct an id from item.get_id().
+  /// Constructs an id from item.get_id().
   /// Only valid when the template parameter Dimensions is equal to 3.
   template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
   id(const item<Dimensions> &item) noexcept
@@ -335,11 +335,11 @@ template <int Dimensions /* = 1*/, bool WithOffset /* = true*/> class item {
   }
 
   /// Deprecated in SYCL 2020.
-  /// For an item converted from an item with no offset this will always return
+  /// For an item converted from an item with no offset, this will always return
   /// an id of all 0 values. This member function is only available if
   /// WithOffset is true.
   /// \return an id representing the n-dimensional offset provided to the
-  /// parallel_for and that is added by the runtime to the global-ID of each
+  /// parallel_for and added by the runtime to the global-ID of each
   /// work-item, if this item represents a global range.
   template <bool HasOffset = WithOffset,
             std::enable_if_t<HasOffset == true, bool> = true>
@@ -348,7 +348,7 @@ template <int Dimensions /* = 1*/, bool WithOffset /* = true*/> class item {
   }
 
   /// Deprecated in SYCL 2020.
-  /// This conversion allow users to seamlessly write code that assumes an
+  /// This conversion allows users to seamlessly write code that assumes an
   /// offset and still provides an offset-less item. Available only when:
   /// WithOffset == false.
   /// \return an item representing the same information as the object holds but
diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index 776cbabfe9c8e..fbde8b39fdaa5 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -167,7 +167,7 @@ class _LIBSYCL_EXPORT queue {
   /// Defines and invokes a SYCL kernel function as a lambda expression or a
   /// named function object type.
   ///
-  /// \param depEvents is a collection of events which specify the kernel
+  /// \param depEvents is a collection of events that specify the kernel
   /// dependencies.
   /// \param kernelFunc is the kernel functor or lambda.
   /// \return an event that represents the status of the submitted kernel.
@@ -191,7 +191,7 @@ class _LIBSYCL_EXPORT queue {
   /// named function object type, for the specified range.
   ///
   /// \param numWorkItems specifies the global work space of the kernel.
-  /// \param rest acts as-if: const KernelType &KernelFunc".
+  /// \param rest acts as if it was "const KernelType &KernelFunc".
   // TODO: Rest will represent reduction types once it is supported.
   template <typename KernelName = detail::AutoName, typename... Rest>
   event parallel_for(range<1> numWorkItems, Rest &&...rest) {
@@ -203,7 +203,7 @@ class _LIBSYCL_EXPORT queue {
   /// named function object type, for the specified range.
   ///
   /// \param numWorkItems specifies the global work space of the kernel.
-  /// \param rest acts as-if: const KernelType &KernelFunc".
+  /// \param rest acts as if it was "const KernelType &KernelFunc".
   // TODO: Rest will represent reduction types once it is supported.
   template <typename KernelName = detail::AutoName, typename... Rest>
   event parallel_for(range<2> numWorkItems, Rest &&...rest) {
@@ -215,7 +215,7 @@ class _LIBSYCL_EXPORT queue {
   /// named function object type, for the specified range.
   ///
   /// \param numWorkItems specifies the global work space of the kernel.
-  /// \param rest acts as-if: const KernelType &KernelFunc".
+  /// \param rest acts as if it was "const KernelType &KernelFunc".
   // TODO: Rest will represent reduction types once it is supported.
   template <typename KernelName = detail::AutoName, typename... Rest>
   event parallel_for(range<3> numWorkItems, Rest &&...rest) {
@@ -229,7 +229,7 @@ class _LIBSYCL_EXPORT queue {
   /// \param numWorkItems specifies the global work space of the kernel.
   /// \param depEvent adds a requirement that the action represented by depEvent
   /// must complete before executing this kernel.
-  /// \param rest acts as-if: const KernelType &KernelFunc".
+  /// \param rest acts as if it was "const KernelType &KernelFunc".
   // TODO: Rest will represent reduction types once it is supported.
   template <typename KernelName = detail::AutoName, typename... Rest>
   event parallel_for(range<1> numWorkItems, event depEvent, Rest &&...rest) {
@@ -243,7 +243,7 @@ class _LIBSYCL_EXPORT queue {
   /// \param numWorkItems specifies the global work space of the kernel.
   /// \param depEvent adds a requirement that the action represented by depEvent
   /// must complete before executing this kernel.
-  /// \param rest acts as-if: const KernelType &KernelFunc".
+  /// \param rest acts as if it was "const KernelType &KernelFunc".
   // TODO: Rest will represent reduction types once it is supported.
   template <typename KernelName = detail::AutoName, typename... Rest>
   event parallel_for(range<2> numWorkItems, event depEvent, Rest &&...rest) {
@@ -257,7 +257,7 @@ class _LIBSYCL_EXPORT queue {
   /// \param numWorkItems specifies the global work space of the kernel.
   /// \param depEvent adds a requirement that the action represented by depEvent
   /// must complete before executing this kernel.
-  /// \param rest acts as-if: const KernelType &KernelFunc".
+  /// \param rest acts as if it was "const KernelType &KernelFunc".
   // TODO: Rest will represent reduction types once it is supported.
   template <typename KernelName = detail::AutoName, typename... Rest>
   event parallel_for(range<3> numWorkItems, event depEvent, Rest &&...rest) {
@@ -271,7 +271,7 @@ class _LIBSYCL_EXPORT queue {
   /// \param numWorkItems specifies the global work space of the kernel
   /// \param depEvents is a vector of events that specifies the kernel
   /// dependencies.
-  /// \param rest acts as-if: const KernelType &KernelFunc".
+  /// \param rest acts as if it was "const KernelType &KernelFunc".
   // TODO: Rest will represent reduction types once it is supported.
   template <typename KernelName = detail::AutoName, typename... Rest>
   event parallel_for(range<1> numWorkItems, const std::vector<event> &depEvents,
@@ -286,7 +286,7 @@ class _LIBSYCL_EXPORT queue {
   /// \param numWorkItems specifies the global work space of the kernel
   /// \param depEvents is a vector of events that specifies the kernel
   /// dependencies.
-  /// \param rest acts as-if: const KernelType &KernelFunc".
+  /// \param rest acts as if it was "const KernelType &KernelFunc".
   // TODO: Rest will represent reduction types once it is supported.
   template <typename KernelName = detail::AutoName, typename... Rest>
   event parallel_for(range<2> numWorkItems, const std::vector<event> &depEvents,
@@ -301,7 +301,7 @@ class _LIBSYCL_EXPORT queue {
   /// \param numWorkItems specifies the global work space of the kernel
   /// \param depEvents is a vector of events that specifies the kernel
   /// dependencies.
-  /// \param rest acts as-if: const KernelType &KernelFunc".
+  /// \param rest acts as if it was "const KernelType &KernelFunc".
   // TODO: Rest will represent reduction types once it is supported.
   template <typename KernelName = detail::AutoName, typename... Rest>
   event parallel_for(range<3> numWorkItems, const std::vector<event> &depEvents,
@@ -339,11 +339,11 @@ class _LIBSYCL_EXPORT queue {
     return getLastEvent();
   }
 
-  /// Name of this function is defined by compiler. It generates call to this
+  /// Name of this function is defined by compiler. It generates a call to this
   /// function in the host implementation of KernelFunc in submitSingleTask or
   /// submitParallelFor.
-  /// \param KernelName a name of the kernel being invoked.
-  /// \param args kernel arguments for kernel invocation.
+  /// \param KernelName the name of the kernel being invoked.
+  /// \param args the kernel arguments for the kernel invocation.
   template <typename KN, typename... Args>
   void sycl_kernel_launch(const char *KernelName, Args &&...args) {
     static_assert(
@@ -358,8 +358,8 @@ class _LIBSYCL_EXPORT queue {
 
   /// The sycl_kernel_entry_point attribute facilitates the generation of an
   /// offload kernel entry point function with parameters corresponding to the
-  /// (potentially decomposed) kernel arguments and a body that (potentially
-  /// reconstructs the arguments and) executes the kernel.
+  /// (potentially decomposed) kernel arguments and a body that executes the
+  /// kernel (after reconstructing the arguments if required).
 #ifdef SYCL_LANGUAGE_VERSION
 #  define _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)                              \
     [[clang::sycl_kernel_entry_point(KernelName)]]
@@ -368,8 +368,8 @@ class _LIBSYCL_EXPORT queue {
 #endif // SYCL_LANGUAGE_VERSION
 
   /// Specifies the parameters and body of the generated offload kernel entry
-  /// point for single_task invocations. On host compiler generates call to
-  /// sycl_kernel_launch instead of KernelFunc invocation.
+  /// point for single_task invocations. On host, the compiler generates a call
+  /// to sycl_kernel_launch instead of the KernelFunc invocation.
   template <typename KernelName, typename KernelType>
   _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
   void submitSingleTask(const KernelType &KernelFunc) {
@@ -377,8 +377,8 @@ class _LIBSYCL_EXPORT queue {
   }
 
   /// Specifies the parameters and body of the generated offload kernel entry
-  /// point for parallel_for invocations. On host compiler generates call to
-  /// sycl_kernel_launch instead of KernelFunc invocation.
+  /// point for parallel_for invocations. On host, the compiler generates a call
+  /// to sycl_kernel_launch instead of the KernelFunc invocation.
   template <typename KernelName, typename ElementType, typename KernelType>
   _LIBSYCL_ENTRY_POINT_ATTR__(KernelName)
   void submitParallelFor(const KernelType &KernelFunc) {
@@ -386,19 +386,19 @@ class _LIBSYCL_EXPORT queue {
   }
 #undef _LIBSYCL_ENTRY_POINT_ATTR__
 
-  /// Passes kernel parameters to runtime.
+  /// Passes kernel parameters to the runtime.
   /// \param Events a collection of events representing dependencies of the
   /// kernel to submit.
-  /// \param Range a unified view of range for kernel execution.
+  /// \param Range a unified view of the kernel execution range.
   void setKernelParameters(const std::vector<event> &Events,
                            const detail::UnifiedRangeView &Range = {});
 
   /// Passes kernel arguments to runtime.
   /// If all dependencies are met and kernel can be submitted to backend - it is
   /// done in this call.
-  /// \param KernelInfo a name of the kernel being invoked.
-  /// \param ArgData a pointer to kernel argument.
-  /// \param ArgSize a size of kernel argument.
+  /// \param KernelInfo the information for the kernel being invoked.
+  /// \param ArgData a pointer to the kernel argument.
+  /// \param ArgSize the size of the kernel argument.
   void submitKernelImpl(detail::DeviceKernelInfo &KernelInfo, void *ArgData,
                         size_t ArgSize);
 

>From d9fd9154b6d12443c293ae8b18041899f80a6b7d Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Mon, 27 Apr 2026 06:47:49 -0700
Subject: [PATCH 24/25] fix comment

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/include/sycl/__impl/queue.hpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libsycl/include/sycl/__impl/queue.hpp b/libsycl/include/sycl/__impl/queue.hpp
index fbde8b39fdaa5..ea69d8ac28fd6 100644
--- a/libsycl/include/sycl/__impl/queue.hpp
+++ b/libsycl/include/sycl/__impl/queue.hpp
@@ -394,8 +394,8 @@ class _LIBSYCL_EXPORT queue {
                            const detail::UnifiedRangeView &Range = {});
 
   /// Passes kernel arguments to runtime.
-  /// If all dependencies are met and kernel can be submitted to backend - it is
-  /// done in this call.
+  /// If all the dependencies can be handled by the backend, the kernel is
+  /// submitted to it directly in this call.
   /// \param KernelInfo the information for the kernel being invoked.
   /// \param ArgData a pointer to the kernel argument.
   /// \param ArgSize the size of the kernel argument.

>From f4711c9a8e8ccaf5969b7403e57b73ae6342875f Mon Sep 17 00:00:00 2001
From: "Tikhomirova, Kseniya" <kseniya.tikhomirova at intel.com>
Date: Mon, 1 Jun 2026 04:42:56 -0700
Subject: [PATCH 25/25] fix merge conflict

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova at intel.com>
---
 libsycl/src/detail/queue_impl.cpp | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp
index 623b326637932..2a1c6c3b980bb 100644
--- a/libsycl/src/detail/queue_impl.cpp
+++ b/libsycl/src/detail/queue_impl.cpp
@@ -121,13 +121,11 @@ void QueueImpl::submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
   assert(ArgData && "At least one argument must exist");
   assert(ArgSize && "Arguments size must be greater than 0");
 
-  // ol_kernel_launch_prop_t Props[2];
-  // Props[0].type = OL_KERNEL_LAUNCH_PROP_TYPE_SIZE;
-  // Props[0].data = &ArgSize;
-  // Props[1] = OL_KERNEL_LAUNCH_PROP_END;
+  void *ArgPtrs[] = {ArgData};
+  size_t ArgSizes[] = {ArgSize};
   auto Result =
-      olLaunchKernel(MOffloadQueue, MDevice.getOLHandle(), Kernel, &ArgData,
-                     ArgSize, &MCurrentSubmitInfo.Range /*, Props*/);
+      olLaunchKernel(MOffloadQueue, MDevice.getOLHandle(), Kernel,
+                     &MCurrentSubmitInfo.Range, NULL, 1, ArgPtrs, ArgSizes);
   // Clean up current kernel submit data to prepare structures for next
   // submission.
   MCurrentSubmitInfo.DepEvents.clear();
@@ -139,7 +137,8 @@ void QueueImpl::submitKernelImpl(DeviceKernelInfo &KernelInfo, void *ArgData,
                               formatCodeString(Result));
 
   ol_event_handle_t NewEvent{};
-  callAndThrow(olCreateEvent, MOffloadQueue, &NewEvent);
+  ol_event_flags_t Flags{};
+  callAndThrow(olCreateEvent, MOffloadQueue, Flags, &NewEvent);
 
   MCurrentSubmitInfo.LastEvent =
       EventImpl::createEventWithHandle(NewEvent, MDevice.getPlatformImpl());



More information about the llvm-commits mailing list