https://github.com/AdityaSinha149 updated https://github.com/llvm/llvm-project/pull/235614
>From f715b64512711d08976f7f6c6858d8fe3590d528 Mon Sep 17 00:00:00 2001 From: AdityaSinha149 <[email protected]> Date: Mon, 26 Oct 2026 10:58:56 +0530 Subject: [PATCH] [clang-repl] Link HIP device object into an HSACO --- clang/include/clang/Interpreter/Interpreter.h | 3 +- clang/lib/Interpreter/DeviceOffload.cpp | 99 ++++++++++++++++++- clang/lib/Interpreter/DeviceOffload.h | 10 +- clang/lib/Interpreter/Interpreter.cpp | 5 +- clang/tools/clang-repl/ClangRepl.cpp | 2 +- .../Interpreter/DeviceOffloadTest.cpp | 31 +++++- 6 files changed, 140 insertions(+), 10 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 94fec23b1c0b2f..ae254ed40aaabc 100644 --- a/clang/lib/Interpreter/DeviceOffload.cpp +++ b/clang/lib/Interpreter/DeviceOffload.cpp @@ -18,10 +18,18 @@ #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Interpreter/PartialTranslationUnit.h" +#include "clang/Options/Options.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" @@ -42,7 +50,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) { @@ -56,6 +65,33 @@ IncrementalHIPDeviceParser::IncrementalHIPDeviceParser( llvm::inconvertibleErrorCode())); return; } + + unsigned MissingArgIndex, MissingArgCount; + llvm::opt::InputArgList Args = getDriverOptTable().ParseArgs( + DriverArgs, MissingArgIndex, MissingArgCount, + llvm::opt::Visibility(options::ClangOption)); + PrefixDirs = Args.getAllArgValues(options::OPT_B); + LdPath = Args.getLastArgValue(options::OPT_ld_path_EQ).str(); +} + +static std::optional<std::string> +findProgram(llvm::StringRef Name, llvm::ArrayRef<std::string> PrefixDirs) { + for (const std::string &Prefix : PrefixDirs) { + llvm::SmallString<128> P(Prefix); + if (llvm::sys::fs::is_directory(Prefix)) + llvm::sys::path::append(P, Name); + else + P += Name; + if (llvm::sys::fs::can_execute(P)) + return std::string(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 std::nullopt; } llvm::Expected<llvm::StringRef> @@ -79,13 +115,70 @@ IncrementalHIPDeviceParser::GenerateDeviceObject() { return llvm::StringRef(ObjectContent.data(), ObjectContent.size()); } +llvm::Expected<llvm::StringRef> +IncrementalHIPDeviceParser::GenerateHSACO(llvm::StringRef Object) { + std::optional<std::string> Linker; + if (LdPath.empty()) + Linker = findProgram("lld", PrefixDirs); + else if (llvm::sys::path::parent_path(LdPath).empty()) + Linker = findProgram(LdPath, PrefixDirs); + else if (llvm::sys::fs::can_execute(LdPath)) + Linker = LdPath; + 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..0ed618c15893cf 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. + std::vector<std::string> 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 1fa0456f6b4ee8..c956292956cbc2 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -572,7 +572,8 @@ llvm::Expected<std::unique_ptr<Interpreter>> Interpreter::create( llvm::Expected<std::unique_ptr<Interpreter>> Interpreter::createWithDevice(std::unique_ptr<CompilerInstance> CI, std::unique_ptr<CompilerInstance> DCI, - OffloadType Type) { + 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 +619,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..ff3096d1f786e4 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,35 @@ 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
