[llvm] [Offload][L0] Include context allocations in kernel indirect access flags (PR #229384)

via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 6 04:40:49 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-offload

Author: Jan Trusiłło (311Volt)

<details>
<summary>Changes</summary>

#<!-- -->224930 moved L0-backed liboffload allocations into the per-context allocators of `LevelZeroPluginContextTy`. However, `L0KernelTy::setIndirectFlags` was never updated to count flags from these new allocators, and as a result `olMemAlloc` allocations no longer count towards kernel indirect access flags. This is significant for libsycl - for instance, a kernel lambda is passed there as a single byval struct, and therefore all captured USM pointers are necessarily indirect access.

This bug was revealed by a buildbot failure when https://github.com/llvm/llvm-project/pull/227589 was merged and later reverted. The libsycl tests on the buildbot (Arc B580) only passed because the counters in `L0ContextTy::HostMemAllocator` are uninitialized and happened to contain leftover heap data, which set SHARED by accident. #<!-- -->227589 changed the size of L0ContextTy, the counters came out as zero, and 15 libsycl tests failed. That PR was reverted in #<!-- -->228485. Its change was correct and can reland once this one is in.

Zero-initialize the implicit arg counters, make `getIndirectFlags` aware of context-local allocators, and add a unit test where a kernel writes managed memory through a pointer held inside a by-value struct, the same pattern as a SYCL lambda.

---
Full diff: https://github.com/llvm/llvm-project/pull/229384.diff


7 Files Affected:

- (modified) offload/plugins-nextgen/level_zero/include/L0Kernel.h (+4-1) 
- (modified) offload/plugins-nextgen/level_zero/include/L0Memory.h (+1-1) 
- (modified) offload/plugins-nextgen/level_zero/include/L0Plugin.h (+10) 
- (modified) offload/plugins-nextgen/level_zero/src/L0Kernel.cpp (+6-1) 
- (modified) offload/unittests/OffloadAPI/device_code/CMakeLists.txt (+2) 
- (added) offload/unittests/OffloadAPI/device_code/byval_ptr.cpp (+12) 
- (modified) offload/unittests/OffloadAPI/kernel/olLaunchKernel.cpp (+27) 


``````````diff
diff --git a/offload/plugins-nextgen/level_zero/include/L0Kernel.h b/offload/plugins-nextgen/level_zero/include/L0Kernel.h
index d9bc1bf5d727f..1760a5846fa07 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Kernel.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Kernel.h
@@ -23,6 +23,7 @@ namespace llvm::omp::target::plugin {
 class L0DeviceTy;
 class L0ProgramTy;
 class L0QueueTy;
+class LevelZeroPluginContextTy;
 struct L0LaunchEnvTy;
 
 /// Kernel properties.
@@ -64,7 +65,9 @@ class L0KernelTy : public GenericKernelTy {
   ze_group_size_t createKernelGroups(L0DeviceTy &L0Device, L0LaunchEnvTy &KEnv,
                                      uint32_t NumThreads[3],
                                      uint32_t NumBlocks[3]) const;
-  Error setIndirectFlags(L0DeviceTy &L0Device, L0LaunchEnvTy &KEnv) const;
+  Error setIndirectFlags(L0DeviceTy &L0Device,
+                         LevelZeroPluginContextTy &UserCtx,
+                         L0LaunchEnvTy &KEnv) const;
 
 public:
   /// Create a L0 kernel with a name and an execution mode.
diff --git a/offload/plugins-nextgen/level_zero/include/L0Memory.h b/offload/plugins-nextgen/level_zero/include/L0Memory.h
index 32bb0a5dc288c..f7efc9cdd05b0 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Memory.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Memory.h
@@ -240,7 +240,7 @@ class MemAllocatorTy {
     /// Map from allocated pointer to allocation information.
     std::map<void *, MemAllocInfoTy> Map;
     /// Map from target alloc kind to number of implicit arguments.
-    std::array<uint32_t, MaxMemKind> NumImplicitArgs;
+    std::array<uint32_t, MaxMemKind> NumImplicitArgs{};
 
   public:
     /// Add allocation information to the map.
diff --git a/offload/plugins-nextgen/level_zero/include/L0Plugin.h b/offload/plugins-nextgen/level_zero/include/L0Plugin.h
index ac95287de23b0..fd5bc733ca8a8 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Plugin.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Plugin.h
@@ -49,6 +49,16 @@ class LevelZeroPluginContextTy final : public PluginContextTy {
                          TargetAllocTy Kind) override;
   Expected<PluginAllocInfoTy> getAllocInfo(const void *Ptr) override;
 
+  /// Get kernel indirect access flags from all allocators in this context.
+  ze_kernel_indirect_access_flags_t getIndirectFlags() {
+    ze_kernel_indirect_access_flags_t Flags = 0;
+    for (auto &[Device, Allocator] : DeviceAllocators)
+      Flags |= Allocator->getIndirectFlags();
+    if (HostAllocator)
+      Flags |= HostAllocator->getIndirectFlags();
+    return Flags;
+  }
+
   /// Initialize per-plugin-context memory allocators. Runs the pool
   /// probe L0 calls up-front so the first user allocation is not delayed.
   Error initAllocators();
diff --git a/offload/plugins-nextgen/level_zero/src/L0Kernel.cpp b/offload/plugins-nextgen/level_zero/src/L0Kernel.cpp
index 52b1d267d05e4..23af19f6df61e 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Kernel.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Kernel.cpp
@@ -12,6 +12,7 @@
 
 #include "L0Kernel.h"
 #include "L0Device.h"
+#include "L0Plugin.h"
 #include "L0Program.h"
 
 namespace llvm::omp::target::plugin {
@@ -102,11 +103,14 @@ ze_group_size_t L0KernelTy::createKernelGroups(L0DeviceTy &L0Device,
 }
 
 Error L0KernelTy::setIndirectFlags(L0DeviceTy &L0Device,
+                                   LevelZeroPluginContextTy &UserCtx,
                                    L0LaunchEnvTy &KEnv) const {
   // Set Kernel Indirect flags.
   ze_kernel_indirect_access_flags_t Flags = 0;
   Flags |= L0Device.getMemAllocator(TARGET_ALLOC_HOST).getIndirectFlags();
   Flags |= L0Device.getMemAllocator(TARGET_ALLOC_DEVICE).getIndirectFlags();
+  // Allocations made through a liboffload context live in its own allocators.
+  Flags |= UserCtx.getIndirectFlags();
 
   if (KEnv.KernelPR.IndirectAccessFlags != Flags) {
     // Combine with common access flags.
@@ -193,7 +197,8 @@ Error L0KernelTy::launchImpl(GenericDeviceTy &GenericDevice,
         "expected by the kernel (%u)",
         LaunchArgs.NumArgs, KernelPR.NumKernelArgs);
 
-  if (auto Err = setIndirectFlags(L0Device, KEnv))
+  assert(Queue->getUserCtx() && "Queue must belong to a plugin context");
+  if (auto Err = setIndirectFlags(L0Device, *Queue->getUserCtx(), KEnv))
     return Err;
 
   // The next call should unlock the KernelLock internally.
diff --git a/offload/unittests/OffloadAPI/device_code/CMakeLists.txt b/offload/unittests/OffloadAPI/device_code/CMakeLists.txt
index 92112484b3df7..2f9dd252120b5 100644
--- a/offload/unittests/OffloadAPI/device_code/CMakeLists.txt
+++ b/offload/unittests/OffloadAPI/device_code/CMakeLists.txt
@@ -15,6 +15,7 @@ add_offload_test_device_code(global_ctor.cpp global_ctor)
 add_offload_test_device_code(global_dtor.cpp global_dtor)
 add_offload_test_device_code(sequence.cpp sequence)
 add_offload_test_device_code(gridsize.cpp gridsize)
+add_offload_test_device_code(byval_ptr.cpp byval_ptr -O3)
 
 add_custom_target(offload_device_binaries DEPENDS
     foo.bin
@@ -33,5 +34,6 @@ add_custom_target(offload_device_binaries DEPENDS
     global_dtor.bin
     sequence.bin
     gridsize.bin
+    byval_ptr.bin
 )
 set(OFFLOAD_TEST_DEVICE_CODE_PATH ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
diff --git a/offload/unittests/OffloadAPI/device_code/byval_ptr.cpp b/offload/unittests/OffloadAPI/device_code/byval_ptr.cpp
new file mode 100644
index 0000000000000..e4731267383f4
--- /dev/null
+++ b/offload/unittests/OffloadAPI/device_code/byval_ptr.cpp
@@ -0,0 +1,12 @@
+#include <gpuintrin.h>
+#include <stdint.h>
+
+// The output pointer is only reachable through a by-value struct, like a
+// captured SYCL kernel lambda, so the device accesses it indirectly.
+struct Wrapper {
+  uint32_t *Out;
+};
+
+extern "C" __gpu_kernel void byval_ptr(Wrapper W) {
+  W.Out[__gpu_thread_id(0)] = __gpu_thread_id(0);
+}
diff --git a/offload/unittests/OffloadAPI/kernel/olLaunchKernel.cpp b/offload/unittests/OffloadAPI/kernel/olLaunchKernel.cpp
index f9cc29d1e9d0c..f8883e0659c17 100644
--- a/offload/unittests/OffloadAPI/kernel/olLaunchKernel.cpp
+++ b/offload/unittests/OffloadAPI/kernel/olLaunchKernel.cpp
@@ -28,6 +28,7 @@ KERNEL_TEST(SingleCounterSyncEvent, single_counter)
 KERNEL_TEST(GlobalCtor, global_ctor)
 KERNEL_TEST(GlobalDtor, global_dtor)
 KERNEL_TEST(GridSize, gridsize)
+KERNEL_TEST(ByValPtr, byval_ptr)
 
 struct LaunchMultipleKernelTestBase : LaunchKernelTestBase {
   void SetUpKernels(const char *program, std::vector<const char *> kernels) {
@@ -167,6 +168,32 @@ TEST_P(olLaunchKernelCompositeTest, Success) {
   ASSERT_SUCCESS(olMemFree(Context, Mem));
 }
 
+struct Wrapper {
+  uint32_t *Out;
+};
+
+TEST_P(olLaunchKernelByValPtrTest, Success) {
+  void *Mem;
+  ASSERT_SUCCESS(olMemAlloc(Context, Device, OL_ALLOC_TYPE_MANAGED,
+                            LaunchArgs.GroupSize.x * sizeof(uint32_t), &Mem));
+
+  Wrapper W{(uint32_t *)Mem};
+
+  void *ArgPtrs[] = {&W};
+  size_t ArgSizes[] = {sizeof(W)};
+
+  ASSERT_SUCCESS(olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, nullptr,
+                                std::size(ArgPtrs), ArgPtrs, ArgSizes));
+
+  ASSERT_SUCCESS(olSyncQueue(Queue));
+
+  uint32_t *Data = (uint32_t *)Mem;
+  for (uint32_t i = 0; i < LaunchArgs.GroupSize.x; i++)
+    ASSERT_EQ(Data[i], i);
+
+  ASSERT_SUCCESS(olMemFree(Context, Mem));
+}
+
 TEST_P(olLaunchKernelFooTest, SuccessSynchronous) {
   void *Mem;
   ASSERT_SUCCESS(olMemAlloc(Context, Device, OL_ALLOC_TYPE_MANAGED,

``````````

</details>


https://github.com/llvm/llvm-project/pull/229384


More information about the llvm-commits mailing list