[llvm] [Offload] Add `--kernel <name>` command to `llvm-gpu-loader` (PR #213738)

Joseph Huber via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 3 11:58:01 PDT 2026


https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/213738

>From 58c767fa3f3c95665e8dce3024fd4492ba21a8e5 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Mon, 3 Aug 2026 13:31:42 -0500
Subject: [PATCH 1/2] [Offload] Add `--kernel <name>` command to
 `llvm-gpu-loader`

Summary:
This makes it easier to test a single function without opting-in to the
whole `crt1.o` infra for `libc` that was originally intended to test
existing CPU tests. Good for possible future unit tests, cheap tool that
can launch a kernel. Only provides `foo(void)` kernels for now, can be
improved.
---
 .../tools/llvm-gpu-loader/llvm-gpu-loader.cpp | 30 +++++++++++++++----
 1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp b/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp
index 2747757e1c200..42b10c0a4b189 100644
--- a/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp
+++ b/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp
@@ -65,6 +65,11 @@ static cl::alias Blocks("blocks", cl::aliasopt(BlocksX),
                         cl::desc("Alias for --blocks-x"),
                         cl::cat(LoaderCategory));
 
+static cl::list<std::string> Kernels(
+    "kernel", cl::value_desc("name"),
+    cl::desc("Launch '<name>(void)' instead of the 'main' entry point."),
+    cl::cat(LoaderCategory));
+
 static cl::opt<std::string> File(cl::Positional, cl::Required,
                                  cl::desc("<gpu executable>"),
                                  cl::cat(LoaderCategory));
@@ -164,6 +169,11 @@ ol_device_handle_t getHostDevice() {
   return Device;
 }
 
+static bool hasKernel(ol_program_handle_t Program, const char *Name) {
+  ol_symbol_handle_t Kernel;
+  return !olGetSymbol(Program, Name, OL_SYMBOL_KIND_KERNEL, &Kernel);
+}
+
 template <typename... Args>
 void launchKernel(ol_queue_handle_t Queue, ol_device_handle_t Device,
                   ol_program_handle_t Program, const char *Name,
@@ -263,9 +273,12 @@ int main(int argc, const char **argv, const char **envp) {
   OFFLOAD_ERR(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, sizeof(int), &DevRet));
   OFFLOAD_ERR(olMemcpy(Queue, DevRet, Device, &Zero, Host, sizeof(int)));
 
+  // The '_begin' and '_end' kernels perform libc startup and teardown. Global
+  // constructors and destructors are handled automatically by the runtime.
   ol_kernel_launch_size_args_t BeginLaunch{1, {1, 1, 1}, {1, 1, 1}, 0};
-  launchKernel(Queue, Device, Program, "_begin", BeginLaunch, DevArgc, DevArgv,
-               DevEnvp);
+  if (hasKernel(Program, "_begin"))
+    launchKernel(Queue, Device, Program, "_begin", BeginLaunch, DevArgc,
+                 DevArgv, DevEnvp);
   OFFLOAD_ERR(olSyncQueue(Queue));
 
   uint32_t Dims = (BlocksZ > 1) ? 3 : (BlocksY > 1) ? 2 : 1;
@@ -273,11 +286,18 @@ int main(int argc, const char **argv, const char **envp) {
                                            {BlocksX, BlocksY, BlocksZ},
                                            {ThreadsX, ThreadsY, ThreadsZ},
                                            /*SharedMemBytes=*/0};
-  launchKernel(Queue, Device, Program, "_start", StartLaunch, DevArgc, DevArgv,
-               DevEnvp, DevRet);
+  if (Kernels.empty()) {
+    launchKernel(Queue, Device, Program, "_start", StartLaunch, DevArgc,
+                 DevArgv, DevEnvp, DevRet);
+  } else {
+    // Launch the user-specified kernels in order. These must take no arguments.
+    for (const std::string &Kernel : Kernels)
+      launchKernel(Queue, Device, Program, Kernel.c_str(), StartLaunch);
+  }
 
   ol_kernel_launch_size_args_t EndLaunch{1, {1, 1, 1}, {1, 1, 1}, 0};
-  launchKernel(Queue, Device, Program, "_end", EndLaunch);
+  if (hasKernel(Program, "_end"))
+    launchKernel(Queue, Device, Program, "_end", EndLaunch);
 
   int Ret;
   OFFLOAD_ERR(olMemcpy(Queue, &Ret, Host, DevRet, Device, sizeof(int)));

>From a62efbd4680b56eebb65de3a3448f3138860e5b7 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Mon, 3 Aug 2026 13:57:43 -0500
Subject: [PATCH 2/2] commnets

---
 .../tools/llvm-gpu-loader/llvm-gpu-loader.cpp | 33 ++++++++-----------
 1 file changed, 13 insertions(+), 20 deletions(-)

diff --git a/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp b/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp
index 42b10c0a4b189..9505355e751ed 100644
--- a/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp
+++ b/llvm/tools/llvm-gpu-loader/llvm-gpu-loader.cpp
@@ -169,11 +169,6 @@ ol_device_handle_t getHostDevice() {
   return Device;
 }
 
-static bool hasKernel(ol_program_handle_t Program, const char *Name) {
-  ol_symbol_handle_t Kernel;
-  return !olGetSymbol(Program, Name, OL_SYMBOL_KIND_KERNEL, &Kernel);
-}
-
 template <typename... Args>
 void launchKernel(ol_queue_handle_t Queue, ol_device_handle_t Device,
                   ol_program_handle_t Program, const char *Name,
@@ -273,31 +268,29 @@ int main(int argc, const char **argv, const char **envp) {
   OFFLOAD_ERR(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, sizeof(int), &DevRet));
   OFFLOAD_ERR(olMemcpy(Queue, DevRet, Device, &Zero, Host, sizeof(int)));
 
-  // The '_begin' and '_end' kernels perform libc startup and teardown. Global
-  // constructors and destructors are handled automatically by the runtime.
-  ol_kernel_launch_size_args_t BeginLaunch{1, {1, 1, 1}, {1, 1, 1}, 0};
-  if (hasKernel(Program, "_begin"))
-    launchKernel(Queue, Device, Program, "_begin", BeginLaunch, DevArgc,
-                 DevArgv, DevEnvp);
-  OFFLOAD_ERR(olSyncQueue(Queue));
-
   uint32_t Dims = (BlocksZ > 1) ? 3 : (BlocksY > 1) ? 2 : 1;
   ol_kernel_launch_size_args_t StartLaunch{Dims,
                                            {BlocksX, BlocksY, BlocksZ},
                                            {ThreadsX, ThreadsY, ThreadsZ},
                                            /*SharedMemBytes=*/0};
-  if (Kernels.empty()) {
-    launchKernel(Queue, Device, Program, "_start", StartLaunch, DevArgc,
-                 DevArgv, DevEnvp, DevRet);
-  } else {
+  if (!Kernels.empty()) {
     // Launch the user-specified kernels in order. These must take no arguments.
     for (const std::string &Kernel : Kernels)
       launchKernel(Queue, Device, Program, Kernel.c_str(), StartLaunch);
-  }
+  } else {
+    // The '_begin' and '_end' kernels perform libc startup and teardown. Global
+    // constructors and destructors are handled automatically by the runtime.
+    ol_kernel_launch_size_args_t BeginLaunch{1, {1, 1, 1}, {1, 1, 1}, 0};
+    launchKernel(Queue, Device, Program, "_begin", BeginLaunch, DevArgc,
+                 DevArgv, DevEnvp);
+    OFFLOAD_ERR(olSyncQueue(Queue));
 
-  ol_kernel_launch_size_args_t EndLaunch{1, {1, 1, 1}, {1, 1, 1}, 0};
-  if (hasKernel(Program, "_end"))
+    launchKernel(Queue, Device, Program, "_start", StartLaunch, DevArgc,
+                 DevArgv, DevEnvp, DevRet);
+
+    ol_kernel_launch_size_args_t EndLaunch{1, {1, 1, 1}, {1, 1, 1}, 0};
     launchKernel(Queue, Device, Program, "_end", EndLaunch);
+  }
 
   int Ret;
   OFFLOAD_ERR(olMemcpy(Queue, &Ret, Host, DevRet, Device, sizeof(int)));



More information about the llvm-commits mailing list