[clang] [NFC][clang][Driver] Move normalizeForBundler to OffloadBundler for reuse (PR #225019)
Aditya Sinha via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 21 00:11:26 PDT 2026
https://github.com/AdityaSinha149 created https://github.com/llvm/llvm-project/pull/225019
This PR is a prerequisite for #218337. It moved the function `normaliseForBundler` from `HIPUtility.cpp` to `OffloadBundler` so that it can be shared by both AOT compiler and clang-repl. There are no functional changes.
>From cd746b1c736757873d2d82687dc4cb7b286ae4a9 Mon Sep 17 00:00:00 2001
From: AdityaSinha149 <adsinha at amd.com>
Date: Mon, 21 Sep 2026 12:24:11 +0530
Subject: [PATCH] [NFC][clang][Driver] Move normalizeForBundler to
OffloadBundler for reuse
---
clang/include/clang/Driver/OffloadBundler.h | 8 +
clang/lib/Driver/OffloadBundler.cpp | 8 +
clang/lib/Driver/ToolChains/HIPUtility.cpp | 248 ++++++++++++++++++--
3 files changed, 246 insertions(+), 18 deletions(-)
diff --git a/clang/include/clang/Driver/OffloadBundler.h b/clang/include/clang/Driver/OffloadBundler.h
index 96839240543af..560fdc3b35363 100644
--- a/clang/include/clang/Driver/OffloadBundler.h
+++ b/clang/include/clang/Driver/OffloadBundler.h
@@ -96,6 +96,14 @@ struct OffloadTargetInfo {
/// <kind>-<triple>[-<target id>[:target features]]
/// <triple> := <arch>-<vendor>-<os>-<env>
bool checkOffloadBundleID(const llvm::StringRef Str);
+
+/// Construct the normalized triple string used inside an offload bundle entry
+/// ID for device triple \p T. When \p HasTargetID is true the four triple
+/// components are kept verbatim (an explicit target ID will be appended by the
+/// caller); otherwise the triple is normalized to its canonical
+/// four-identifier form. Shared by the HIP toolchain and clang-repl device
+/// offloading so both agree on the bundle target-name policy.
+std::string normalizeForBundler(const llvm::Triple &T, bool HasTargetID);
} // namespace clang
#endif // LLVM_CLANG_DRIVER_OFFLOADBUNDLER_H
diff --git a/clang/lib/Driver/OffloadBundler.cpp b/clang/lib/Driver/OffloadBundler.cpp
index 2d0c326ab6f40..9b6d89e9d549f 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -1752,3 +1752,11 @@ bool clang::checkOffloadBundleID(const llvm::StringRef Str) {
Str.split(Components, '-', /*MaxSplit=*/5);
return Components.size() == 5 || Components.size() == 6;
}
+
+std::string clang::normalizeForBundler(const llvm::Triple &T,
+ bool HasTargetID) {
+ return HasTargetID ? (T.getArchName() + "-" + T.getVendorName() + "-" +
+ T.getOSName() + "-" + T.getEnvironmentName())
+ .str()
+ : T.normalize(llvm::Triple::CanonicalForm::FOUR_IDENT);
+}
diff --git a/clang/lib/Driver/ToolChains/HIPUtility.cpp b/clang/lib/Driver/ToolChains/HIPUtility.cpp
index 34a35e18c2e12..636c0803187e7 100644
--- a/clang/lib/Driver/ToolChains/HIPUtility.cpp
+++ b/clang/lib/Driver/ToolChains/HIPUtility.cpp
@@ -9,6 +9,7 @@
#include "HIPUtility.h"
#include "clang/Driver/CommonArgs.h"
#include "clang/Driver/Compilation.h"
+#include "clang/Driver/OffloadBundler.h"
#include "clang/Options/Options.h"
#include "llvm/Support/Path.h"
@@ -27,26 +28,237 @@ namespace {
const unsigned HIPCodeObjectAlign = 4096;
} // namespace
-// Constructs a triple string for clang offload bundler.
-static std::string normalizeForBundler(const llvm::Triple &OrigT,
- StringRef BoundArch) {
- llvm::Triple T(OrigT);
- bool HasTargetID = !BoundArch.empty();
-
- // FIXME: Short-term hack. The HIP runtime hardcodes the legacy
- // "amdgcn-amd-amdhsa--" prefix when parsing the target IDs embedded in the
- // fatbin bundle, so force it.
- if (HasTargetID && T.isAMDGCN()) {
- return ("amdgcn-" + T.getVendorName() + "-" + T.getOSName() + "-" +
- T.getEnvironmentName())
- .str();
+// Collect undefined __hip_fatbin* and __hip_gpubin_handle* symbols from all
+// input object or archive files.
+class HIPUndefinedFatBinSymbols {
+public:
+ HIPUndefinedFatBinSymbols(const Compilation &C,
+ const llvm::opt::ArgList &Args_)
+ : C(C), Args(Args_),
+ DiagID(C.getDriver().getDiags().getCustomDiagID(
+ DiagnosticsEngine::Error,
+ "Error collecting HIP undefined fatbin symbols: %0")),
+ Quiet(C.getArgs().hasArg(options::OPT__HASH_HASH_HASH)),
+ Verbose(C.getArgs().hasArg(options::OPT_v)) {
+ populateSymbols();
+ processStaticLibraries();
+ if (Verbose) {
+ for (const auto &Name : FatBinSymbols)
+ llvm::errs() << "Found undefined HIP fatbin symbol: " << Name << "\n";
+ for (const auto &Name : GPUBinHandleSymbols)
+ llvm::errs() << "Found undefined HIP gpubin handle symbol: " << Name
+ << "\n";
+ }
}
- return HasTargetID ? (T.getArchName() + "-" + T.getVendorName() + "-" +
- T.getOSName() + "-" + T.getEnvironmentName())
- .str()
- : T.normalize(llvm::Triple::CanonicalForm::FOUR_IDENT);
-}
+ const std::set<std::string> &getFatBinSymbols() const {
+ return FatBinSymbols;
+ }
+
+ const std::set<std::string> &getGPUBinHandleSymbols() const {
+ return GPUBinHandleSymbols;
+ }
+
+ // Collect symbols from static libraries specified by -l options.
+ void processStaticLibraries() {
+ llvm::SmallVector<llvm::StringRef, 16> LibNames;
+ llvm::SmallVector<llvm::StringRef, 16> LibPaths;
+ llvm::SmallVector<llvm::StringRef, 16> ExactLibNames;
+ llvm::Triple Triple(C.getDriver().getTargetTriple());
+ bool IsMSVC = Triple.isWindowsMSVCEnvironment();
+ llvm::StringRef Ext = IsMSVC ? ".lib" : ".a";
+
+ for (const auto *Arg : Args.filtered(options::OPT_l)) {
+ llvm::StringRef Value = Arg->getValue();
+ if (Value.starts_with(":"))
+ ExactLibNames.push_back(Value.drop_front());
+ else
+ LibNames.push_back(Value);
+ }
+ for (const auto *Arg : Args.filtered(options::OPT_L)) {
+ auto Path = Arg->getValue();
+ LibPaths.push_back(Path);
+ if (Verbose)
+ llvm::errs() << "HIP fatbin symbol search uses library path: " << Path
+ << "\n";
+ }
+
+ auto ProcessLib = [&](llvm::StringRef LibName, bool IsExact) {
+ llvm::SmallString<256> FullLibName(
+ IsExact ? Twine(LibName).str()
+ : IsMSVC ? (Twine(LibName) + Ext).str()
+ : (Twine("lib") + LibName + Ext).str());
+
+ bool Found = false;
+ for (const auto Path : LibPaths) {
+ llvm::SmallString<256> FullPath = Path;
+ llvm::sys::path::append(FullPath, FullLibName);
+
+ if (llvm::sys::fs::exists(FullPath)) {
+ if (Verbose)
+ llvm::errs() << "HIP fatbin symbol search found library: "
+ << FullPath << "\n";
+ auto BufferOrErr = llvm::MemoryBuffer::getFile(FullPath);
+ if (!BufferOrErr) {
+ errorHandler(llvm::errorCodeToError(BufferOrErr.getError()));
+ continue;
+ }
+ processInput(BufferOrErr.get()->getMemBufferRef());
+ Found = true;
+ break;
+ }
+ }
+ if (!Found && Verbose)
+ llvm::errs() << "HIP fatbin symbol search could not find library: "
+ << FullLibName << "\n";
+ };
+
+ for (const auto LibName : ExactLibNames)
+ ProcessLib(LibName, true);
+
+ for (const auto LibName : LibNames)
+ ProcessLib(LibName, false);
+ }
+
+private:
+ const Compilation &C;
+ const llvm::opt::ArgList &Args;
+ unsigned DiagID;
+ bool Quiet;
+ bool Verbose;
+ std::set<std::string> FatBinSymbols;
+ std::set<std::string> GPUBinHandleSymbols;
+ std::set<std::string, std::less<>> DefinedFatBinSymbols;
+ std::set<std::string, std::less<>> DefinedGPUBinHandleSymbols;
+ const std::string FatBinPrefix = "__hip_fatbin";
+ const std::string GPUBinHandlePrefix = "__hip_gpubin_handle";
+
+ void populateSymbols() {
+ std::deque<const Action *> WorkList;
+ std::set<const Action *> Visited;
+
+ for (const auto &Action : C.getActions())
+ WorkList.push_back(Action);
+
+ while (!WorkList.empty()) {
+ const Action *CurrentAction = WorkList.front();
+ WorkList.pop_front();
+
+ if (!CurrentAction || !Visited.insert(CurrentAction).second)
+ continue;
+
+ if (const auto *IA = dyn_cast<InputAction>(CurrentAction)) {
+ std::string ID = IA->getId().str();
+ if (!ID.empty()) {
+ ID = llvm::utohexstr(llvm::MD5Hash(ID), /*LowerCase=*/true);
+ FatBinSymbols.insert((FatBinPrefix + Twine('_') + ID).str());
+ GPUBinHandleSymbols.insert(
+ (GPUBinHandlePrefix + Twine('_') + ID).str());
+ continue;
+ }
+ if (IA->getInputArg().getNumValues() == 0)
+ continue;
+ const char *Filename = IA->getInputArg().getValue();
+ if (!Filename)
+ continue;
+ auto BufferOrErr = llvm::MemoryBuffer::getFile(Filename);
+ // Input action could be options to linker, therefore, ignore it
+ // if cannot read it. If it turns out to be a file that cannot be read,
+ // the error will be caught by the linker.
+ if (!BufferOrErr)
+ continue;
+
+ processInput(BufferOrErr.get()->getMemBufferRef());
+ } else
+ llvm::append_range(WorkList, CurrentAction->getInputs());
+ }
+ }
+
+ void processInput(const llvm::MemoryBufferRef &Buffer) {
+ // Try processing as object file first.
+ auto ObjFileOrErr = llvm::object::ObjectFile::createObjectFile(Buffer);
+ if (ObjFileOrErr) {
+ processSymbols(**ObjFileOrErr);
+ return;
+ }
+
+ // Then try processing as archive files.
+ llvm::consumeError(ObjFileOrErr.takeError());
+ auto ArchiveOrErr = llvm::object::Archive::create(Buffer);
+ if (ArchiveOrErr) {
+ llvm::Error Err = llvm::Error::success();
+ llvm::object::Archive &Archive = *ArchiveOrErr.get();
+ for (auto &Child : Archive.children(Err)) {
+ auto ChildBufOrErr = Child.getMemoryBufferRef();
+ if (ChildBufOrErr)
+ processInput(*ChildBufOrErr);
+ else
+ errorHandler(ChildBufOrErr.takeError());
+ }
+
+ if (Err)
+ errorHandler(std::move(Err));
+ return;
+ }
+
+ // Ignore other files.
+ llvm::consumeError(ArchiveOrErr.takeError());
+ }
+
+ void processSymbols(const llvm::object::ObjectFile &Obj) {
+ for (const auto &Symbol : Obj.symbols()) {
+ auto FlagOrErr = Symbol.getFlags();
+ if (!FlagOrErr) {
+ errorHandler(FlagOrErr.takeError());
+ continue;
+ }
+
+ auto NameOrErr = Symbol.getName();
+ if (!NameOrErr) {
+ errorHandler(NameOrErr.takeError());
+ continue;
+ }
+ llvm::StringRef Name = *NameOrErr;
+
+ bool isUndefined =
+ FlagOrErr.get() & llvm::object::SymbolRef::SF_Undefined;
+ bool isHidden = FlagOrErr.get() & llvm::object::SymbolRef::SF_Hidden;
+ bool isFatBinSymbol = Name.starts_with(FatBinPrefix);
+ bool isGPUBinHandleSymbol = Name.starts_with(GPUBinHandlePrefix);
+
+ // Add undefined symbols if they are not in the defined sets
+ if (isUndefined) {
+ if (isFatBinSymbol &&
+ DefinedFatBinSymbols.find(Name) == DefinedFatBinSymbols.end())
+ FatBinSymbols.insert(Name.str());
+ else if (isGPUBinHandleSymbol &&
+ DefinedGPUBinHandleSymbols.find(Name) ==
+ DefinedGPUBinHandleSymbols.end())
+ GPUBinHandleSymbols.insert(Name.str());
+ continue;
+ }
+
+ // Ignore hidden defined symbols
+ if (isHidden)
+ continue;
+
+ // Handling for non-hidden defined symbols
+ if (isFatBinSymbol) {
+ DefinedFatBinSymbols.insert(Name.str());
+ FatBinSymbols.erase(Name.str());
+ } else if (isGPUBinHandleSymbol) {
+ DefinedGPUBinHandleSymbols.insert(Name.str());
+ GPUBinHandleSymbols.erase(Name.str());
+ }
+ }
+ }
+
+ void errorHandler(llvm::Error Err) {
+ if (Quiet)
+ return;
+ C.getDriver().Diag(DiagID) << llvm::toString(std::move(Err));
+ }
+};
// Construct a clang-offload-bundler command to bundle code objects for
// different devices into a HIP fat binary.
More information about the cfe-commits
mailing list