[llvm-branch-commits] [clang] Cir/cirgenaction state lift (PR #197751)
Konstantinos Parasyris via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu May 14 09:53:13 PDT 2026
https://github.com/koparasy created https://github.com/llvm/llvm-project/pull/197751
None
>From 09f730c2ed5ae3c5e08232f8d6f4050f2c341b08 Mon Sep 17 00:00:00 2001
From: y <konstantinos.parasyris at intel.com>
Date: Thu, 14 May 2026 08:29:04 -0700
Subject: [PATCH 1/2] [CIR][CodeGen] Extract shared LinkModule struct and
loadLinkModules helper
The bitcode-link plumbing is ABI-neutral and identical between the classic
CodeGen path and the ClangIR path. Prior to this change, each side carried
its own copy of the `LinkModule` struct and `loadLinkModules` routine;
CIRGenAction.cpp explicitly flagged the copy with a TODO.
Move the struct and loader into clang/CodeGen/ModuleLinker.{h,cpp} so both
frontends share one definition. `LinkInModules` remains per-consumer because
the classic path threads `CurLinkModule` into its diagnostic handler while
CIR does not.
This is a straight refactor -- no behavior change.
---
clang/include/clang/CodeGen/CodeGenAction.h | 24 +-------
clang/include/clang/CodeGen/ModuleLinker.h | 40 +++++++++++++
clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 58 +++----------------
clang/lib/CIR/FrontendAction/CMakeLists.txt | 2 +
clang/lib/CodeGen/BackendConsumer.h | 3 +-
clang/lib/CodeGen/CMakeLists.txt | 1 +
clang/lib/CodeGen/CodeGenAction.cpp | 34 +----------
clang/lib/CodeGen/ModuleLinker.cpp | 50 ++++++++++++++++
8 files changed, 104 insertions(+), 108 deletions(-)
create mode 100644 clang/include/clang/CodeGen/ModuleLinker.h
create mode 100644 clang/lib/CodeGen/ModuleLinker.cpp
diff --git a/clang/include/clang/CodeGen/CodeGenAction.h b/clang/include/clang/CodeGen/CodeGenAction.h
index 186dbb43f01ef..84fa4549d5033 100644
--- a/clang/include/clang/CodeGen/CodeGenAction.h
+++ b/clang/include/clang/CodeGen/CodeGenAction.h
@@ -9,6 +9,7 @@
#ifndef LLVM_CLANG_CODEGEN_CODEGENACTION_H
#define LLVM_CLANG_CODEGEN_CODEGENACTION_H
+#include "clang/CodeGen/ModuleLinker.h"
#include "clang/Frontend/FrontendAction.h"
#include <memory>
@@ -23,26 +24,6 @@ class CodeGenerator;
class CodeGenAction : public ASTFrontendAction {
private:
- // Let BackendConsumer access LinkModule.
- friend class BackendConsumer;
-
- /// Info about module to link into a module we're generating.
- struct LinkModule {
- /// The module to link in.
- std::unique_ptr<llvm::Module> Module;
-
- /// If true, we set attributes on Module's functions according to our
- /// CodeGenOptions and LangOptions, as though we were generating the
- /// function ourselves.
- bool PropagateAttrs;
-
- /// If true, we use LLVM module internalizer.
- bool Internalize;
-
- /// Bitwise combination of llvm::LinkerFlags used when we link the module.
- unsigned LinkFlags;
- };
-
unsigned Act;
std::unique_ptr<llvm::Module> TheModule;
@@ -53,9 +34,6 @@ class CodeGenAction : public ASTFrontendAction {
std::unique_ptr<llvm::Module> loadModule(llvm::MemoryBufferRef MBRef);
- /// Load bitcode modules to link into our module from the options.
- bool loadLinkModules(CompilerInstance &CI);
-
protected:
bool BeginSourceFileAction(CompilerInstance &CI) override;
diff --git a/clang/include/clang/CodeGen/ModuleLinker.h b/clang/include/clang/CodeGen/ModuleLinker.h
new file mode 100644
index 0000000000000..8a326df763f47
--- /dev/null
+++ b/clang/include/clang/CodeGen/ModuleLinker.h
@@ -0,0 +1,40 @@
+//===--- ModuleLinker.h - Shared bitcode link helpers ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_CODEGEN_MODULELINKER_H
+#define LLVM_CLANG_CODEGEN_MODULELINKER_H
+
+#include "llvm/ADT/SmallVector.h"
+#include <memory>
+
+namespace llvm {
+class LLVMContext;
+class Module;
+} // namespace llvm
+
+namespace clang {
+class CompilerInstance;
+
+/// Info about a module to link into the module currently being generated.
+/// Shared between the classic clang CodeGen path and the ClangIR path.
+struct LinkModule {
+ std::unique_ptr<llvm::Module> Module;
+ bool PropagateAttrs;
+ bool Internalize;
+ unsigned LinkFlags;
+};
+
+/// Load every bitcode file listed in CodeGenOpts.LinkBitcodeFiles into
+/// \p LinkModules. Returns true on error (diagnostic already reported).
+/// Appends to \p LinkModules; does not clear it.
+bool loadLinkModules(CompilerInstance &CI, llvm::LLVMContext &Ctx,
+ llvm::SmallVectorImpl<LinkModule> &LinkModules);
+
+} // namespace clang
+
+#endif
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index ad358f802e86a..1e39ae79eec39 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -14,16 +14,15 @@
#include "clang/CIR/CIRToCIRPasses.h"
#include "clang/CIR/LowerToLLVM.h"
#include "clang/CodeGen/BackendUtil.h"
+#include "clang/CodeGen/ModuleLinker.h"
#include "clang/Frontend/CompilerInstance.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSet.h"
-#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/IR/DiagnosticHandler.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/Module.h"
#include "llvm/Linker/Linker.h"
-#include "llvm/Support/Error.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/Internalize.h"
@@ -78,16 +77,7 @@ class CIRGenConsumer : public clang::ASTConsumer {
const FrontendOptions &FEOptions;
CodeGenOptions &CGO;
- // TODO(cir): This is a 1:1 copy from OG Codegen, we might need to find a way
- // to share this
- struct LinkModule {
- std::unique_ptr<llvm::Module> module;
- bool propagateAttrs;
- bool internalize;
- unsigned linkFlags;
- };
-
- SmallVector<LinkModule, 4> linkModules;
+ SmallVector<::clang::LinkModule, 4> linkModules;
public:
CIRGenConsumer(CIRGenAction::OutputType Action, CompilerInstance &CI,
@@ -179,7 +169,7 @@ class CIRGenConsumer : public clang::ASTConsumer {
}
llvm::LLVMContext LLVMCtx;
- if (loadLinkModules(LLVMCtx))
+ if (clang::loadLinkModules(CI, LLVMCtx, linkModules))
return;
std::unique_ptr<llvm::Module> LLVMModule =
@@ -198,55 +188,21 @@ class CIRGenConsumer : public clang::ASTConsumer {
}
}
- bool loadLinkModules(llvm::LLVMContext &llvmCtx) {
- if (!linkModules.empty())
- return false;
-
- for (const CodeGenOptions::BitcodeFileToLink &F :
- CI.getCodeGenOpts().LinkBitcodeFiles) {
- llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BCBuf =
- CI.getFileManager().getBufferForFile(F.Filename);
- if (!BCBuf) {
- CI.getDiagnostics().Report(diag::err_cannot_open_file)
- << F.Filename << BCBuf.getError().message();
- linkModules.clear();
- return true;
- }
-
- llvm::Expected<std::unique_ptr<llvm::Module>> ModuleOrErr =
- llvm::getOwningLazyBitcodeModule(std::move(*BCBuf), llvmCtx);
- if (!ModuleOrErr) {
- llvm::handleAllErrors(
- ModuleOrErr.takeError(), [&](llvm::ErrorInfoBase &EIB) {
- CI.getDiagnostics().Report(diag::err_cannot_open_file)
- << F.Filename << EIB.message();
- });
- linkModules.clear();
- return true;
- }
-
- linkModules.push_back({std::move(ModuleOrErr.get()), F.PropagateAttrs,
- F.Internalize, F.LinkFlags});
- }
-
- return false;
- }
-
bool linkInModules(llvm::Module &M) {
for (auto &LM : linkModules) {
- assert(LM.module && "LinkModule does not actually have a module");
+ assert(LM.Module && "LinkModule does not actually have a module");
bool Err;
- if (LM.internalize) {
+ if (LM.Internalize) {
Err = llvm::Linker::linkModules(
- M, std::move(LM.module), LM.linkFlags,
+ 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 {
- Err = llvm::Linker::linkModules(M, std::move(LM.module), LM.linkFlags);
+ Err = llvm::Linker::linkModules(M, std::move(LM.Module), LM.LinkFlags);
}
if (Err)
diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt
index 50d6ea7108ce1..b2d2fc1621693 100644
--- a/clang/lib/CIR/FrontendAction/CMakeLists.txt
+++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt
@@ -1,5 +1,7 @@
set(LLVM_LINK_COMPONENTS
Core
+ ipo
+ Linker
Support
)
diff --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h
index b7bbb81074836..eeac13bd42379 100644
--- a/clang/lib/CodeGen/BackendConsumer.h
+++ b/clang/lib/CodeGen/BackendConsumer.h
@@ -11,6 +11,7 @@
#include "clang/CodeGen/BackendUtil.h"
#include "clang/CodeGen/CodeGenAction.h"
+#include "clang/CodeGen/ModuleLinker.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/Support/Timer.h"
@@ -25,8 +26,6 @@ class CodeGenAction;
class CoverageSourceInfo;
class BackendConsumer : public ASTConsumer {
- using LinkModule = CodeGenAction::LinkModule;
-
virtual void anchor();
CompilerInstance &CI;
DiagnosticsEngine &Diags;
diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt
index 434781b3c4f02..f1d32fbab1a0f 100644
--- a/clang/lib/CodeGen/CMakeLists.txt
+++ b/clang/lib/CodeGen/CMakeLists.txt
@@ -114,6 +114,7 @@ add_clang_library(clangCodeGen
MacroPPCallbacks.cpp
MicrosoftCXXABI.cpp
ModuleBuilder.cpp
+ ModuleLinker.cpp
ObjectFilePCHContainerWriter.cpp
PatternInit.cpp
QualTypeMapper.cpp
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index 73bf49b1a8046..fb1a410ce1761 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -911,36 +911,6 @@ CodeGenAction::~CodeGenAction() {
delete VMContext;
}
-bool CodeGenAction::loadLinkModules(CompilerInstance &CI) {
- if (!LinkModules.empty())
- return false;
-
- for (const CodeGenOptions::BitcodeFileToLink &F :
- CI.getCodeGenOpts().LinkBitcodeFiles) {
- auto BCBuf = CI.getFileManager().getBufferForFile(F.Filename);
- if (!BCBuf) {
- CI.getDiagnostics().Report(diag::err_cannot_open_file)
- << F.Filename << BCBuf.getError().message();
- LinkModules.clear();
- return true;
- }
-
- Expected<std::unique_ptr<llvm::Module>> ModuleOrErr =
- getOwningLazyBitcodeModule(std::move(*BCBuf), *VMContext);
- if (!ModuleOrErr) {
- handleAllErrors(ModuleOrErr.takeError(), [&](ErrorInfoBase &EIB) {
- CI.getDiagnostics().Report(diag::err_cannot_open_file)
- << F.Filename << EIB.message();
- });
- LinkModules.clear();
- return true;
- }
- LinkModules.push_back({std::move(ModuleOrErr.get()), F.PropagateAttrs,
- F.Internalize, F.LinkFlags});
- }
- return false;
-}
-
bool CodeGenAction::hasIRSupport() const { return true; }
void CodeGenAction::EndSourceFileAction() {
@@ -1004,7 +974,7 @@ CodeGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
return nullptr;
// Load bitcode modules to link with, if we need to.
- if (loadLinkModules(CI))
+ if (clang::loadLinkModules(CI, *VMContext, LinkModules))
return nullptr;
CoverageSourceInfo *CoverageInfo = nullptr;
@@ -1082,7 +1052,7 @@ CodeGenAction::loadModule(MemoryBufferRef MBRef) {
}
// Load bitcode modules to link with, if we need to.
- if (loadLinkModules(CI))
+ if (clang::loadLinkModules(CI, *VMContext, LinkModules))
return nullptr;
// Handle textual IR and bitcode file with one single module.
diff --git a/clang/lib/CodeGen/ModuleLinker.cpp b/clang/lib/CodeGen/ModuleLinker.cpp
new file mode 100644
index 0000000000000..fcf13f5bd59b3
--- /dev/null
+++ b/clang/lib/CodeGen/ModuleLinker.cpp
@@ -0,0 +1,50 @@
+//===--- ModuleLinker.cpp - Shared bitcode link helpers -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/CodeGen/ModuleLinker.h"
+
+#include "clang/Basic/CodeGenOptions.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/IR/Module.h"
+
+using namespace clang;
+
+bool clang::loadLinkModules(CompilerInstance &CI, llvm::LLVMContext &Ctx,
+ llvm::SmallVectorImpl<LinkModule> &LinkModules) {
+ if (!LinkModules.empty())
+ return false;
+
+ for (const CodeGenOptions::BitcodeFileToLink &F :
+ CI.getCodeGenOpts().LinkBitcodeFiles) {
+ auto BCBuf = CI.getFileManager().getBufferForFile(F.Filename);
+ if (!BCBuf) {
+ CI.getDiagnostics().Report(diag::err_cannot_open_file)
+ << F.Filename << BCBuf.getError().message();
+ LinkModules.clear();
+ return true;
+ }
+
+ llvm::Expected<std::unique_ptr<llvm::Module>> ModuleOrErr =
+ llvm::getOwningLazyBitcodeModule(std::move(*BCBuf), Ctx);
+ if (!ModuleOrErr) {
+ llvm::handleAllErrors(ModuleOrErr.takeError(),
+ [&](llvm::ErrorInfoBase &EIB) {
+ CI.getDiagnostics().Report(
+ diag::err_cannot_open_file)
+ << F.Filename << EIB.message();
+ });
+ LinkModules.clear();
+ return true;
+ }
+
+ LinkModules.push_back({std::move(ModuleOrErr.get()), F.PropagateAttrs,
+ F.Internalize, F.LinkFlags});
+ }
+ return false;
+}
>From 91f4a07c91a898423b085507289cf92ab92b5a33 Mon Sep 17 00:00:00 2001
From: y <konstantinos.parasyris at intel.com>
Date: Thu, 14 May 2026 08:33:40 -0700
Subject: [PATCH 2/2] [CIR] Move bitcode-link state from CIRGenConsumer to
CIRGenAction
Match the classic CodeGenAction architecture: the action owns the
LLVMContext and the vector of LinkModules. CIRGenConsumer receives them
by reference. Bitcode files are loaded in BeginSourceFileAction (matching
OG timing) so missing-file errors fire before the translation unit is
parsed.
Prepares CIRGen for driver-side offload work (CUDA/HIP split-compilation)
that needs to inspect the llvm::Module after lowering. The previous
shape kept the LLVMContext on the stack inside HandleTranslationUnit,
making it impossible to hand the module back to the driver.
No behavior change for existing callers.
---
.../clang/CIR/FrontendAction/CIRGenAction.h | 11 +++++++
clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 30 ++++++++++++-------
2 files changed, 30 insertions(+), 11 deletions(-)
diff --git a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h
index 99495f4718c5f..7f8c90f285521 100644
--- a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h
+++ b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h
@@ -9,11 +9,17 @@
#ifndef LLVM_CLANG_CIR_CIRGENACTION_H
#define LLVM_CLANG_CIR_CIRGENACTION_H
+#include "clang/CodeGen/ModuleLinker.h"
#include "clang/Frontend/FrontendAction.h"
+#include "llvm/ADT/SmallVector.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/OwningOpRef.h"
+namespace llvm {
+class LLVMContext;
+} // namespace llvm
+
namespace mlir {
class MLIRContext;
class ModuleOp;
@@ -39,9 +45,14 @@ class CIRGenAction : public clang::ASTFrontendAction {
mlir::MLIRContext *MLIRCtx;
+ std::unique_ptr<llvm::LLVMContext> VMContext;
+ llvm::SmallVector<::clang::LinkModule, 4> LinkModules;
+
protected:
CIRGenAction(OutputType Action, mlir::MLIRContext *MLIRCtx = nullptr);
+ bool BeginSourceFileAction(clang::CompilerInstance &CI) override;
+
std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &CI,
llvm::StringRef InFile) override;
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index 1e39ae79eec39..9a74ba93e3152 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -21,6 +21,7 @@
#include "llvm/ADT/StringSet.h"
#include "llvm/IR/DiagnosticHandler.h"
#include "llvm/IR/GlobalValue.h"
+#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Linker/Linker.h"
#include "llvm/Support/Path.h"
@@ -77,16 +78,20 @@ class CIRGenConsumer : public clang::ASTConsumer {
const FrontendOptions &FEOptions;
CodeGenOptions &CGO;
- SmallVector<::clang::LinkModule, 4> linkModules;
+ llvm::LLVMContext &LLVMCtx;
+ SmallVectorImpl<::clang::LinkModule> &LinkModules;
public:
CIRGenConsumer(CIRGenAction::OutputType Action, CompilerInstance &CI,
- CodeGenOptions &CGO, std::unique_ptr<raw_pwrite_stream> OS)
+ CodeGenOptions &CGO, std::unique_ptr<raw_pwrite_stream> OS,
+ llvm::LLVMContext &LLVMCtx,
+ SmallVectorImpl<::clang::LinkModule> &LinkModules)
: Action(Action), CI(CI), OutputStream(std::move(OS)),
FS(&CI.getVirtualFileSystem()),
Gen(std::make_unique<CIRGenerator>(CI.getDiagnostics(), std::move(FS),
CI.getCodeGenOpts())),
- FEOptions(CI.getFrontendOpts()), CGO(CGO) {}
+ FEOptions(CI.getFrontendOpts()), CGO(CGO), LLVMCtx(LLVMCtx),
+ LinkModules(LinkModules) {}
void Initialize(ASTContext &Ctx) override {
assert(!Context && "initialized multiple times");
@@ -168,10 +173,6 @@ class CIRGenConsumer : public clang::ASTConsumer {
MlirModule->print(out);
}
- llvm::LLVMContext LLVMCtx;
- if (clang::loadLinkModules(CI, LLVMCtx, linkModules))
- return;
-
std::unique_ptr<llvm::Module> LLVMModule =
lowerFromCIRToLLVMIR(MlirModule, LLVMCtx, mlirSaveTempsOutFile,
&CI.getVirtualFileSystem());
@@ -189,7 +190,7 @@ class CIRGenConsumer : public clang::ASTConsumer {
}
bool linkInModules(llvm::Module &M) {
- for (auto &LM : linkModules) {
+ for (auto &LM : LinkModules) {
assert(LM.Module && "LinkModule does not actually have a module");
bool Err;
@@ -209,7 +210,7 @@ class CIRGenConsumer : public clang::ASTConsumer {
return true;
}
- linkModules.clear();
+ LinkModules.clear();
return false;
}
@@ -235,10 +236,17 @@ class CIRGenConsumer : public clang::ASTConsumer {
void CIRGenConsumer::anchor() {}
CIRGenAction::CIRGenAction(OutputType Act, mlir::MLIRContext *MLIRCtx)
- : MLIRCtx(MLIRCtx ? MLIRCtx : new mlir::MLIRContext), Action(Act) {}
+ : MLIRCtx(MLIRCtx ? MLIRCtx : new mlir::MLIRContext),
+ VMContext(std::make_unique<llvm::LLVMContext>()), Action(Act) {}
CIRGenAction::~CIRGenAction() { MLIRMod.release(); }
+bool CIRGenAction::BeginSourceFileAction(CompilerInstance &CI) {
+ if (clang::loadLinkModules(CI, *VMContext, LinkModules))
+ return false;
+ return ASTFrontendAction::BeginSourceFileAction(CI);
+}
+
static std::unique_ptr<raw_pwrite_stream>
getOutputStream(CompilerInstance &CI, StringRef InFile,
CIRGenAction::OutputType Action) {
@@ -265,7 +273,7 @@ CIRGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
Out = getOutputStream(CI, InFile, Action);
auto Result = std::make_unique<cir::CIRGenConsumer>(
- Action, CI, CI.getCodeGenOpts(), std::move(Out));
+ Action, CI, CI.getCodeGenOpts(), std::move(Out), *VMContext, LinkModules);
return Result;
}
More information about the llvm-branch-commits
mailing list