[clang] [clang-repl] Implement IncrementalHIPDeviceParser for HIP device compilation (PR #218337)

Aditya Sinha via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 9 02:29:28 PDT 2026


https://github.com/AdityaSinha149 updated https://github.com/llvm/llvm-project/pull/218337

>From 7dfaa1317187c56a7e31b89e6d9d5d807c68a8f3 Mon Sep 17 00:00:00 2001
From: AdityaSinha149 <adsinha at amd.com>
Date: Mon, 24 Aug 2026 12:58:31 +0530
Subject: [PATCH 1/2] [clang-repl] Made IncrementalHipDeviceParser class

---
 clang/lib/Interpreter/DeviceOffload.cpp | 244 ++++++++++++++++++++++--
 clang/lib/Interpreter/DeviceOffload.h   |  34 ++++
 2 files changed, 266 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp
index 38cecd142a8e6..b53d59d790042 100644
--- a/clang/lib/Interpreter/DeviceOffload.cpp
+++ b/clang/lib/Interpreter/DeviceOffload.cpp
@@ -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() {
+  // The host embeds this blob as __hip_fatbin; __hipRegisterFatBinary parses
+  // it as a clang-offload-bundle:
+  //   char     Magic["__CLANG_OFFLOAD_BUNDLE__"]  (no NUL terminator)
+  //   uint64_t NumberOfEntries
+  //   for each entry: uint64_t Offset, Size, TripleSize; char Triple[]
+  //   the code objects follow; HIP requires each to be page-aligned (4096).
+  static constexpr llvm::StringRef Magic = "__CLANG_OFFLOAD_BUNDLE__";
+  static constexpr uint64_t CodeObjectAlign = 4096;
+
+  const PartialTranslationUnit &PTU = PTUs.back();
+  // Triples use the normalized 4-field form ending in a dash; the device entry
+  // additionally appends the offload arch, e.g.
+  // "hip-amdgcn-amd-amdhsa--gfx90a".
+  std::string HostTriple = "host-" + llvm::sys::getProcessTriple() + "-";
+  std::string DeviceTriple =
+      "hip-" + PTU.TheModule->getTargetTriple().str() + "--" + TargetOpts.CPU;
+
+  const uint64_t NumEntries = 2;
+  const uint64_t HeaderSize = Magic.size() + sizeof(uint64_t) +
+                              NumEntries * (3 * sizeof(uint64_t)) +
+                              HostTriple.size() + DeviceTriple.size();
+  const uint64_t CodeObjectOffset = llvm::alignTo(HeaderSize, CodeObjectAlign);
+
+  llvm::SmallVector<char, 4096> Bundle;
+  llvm::raw_svector_ostream OS(Bundle);
+  auto WriteU64 = [&OS](uint64_t V) {
+    OS.write(reinterpret_cast<const char *>(&V), sizeof(V));
+  };
+
+  OS << Magic;
+  WriteU64(NumEntries);
+
+  // Host entry: empty content. Not required (the runtime matches by triple and
+  // skips host entries); emitted only to mirror the canonical bundler layout,
+  // and free since page alignment keeps the bundle the same size regardless.
+  WriteU64(CodeObjectOffset);
+  WriteU64(/*Size=*/0);
+  WriteU64(HostTriple.size());
+  OS << HostTriple;
+
+  // Device entry: the page-aligned .hsaco content.
+  WriteU64(CodeObjectOffset);
+  WriteU64(HSACOContent.size());
+  WriteU64(DeviceTriple.size());
+  OS << DeviceTriple;
+
+  // Zero-pad the header up to the aligned code-object offset.
+  OS << std::string(CodeObjectOffset - HeaderSize, '\0');
+  OS << llvm::StringRef(HSACOContent.data(), HSACOContent.size());
+
+  std::string BundleFileName = "/" + PTU.TheModule->getName().str() + ".hipfb";
+  VFS->addFile(BundleFileName, 0,
+               llvm::MemoryBuffer::getMemBufferCopy(
+                   llvm::StringRef(Bundle.data(), Bundle.size())));
+
+  CodeGenOpts.OffloadBinaryToEmbedFile = std::move(BundleFileName);
+  return llvm::Error::success();
+}
+
+IncrementalHIPDeviceParser::~IncrementalHIPDeviceParser() {}
+
 IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
     CompilerInstance &DeviceInstance, CompilerInstance &HostInstance,
     IncrementalAction *DeviceAct,
@@ -45,18 +271,12 @@ IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
 
 llvm::Expected<llvm::StringRef> IncrementalCUDADeviceParser::GeneratePTX() {
   auto &PTU = PTUs.back();
-  std::string Error;
-
-  const llvm::Target *Target = llvm::TargetRegistry::lookupTarget(
-      PTU.TheModule->getTargetTriple(), Error);
-  if (!Target)
-    return llvm::make_error<llvm::StringError>(std::move(Error),
-                                               std::error_code());
-  llvm::TargetOptions TO = llvm::TargetOptions();
-  llvm::TargetMachine *TargetMachine = Target->createTargetMachine(
-      PTU.TheModule->getTargetTriple(), TargetOpts.CPU, "", TO,
-      llvm::Reloc::Model::PIC_);
-  PTU.TheModule->setDataLayout(TargetMachine->createDataLayout());
+
+  llvm::Expected<llvm::TargetMachine *> TMOrErr =
+      getOrCreateTargetMachine(TM, *PTU.TheModule, TargetOpts.CPU);
+  if (!TMOrErr)
+    return TMOrErr.takeError();
+  llvm::TargetMachine *TargetMachine = *TMOrErr;
 
   PTXCode.clear();
   llvm::raw_svector_ostream dest(PTXCode);
diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h
index a31bd5a0499b8..ea1f48fe43a59 100644
--- a/clang/lib/Interpreter/DeviceOffload.h
+++ b/clang/lib/Interpreter/DeviceOffload.h
@@ -14,9 +14,16 @@
 #define LLVM_CLANG_LIB_INTERPRETER_DEVICE_OFFLOAD_H
 
 #include "IncrementalParser.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/VirtualFileSystem.h"
 
+#include <memory>
+
+namespace llvm {
+class TargetMachine;
+} // namespace llvm
+
 namespace clang {
 struct PartialTranslationUnit;
 class CompilerInstance;
@@ -24,6 +31,32 @@ class CodeGenOptions;
 class TargetOptions;
 class IncrementalAction;
 
+class IncrementalHIPDeviceParser : public IncrementalParser {
+
+public:
+  IncrementalHIPDeviceParser(
+      CompilerInstance &DeviceInstance, CompilerInstance &HostInstance,
+      IncrementalAction *DeviceAct,
+      llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS,
+      llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs);
+
+  llvm::Error optimize();
+
+  llvm::Expected<llvm::StringRef> GenerateHSACO();
+
+  llvm::Error GenerateOffloadBundle();
+
+  ~IncrementalHIPDeviceParser();
+
+protected:
+  llvm::SmallVector<char, 1024> HSACOContent;
+  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS;
+  CodeGenOptions &CodeGenOpts; // Host opts, intentionally a reference.
+  const CodeGenOptions &DeviceCodeGenOpts;
+  const TargetOptions &TargetOpts;
+  std::unique_ptr<llvm::TargetMachine> TM;
+};
+
 class IncrementalCUDADeviceParser : public IncrementalParser {
 
 public:
@@ -48,6 +81,7 @@ class IncrementalCUDADeviceParser : public IncrementalParser {
   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS;
   CodeGenOptions &CodeGenOpts; // Intentionally a reference.
   const TargetOptions &TargetOpts;
+  std::unique_ptr<llvm::TargetMachine> TM;
 };
 
 } // namespace clang

>From 22d0cc4a490d1cafe364334c5d631c0811140b3f Mon Sep 17 00:00:00 2001
From: AdityaSinha149 <adsinha at amd.com>
Date: Wed, 9 Sep 2026 14:59:13 +0530
Subject: [PATCH 2/2] added devicelibs

---
 clang/lib/Interpreter/DeviceOffload.cpp | 44 ++++++++++++++++++++++++-
 clang/lib/Interpreter/DeviceOffload.h   |  3 ++
 2 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp
index b53d59d790042..8002e8a50dd64 100644
--- a/clang/lib/Interpreter/DeviceOffload.cpp
+++ b/clang/lib/Interpreter/DeviceOffload.cpp
@@ -15,10 +15,12 @@
 
 #include "clang/Basic/TargetOptions.h"
 #include "clang/CodeGen/ModuleBuilder.h"
+#include "clang/CodeGen/ModuleLinker.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/PartialTranslationUnit.h"
 
 #include "llvm/ADT/StringSet.h"
+#include "llvm/IR/Function.h"
 #include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/Module.h"
 #include "llvm/IRReader/IRReader.h"
@@ -60,7 +62,8 @@ IncrementalHIPDeviceParser::IncrementalHIPDeviceParser(
     IncrementalAction *DeviceAct,
     llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS,
     llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs)
-    : IncrementalParser(DeviceInstance, DeviceAct, Err, PTUs), VFS(FS),
+    : IncrementalParser(DeviceInstance, DeviceAct, Err, PTUs),
+      DeviceCI(DeviceInstance), VFS(FS),
       CodeGenOpts(HostInstance.getCodeGenOpts()),
       DeviceCodeGenOpts(DeviceInstance.getCodeGenOpts()),
       TargetOpts(DeviceInstance.getTargetOpts()) {
@@ -75,9 +78,48 @@ IncrementalHIPDeviceParser::IncrementalHIPDeviceParser(
   }
 }
 
+llvm::Error IncrementalHIPDeviceParser::linkDeviceLibs(llvm::Module &M) {
+  llvm::SmallVector<LinkModule, 4> LinkModules;
+  if (loadLinkModules(DeviceCI, M.getContext(), LinkModules))
+    return llvm::make_error<llvm::StringError>(
+        "Failed to load HIP device libraries", llvm::inconvertibleErrorCode());
+
+  for (auto &LM : LinkModules) {
+    if (LM.PropagateAttrs)
+      for (llvm::Function &F : *LM.Module) {
+        if (F.isIntrinsic())
+          continue;
+        clang::CodeGen::mergeDefaultFunctionDefinitionAttributes(
+            F, DeviceCodeGenOpts, DeviceCI.getLangOpts(), TargetOpts,
+            LM.Internalize);
+      }
+
+    bool LinkErr;
+    if (LM.Internalize)
+      LinkErr = llvm::Linker::linkModules(
+          M, std::move(LM.Module), LM.LinkFlags,
+          [](llvm::Module &M, const llvm::StringSet<> &GVS) {
+            llvm::internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) {
+              return !GV.hasName() || (GVS.count(GV.getName()) == 0);
+            });
+          });
+    else
+      LinkErr =
+          llvm::Linker::linkModules(M, std::move(LM.Module), LM.LinkFlags);
+
+    if (LinkErr)
+      return llvm::make_error<llvm::StringError>(
+          "Failed to link HIP device library", llvm::inconvertibleErrorCode());
+  }
+  return llvm::Error::success();
+}
+
 llvm::Error IncrementalHIPDeviceParser::optimize() {
   auto &PTU = PTUs.back();
 
+  if (llvm::Error Err = linkDeviceLibs(*PTU.TheModule))
+    return Err;
+
   llvm::Expected<llvm::TargetMachine *> TMOrErr =
       getOrCreateTargetMachine(TM, *PTU.TheModule, TargetOpts.CPU);
   if (!TMOrErr)
diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h
index ea1f48fe43a59..6a37987ed2d75 100644
--- a/clang/lib/Interpreter/DeviceOffload.h
+++ b/clang/lib/Interpreter/DeviceOffload.h
@@ -21,6 +21,7 @@
 #include <memory>
 
 namespace llvm {
+class Module;
 class TargetMachine;
 } // namespace llvm
 
@@ -49,6 +50,8 @@ class IncrementalHIPDeviceParser : public IncrementalParser {
   ~IncrementalHIPDeviceParser();
 
 protected:
+  llvm::Error linkDeviceLibs(llvm::Module &M);
+  CompilerInstance &DeviceCI;
   llvm::SmallVector<char, 1024> HSACOContent;
   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS;
   CodeGenOptions &CodeGenOpts; // Host opts, intentionally a reference.



More information about the cfe-commits mailing list