[clang] [clang-repl] Implement IncrementalHIPDeviceParser for HIP device compilation (PR #218337)
Yaxun Liu via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 10 05:57:57 PDT 2026
================
@@ -11,19 +11,245 @@
//===----------------------------------------------------------------------===//
#include "DeviceOffload.h"
+#include "IncrementalAction.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Interpreter/PartialTranslationUnit.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Linker/Linker.h"
#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
#include "llvm/Target/TargetMachine.h"
+#include "llvm/TargetParser/Host.h"
+#include "llvm/Transforms/IPO/Internalize.h"
namespace clang {
+static llvm::Expected<llvm::TargetMachine *>
+getOrCreateTargetMachine(std::unique_ptr<llvm::TargetMachine> &Cache,
+ llvm::Module &M, llvm::StringRef CPU) {
+ if (!Cache) {
+ std::string Error;
+ const llvm::Target *Target =
+ llvm::TargetRegistry::lookupTarget(M.getTargetTriple(), Error);
+ if (!Target)
+ return llvm::make_error<llvm::StringError>(std::move(Error),
+ std::error_code());
+ llvm::TargetOptions TO = llvm::TargetOptions();
+ Cache.reset(Target->createTargetMachine(M.getTargetTriple(), CPU, "", TO,
+ llvm::Reloc::Model::PIC_));
+ }
+ M.setDataLayout(Cache->createDataLayout());
+ return Cache.get();
+}
+
+IncrementalHIPDeviceParser::IncrementalHIPDeviceParser(
+ CompilerInstance &DeviceInstance, CompilerInstance &HostInstance,
+ IncrementalAction *DeviceAct,
+ llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS,
+ llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs)
+ : IncrementalParser(DeviceInstance, DeviceAct, Err, PTUs), VFS(FS),
+ CodeGenOpts(HostInstance.getCodeGenOpts()),
+ DeviceCodeGenOpts(DeviceInstance.getCodeGenOpts()),
+ TargetOpts(DeviceInstance.getTargetOpts()) {
+ if (Err)
+ return;
+ StringRef Arch = TargetOpts.CPU;
+ if (!Arch.starts_with("gfx")) {
+ Err = llvm::joinErrors(std::move(Err), llvm::make_error<llvm::StringError>(
+ "Invalid HIP architecture",
+ llvm::inconvertibleErrorCode()));
+ return;
+ }
+}
+
+llvm::Error IncrementalHIPDeviceParser::optimize() {
+ auto &PTU = PTUs.back();
+
+ llvm::Expected<llvm::TargetMachine *> TMOrErr =
+ getOrCreateTargetMachine(TM, *PTU.TheModule, TargetOpts.CPU);
+ if (!TMOrErr)
+ return TMOrErr.takeError();
+
+ llvm::LoopAnalysisManager LAM;
+ llvm::FunctionAnalysisManager FAM;
+ llvm::CGSCCAnalysisManager CGAM;
+ llvm::ModuleAnalysisManager MAM;
+
+ llvm::PassBuilder PB(*TMOrErr);
+ PB.registerModuleAnalyses(MAM);
+ PB.registerCGSCCAnalyses(CGAM);
+ PB.registerFunctionAnalyses(FAM);
+ PB.registerLoopAnalyses(LAM);
+ PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+
+ llvm::OptimizationLevel OptLevel;
+ switch (DeviceCodeGenOpts.OptimizationLevel) {
+ case 0:
+ OptLevel = llvm::OptimizationLevel::O0;
+ break;
+ case 1:
+ OptLevel = llvm::OptimizationLevel::O1;
+ break;
+ case 2:
+ OptLevel = llvm::OptimizationLevel::O2;
+ break;
+ default:
+ OptLevel = llvm::OptimizationLevel::O3;
+ break;
+ }
+
+ llvm::ModulePassManager MPM =
+ OptLevel == llvm::OptimizationLevel::O0
+ ? PB.buildO0DefaultPipeline(OptLevel)
+ : PB.buildPerModuleDefaultPipeline(OptLevel);
+ MPM.run(*PTU.TheModule, MAM);
+ return llvm::Error::success();
+}
+
+llvm::Expected<llvm::StringRef> IncrementalHIPDeviceParser::GenerateHSACO() {
+ auto &PTU = PTUs.back();
+
+ llvm::Expected<llvm::TargetMachine *> TMOrErr =
+ getOrCreateTargetMachine(TM, *PTU.TheModule, TargetOpts.CPU);
+ if (!TMOrErr)
+ return TMOrErr.takeError();
+ llvm::TargetMachine *TargetMachine = *TMOrErr;
+
+ llvm::SmallVector<char, 0> Object;
+ llvm::raw_svector_ostream ObjOS(Object);
+
+ llvm::legacy::PassManager PM;
+ if (TargetMachine->addPassesToEmitFile(PM, ObjOS, nullptr,
+ llvm::CodeGenFileType::ObjectFile))
+ return llvm::make_error<llvm::StringError>(
+ "AMDGPU backend cannot produce an object file.",
+ llvm::inconvertibleErrorCode());
+
+ if (!PM.run(*PTU.TheModule))
+ return llvm::make_error<llvm::StringError>(
+ "Failed to emit the object file.", llvm::inconvertibleErrorCode());
+
+ // Link the object into a shared .hsaco code object with ld.lld.
+ std::string Exe = llvm::sys::fs::getMainExecutable(nullptr, nullptr);
+ llvm::StringRef ExeDir = llvm::sys::path::parent_path(Exe);
+ llvm::ErrorOr<std::string> LLDPath =
+ llvm::sys::findProgramByName("ld.lld", {ExeDir});
+ if (!LLDPath)
+ LLDPath = llvm::sys::findProgramByName("ld.lld");
+ if (!LLDPath)
+ return llvm::make_error<llvm::StringError>(
+ "Could not find ld.lld next to the executable or on PATH.",
+ 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 << llvm::StringRef(Object.data(), Object.size());
+ }
+
+ 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);
+
+ llvm::StringRef Args[] = {"ld.lld", "-shared", ObjFile, "-o", HsacoFile};
+ if (llvm::sys::ExecuteAndWait(*LLDPath, Args) != 0)
+ return llvm::make_error<llvm::StringError>("ld.lld 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::GenerateOffloadBundle() {
----------------
yxsamliu wrote:
Thanks for switching this to `OffloadBundler`. One target-ID issue still remains.
For an architecture such as `gfx90a:xnack+`, `TargetOpts.CPU` contains only `gfx90a`. This means the generated bundle ID loses the `xnack+` feature and may describe the code object as compatible with the wrong device configuration.
Could we preserve the full canonical target ID from `--offload-arch` when building `DeviceTriple`? It would also be useful to add a test that checks a bundle ID containing a feature such as `gfx90a:xnack+`.
https://github.com/llvm/llvm-project/pull/218337
More information about the cfe-commits
mailing list