[llvm] [Offload] add 'olIterateSymbols' runtime function (PR #213036)
Joseph Huber via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 30 08:47:39 PDT 2026
https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/213036
>From 8b39ee55cda0ad4e663f4503eb80d481d2870574 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Thu, 30 Jul 2026 09:14:45 -0500
Subject: [PATCH 1/2] [Offload] add 'olIterateSymbols' runtime function
Summary:
This provides users with a way to check all symbols present in a given
program. This is useful for many cases where a power user wants to do
something dynamic with the objects present.
---
offload/liboffload/API/Symbol.td | 32 +++++
offload/liboffload/src/OffloadImpl.cpp | 75 ++++++++---
offload/plugins-nextgen/amdgpu/src/rtl.cpp | 33 ++++-
.../common/include/GlobalHandler.h | 18 ++-
.../common/include/PluginInterface.h | 4 +-
.../common/src/GlobalHandler.cpp | 46 +++++++
offload/plugins-nextgen/cuda/src/rtl.cpp | 4 +-
offload/plugins-nextgen/host/src/rtl.cpp | 4 +-
.../level_zero/include/L0Device.h | 2 +-
.../level_zero/include/L0Kernel.h | 2 +-
.../level_zero/include/L0Program.h | 8 ++
.../level_zero/src/L0Device.cpp | 2 +-
.../level_zero/src/L0Program.cpp | 15 +++
offload/unittests/OffloadAPI/CMakeLists.txt | 3 +-
.../OffloadAPI/symbol/olGetSymbolInfo.cpp | 18 +++
.../OffloadAPI/symbol/olIterateSymbols.cpp | 118 ++++++++++++++++++
16 files changed, 355 insertions(+), 29 deletions(-)
create mode 100644 offload/unittests/OffloadAPI/symbol/olIterateSymbols.cpp
diff --git a/offload/liboffload/API/Symbol.td b/offload/liboffload/API/Symbol.td
index c57a2e1b83632..9e2f92fbc3e16 100644
--- a/offload/liboffload/API/Symbol.td
+++ b/offload/liboffload/API/Symbol.td
@@ -32,6 +32,37 @@ def olGetSymbol : Function {
let returns = [];
}
+def ol_symbol_iterate_cb_t : FptrTypedef {
+ let desc = "User-provided function to be used with `olIterateSymbols`";
+ let params = [
+ Param<"ol_symbol_handle_t", "Symbol", "the symbol handle of the current iteration", PARAM_IN>,
+ Param<"void*", "UserData", "optional user data", PARAM_IN_OPTIONAL>
+ ];
+ let return = "bool";
+}
+
+def olIterateSymbols : Function {
+ let desc = "Iterates over all symbols of the given kind in the program, calling the callback for each symbol.";
+ let details = [
+ "If the user-provided callback returns `false`, the iteration is stopped.",
+ "Symbol handles are owned by the program and do not need to be manually destroyed."
+ ];
+ let params = [
+ Param<"ol_program_handle_t", "Program", "handle of the program", PARAM_IN>,
+ Param<"ol_symbol_kind_t", "Kind", "symbol kind to iterate over", PARAM_IN>,
+ Param<"ol_symbol_iterate_cb_t", "Callback", "User-provided function called for each symbol", PARAM_IN>,
+ Param<"void*", "UserData", "Optional user data to pass to the callback", PARAM_IN_OPTIONAL>
+ ];
+ let returns = [
+ Return<"OL_ERRC_UNSUPPORTED", [
+ "The platform could not enumerate symbols of the given kind."
+ ]>,
+ Return<"OL_ERRC_INVALID_BINARY", [
+ "The program's image could not be parsed to enumerate its symbols."
+ ]>
+ ];
+}
+
def ol_symbol_info_t : Enum {
let desc = "Supported symbol info.";
let is_typed = 1;
@@ -39,6 +70,7 @@ def ol_symbol_info_t : Enum {
TaggedEtor<"KIND", "ol_symbol_kind_t", "The kind of this symbol.">,
TaggedEtor<"GLOBAL_VARIABLE_ADDRESS", "void *", "The address in memory for this global variable.">,
TaggedEtor<"GLOBAL_VARIABLE_SIZE", "size_t", "The size in bytes for this global variable.">,
+ TaggedEtor<"NAME", "char[]", "The name of this symbol.">,
];
}
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 63fe726fbe544..a57e34c7fb650 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -199,10 +199,12 @@ struct ol_program_impl_t {
};
struct ol_symbol_impl_t {
- ol_symbol_impl_t(const char *Name, GenericKernelTy *Kernel)
- : PluginImpl(Kernel), Kind(OL_SYMBOL_KIND_KERNEL), Name(Name) {}
- ol_symbol_impl_t(const char *Name, GlobalTy &&Global)
- : PluginImpl(Global), Kind(OL_SYMBOL_KIND_GLOBAL_VARIABLE), Name(Name) {}
+ ol_symbol_impl_t(GenericKernelTy *Kernel)
+ : PluginImpl(Kernel), Kind(OL_SYMBOL_KIND_KERNEL),
+ Name(Kernel->getName()) {}
+ ol_symbol_impl_t(GlobalTy &&Global)
+ : PluginImpl(std::move(Global)), Kind(OL_SYMBOL_KIND_GLOBAL_VARIABLE),
+ Name(std::get<GlobalTy>(PluginImpl).getName()) {}
std::variant<GenericKernelTy *, GlobalTy> PluginImpl;
ol_symbol_kind_t Kind;
llvm::StringRef Name;
@@ -1281,12 +1283,11 @@ Error olLaunchKernel_impl(ol_queue_handle_t Queue, ol_device_handle_t Device,
return Error::success();
}
-Error olGetSymbol_impl(ol_program_handle_t Program, const char *Name,
- ol_symbol_kind_t Kind, ol_symbol_handle_t *Symbol) {
+Expected<ol_symbol_handle_t> getSymbolImplDetail(ol_program_handle_t Program,
+ StringRef Name,
+ ol_symbol_kind_t Kind) {
auto &Device = Program->Image->getDevice();
- std::lock_guard<std::mutex> Lock(Program->SymbolListMutex);
-
switch (Kind) {
case OL_SYMBOL_KIND_KERNEL: {
auto &Kernel = Program->KernelSymbols[Name];
@@ -1298,12 +1299,10 @@ Error olGetSymbol_impl(ol_program_handle_t Program, const char *Name,
if (auto Err = KernelImpl->init(Device, *Program->Image))
return Err;
- Kernel = std::make_unique<ol_symbol_impl_t>(KernelImpl->getName(),
- &*KernelImpl);
+ Kernel = std::make_unique<ol_symbol_impl_t>(&*KernelImpl);
}
- *Symbol = Kernel.get();
- return Error::success();
+ return Kernel.get();
}
case OL_SYMBOL_KIND_GLOBAL_VARIABLE: {
auto &Global = Program->GlobalSymbols[Name];
@@ -1314,12 +1313,10 @@ Error olGetSymbol_impl(ol_program_handle_t Program, const char *Name,
Device, *Program->Image, GlobalObj))
return Res;
- Global = std::make_unique<ol_symbol_impl_t>(GlobalObj.getName().c_str(),
- std::move(GlobalObj));
+ Global = std::make_unique<ol_symbol_impl_t>(std::move(GlobalObj));
}
- *Symbol = Global.get();
- return Error::success();
+ return Global.get();
}
default:
return createOffloadError(ErrorCode::INVALID_ENUMERATION,
@@ -1327,6 +1324,50 @@ Error olGetSymbol_impl(ol_program_handle_t Program, const char *Name,
}
}
+Error olGetSymbol_impl(ol_program_handle_t Program, const char *Name,
+ ol_symbol_kind_t Kind, ol_symbol_handle_t *Symbol) {
+ std::lock_guard<std::mutex> Lock(Program->SymbolListMutex);
+
+ auto SymbolOrErr = getSymbolImplDetail(Program, Name, Kind);
+ if (!SymbolOrErr)
+ return SymbolOrErr.takeError();
+
+ *Symbol = *SymbolOrErr;
+ return Error::success();
+}
+
+Error olIterateSymbols_impl(ol_program_handle_t Program, ol_symbol_kind_t Kind,
+ ol_symbol_iterate_cb_t Callback, void *UserData) {
+ SymbolKindTy PluginKind;
+ switch (Kind) {
+ case OL_SYMBOL_KIND_KERNEL:
+ PluginKind = SymbolKindTy::Kernel;
+ break;
+ case OL_SYMBOL_KIND_GLOBAL_VARIABLE:
+ PluginKind = SymbolKindTy::GlobalVariable;
+ break;
+ default:
+ return createOffloadError(ErrorCode::INVALID_ENUMERATION,
+ "iterateSymbols kind enum '%i' is invalid", Kind);
+ }
+
+ auto &Device = Program->Image->getDevice();
+ std::lock_guard<std::mutex> Lock(Program->SymbolListMutex);
+
+ Error SymbolErr = Error::success();
+ Error IterateErr = Device.Plugin.getGlobalHandler().iterateSymbols(
+ *Program->Image, PluginKind, [&](StringRef Name) {
+ auto SymbolOrErr = getSymbolImplDetail(Program, Name, Kind);
+ if (!SymbolOrErr) {
+ SymbolErr = SymbolOrErr.takeError();
+ return false;
+ }
+ return Callback(*SymbolOrErr, UserData);
+ });
+
+ return joinErrors(std::move(IterateErr), std::move(SymbolErr));
+}
+
Error olGetSymbolInfoImplDetail(ol_symbol_handle_t Symbol,
ol_symbol_info_t PropName, size_t PropSize,
void *PropValue, size_t *PropSizeRet) {
@@ -1346,6 +1387,8 @@ Error olGetSymbolInfoImplDetail(ol_symbol_handle_t Symbol,
switch (PropName) {
case OL_SYMBOL_INFO_KIND:
return Info.write<ol_symbol_kind_t>(Symbol->Kind);
+ case OL_SYMBOL_INFO_NAME:
+ return Info.writeString(Symbol->Name);
case OL_SYMBOL_INFO_GLOBAL_VARIABLE_ADDRESS:
if (auto Err = CheckKind(OL_SYMBOL_KIND_GLOBAL_VARIABLE))
return Err;
diff --git a/offload/plugins-nextgen/amdgpu/src/rtl.cpp b/offload/plugins-nextgen/amdgpu/src/rtl.cpp
index 65ef83c394f6a..8fcbfbf69447b 100644
--- a/offload/plugins-nextgen/amdgpu/src/rtl.cpp
+++ b/offload/plugins-nextgen/amdgpu/src/rtl.cpp
@@ -586,7 +586,7 @@ struct AMDGPUDeviceImageTy : public DeviceImageTy {
/// generic kernel class.
struct AMDGPUKernelTy : public GenericKernelTy {
/// Create an AMDGPU kernel with a name and an execution mode.
- AMDGPUKernelTy(const char *Name) : GenericKernelTy(Name) {}
+ AMDGPUKernelTy(StringRef Name) : GenericKernelTy(Name) {}
/// Initialize the AMDGPU kernel.
Error initImpl(GenericDeviceTy &Device, DeviceImageTy &Image) override {
@@ -2636,7 +2636,7 @@ struct AMDGPUDeviceTy : public GenericDeviceTy, AMDGenericDeviceTy {
}
/// Allocate and construct an AMDGPU kernel.
- Expected<GenericKernelTy &> constructKernel(const char *Name) override {
+ Expected<GenericKernelTy &> constructKernel(StringRef Name) override {
// Allocate and construct the AMDGPU kernel.
AMDGPUKernelTy *AMDGPUKernel = Plugin.allocate<AMDGPUKernelTy>();
if (!AMDGPUKernel)
@@ -3920,6 +3920,35 @@ struct AMDGPUGlobalHandlerTy final : public GenericGlobalHandlerTy {
return Plugin::success();
}
+
+protected:
+ /// Kernels are represented by a kernel descriptor, which is an object named
+ /// after the function it describes with a '.kd' suffix.
+ std::optional<StringRef> matchSymbol(const ELFSymbolRef &Symbol,
+ StringRef Name,
+ SymbolKindTy Kind) override {
+ if (Symbol.getELFType() != ELF::STT_OBJECT)
+ return std::nullopt;
+
+ StringRef Function = Name;
+ bool IsDescriptor =
+ Function.consume_back(".kd") && isFunction(Symbol, Function);
+ if (IsDescriptor != (Kind == SymbolKindTy::Kernel))
+ return std::nullopt;
+
+ return IsDescriptor ? Function : Name;
+ }
+
+private:
+ /// Returns whether \p Name is a function in the image containing \p Symbol.
+ static bool isFunction(const ELFSymbolRef &Symbol, StringRef Name) {
+ auto SymbolOrErr = utils::elf::getSymbol(*Symbol.getObject(), Name);
+ if (!SymbolOrErr) {
+ consumeError(SymbolOrErr.takeError());
+ return false;
+ }
+ return *SymbolOrErr && (*SymbolOrErr)->getELFType() == ELF::STT_FUNC;
+ }
};
/// Class implementing the AMDGPU-specific functionalities of the plugin.
diff --git a/offload/plugins-nextgen/common/include/GlobalHandler.h b/offload/plugins-nextgen/common/include/GlobalHandler.h
index 782f7ecba8361..482923dccf242 100644
--- a/offload/plugins-nextgen/common/include/GlobalHandler.h
+++ b/offload/plugins-nextgen/common/include/GlobalHandler.h
@@ -13,9 +13,11 @@
#ifndef LLVM_OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_COMMON_GLOBALHANDLER_H
#define LLVM_OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_COMMON_GLOBALHANDLER_H
+#include <optional>
#include <type_traits>
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Compiler.h"
@@ -35,6 +37,9 @@ struct GenericDeviceTy;
using namespace llvm::object;
+/// The kinds of symbols that can be enumerated in a device image.
+enum class SymbolKindTy { Kernel, GlobalVariable };
+
/// Common abstraction for globals that live on the host and device.
/// It simply encapsulates the symbol name, symbol size, and symbol address
/// (which might be host or device depending on the context).
@@ -48,7 +53,7 @@ class GlobalTy {
void *Ptr;
public:
- GlobalTy(const std::string &Name, uint32_t Size = 0, void *Ptr = nullptr)
+ GlobalTy(StringRef Name, uint32_t Size = 0, void *Ptr = nullptr)
: Name(Name), Size(Size), Ptr(Ptr) {}
const std::string &getName() const { return Name; }
@@ -216,6 +221,17 @@ class GenericGlobalHandlerTy {
/// with profiling prefixes.
Expected<GPUProfGlobals> readProfilingGlobals(GenericDeviceTy &Device,
DeviceImageTy &Image);
+
+ /// Enumerate the names of the symbols of the given \p Kind in \p Image,
+ /// stopping early if \p Callback returns false.
+ virtual Error iterateSymbols(DeviceImageTy &Image, SymbolKindTy Kind,
+ function_ref<bool(StringRef)> Callback);
+
+protected:
+ /// Returns the name \p Symbol is known by if it identifies a symbol of the
+ /// given \p Kind, otherwise std::nullopt.
+ virtual std::optional<StringRef>
+ matchSymbol(const ELFSymbolRef &Symbol, StringRef Name, SymbolKindTy Kind);
};
} // namespace plugin
diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index 67ebfbc943fdc..8753b05973342 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -429,7 +429,7 @@ class DeviceImageTy {
/// implement the necessary virtual function members.
struct GenericKernelTy {
/// Construct a kernel with a name and a execution mode.
- GenericKernelTy(const char *Name)
+ GenericKernelTy(StringRef Name)
: Name(Name), PreferredNumThreads(0), MaxNumThreads(0) {}
virtual ~GenericKernelTy() {}
@@ -1266,7 +1266,7 @@ struct GenericDeviceTy : public DeviceAllocatorTy {
}
/// Allocate and construct a kernel object.
- virtual Expected<GenericKernelTy &> constructKernel(const char *Name) = 0;
+ virtual Expected<GenericKernelTy &> constructKernel(StringRef Name) = 0;
/// Reference to the underlying plugin that created this device.
GenericPluginTy &Plugin;
diff --git a/offload/plugins-nextgen/common/src/GlobalHandler.cpp b/offload/plugins-nextgen/common/src/GlobalHandler.cpp
index 1611a7a6b1ce2..f78b347323c60 100644
--- a/offload/plugins-nextgen/common/src/GlobalHandler.cpp
+++ b/offload/plugins-nextgen/common/src/GlobalHandler.cpp
@@ -193,6 +193,52 @@ Error GenericGlobalHandlerTy::readGlobalFromImage(GenericDeviceTy &Device,
return Plugin::success();
}
+std::optional<StringRef>
+GenericGlobalHandlerTy::matchSymbol(const ELFSymbolRef &Symbol, StringRef Name,
+ SymbolKindTy Kind) {
+ uint8_t Type = Kind == SymbolKindTy::Kernel ? ELF::STT_FUNC : ELF::STT_OBJECT;
+ if (Symbol.getELFType() != Type)
+ return std::nullopt;
+ return Name;
+}
+
+Error GenericGlobalHandlerTy::iterateSymbols(
+ DeviceImageTy &Image, SymbolKindTy Kind,
+ function_ref<bool(StringRef)> Callback) {
+ auto ELFObjOrErr = getELFObjectFile(Image);
+ if (!ELFObjOrErr)
+ return ELFObjOrErr.takeError();
+
+ auto &ELFObj = cast<ELFObjectFileBase>(**ELFObjOrErr);
+ auto Symbols = ELFObj.symbols();
+ if (Symbols.empty())
+ Symbols = ELFObj.getDynamicSymbolIterators();
+
+ for (ELFSymbolRef Symbol : Symbols) {
+ auto FlagsOrErr = Symbol.getFlags();
+ if (!FlagsOrErr)
+ return Plugin::error(ErrorCode::INVALID_BINARY, FlagsOrErr.takeError(),
+ "error reading ELF symbol");
+
+ // Only symbols that are defined by and exported from the image can be used.
+ uint32_t Ignored = SymbolRef::SF_Undefined | SymbolRef::SF_Hidden |
+ SymbolRef::SF_FormatSpecific;
+ if (!(*FlagsOrErr & SymbolRef::SF_Global) || (*FlagsOrErr & Ignored))
+ continue;
+
+ auto NameOrErr = Symbol.getName();
+ if (!NameOrErr)
+ return Plugin::error(ErrorCode::INVALID_BINARY, NameOrErr.takeError(),
+ "error reading ELF symbol name");
+
+ if (auto Name = matchSymbol(Symbol, *NameOrErr, Kind))
+ if (!Callback(*Name))
+ break;
+ }
+
+ return Plugin::success();
+}
+
Expected<GPUProfGlobals>
GenericGlobalHandlerTy::readProfilingGlobals(GenericDeviceTy &Device,
DeviceImageTy &Image) {
diff --git a/offload/plugins-nextgen/cuda/src/rtl.cpp b/offload/plugins-nextgen/cuda/src/rtl.cpp
index 9bf835b63813c..4c8af289d8b6f 100644
--- a/offload/plugins-nextgen/cuda/src/rtl.cpp
+++ b/offload/plugins-nextgen/cuda/src/rtl.cpp
@@ -96,7 +96,7 @@ struct CUDADeviceImageTy : public DeviceImageTy {
/// generic kernel class.
struct CUDAKernelTy : public GenericKernelTy {
/// Create a CUDA kernel with a name and an execution mode.
- CUDAKernelTy(const char *Name) : GenericKernelTy(Name), Func(nullptr) {}
+ CUDAKernelTy(StringRef Name) : GenericKernelTy(Name), Func(nullptr) {}
/// Initialize the CUDA kernel.
Error initImpl(GenericDeviceTy &GenericDevice,
@@ -512,7 +512,7 @@ struct CUDADeviceTy : public GenericDeviceTy {
}
/// Allocate and construct a CUDA kernel.
- Expected<GenericKernelTy &> constructKernel(const char *Name) override {
+ Expected<GenericKernelTy &> constructKernel(StringRef Name) override {
// Allocate and construct the CUDA kernel.
CUDAKernelTy *CUDAKernel = Plugin.allocate<CUDAKernelTy>();
if (!CUDAKernel)
diff --git a/offload/plugins-nextgen/host/src/rtl.cpp b/offload/plugins-nextgen/host/src/rtl.cpp
index 95ec3820f2657..b08595c258a19 100644
--- a/offload/plugins-nextgen/host/src/rtl.cpp
+++ b/offload/plugins-nextgen/host/src/rtl.cpp
@@ -56,7 +56,7 @@ using namespace error;
/// Class implementing kernel functionalities for GenELF64.
struct GenELF64KernelTy : public GenericKernelTy {
/// Construct the kernel with a name and an execution mode.
- GenELF64KernelTy(const char *Name) : GenericKernelTy(Name), Func(nullptr) {}
+ GenELF64KernelTy(StringRef Name) : GenericKernelTy(Name), Func(nullptr) {}
/// Initialize the kernel.
Error initImpl(GenericDeviceTy &Device, DeviceImageTy &Image) override {
@@ -165,7 +165,7 @@ struct GenELF64DeviceTy : public GenericDeviceTy {
std::string getComputeUnitKind() const override { return "generic-64bit"; }
/// Construct the kernel for a specific image on the device.
- Expected<GenericKernelTy &> constructKernel(const char *Name) override {
+ Expected<GenericKernelTy &> constructKernel(StringRef Name) override {
// Allocate and construct the kernel.
GenELF64KernelTy *GenELF64Kernel = Plugin.allocate<GenELF64KernelTy>();
if (!GenELF64Kernel)
diff --git a/offload/plugins-nextgen/level_zero/include/L0Device.h b/offload/plugins-nextgen/level_zero/include/L0Device.h
index 3e8129d057095..88332f3818dfe 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Device.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Device.h
@@ -576,7 +576,7 @@ class L0DeviceTy final : public GenericDeviceTy {
V = 0;
return Plugin::success();
}
- Expected<GenericKernelTy &> constructKernel(const char *Name) override;
+ Expected<GenericKernelTy &> constructKernel(StringRef Name) override;
Error callGlobalConstructors(GenericPluginTy &Plugin,
DeviceImageTy &Image) override;
diff --git a/offload/plugins-nextgen/level_zero/include/L0Kernel.h b/offload/plugins-nextgen/level_zero/include/L0Kernel.h
index 5630dfe4ba585..bbb1ce1046949 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Kernel.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Kernel.h
@@ -67,7 +67,7 @@ class L0KernelTy : public GenericKernelTy {
public:
/// Create a L0 kernel with a name and an execution mode.
- L0KernelTy(const char *Name) : GenericKernelTy(Name), zeKernel(nullptr) {}
+ L0KernelTy(StringRef Name) : GenericKernelTy(Name), zeKernel(nullptr) {}
~L0KernelTy() = default;
L0KernelTy(const L0KernelTy &) = delete;
L0KernelTy(L0KernelTy &&) = delete;
diff --git a/offload/plugins-nextgen/level_zero/include/L0Program.h b/offload/plugins-nextgen/level_zero/include/L0Program.h
index af2d0a05f7e3b..3a78ddeeb81de 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Program.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Program.h
@@ -110,6 +110,11 @@ class L0ProgramTy : public DeviceImageTy {
Error getSymbolMetadata(const char *Name, void **AddrPtr,
size_t *SizePtr) const;
+ /// Returns the names of every kernel in this program.
+ auto getKernelNames() const {
+ return llvm::make_first_range(KernelsToModuleMap);
+ }
+
/// Returns the handle of a module that contains a given Kernel name.
ze_module_handle_t findModuleFromKernelName(const char *KernelName) const {
auto K = KernelsToModuleMap.find(std::string(KernelName));
@@ -126,6 +131,9 @@ struct L0GlobalHandlerTy final : public GenericGlobalHandlerTy {
Error getGlobalMetadataFromDevice(GenericDeviceTy &Device,
DeviceImageTy &Image,
GlobalTy &DeviceGlobal) override;
+
+ Error iterateSymbols(DeviceImageTy &Image, SymbolKindTy Kind,
+ function_ref<bool(StringRef)> Callback) override;
};
bool isValidOneOmpImage(StringRef Image, uint64_t &MajorVer,
diff --git a/offload/plugins-nextgen/level_zero/src/L0Device.cpp b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
index 076dfa080f86e..9b39f49b0db58 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Device.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
@@ -554,7 +554,7 @@ Expected<InfoTreeNode> L0DeviceTy::obtainInfoImpl() {
return Info;
}
-Expected<GenericKernelTy &> L0DeviceTy::constructKernel(const char *Name) {
+Expected<GenericKernelTy &> L0DeviceTy::constructKernel(StringRef Name) {
// Allocate and construct the L0 kernel.
L0KernelTy *L0Kernel = getPlugin().allocate<L0KernelTy>();
if (!L0Kernel)
diff --git a/offload/plugins-nextgen/level_zero/src/L0Program.cpp b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
index 08c784a758367..7b821eeea14ee 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Program.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
@@ -43,6 +43,21 @@ Error L0GlobalHandlerTy::getGlobalMetadataFromDevice(GenericDeviceTy &Device,
return Plugin::success();
}
+Error L0GlobalHandlerTy::iterateSymbols(
+ DeviceImageTy &Image, SymbolKindTy Kind,
+ function_ref<bool(StringRef)> Callback) {
+ // The images are SPIR-V, so only the kernels the module reports are known.
+ if (Kind != SymbolKindTy::Kernel)
+ return Plugin::error(ErrorCode::UNSUPPORTED,
+ "cannot enumerate global variables in a module");
+
+ for (StringRef Name : L0ProgramTy::makeL0Program(Image).getKernelNames())
+ if (!Callback(Name))
+ break;
+
+ return Plugin::success();
+}
+
inline L0DeviceTy &L0ProgramTy::getL0Device() const {
return L0DeviceTy::makeL0Device(getDevice());
}
diff --git a/offload/unittests/OffloadAPI/CMakeLists.txt b/offload/unittests/OffloadAPI/CMakeLists.txt
index c967df1ae6518..c0dbc31a3a1ca 100644
--- a/offload/unittests/OffloadAPI/CMakeLists.txt
+++ b/offload/unittests/OffloadAPI/CMakeLists.txt
@@ -65,4 +65,5 @@ add_offload_unittest("queue"
add_offload_unittest("symbol"
symbol/olGetSymbol.cpp
symbol/olGetSymbolInfo.cpp
- symbol/olGetSymbolInfoSize.cpp)
+ symbol/olGetSymbolInfoSize.cpp
+ symbol/olIterateSymbols.cpp)
diff --git a/offload/unittests/OffloadAPI/symbol/olGetSymbolInfo.cpp b/offload/unittests/OffloadAPI/symbol/olGetSymbolInfo.cpp
index ed8f4716974cd..55b0eed095790 100644
--- a/offload/unittests/OffloadAPI/symbol/olGetSymbolInfo.cpp
+++ b/offload/unittests/OffloadAPI/symbol/olGetSymbolInfo.cpp
@@ -30,6 +30,24 @@ TEST_P(olGetSymbolInfoGlobalTest, SuccessKind) {
ASSERT_EQ(RetrievedKind, OL_SYMBOL_KIND_GLOBAL_VARIABLE);
}
+TEST_P(olGetSymbolInfoKernelTest, SuccessName) {
+ size_t Size = 0;
+ ASSERT_SUCCESS(olGetSymbolInfoSize(Kernel, OL_SYMBOL_INFO_NAME, &Size));
+ std::vector<char> Name(Size);
+ ASSERT_SUCCESS(
+ olGetSymbolInfo(Kernel, OL_SYMBOL_INFO_NAME, Size, Name.data()));
+ ASSERT_STREQ(Name.data(), "foo");
+}
+
+TEST_P(olGetSymbolInfoGlobalTest, SuccessName) {
+ size_t Size = 0;
+ ASSERT_SUCCESS(olGetSymbolInfoSize(Global, OL_SYMBOL_INFO_NAME, &Size));
+ std::vector<char> Name(Size);
+ ASSERT_SUCCESS(
+ olGetSymbolInfo(Global, OL_SYMBOL_INFO_NAME, Size, Name.data()));
+ ASSERT_STREQ(Name.data(), "global");
+}
+
TEST_P(olGetSymbolInfoKernelTest, InvalidAddress) {
void *RetrievedAddr;
ASSERT_ERROR(OL_ERRC_SYMBOL_KIND,
diff --git a/offload/unittests/OffloadAPI/symbol/olIterateSymbols.cpp b/offload/unittests/OffloadAPI/symbol/olIterateSymbols.cpp
new file mode 100644
index 0000000000000..aba850d5bc684
--- /dev/null
+++ b/offload/unittests/OffloadAPI/symbol/olIterateSymbols.cpp
@@ -0,0 +1,118 @@
+//===------- Offload API tests - olIterateSymbols -------------------------===//
+//
+// 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 "../common/Fixtures.hpp"
+#include <OffloadAPI.h>
+#include <gtest/gtest.h>
+
+// The 'global' program contains the 'read' and 'write' kernels as well as the
+// 'global' global variable.
+struct olIterateSymbolsTest : OffloadProgramTest {
+ void SetUp() override { SetUpWith("global"); }
+};
+OFFLOAD_TESTS_INSTANTIATE_DEVICE_FIXTURE(olIterateSymbolsTest);
+
+using SymbolVecT = std::vector<ol_symbol_handle_t>;
+
+static bool collectSymbol(ol_symbol_handle_t Symbol, void *Data) {
+ static_cast<SymbolVecT *>(Data)->push_back(Symbol);
+ return true;
+}
+
+static std::string getName(ol_symbol_handle_t Symbol) {
+ size_t Size = 0;
+ if (olGetSymbolInfoSize(Symbol, OL_SYMBOL_INFO_NAME, &Size))
+ return {};
+ std::vector<char> Name(Size);
+ if (olGetSymbolInfo(Symbol, OL_SYMBOL_INFO_NAME, Size, Name.data()))
+ return {};
+ return std::string{Name.data()};
+}
+
+static std::vector<std::string> getNames(const SymbolVecT &Symbols,
+ ol_symbol_kind_t Expected) {
+ std::vector<std::string> Names;
+ for (auto *Symbol : Symbols) {
+ ol_symbol_kind_t Kind;
+ if (olGetSymbolInfo(Symbol, OL_SYMBOL_INFO_KIND, sizeof(Kind), &Kind))
+ return {};
+ EXPECT_EQ(Kind, Expected);
+ Names.push_back(getName(Symbol));
+ }
+ return Names;
+}
+
+static bool contains(const std::vector<std::string> &Names,
+ const std::string &Name) {
+ return std::find(Names.begin(), Names.end(), Name) != Names.end();
+}
+
+TEST_P(olIterateSymbolsTest, SuccessKernels) {
+ SymbolVecT Symbols;
+ ASSERT_SUCCESS(olIterateSymbols(Program, OL_SYMBOL_KIND_KERNEL, collectSymbol,
+ &Symbols));
+
+ auto Names = getNames(Symbols, OL_SYMBOL_KIND_KERNEL);
+ ASSERT_TRUE(contains(Names, "read"));
+ ASSERT_TRUE(contains(Names, "write"));
+}
+
+TEST_P(olIterateSymbolsTest, SuccessGlobals) {
+ SymbolVecT Symbols;
+ ASSERT_SUCCESS_OR_UNSUPPORTED(olIterateSymbols(
+ Program, OL_SYMBOL_KIND_GLOBAL_VARIABLE, collectSymbol, &Symbols));
+
+ auto Names = getNames(Symbols, OL_SYMBOL_KIND_GLOBAL_VARIABLE);
+ ASSERT_TRUE(contains(Names, "global"));
+}
+
+TEST_P(olIterateSymbolsTest, SuccessSameSymbol) {
+ SymbolVecT Symbols;
+ ASSERT_SUCCESS(olIterateSymbols(Program, OL_SYMBOL_KIND_KERNEL, collectSymbol,
+ &Symbols));
+ ASSERT_FALSE(Symbols.empty());
+
+ for (auto *Symbol : Symbols) {
+ ol_symbol_handle_t Looked = nullptr;
+ ASSERT_SUCCESS(olGetSymbol(Program, getName(Symbol).c_str(),
+ OL_SYMBOL_KIND_KERNEL, &Looked));
+ ASSERT_EQ(Symbol, Looked);
+ }
+}
+
+TEST_P(olIterateSymbolsTest, SuccessStopIteration) {
+ size_t Count = 0;
+ ASSERT_SUCCESS(olIterateSymbols(
+ Program, OL_SYMBOL_KIND_KERNEL,
+ [](ol_symbol_handle_t, void *Data) {
+ (*static_cast<size_t *>(Data))++;
+ return false;
+ },
+ &Count));
+ ASSERT_EQ(Count, 1u);
+}
+
+TEST_P(olIterateSymbolsTest, InvalidNullProgram) {
+ SymbolVecT Symbols;
+ ASSERT_ERROR(OL_ERRC_INVALID_NULL_HANDLE,
+ olIterateSymbols(nullptr, OL_SYMBOL_KIND_KERNEL, collectSymbol,
+ &Symbols));
+}
+
+TEST_P(olIterateSymbolsTest, InvalidNullCallback) {
+ ASSERT_ERROR(
+ OL_ERRC_INVALID_NULL_POINTER,
+ olIterateSymbols(Program, OL_SYMBOL_KIND_KERNEL, nullptr, nullptr));
+}
+
+TEST_P(olIterateSymbolsTest, InvalidKind) {
+ SymbolVecT Symbols;
+ ASSERT_ERROR(OL_ERRC_INVALID_ENUMERATION,
+ olIterateSymbols(Program, OL_SYMBOL_KIND_FORCE_UINT32,
+ collectSymbol, &Symbols));
+}
>From 78724f9429f191d6a68807c48ad8127a9cd254d1 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Thu, 30 Jul 2026 10:47:26 -0500
Subject: [PATCH 2/2] Remove special L0 handling
---
.../level_zero/include/L0Program.h | 8 --------
.../plugins-nextgen/level_zero/src/L0Program.cpp | 15 ---------------
2 files changed, 23 deletions(-)
diff --git a/offload/plugins-nextgen/level_zero/include/L0Program.h b/offload/plugins-nextgen/level_zero/include/L0Program.h
index 3a78ddeeb81de..af2d0a05f7e3b 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Program.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Program.h
@@ -110,11 +110,6 @@ class L0ProgramTy : public DeviceImageTy {
Error getSymbolMetadata(const char *Name, void **AddrPtr,
size_t *SizePtr) const;
- /// Returns the names of every kernel in this program.
- auto getKernelNames() const {
- return llvm::make_first_range(KernelsToModuleMap);
- }
-
/// Returns the handle of a module that contains a given Kernel name.
ze_module_handle_t findModuleFromKernelName(const char *KernelName) const {
auto K = KernelsToModuleMap.find(std::string(KernelName));
@@ -131,9 +126,6 @@ struct L0GlobalHandlerTy final : public GenericGlobalHandlerTy {
Error getGlobalMetadataFromDevice(GenericDeviceTy &Device,
DeviceImageTy &Image,
GlobalTy &DeviceGlobal) override;
-
- Error iterateSymbols(DeviceImageTy &Image, SymbolKindTy Kind,
- function_ref<bool(StringRef)> Callback) override;
};
bool isValidOneOmpImage(StringRef Image, uint64_t &MajorVer,
diff --git a/offload/plugins-nextgen/level_zero/src/L0Program.cpp b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
index 7b821eeea14ee..08c784a758367 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Program.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
@@ -43,21 +43,6 @@ Error L0GlobalHandlerTy::getGlobalMetadataFromDevice(GenericDeviceTy &Device,
return Plugin::success();
}
-Error L0GlobalHandlerTy::iterateSymbols(
- DeviceImageTy &Image, SymbolKindTy Kind,
- function_ref<bool(StringRef)> Callback) {
- // The images are SPIR-V, so only the kernels the module reports are known.
- if (Kind != SymbolKindTy::Kernel)
- return Plugin::error(ErrorCode::UNSUPPORTED,
- "cannot enumerate global variables in a module");
-
- for (StringRef Name : L0ProgramTy::makeL0Program(Image).getKernelNames())
- if (!Callback(Name))
- break;
-
- return Plugin::success();
-}
-
inline L0DeviceTy &L0ProgramTy::getL0Device() const {
return L0DeviceTy::makeL0Device(getDevice());
}
More information about the llvm-commits
mailing list