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

Reply via email to