[llvm] [offload] add tests for event synchronization (PR #193927)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 28 04:30:00 PDT 2026
https://github.com/EuphoricThinking updated https://github.com/llvm/llvm-project/pull/193927
>From 5827282353dba2bc8b2c0f8fbf53ecd5d944aae1 Mon Sep 17 00:00:00 2001
From: Agata Momot <agata.momot at intel.com>
Date: Tue, 31 Mar 2026 08:24:25 +0000
Subject: [PATCH] [offload] draft POC: verify event synchronization
---
.../OffloadAPI/device_code/CMakeLists.txt | 2 +
.../OffloadAPI/device_code/single_counter.cpp | 22 +++++
.../OffloadAPI/kernel/olLaunchKernel.cpp | 97 +++++++++++++++++++
3 files changed, 121 insertions(+)
create mode 100644 offload/unittests/OffloadAPI/device_code/single_counter.cpp
diff --git a/offload/unittests/OffloadAPI/device_code/CMakeLists.txt b/offload/unittests/OffloadAPI/device_code/CMakeLists.txt
index 22ebacf62e83e..57f529e1a4ae6 100644
--- a/offload/unittests/OffloadAPI/device_code/CMakeLists.txt
+++ b/offload/unittests/OffloadAPI/device_code/CMakeLists.txt
@@ -7,6 +7,7 @@ add_offload_test_device_code(byte.cpp byte)
add_offload_test_device_code(localmem.cpp localmem)
add_offload_test_device_code(localmem_reduction.cpp localmem_reduction)
add_offload_test_device_code(localmem_static.cpp localmem_static)
+add_offload_test_device_code(single_counter.cpp single_counter)
add_offload_test_device_code(global.cpp global)
add_offload_test_device_code(global_ctor.cpp global_ctor)
add_offload_test_device_code(global_dtor.cpp global_dtor)
@@ -21,6 +22,7 @@ add_custom_target(offload_device_binaries DEPENDS
localmem.bin
localmem_reduction.bin
localmem_static.bin
+ single_counter.bin
global.bin
global_ctor.bin
global_dtor.bin
diff --git a/offload/unittests/OffloadAPI/device_code/single_counter.cpp b/offload/unittests/OffloadAPI/device_code/single_counter.cpp
new file mode 100644
index 0000000000000..e686254aaba5b
--- /dev/null
+++ b/offload/unittests/OffloadAPI/device_code/single_counter.cpp
@@ -0,0 +1,22 @@
+#include <gpuintrin.h>
+#include <stdint.h>
+
+extern "C" __gpu_kernel void single_counter(int32_t init_loop, int32_t addend,
+ uint32_t *init_val, uint32_t *out) {
+
+ volatile int32_t counter = init_loop;
+
+ // We save the value at the beginning of the function in order to verify later
+ // whether the previously submitted kernel has finished before the new enqueue
+ uint32_t local_sum = *init_val;
+
+ while (counter--) {
+ if (__gpu_thread_id(0) == 0) {
+ local_sum += addend;
+ }
+ }
+
+ if (__gpu_thread_id(0) == 0) {
+ *out = local_sum;
+ }
+}
diff --git a/offload/unittests/OffloadAPI/kernel/olLaunchKernel.cpp b/offload/unittests/OffloadAPI/kernel/olLaunchKernel.cpp
index 166b8dabff0d8..7f9557a9306a6 100644
--- a/offload/unittests/OffloadAPI/kernel/olLaunchKernel.cpp
+++ b/offload/unittests/OffloadAPI/kernel/olLaunchKernel.cpp
@@ -60,6 +60,7 @@ KERNEL_TEST(Byte, byte)
KERNEL_TEST(LocalMem, localmem)
KERNEL_TEST(LocalMemReduction, localmem_reduction)
KERNEL_TEST(LocalMemStatic, localmem_static)
+KERNEL_TEST(SingleCounterSyncEvent, single_counter)
KERNEL_TEST(GlobalCtor, global_ctor)
KERNEL_TEST(GlobalDtor, global_dtor)
@@ -77,6 +78,13 @@ struct LaunchMultipleKernelTestBase : LaunchKernelTestBase {
std::vector<ol_symbol_handle_t> Kernels;
};
+struct ArgsSingleCounter {
+ int32_t InitLoop;
+ int32_t Addend;
+ uint32_t *InitVal;
+ uint32_t *Out;
+};
+
#define KERNEL_MULTI_TEST(NAME, PROGRAM, ...) \
struct olLaunchKernel##NAME##Test : LaunchMultipleKernelTestBase { \
void SetUp() override { SetUpKernels(#PROGRAM, {__VA_ARGS__}); } \
@@ -240,6 +248,95 @@ TEST_P(olLaunchKernelLocalMemStaticTest, Success) {
ASSERT_SUCCESS(olMemFree(Mem));
}
+// The test intends to verify the correctness of the current implementation of
+// the event synchronisation.
+TEST_P(olLaunchKernelSingleCounterSyncEventTest, SuccessSyncEvent) {
+ void *InitValuePassed;
+ ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, sizeof(uint32_t),
+ &InitValuePassed));
+ *(uint32_t *)InitValuePassed = 0;
+
+ void *ResNum;
+ ASSERT_SUCCESS(
+ olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, sizeof(uint32_t), &ResNum));
+
+ // The execution time of the provided kernel should be high enough to ensure
+ // that the read value of the final result is not correct without explicit
+ // synchronization: explicit waiting for the event following the submitted
+ // operation. LoopRange is arbitrarily set with the goal of prolonging the
+ // kernel execution through a high number of loop iterations. In each
+ // iteration, NumberToAdd is added to the final sum, which is stored in
+ // ResNum.
+ int32_t LoopRange = 1000000;
+ int32_t NumberToAdd = 2;
+
+ ArgsSingleCounter Args{LoopRange, NumberToAdd, (uint32_t *)InitValuePassed,
+ (uint32_t *)ResNum};
+
+ ASSERT_SUCCESS(
+ olLaunchKernel(Queue, Device, Kernel, &Args, sizeof(Args), &LaunchArgs));
+
+ ol_event_handle_t Event = nullptr;
+ ASSERT_SUCCESS(olCreateEvent(Queue, &Event));
+ ASSERT_SUCCESS(olSyncEvent(Event));
+
+ uint32_t FinalResVal = *(uint32_t *)ResNum;
+ ASSERT_EQ(FinalResVal, NumberToAdd * LoopRange);
+
+ ASSERT_SUCCESS(olMemFree(InitValuePassed));
+ ASSERT_SUCCESS(olMemFree(ResNum));
+}
+
+TEST_P(olLaunchKernelSingleCounterSyncEventTest, SuccessTwoQueues) {
+ void *InitValuePassed;
+ ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, sizeof(uint32_t),
+ &InitValuePassed));
+ *(uint32_t *)InitValuePassed = 0;
+
+ void *ResNum;
+ ASSERT_SUCCESS(
+ olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, sizeof(uint32_t), &ResNum));
+
+ void *ResNum2;
+ ASSERT_SUCCESS(
+ olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, sizeof(uint32_t), &ResNum2));
+
+ ol_queue_handle_t Queue2 = nullptr;
+ ASSERT_SUCCESS(olCreateQueue(Device, &Queue2));
+
+ // For the explanation of the reasoning behind particular values assigned to
+ // parameters, see the comment in the Success test from the same test suite
+ int32_t LoopRange = 1000000;
+ int32_t NumberToAdd = 2;
+
+ ArgsSingleCounter Args{LoopRange, NumberToAdd, (uint32_t *)InitValuePassed,
+ (uint32_t *)ResNum};
+ ASSERT_SUCCESS(
+ olLaunchKernel(Queue, Device, Kernel, &Args, sizeof(Args), &LaunchArgs));
+
+ ol_event_handle_t Event = nullptr;
+ ASSERT_SUCCESS(olCreateEvent(Queue, &Event));
+ ASSERT_SUCCESS(olWaitEvents(Queue2, &Event, 1));
+ ArgsSingleCounter Args2{LoopRange, NumberToAdd, (uint32_t *)ResNum,
+ (uint32_t *)ResNum2};
+
+ // At the beginning of the kernel, ResNum from the first queue is saved
+ // locally as the initial value. If operations enqueued before Event have not
+ // been completed by the time the kernel is executed using the second queue,
+ // the FinalResVal would be incorrect.
+ ASSERT_SUCCESS(olLaunchKernel(Queue2, Device, Kernel, &Args2, sizeof(Args2),
+ &LaunchArgs));
+
+ ASSERT_SUCCESS(olSyncQueue(Queue2));
+
+ uint32_t FinalResVal = *(uint32_t *)ResNum2;
+ ASSERT_EQ(FinalResVal, 2 * NumberToAdd * LoopRange);
+
+ ASSERT_SUCCESS(olMemFree(InitValuePassed));
+ ASSERT_SUCCESS(olMemFree(ResNum));
+ ASSERT_SUCCESS(olMemFree(ResNum2));
+}
+
TEST_P(olLaunchKernelGlobalTest, Success) {
void *Mem;
ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED,
More information about the llvm-commits
mailing list