https://github.com/AdityaSinha149 updated https://github.com/llvm/llvm-project/pull/235614
>From 332d86e7cda0036804e6bd883b335efaef48a76c Mon Sep 17 00:00:00 2001 From: AdityaSinha149 <[email protected]> Date: Tue, 3 Nov 2026 23:40:49 +0530 Subject: [PATCH 1/2] [clang][Driver] NFC: Share -B and --ld-path lookup helpers --- clang/include/clang/Driver/CommonArgs.h | 16 ++++++++ clang/lib/Driver/Driver.cpp | 29 ++------------ clang/lib/Driver/ToolChain.cpp | 17 ++++---- clang/lib/Driver/ToolChains/CommonArgs.cpp | 45 ++++++++++++++++++++++ 4 files changed, 73 insertions(+), 34 deletions(-) diff --git a/clang/include/clang/Driver/CommonArgs.h b/clang/include/clang/Driver/CommonArgs.h index 138e18f8641b90..c7b648f0001fcc 100644 --- a/clang/include/clang/Driver/CommonArgs.h +++ b/clang/include/clang/Driver/CommonArgs.h @@ -19,6 +19,7 @@ #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/CodeGen.h" +#include <optional> namespace clang { namespace driver { @@ -40,6 +41,21 @@ OffloadJobsOpt parseOffloadJobs(const llvm::opt::ArgList &Args); void addPathIfExists(const Driver &D, const Twine &Path, ToolChain::path_list &Paths); +/// Appends the -B prefixes and then the COMPILER_PATH entries to +/// \p PrefixDirs. +void addPrefixDirs(const llvm::opt::ArgList &Args, + SmallVectorImpl<std::string> &PrefixDirs); + +/// Looks for the program \p Name in \p PrefixDirs. \p Name is looked up in a +/// prefix that is a directory and appended to any other prefix. +std::optional<std::string> +findProgramInPrefixDirs(StringRef Name, ArrayRef<std::string> PrefixDirs); + +/// Resolves the --ld-path= value \p LdPath. \p FoundPath is where the caller's +/// program search found \p LdPath by name; it is used only when \p LdPath has +/// no directory part. Returns std::nullopt if the result is not executable. +std::optional<std::string> resolveLdPath(StringRef LdPath, StringRef FoundPath); + void AddLinkerInputs(const ToolChain &TC, const InputInfoList &Inputs, const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs, const JobAction &JA); diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 37fb69baf6a3eb..fe861454b5ad68 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1697,20 +1697,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { TargetTriple = A->getValue(); if (const Arg *A = Args.getLastArg(options::OPT_ccc_install_dir)) Dir = A->getValue(); - for (const Arg *A : Args.filtered(options::OPT_B)) { - A->claim(); - PrefixDirs.push_back(A->getValue(0)); - } - if (std::optional<std::string> CompilerPathValue = - llvm::sys::Process::GetEnv("COMPILER_PATH")) { - StringRef CompilerPath = *CompilerPathValue; - while (!CompilerPath.empty()) { - std::pair<StringRef, StringRef> Split = - CompilerPath.split(llvm::sys::EnvPathSeparator); - PrefixDirs.push_back(std::string(Split.first)); - CompilerPath = Split.second; - } - } + tools::addPrefixDirs(Args, PrefixDirs); if (const Arg *A = Args.getLastArg(options::OPT__sysroot_EQ)) SysRoot = A->getValue(); if (const Arg *A = Args.getLastArg(options::OPT__dyld_prefix_EQ)) @@ -5985,17 +5972,9 @@ std::string Driver::GetProgramPath(StringRef Name, const ToolChain &TC) const { // Respect a limited subset of the '-Bprefix' functionality in GCC by // attempting to use this prefix when looking for program paths. - for (const auto &PrefixDir : PrefixDirs) { - if (llvm::sys::fs::is_directory(PrefixDir)) { - SmallString<128> P(PrefixDir); - if (ScanDirForExecutable(P, Name)) - return std::string(P); - } else { - SmallString<128> P((PrefixDir + Name).str()); - if (llvm::sys::fs::can_execute(Twine(P))) - return std::string(P); - } - } + if (std::optional<std::string> P = + tools::findProgramInPrefixDirs(Name, PrefixDirs)) + return *P; const ToolChain::path_list &List = TC.getProgramPaths(); for (const auto &TargetSpecificExecutable : TargetSpecificExecutables) { diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index b3e7043a64fe56..46e5ad52f28bdf 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -1321,15 +1321,14 @@ std::string ToolChain::GetLinkerPath(bool *LinkerIsLLD) const { // -fuse-ld=lld can be used with --ld-path= to inform clang that the binary // that --ld-path= points to is lld. if (const Arg *A = Args.getLastArg(options::OPT_ld_path_EQ)) { - std::string Path(A->getValue()); - if (!Path.empty()) { - if (llvm::sys::path::parent_path(Path).empty()) - Path = GetProgramPath(A->getValue()); - if (llvm::sys::fs::can_execute(Path)) { - if (LinkerIsLLD) - *LinkerIsLLD = UseLinker == "lld"; - return std::string(Path); - } + StringRef LdPath = A->getValue(); + // An empty name must not be searched for: findProgramByName asserts. + std::string FoundPath = LdPath.empty() ? "" : GetProgramPath(A->getValue()); + if (std::optional<std::string> Path = + tools::resolveLdPath(LdPath, FoundPath)) { + if (LinkerIsLLD) + *LinkerIsLLD = UseLinker == "lld"; + return *Path; } getDriver().Diag(diag::err_drv_invalid_linker_name) << A->getAsString(Args); return GetProgramPath(getDefaultLinker()); diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 0590c297edf397..21e0a6f6ba1030 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -392,6 +392,51 @@ void tools::addPathIfExists(const Driver &D, const Twine &Path, Paths.push_back(Path.str()); } +void tools::addPrefixDirs(const ArgList &Args, + SmallVectorImpl<std::string> &PrefixDirs) { + for (const Arg *A : Args.filtered(options::OPT_B)) { + A->claim(); + PrefixDirs.push_back(A->getValue(0)); + } + if (std::optional<std::string> CompilerPathValue = + llvm::sys::Process::GetEnv("COMPILER_PATH")) { + StringRef CompilerPath = *CompilerPathValue; + while (!CompilerPath.empty()) { + std::pair<StringRef, StringRef> Split = + CompilerPath.split(llvm::sys::EnvPathSeparator); + PrefixDirs.push_back(std::string(Split.first)); + CompilerPath = Split.second; + } + } +} + +std::optional<std::string> +tools::findProgramInPrefixDirs(StringRef Name, + ArrayRef<std::string> PrefixDirs) { + for (const std::string &PrefixDir : PrefixDirs) { + SmallString<128> P(PrefixDir); + if (llvm::sys::fs::is_directory(PrefixDir)) + llvm::sys::path::append(P, Name); + else + P += Name; + if (llvm::sys::fs::can_execute(P)) + return std::string(P); + } + return std::nullopt; +} + +std::optional<std::string> tools::resolveLdPath(StringRef LdPath, + StringRef FoundPath) { + if (LdPath.empty()) + return std::nullopt; + std::string Path = llvm::sys::path::parent_path(LdPath).empty() + ? FoundPath.str() + : LdPath.str(); + if (!llvm::sys::fs::can_execute(Path)) + return std::nullopt; + return Path; +} + void tools::handleTargetFeaturesGroup(const Driver &D, const llvm::Triple &Triple, const ArgList &Args, >From e797be938bebe29d17fff6c973dc7b71d6503439 Mon Sep 17 00:00:00 2001 From: AdityaSinha149 <[email protected]> Date: Tue, 3 Nov 2026 23:42:41 +0530 Subject: [PATCH 2/2] [clang-repl] Link HIP device object into an HSACO --- clang/include/clang/Interpreter/Interpreter.h | 3 +- clang/lib/Interpreter/DeviceOffload.cpp | 93 ++++++++++++++++++- clang/lib/Interpreter/DeviceOffload.h | 10 +- clang/lib/Interpreter/Interpreter.cpp | 9 +- clang/tools/clang-repl/ClangRepl.cpp | 2 +- .../Interpreter/DeviceOffloadTest.cpp | 30 +++++- 6 files changed, 134 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 5228b7fe80bd32..644f6f6247b79f 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -158,7 +158,8 @@ class Interpreter { static llvm::Expected<std::unique_ptr<Interpreter>> createWithDevice(std::unique_ptr<CompilerInstance> CI, std::unique_ptr<CompilerInstance> DCI, - OffloadType Type = OffloadType::Detect); + OffloadType Type = OffloadType::Detect, + llvm::ArrayRef<const char *> DriverArgs = {}); const ASTContext &getASTContext() const; ASTContext &getASTContext(); diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp index d7ae386466f40f..5c4934298a69a0 100644 --- a/clang/lib/Interpreter/DeviceOffload.cpp +++ b/clang/lib/Interpreter/DeviceOffload.cpp @@ -16,13 +16,22 @@ #include "clang/Basic/TargetOptions.h" #include "clang/CodeGen/BackendUtil.h" #include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Driver/CommonArgs.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Interpreter/PartialTranslationUnit.h" +#include "clang/Options/Options.h" #include "llvm/ADT/StringExtras.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/MC/TargetRegistry.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" #include "llvm/TargetParser/Triple.h" @@ -43,7 +52,8 @@ IncrementalHIPDeviceParser::IncrementalHIPDeviceParser( CompilerInstance &DeviceInstance, CompilerInstance &HostInstance, IncrementalAction *DeviceAct, llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS, - llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs) + llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs, + llvm::ArrayRef<const char *> DriverArgs) : IncrementalDeviceParser(DeviceInstance, HostInstance, DeviceAct, FS, Err, PTUs), DeviceCI(DeviceInstance) { @@ -57,6 +67,27 @@ IncrementalHIPDeviceParser::IncrementalHIPDeviceParser( llvm::inconvertibleErrorCode())); return; } + + unsigned MissingArgIndex, MissingArgCount; + llvm::opt::InputArgList Args = getDriverOptTable().ParseArgs( + DriverArgs, MissingArgIndex, MissingArgCount, + llvm::opt::Visibility(options::ClangOption)); + driver::tools::addPrefixDirs(Args, PrefixDirs); + LdPath = Args.getLastArgValue(options::OPT_ld_path_EQ).str(); +} + +static std::string getProgramPath(llvm::StringRef Name, + llvm::ArrayRef<std::string> PrefixDirs) { + if (std::optional<std::string> P = + driver::tools::findProgramInPrefixDirs(Name, PrefixDirs)) + return *P; + std::string Exe = llvm::sys::fs::getMainExecutable(nullptr, nullptr); + if (llvm::ErrorOr<std::string> P = llvm::sys::findProgramByName( + Name, {llvm::sys::path::parent_path(Exe)})) + return *P; + if (llvm::ErrorOr<std::string> P = llvm::sys::findProgramByName(Name)) + return *P; + return Name.str(); } llvm::Expected<llvm::StringRef> @@ -80,13 +111,69 @@ IncrementalHIPDeviceParser::GenerateDeviceObject() { return llvm::StringRef(ObjectContent.data(), ObjectContent.size()); } +llvm::Expected<llvm::StringRef> +IncrementalHIPDeviceParser::GenerateHSACO(llvm::StringRef Object) { + // Without --ld-path, look for a program named "lld". + llvm::StringRef LinkerName = LdPath; + if (LinkerName.empty()) + LinkerName = "lld"; + std::optional<std::string> Linker = driver::tools::resolveLdPath( + LinkerName, getProgramPath(LinkerName, PrefixDirs)); + if (!Linker) + return llvm::make_error<llvm::StringError>( + LdPath.empty() + ? "Could not find lld." + : "Invalid linker name in argument '--ld-path=" + LdPath + "'.", + llvm::inconvertibleErrorCode()); + + int ObjFD = -1; + llvm::SmallString<128> ObjFile; + if (llvm::sys::fs::createTemporaryFile("kernel", "o", ObjFD, ObjFile)) + return llvm::make_error<llvm::StringError>( + "Failed to create a temporary object file.", + llvm::inconvertibleErrorCode()); + llvm::FileRemover ObjRemover(ObjFile); + { + llvm::raw_fd_ostream OS(ObjFD, /*shouldClose=*/true); + OS << Object; + } + + llvm::SmallString<128> HSACOFile; + if (llvm::sys::fs::createTemporaryFile("kernel", "hsaco", HSACOFile)) + return llvm::make_error<llvm::StringError>( + "Failed to create a temporary code object file.", + llvm::inconvertibleErrorCode()); + llvm::FileRemover HSACORemover(HSACOFile); + + // These match the driver's HIP device link (AMDGCN::Linker) for an object. + llvm::StringRef Args[] = {*Linker, "-flavor", "gnu", + "-m", "elf64_amdgpu", "--no-undefined", + "-shared", "--whole-archive", "-o", + HSACOFile, ObjFile, "--no-whole-archive"}; + if (llvm::sys::ExecuteAndWait(*Linker, Args) != 0) + return llvm::make_error<llvm::StringError>("The linker invocation failed.", + llvm::inconvertibleErrorCode()); + + auto HSACOBuf = llvm::MemoryBuffer::getFile(HSACOFile, /*IsText=*/false); + if (!HSACOBuf) + return llvm::make_error<llvm::StringError>( + "Failed to read the code object.", llvm::inconvertibleErrorCode()); + + llvm::StringRef Buffer = (*HSACOBuf)->getBuffer(); + HSACOContent.assign(Buffer.begin(), Buffer.end()); + return llvm::StringRef(HSACOContent.data(), HSACOContent.size()); +} + llvm::Error IncrementalHIPDeviceParser::GenerateOffloadBinary() { llvm::Expected<llvm::StringRef> Object = GenerateDeviceObject(); if (!Object) return Object.takeError(); - // FIXME: Link the object into an HSACO and bundle it for the host - // compilation. + llvm::Expected<llvm::StringRef> HSACO = GenerateHSACO(*Object); + if (!HSACO) + return HSACO.takeError(); + + // FIXME: Bundle the HSACO and embed it into the host compilation. return llvm::make_error<llvm::StringError>( "HIP offload binary generation is not supported yet.", llvm::inconvertibleErrorCode()); diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h index c01e373d5a2f94..0daec6c5f57505 100644 --- a/clang/lib/Interpreter/DeviceOffload.h +++ b/clang/lib/Interpreter/DeviceOffload.h @@ -47,16 +47,24 @@ class IncrementalDeviceParser : public IncrementalParser { class IncrementalHIPDeviceParser : public IncrementalDeviceParser { CompilerInstance &DeviceCI; llvm::SmallVector<char, 0> ObjectContent; + llvm::SmallVector<char, 0> HSACOContent; + // The -B prefixes and --ld-path value from the driver arguments. + llvm::SmallVector<std::string, 4> PrefixDirs; + std::string LdPath; // Generate the AMDGPU object file for the last PTU. llvm::Expected<llvm::StringRef> GenerateDeviceObject(); + // Link the object file into a loadable AMDGPU code object (HSACO). + llvm::Expected<llvm::StringRef> GenerateHSACO(llvm::StringRef Object); + public: IncrementalHIPDeviceParser( CompilerInstance &DeviceInstance, CompilerInstance &HostInstance, IncrementalAction *DeviceAct, llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS, - llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs); + llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs, + llvm::ArrayRef<const char *> DriverArgs); llvm::Error GenerateOffloadBinary() override; diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index d6cce74b7275f7..b0fc3e143c4211 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -569,10 +569,9 @@ llvm::Expected<std::unique_ptr<Interpreter>> Interpreter::create( return std::move(Interp); } -llvm::Expected<std::unique_ptr<Interpreter>> -Interpreter::createWithDevice(std::unique_ptr<CompilerInstance> CI, - std::unique_ptr<CompilerInstance> DCI, - OffloadType Type) { +llvm::Expected<std::unique_ptr<Interpreter>> Interpreter::createWithDevice( + std::unique_ptr<CompilerInstance> CI, std::unique_ptr<CompilerInstance> DCI, + OffloadType Type, llvm::ArrayRef<const char *> DriverArgs) { if (Type == OffloadType::Detect) return llvm::make_error<llvm::StringError>( "Detecting the device vendor is not supported yet.", @@ -618,7 +617,7 @@ Interpreter::createWithDevice(std::unique_ptr<CompilerInstance> CI, if (Type == OffloadType::HIP) DeviceParser = std::make_unique<IncrementalHIPDeviceParser>( *Interp->DeviceCI, *Interp->getCompilerInstance(), - Interp->DeviceAct.get(), IMVFS, Err, Interp->PTUs); + Interp->DeviceAct.get(), IMVFS, Err, Interp->PTUs, DriverArgs); else DeviceParser = std::make_unique<IncrementalCUDADeviceParser>( *Interp->DeviceCI, *Interp->getCompilerInstance(), diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index df0786796cb8ca..8fc30f874444e8 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -349,7 +349,7 @@ int main(int argc, const char **argv) { if (DeviceEnabled) { Interp = ExitOnErr(clang::Interpreter::createWithDevice( - std::move(CI), std::move(DeviceCI), OffloadKind)); + std::move(CI), std::move(DeviceCI), OffloadKind, ClangArgv)); const char *RuntimeLib = UseHip ? "libamdhip64.so" : "libcudart.so"; if (DevicePath.empty()) { diff --git a/clang/unittests/Interpreter/DeviceOffloadTest.cpp b/clang/unittests/Interpreter/DeviceOffloadTest.cpp index fa624f23f435b7..d79af4812eae80 100644 --- a/clang/unittests/Interpreter/DeviceOffloadTest.cpp +++ b/clang/unittests/Interpreter/DeviceOffloadTest.cpp @@ -27,6 +27,8 @@ #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" @@ -156,10 +158,34 @@ TEST_F(DeviceOffloadHIPTest, DeviceObjectIsGenerated) { // FIXME: Replace this with a check of the embedded bundle once the offload // binary is generated. + std::string Exe = llvm::sys::fs::getMainExecutable(nullptr, nullptr); + bool HasLLD = llvm::sys::findProgramByName( + "lld", {llvm::sys::path::parent_path(Exe)}) || + llvm::sys::findProgramByName("lld"); auto PTU = (*Interp)->Parse("__attribute__((device)) void f() {}"); EXPECT_THAT_EXPECTED( - PTU, llvm::FailedWithMessage("HIP offload binary generation is not " - "supported yet.")); + PTU, llvm::FailedWithMessage( + HasLLD ? "HIP offload binary generation is not supported yet." + : "Could not find lld.")); +} + +TEST_F(DeviceOffloadHIPTest, LdPathSelectsTheLinker) { + std::vector<const char *> Args = {"--offload-arch=gfx1100", "-nogpulib", + "-nogpuinc", "--ld-path=/nonexistent/lld"}; + IncrementalCompilerBuilder CB; + CB.SetCompilerArgs(Args); + auto DeviceCI = CB.CreateDevice(OffloadType::HIP); + ASSERT_THAT_EXPECTED(DeviceCI, llvm::Succeeded()); + auto HostCI = CB.CreateHost(OffloadType::HIP); + ASSERT_THAT_EXPECTED(HostCI, llvm::Succeeded()); + auto Interp = Interpreter::createWithDevice( + std::move(*HostCI), std::move(*DeviceCI), OffloadType::HIP, Args); + ASSERT_THAT_EXPECTED(Interp, llvm::Succeeded()); + + auto PTU = (*Interp)->Parse("__attribute__((device)) void f() {}"); + EXPECT_THAT_EXPECTED( + PTU, llvm::FailedWithMessage("Invalid linker name in argument " + "'--ld-path=/nonexistent/lld'.")); } // A bitcode library with one float(float) function that doubles its argument, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
