From 14ec3c42cedcfa5e54dd06f93e611b7b55397360 Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Fri, 31 Jul 2026 14:29:06 -0500 Subject: [PATCH] [Offload] add 'olIterateSymbols' runtime function (#213036) 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 | 22 +++- .../common/include/PluginInterface.h | 4 +- .../common/src/GlobalHandler.cpp | 49 ++++++++ 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 | 3 + .../level_zero/src/L0Device.cpp | 2 +- .../level_zero/src/L0Program.cpp | 8 ++ offload/unittests/OffloadAPI/CMakeLists.txt | 3 +- .../OffloadAPI/symbol/olGetSymbolInfo.cpp | 18 +++ .../OffloadAPI/symbol/olIterateSymbols.cpp | 118 ++++++++++++++++++ 16 files changed, 350 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 c57a2e1b8363..9e2f92fbc3e1 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 63fe726fbe54..a57e34c7fb65 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(PluginImpl).getName()) {} std::variant 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 getSymbolImplDetail(ol_program_handle_t Program, + StringRef Name, + ol_symbol_kind_t Kind) { auto &Device = Program->Image->getDevice(); - std::lock_guard 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(KernelImpl->getName(), - &*KernelImpl); + Kernel = std::make_unique(&*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(GlobalObj.getName().c_str(), - std::move(GlobalObj)); + Global = std::make_unique(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 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 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(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 65ef83c394f6..8fcbfbf69447 100644 --- a/offload/plugins-nextgen/amdgpu/src/rtl.cpp +++ b/offload/plugins-nextgen/amdgpu/src/rtl.cpp @@ -586,7 +586,7 @@ private: /// 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 constructKernel(const char *Name) override { + Expected constructKernel(StringRef Name) override { // Allocate and construct the AMDGPU kernel. AMDGPUKernelTy *AMDGPUKernel = Plugin.allocate(); 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 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 782f7ecba836..e9bba45a3a80 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 #include #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,21 @@ public: /// with profiling prefixes. Expected 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 Callback); + +protected: + /// Returns whether a symbol with the given \p Flags is defined by and + /// exported from the image, and therefore usable by the runtime. + virtual bool isExportedSymbol(uint32_t Flags); + + /// Returns the name \p Symbol is known by if it identifies a symbol of the + /// given \p Kind, otherwise std::nullopt. + virtual std::optional + 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 67ebfbc943fd..8753b0597334 100644 --- a/offload/plugins-nextgen/common/include/PluginInterface.h +++ b/offload/plugins-nextgen/common/include/PluginInterface.h @@ -429,7 +429,7 @@ public: /// 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 constructKernel(const char *Name) = 0; + virtual Expected 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 1611a7a6b1ce..5908eff8563a 100644 --- a/offload/plugins-nextgen/common/src/GlobalHandler.cpp +++ b/offload/plugins-nextgen/common/src/GlobalHandler.cpp @@ -193,6 +193,55 @@ Error GenericGlobalHandlerTy::readGlobalFromImage(GenericDeviceTy &Device, return Plugin::success(); } +bool GenericGlobalHandlerTy::isExportedSymbol(uint32_t Flags) { + uint32_t Ignored = SymbolRef::SF_Undefined | SymbolRef::SF_Hidden | + SymbolRef::SF_FormatSpecific; + return (Flags & SymbolRef::SF_Global) && !(Flags & Ignored); +} + +std::optional +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 Callback) { + auto ELFObjOrErr = getELFObjectFile(Image); + if (!ELFObjOrErr) + return ELFObjOrErr.takeError(); + + auto &ELFObj = cast(**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"); + + if (!isExportedSymbol(*FlagsOrErr)) + 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 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 9bf835b63813..4c8af289d8b6 100644 --- a/offload/plugins-nextgen/cuda/src/rtl.cpp +++ b/offload/plugins-nextgen/cuda/src/rtl.cpp @@ -96,7 +96,7 @@ private: /// 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 constructKernel(const char *Name) override { + Expected constructKernel(StringRef Name) override { // Allocate and construct the CUDA kernel. CUDAKernelTy *CUDAKernel = Plugin.allocate(); if (!CUDAKernel) diff --git a/offload/plugins-nextgen/host/src/rtl.cpp b/offload/plugins-nextgen/host/src/rtl.cpp index 95ec3820f265..b08595c258a1 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 constructKernel(const char *Name) override { + Expected constructKernel(StringRef Name) override { // Allocate and construct the kernel. GenELF64KernelTy *GenELF64Kernel = Plugin.allocate(); if (!GenELF64Kernel) diff --git a/offload/plugins-nextgen/level_zero/include/L0Device.h b/offload/plugins-nextgen/level_zero/include/L0Device.h index 3e8129d05709..88332f3818df 100644 --- a/offload/plugins-nextgen/level_zero/include/L0Device.h +++ b/offload/plugins-nextgen/level_zero/include/L0Device.h @@ -576,7 +576,7 @@ public: V = 0; return Plugin::success(); } - Expected constructKernel(const char *Name) override; + Expected 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 5630dfe4ba58..bbb1ce104694 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 af2d0a05f7e3..68e901e984d2 100644 --- a/offload/plugins-nextgen/level_zero/include/L0Program.h +++ b/offload/plugins-nextgen/level_zero/include/L0Program.h @@ -126,6 +126,9 @@ struct L0GlobalHandlerTy final : public GenericGlobalHandlerTy { Error getGlobalMetadataFromDevice(GenericDeviceTy &Device, DeviceImageTy &Image, GlobalTy &DeviceGlobal) override; + +protected: + bool isExportedSymbol(uint32_t Flags) 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 076dfa080f86..9b39f49b0db5 100644 --- a/offload/plugins-nextgen/level_zero/src/L0Device.cpp +++ b/offload/plugins-nextgen/level_zero/src/L0Device.cpp @@ -554,7 +554,7 @@ Expected L0DeviceTy::obtainInfoImpl() { return Info; } -Expected L0DeviceTy::constructKernel(const char *Name) { +Expected L0DeviceTy::constructKernel(StringRef Name) { // Allocate and construct the L0 kernel. L0KernelTy *L0Kernel = getPlugin().allocate(); if (!L0Kernel) diff --git a/offload/plugins-nextgen/level_zero/src/L0Program.cpp b/offload/plugins-nextgen/level_zero/src/L0Program.cpp index 08c784a75836..6cadfe44d081 100644 --- a/offload/plugins-nextgen/level_zero/src/L0Program.cpp +++ b/offload/plugins-nextgen/level_zero/src/L0Program.cpp @@ -43,6 +43,14 @@ Error L0GlobalHandlerTy::getGlobalMetadataFromDevice(GenericDeviceTy &Device, return Plugin::success(); } +bool L0GlobalHandlerTy::isExportedSymbol(uint32_t Flags) { + // Images returned by the Level Zero runtime do not correctly expose kernel + // functions as global symbols. Bypass the normal ELF handling.here. + uint32_t Ignored = SymbolRef::SF_Undefined | SymbolRef::SF_Hidden | + SymbolRef::SF_FormatSpecific; + return !(Flags & Ignored); +} + inline L0DeviceTy &L0ProgramTy::getL0Device() const { return L0DeviceTy::makeL0Device(getDevice()); } diff --git a/offload/unittests/OffloadAPI/CMakeLists.txt b/offload/unittests/OffloadAPI/CMakeLists.txt index c967df1ae651..c0dbc31a3a1c 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 ed8f4716974c..55b0eed09579 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 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 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 000000000000..aba850d5bc68 --- /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 +#include + +// 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; + +static bool collectSymbol(ol_symbol_handle_t Symbol, void *Data) { + static_cast(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 Name(Size); + if (olGetSymbolInfo(Symbol, OL_SYMBOL_INFO_NAME, Size, Name.data())) + return {}; + return std::string{Name.data()}; +} + +static std::vector getNames(const SymbolVecT &Symbols, + ol_symbol_kind_t Expected) { + std::vector 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 &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(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)); +}