[clang] [CIR] Factor CIR module output emission out of CIRGenConsumer (PR #229441)
Konstantinos Parasyris via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 13 17:48:28 PDT 2026
https://github.com/koparasy updated https://github.com/llvm/llvm-project/pull/229441
>From 61cd8ccff15407b06ed24c2f44901f5766318579 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Tue, 6 Oct 2026 07:42:16 -0700
Subject: [PATCH 1/2] [CIR] Make the post-CIRGen emission path independent of
ASTContext
NFC. The SYCL-host and CUDA/OpenMP-offload embed decisions now read
CI.getLangOpts() instead of ASTContext::getLangOpts(); both refer to
the same object, and CI's is populated for .cir input too.
This is preliminary work towards reusing code when reading `cir` and
emitting its output.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply at anthropic.com>
---
clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 322 ++++++++++--------
1 file changed, 179 insertions(+), 143 deletions(-)
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index 71a7a17146af3..12dbbf05bd3e6 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -25,6 +25,7 @@
#include "clang/CodeGen/ModuleLinker.h"
#include "clang/CodeGenUtils/BackendDiagnosticHandler.h"
#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSet.h"
@@ -84,6 +85,176 @@ static void printCIRModule(mlir::ModuleOp MLIRModule, raw_ostream &OS) {
MLIRModule->print(OS, Flags);
}
+// TODO: share with BackendConsumer::LinkInModules once the rest of the
+// linking logic (not just diagnostics) is unified.
+static bool linkInModules(CompilerInstance &CI, llvm::Module &M,
+ SmallVectorImpl<::clang::LinkModule> &LinkModules,
+ BackendDiagnosticConsumer &DiagConsumer) {
+ for (auto &LM : LinkModules) {
+ assert(LM.Module && "LinkModule does not actually have a module");
+
+ if (LM.PropagateAttrs)
+ for (llvm::Function &F : *LM.Module) {
+ if (F.isIntrinsic())
+ continue;
+ clang::CodeGen::mergeDefaultFunctionDefinitionAttributes(
+ F, CI.getCodeGenOpts(), CI.getLangOpts(), CI.getTargetOpts(),
+ LM.Internalize);
+ }
+
+ DiagConsumer.setCurLinkModule(LM.Module.get());
+ bool Err;
+ if (LM.Internalize) {
+ Err = 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 {
+ Err = llvm::Linker::linkModules(M, std::move(LM.Module), LM.LinkFlags);
+ }
+
+ if (Err)
+ return true;
+ }
+
+ LinkModules.clear();
+ return false;
+}
+
+// Reads the device binary named by -foffload-include-binary and embeds it
+// into the host module. wrapSYCLBinaries also appends the registration ctor
+// at priority 101 when no registration-function out-param is supplied.
+static void embedSYCLDeviceBinary(CompilerInstance &CI, llvm::Module &M) {
+ StringRef fileName = CI.getCodeGenOpts().OffloadBinaryToEmbedFile;
+ auto bufferOrErr = CI.getVirtualFileSystem().getBufferForFile(fileName);
+ if (std::error_code ec = bufferOrErr.getError()) {
+ CI.getDiagnostics().Report(diag::err_cannot_open_file)
+ << fileName << ec.message();
+ return;
+ }
+ std::unique_ptr<llvm::MemoryBuffer> buffer = std::move(bufferOrErr.get());
+ if (llvm::Error err = llvm::offloading::wrapSYCLBinaries(
+ M, ArrayRef<char>(buffer->getBufferStart(), buffer->getBufferSize()),
+ llvm::offloading::SYCLJITOptions(), /*IsFinalizedImage=*/true)) {
+ CI.getDiagnostics().Report(diag::err_fe_error_backend)
+ << llvm::toString(std::move(err));
+ return;
+ }
+}
+
+// Emits the output requested by \p Action for \p MlirModule, a CIR module that
+// has been through the CIR-to-CIR pipeline. Language facts are read from the
+// module's #cir.lowering_lang_options and target facts from \p CI, so this
+// does not need an ASTContext and can also run on CIR loaded from a file.
+// \p AddFunctionSourceLocations, if set, runs on the lowered LLVM module
+// before linking.
+static void emitCIRModuleOutput(
+ CIRGenAction::OutputType Action, CompilerInstance &CI,
+ mlir::ModuleOp MlirModule, llvm::LLVMContext &LLVMCtx,
+ SmallVectorImpl<::clang::LinkModule> &LinkModules,
+ BackendDiagnosticConsumer &DiagConsumer,
+ std::unique_ptr<raw_pwrite_stream> OutputStream,
+ llvm::function_ref<void(llvm::Module &)> AddFunctionSourceLocations = {}) {
+ CodeGenOptions &CGO = CI.getCodeGenOpts();
+ switch (Action) {
+ case CIRGenAction::OutputType::EmitCIR:
+ if (OutputStream && MlirModule)
+ printCIRModule(MlirModule, *OutputStream);
+ break;
+ case CIRGenAction::OutputType::EmitCIRBC:
+ if (OutputStream && MlirModule &&
+ failed(mlir::writeBytecodeToFile(MlirModule, *OutputStream)) &&
+ !CI.getDiagnostics().hasErrorOccurred())
+ CI.getDiagnostics().Report(diag::err_cir_bc_write_failed);
+ break;
+ case CIRGenAction::OutputType::EmitLLVM:
+ case CIRGenAction::OutputType::EmitBC:
+ case CIRGenAction::OutputType::EmitObj:
+ case CIRGenAction::OutputType::EmitAssembly: {
+ StringRef saveTempsPrefix = CGO.SaveTempsFilePrefix;
+ std::string cirSaveTempsOutFile, mlirSaveTempsOutFile;
+ if (!saveTempsPrefix.empty()) {
+ SmallString<128> stem(saveTempsPrefix);
+ llvm::sys::path::replace_extension(stem, "cir");
+ cirSaveTempsOutFile = std::string(stem);
+ llvm::sys::path::replace_extension(stem, "mlir");
+ mlirSaveTempsOutFile = std::string(stem);
+ }
+
+ if (!cirSaveTempsOutFile.empty()) {
+ std::error_code ec;
+ llvm::raw_fd_ostream out(cirSaveTempsOutFile, ec);
+ if (!ec)
+ MlirModule->print(out);
+ }
+
+ // If errors occurred during codegen, stop before running the backend.
+ if (CI.getDiagnostics().hasErrorOccurred())
+ return;
+
+ // Read the lowering facts before lowering rewrites the module. Without the
+ // attribute (e.g. hand-written CIR) every fact defaults to off.
+ auto LangOptsAttr = MlirModule->getAttrOfType<cir::LoweringLangOptionsAttr>(
+ cir::CIRDialect::getLoweringLangOptionsAttrName());
+ const bool EnableOpenMP = LangOptsAttr && LangOptsAttr.getOpenmp();
+
+ // Route LLVM backend diagnostics (optimization remarks, unsupported
+ // features, inline-asm errors, etc.) through clang diagnostics for
+ // the remainder of the LLVM-emitting pipeline.
+ std::unique_ptr<llvm::DiagnosticHandler> OldDiagnosticHandler =
+ LLVMCtx.getDiagnosticHandler();
+ llvm::scope_exit RestoreDiagnosticHandler([&]() {
+ LLVMCtx.setDiagnosticHandler(std::move(OldDiagnosticHandler));
+ });
+ LLVMCtx.setDiagnosticHandler(DiagConsumer.createDiagnosticHandler());
+
+ std::unique_ptr<llvm::Module> LLVMModule =
+ lowerFromCIRToLLVMIR(MlirModule, LLVMCtx, EnableOpenMP,
+ mlirSaveTempsOutFile, &CI.getVirtualFileSystem());
+
+ LLVMModule->setDataLayout(CI.getTarget().getDataLayoutString());
+
+ if (AddFunctionSourceLocations)
+ AddFunctionSourceLocations(*LLVMModule);
+
+ if (linkInModules(CI, *LLVMModule, LinkModules, DiagConsumer))
+ return;
+
+ const LangOptions &LangOpts = CI.getLangOpts();
+
+ // Embed the offloaded SYCL device binary into the host module.
+ if (LangOpts.SYCLIsHost && !CGO.OffloadBinaryToEmbedFile.empty())
+ embedSYCLDeviceBinary(CI, *LLVMModule);
+
+ // CUDA, HIP and OpenMP offloading rely on host-side offload entries that
+ // are not emitted on the ClangIR path yet, so embedding their device
+ // objects would produce a host object that cannot be registered.
+ // LangOptions::CUDA is also set for HIP.
+ if (!CGO.OffloadObjects.empty() &&
+ (LangOpts.CUDA || !LangOpts.OMPTargetTriples.empty())) {
+ DiagnosticsEngine &Diags = CI.getDiagnostics();
+ Diags.Report(Diags.getCustomDiagID(
+ DiagnosticsEngine::Error,
+ "ClangIR code gen Not Yet Implemented: embedding offload objects "
+ "for CUDA, HIP or OpenMP offloading"));
+ return;
+ }
+
+ // If there is device offloading code embed it in the host now.
+ EmbedObject(LLVMModule.get(), CGO, CI.getVirtualFileSystem(),
+ CI.getDiagnostics());
+
+ BackendAction BEAction = getBackendActionFromOutputType(Action);
+ emitBackendOutput(CI, CGO, LLVMModule.get(), BEAction,
+ CI.getVirtualFileSystemPtr(), std::move(OutputStream));
+ break;
+ }
+ }
+}
+
class CIRGenConsumer : public clang::ASTConsumer {
virtual void anchor();
@@ -191,152 +362,17 @@ class CIRGenConsumer : public clang::ASTConsumer {
}
}
- switch (Action) {
- case CIRGenAction::OutputType::EmitCIR:
- if (OutputStream && MlirModule)
- printCIRModule(MlirModule, *OutputStream);
- break;
- case CIRGenAction::OutputType::EmitCIRBC:
- if (OutputStream && MlirModule &&
- failed(mlir::writeBytecodeToFile(MlirModule, *OutputStream)) &&
- !CI.getDiagnostics().hasErrorOccurred())
- CI.getDiagnostics().Report(diag::err_cir_bc_write_failed);
- break;
- case CIRGenAction::OutputType::EmitLLVM:
- case CIRGenAction::OutputType::EmitBC:
- case CIRGenAction::OutputType::EmitObj:
- case CIRGenAction::OutputType::EmitAssembly: {
- StringRef saveTempsPrefix = CGO.SaveTempsFilePrefix;
- std::string cirSaveTempsOutFile, mlirSaveTempsOutFile;
- if (!saveTempsPrefix.empty()) {
- SmallString<128> stem(saveTempsPrefix);
- llvm::sys::path::replace_extension(stem, "cir");
- cirSaveTempsOutFile = std::string(stem);
- llvm::sys::path::replace_extension(stem, "mlir");
- mlirSaveTempsOutFile = std::string(stem);
- }
-
- if (!cirSaveTempsOutFile.empty()) {
- std::error_code ec;
- llvm::raw_fd_ostream out(cirSaveTempsOutFile, ec);
- if (!ec)
- MlirModule->print(out);
- }
-
- // If errors occurred during codegen, stop before running the backend.
- if (CI.getDiagnostics().hasErrorOccurred())
- return;
-
- // Route LLVM backend diagnostics (optimization remarks, unsupported
- // features, inline-asm errors, etc.) through clang diagnostics for
- // the remainder of the LLVM-emitting pipeline.
- std::unique_ptr<llvm::DiagnosticHandler> OldDiagnosticHandler =
- LLVMCtx.getDiagnosticHandler();
- llvm::scope_exit RestoreDiagnosticHandler([&]() {
- LLVMCtx.setDiagnosticHandler(std::move(OldDiagnosticHandler));
- });
- LLVMCtx.setDiagnosticHandler(DiagConsumer.createDiagnosticHandler());
-
- std::unique_ptr<llvm::Module> LLVMModule = lowerFromCIRToLLVMIR(
- MlirModule, LLVMCtx, C.getLangOpts().OpenMP, mlirSaveTempsOutFile,
- &CI.getVirtualFileSystem());
-
- LLVMModule->setDataLayout(C.getTargetInfo().getDataLayoutString());
-
- for (llvm::Function &F : LLVMModule->functions())
+ // CIRGen-only step: map the emitted LLVM functions back to their source
+ // declarations so backend diagnostics point at the original source.
+ auto AddFunctionSourceLocations = [&](llvm::Module &LLVMModule) {
+ for (llvm::Function &F : LLVMModule.functions())
if (const Decl *FD = Gen->getDeclForMangledName(F.getName()))
DiagConsumer.addFunctionSourceLocation(
F.getName(), FD->getASTContext().getFullLoc(FD->getLocation()));
-
- if (linkInModules(*LLVMModule))
- return;
-
- // Embed the offloaded SYCL device binary into the host module.
- if (C.getLangOpts().SYCLIsHost && !CGO.OffloadBinaryToEmbedFile.empty())
- embedSYCLDeviceBinary(*LLVMModule);
-
- // CUDA, HIP and OpenMP offloading rely on host-side offload entries that
- // are not emitted on the ClangIR path yet, so embedding their device
- // objects would produce a host object that cannot be registered.
- const LangOptions &LangOpts = C.getLangOpts();
- if (!CGO.OffloadObjects.empty() &&
- (LangOpts.CUDA || !LangOpts.OMPTargetTriples.empty())) {
- DiagnosticsEngine &Diags = CI.getDiagnostics();
- Diags.Report(Diags.getCustomDiagID(
- DiagnosticsEngine::Error,
- "ClangIR code gen Not Yet Implemented: embedding offload objects "
- "for CUDA, HIP or OpenMP offloading"));
- return;
- }
-
- // If there is device offloading code embed it in the host now.
- EmbedObject(LLVMModule.get(), CGO, CI.getVirtualFileSystem(),
- CI.getDiagnostics());
-
- BackendAction BEAction = getBackendActionFromOutputType(Action);
- emitBackendOutput(CI, CI.getCodeGenOpts(), LLVMModule.get(), BEAction, FS,
- std::move(OutputStream));
- break;
- }
- }
- }
-
- // TODO: share with BackendConsumer::LinkInModules once the rest of the
- // linking logic (not just diagnostics) is unified.
- bool linkInModules(llvm::Module &M) {
- for (auto &LM : LinkModules) {
- assert(LM.Module && "LinkModule does not actually have a module");
-
- if (LM.PropagateAttrs)
- for (llvm::Function &F : *LM.Module) {
- if (F.isIntrinsic())
- continue;
- clang::CodeGen::mergeDefaultFunctionDefinitionAttributes(
- F, CGO, CI.getLangOpts(), CI.getTargetOpts(), LM.Internalize);
- }
-
- DiagConsumer.setCurLinkModule(LM.Module.get());
- bool Err;
- if (LM.Internalize) {
- Err = 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 {
- Err = llvm::Linker::linkModules(M, std::move(LM.Module), LM.LinkFlags);
- }
-
- if (Err)
- return true;
- }
-
- LinkModules.clear();
- return false;
- }
-
- // Reads the device binary named by -foffload-include-binary and embeds it
- // into the host module. wrapSYCLBinaries also appends the registration ctor
- // at priority 101 when no registration-function out-param is supplied.
- void embedSYCLDeviceBinary(llvm::Module &M) {
- StringRef fileName = CGO.OffloadBinaryToEmbedFile;
- auto bufferOrErr = CI.getVirtualFileSystem().getBufferForFile(fileName);
- if (std::error_code ec = bufferOrErr.getError()) {
- CI.getDiagnostics().Report(diag::err_cannot_open_file)
- << fileName << ec.message();
- return;
- }
- std::unique_ptr<llvm::MemoryBuffer> buffer = std::move(bufferOrErr.get());
- if (llvm::Error err = llvm::offloading::wrapSYCLBinaries(
- M,
- ArrayRef<char>(buffer->getBufferStart(), buffer->getBufferSize()),
- llvm::offloading::SYCLJITOptions(), /*IsFinalizedImage=*/true)) {
- CI.getDiagnostics().Report(diag::err_fe_error_backend)
- << llvm::toString(std::move(err));
- return;
- }
+ };
+ emitCIRModuleOutput(Action, CI, MlirModule, LLVMCtx, LinkModules,
+ DiagConsumer, std::move(OutputStream),
+ AddFunctionSourceLocations);
}
void HandleTagDeclDefinition(TagDecl *D) override {
>From 1353f7f61c0879cc626e66341aeb6c60effb3b17 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Tue, 13 Oct 2026 17:35:48 -0700
Subject: [PATCH 2/2] [clang][CIR] Share linkInModules between BackendConsumer
and CIRGenAction
BackendConsumer::LinkInModules and the ClangIR copy in CIRGenAction.cpp
were near-identical. Move the logic to a free clang::linkInModules in
ModuleLinker.h/.cpp, next to LinkModule and loadLinkModules, and have
both paths call it. BackendConsumer::LinkInModules now forwards to it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply at anthropic.com>
---
clang/include/clang/CodeGen/ModuleLinker.h | 11 +++++
clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 45 +------------------
clang/lib/CodeGen/CodeGenAction.cpp | 37 +--------------
clang/lib/CodeGen/ModuleLinker.cpp | 45 +++++++++++++++++++
4 files changed, 60 insertions(+), 78 deletions(-)
diff --git a/clang/include/clang/CodeGen/ModuleLinker.h b/clang/include/clang/CodeGen/ModuleLinker.h
index 4f94e6057f1e8..f24b40f7cba23 100644
--- a/clang/include/clang/CodeGen/ModuleLinker.h
+++ b/clang/include/clang/CodeGen/ModuleLinker.h
@@ -19,6 +19,7 @@ class Module;
} // namespace llvm
namespace clang {
+class BackendDiagnosticConsumer;
class CodeGenOptions;
class CompilerInstance;
class LangOptions;
@@ -39,6 +40,16 @@ struct LinkModule {
bool loadLinkModules(CompilerInstance &CI, llvm::LLVMContext &Ctx,
llvm::SmallVectorImpl<LinkModule> &LinkModules);
+/// Link every module in \p LinkModules into \p M, propagating default
+/// function attributes and internalizing as each entry requests. Linker
+/// diagnostics are attributed to the module being linked through
+/// \p DiagConsumer. Returns true on error. Clears \p LinkModules on success.
+bool linkInModules(llvm::Module &M,
+ llvm::SmallVectorImpl<LinkModule> &LinkModules,
+ const CodeGenOptions &CodeGenOpts,
+ const LangOptions &LangOpts, const TargetOptions &TargetOpts,
+ BackendDiagnosticConsumer &DiagConsumer);
+
namespace CodeGen {
/// Adds attributes to \p F according to our \p CodeGenOpts and \p LangOpts, as
/// though we had emitted it ourselves. We remove any attributes on F that
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index 12dbbf05bd3e6..8ae7b7824abf9 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -28,18 +28,15 @@
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringSet.h"
#include "llvm/Frontend/Offloading/OffloadWrapper.h"
#include "llvm/IR/DiagnosticHandler.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
-#include "llvm/Linker/Linker.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/IPO/Internalize.h"
using namespace cir;
using namespace clang;
@@ -85,45 +82,6 @@ static void printCIRModule(mlir::ModuleOp MLIRModule, raw_ostream &OS) {
MLIRModule->print(OS, Flags);
}
-// TODO: share with BackendConsumer::LinkInModules once the rest of the
-// linking logic (not just diagnostics) is unified.
-static bool linkInModules(CompilerInstance &CI, llvm::Module &M,
- SmallVectorImpl<::clang::LinkModule> &LinkModules,
- BackendDiagnosticConsumer &DiagConsumer) {
- for (auto &LM : LinkModules) {
- assert(LM.Module && "LinkModule does not actually have a module");
-
- if (LM.PropagateAttrs)
- for (llvm::Function &F : *LM.Module) {
- if (F.isIntrinsic())
- continue;
- clang::CodeGen::mergeDefaultFunctionDefinitionAttributes(
- F, CI.getCodeGenOpts(), CI.getLangOpts(), CI.getTargetOpts(),
- LM.Internalize);
- }
-
- DiagConsumer.setCurLinkModule(LM.Module.get());
- bool Err;
- if (LM.Internalize) {
- Err = 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 {
- Err = llvm::Linker::linkModules(M, std::move(LM.Module), LM.LinkFlags);
- }
-
- if (Err)
- return true;
- }
-
- LinkModules.clear();
- return false;
-}
-
// Reads the device binary named by -foffload-include-binary and embeds it
// into the host module. wrapSYCLBinaries also appends the registration ctor
// at priority 101 when no registration-function out-param is supplied.
@@ -220,7 +178,8 @@ static void emitCIRModuleOutput(
if (AddFunctionSourceLocations)
AddFunctionSourceLocations(*LLVMModule);
- if (linkInModules(CI, *LLVMModule, LinkModules, DiagConsumer))
+ if (linkInModules(*LLVMModule, LinkModules, CGO, CI.getLangOpts(),
+ CI.getTargetOpts(), DiagConsumer))
return;
const LangOptions &LangOpts = CI.getLangOpts();
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index 1abd8b0455a19..f9687eb904aaf 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -40,7 +40,6 @@
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/LTO/LTOBackend.h"
-#include "llvm/Linker/Linker.h"
#include "llvm/Pass.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -49,7 +48,6 @@
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/ToolOutputFile.h"
-#include "llvm/Transforms/IPO/Internalize.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include <optional>
@@ -173,39 +171,8 @@ void BackendConsumer::HandleInterestingDecl(DeclGroupRef D) {
// Links each entry in LinkModules into our module. Returns true on error.
bool BackendConsumer::LinkInModules(llvm::Module *M) {
- for (auto &LM : LinkModules) {
- assert(LM.Module && "LinkModule does not actually have a module");
-
- if (LM.PropagateAttrs)
- for (Function &F : *LM.Module) {
- // Skip intrinsics. Keep consistent with how intrinsics are created
- // in LLVM IR.
- if (F.isIntrinsic())
- continue;
- CodeGen::mergeDefaultFunctionDefinitionAttributes(
- F, CodeGenOpts, LangOpts, TargetOpts, LM.Internalize);
- }
-
- DiagConsumer.setCurLinkModule(LM.Module.get());
- bool Err;
-
- if (LM.Internalize) {
- Err = Linker::linkModules(
- *M, std::move(LM.Module), LM.LinkFlags,
- [](llvm::Module &M, const llvm::StringSet<> &GVS) {
- internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) {
- return !GV.hasName() || (GVS.count(GV.getName()) == 0);
- });
- });
- } else
- Err = Linker::linkModules(*M, std::move(LM.Module), LM.LinkFlags);
-
- if (Err)
- return true;
- }
-
- LinkModules.clear();
- return false; // success
+ return linkInModules(*M, LinkModules, CodeGenOpts, LangOpts, TargetOpts,
+ DiagConsumer);
}
void BackendConsumer::HandleTranslationUnit(ASTContext &C) {
diff --git a/clang/lib/CodeGen/ModuleLinker.cpp b/clang/lib/CodeGen/ModuleLinker.cpp
index 6c876276e344b..b50b412622f43 100644
--- a/clang/lib/CodeGen/ModuleLinker.cpp
+++ b/clang/lib/CodeGen/ModuleLinker.cpp
@@ -9,9 +9,13 @@
#include "clang/CodeGen/ModuleLinker.h"
#include "clang/Basic/CodeGenOptions.h"
+#include "clang/CodeGenUtils/BackendDiagnosticHandler.h"
#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/IR/Module.h"
+#include "llvm/Linker/Linker.h"
+#include "llvm/Transforms/IPO/Internalize.h"
using namespace clang;
@@ -47,3 +51,44 @@ bool clang::loadLinkModules(CompilerInstance &CI, llvm::LLVMContext &Ctx,
}
return false;
}
+
+bool clang::linkInModules(llvm::Module &M,
+ llvm::SmallVectorImpl<LinkModule> &LinkModules,
+ const CodeGenOptions &CodeGenOpts,
+ const LangOptions &LangOpts,
+ const TargetOptions &TargetOpts,
+ BackendDiagnosticConsumer &DiagConsumer) {
+ for (LinkModule &LM : LinkModules) {
+ assert(LM.Module && "LinkModule does not actually have a module");
+
+ if (LM.PropagateAttrs)
+ for (llvm::Function &F : *LM.Module) {
+ // Skip intrinsics. Keep consistent with how intrinsics are created
+ // in LLVM IR.
+ if (F.isIntrinsic())
+ continue;
+ CodeGen::mergeDefaultFunctionDefinitionAttributes(
+ F, CodeGenOpts, LangOpts, TargetOpts, LM.Internalize);
+ }
+
+ DiagConsumer.setCurLinkModule(LM.Module.get());
+ bool Err;
+
+ if (LM.Internalize) {
+ Err = 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
+ Err = llvm::Linker::linkModules(M, std::move(LM.Module), LM.LinkFlags);
+
+ if (Err)
+ return true;
+ }
+
+ LinkModules.clear();
+ return false; // success
+}
More information about the cfe-commits
mailing list