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 <[email protected]> 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. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
