[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