https://github.com/AdityaSinha149 updated https://github.com/llvm/llvm-project/pull/229741
>From 0d02031ddda7c2250a2840a231a50e6d2d71db92 Mon Sep 17 00:00:00 2001 From: AdityaSinha149 <[email protected]> Date: Wed, 7 Oct 2026 17:19:51 +0530 Subject: [PATCH] [clang][CodeGen] Add reloadLinkModules to reload link modules for incremental compilation --- clang/include/clang/CodeGen/CodeGenAction.h | 3 + clang/lib/CodeGen/BackendConsumer.h | 8 + clang/lib/CodeGen/CodeGenAction.cpp | 9 ++ clang/unittests/CodeGen/CMakeLists.txt | 1 + .../CodeGen/ReloadLinkModulesTest.cpp | 137 ++++++++++++++++++ 5 files changed, 158 insertions(+) create mode 100644 clang/unittests/CodeGen/ReloadLinkModulesTest.cpp diff --git a/clang/include/clang/CodeGen/CodeGenAction.h b/clang/include/clang/CodeGen/CodeGenAction.h index 84fa4549d5033..e7d89306154d1 100644 --- a/clang/include/clang/CodeGen/CodeGenAction.h +++ b/clang/include/clang/CodeGen/CodeGenAction.h @@ -63,6 +63,9 @@ class CodeGenAction : public ASTFrontendAction { CodeGenerator *getCodeGenerator() const; + /// Reload the -mlink-builtin-bitcode modules into the backend consumer. + void reloadLinkModules(CompilerInstance &CI); + BackendConsumer *BEConsumer = nullptr; }; diff --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h index 5337a5f7a1b4a..490be03bf14f7 100644 --- a/clang/lib/CodeGen/BackendConsumer.h +++ b/clang/lib/CodeGen/BackendConsumer.h @@ -76,6 +76,14 @@ class BackendConsumer : public ASTConsumer { // Links each entry in LinkModules into our module. Returns true on error. bool LinkInModules(llvm::Module *M); + + /// Reloads a set of modules to link in. LinkInModules() consume the modules, + /// so incremental compilation(clang-repl) needs to reload the modules for + /// every TU. + void setLinkModules(SmallVector<LinkModule, 4> LMs) { + LinkModules = std::move(LMs); + } + /// Create an llvm::DiagnosticHandler that routes LLVM backend diagnostics /// through this consumer's clang diagnostics. std::unique_ptr<llvm::DiagnosticHandler> createDiagnosticHandler(); diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp index 1abd8b0455a19..0511ddf596247 100644 --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -365,6 +365,15 @@ CodeGenerator *CodeGenAction::getCodeGenerator() const { return BEConsumer->getCodeGenerator(); } +void CodeGenAction::reloadLinkModules(CompilerInstance &CI) { + if (!BEConsumer) + return; + SmallVector<LinkModule, 4> LMs; + if (clang::loadLinkModules(CI, *VMContext, LMs)) + return; + BEConsumer->setLinkModules(std::move(LMs)); +} + bool CodeGenAction::BeginSourceFileAction(CompilerInstance &CI) { if (CI.getFrontendOpts().GenReducedBMI) CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleInterface); diff --git a/clang/unittests/CodeGen/CMakeLists.txt b/clang/unittests/CodeGen/CMakeLists.txt index 7e2782e09117c..58a1b9a2a28c9 100644 --- a/clang/unittests/CodeGen/CMakeLists.txt +++ b/clang/unittests/CodeGen/CMakeLists.txt @@ -2,6 +2,7 @@ add_clang_unittest(ClangCodeGenTests BufferSourceTest.cpp CodeGenExternalTest.cpp DemangleTrapReasonInDebugInfo.cpp + ReloadLinkModulesTest.cpp TBAAMetadataTest.cpp CheckTargetFeaturesTest.cpp QualTypeMapperTest.cpp diff --git a/clang/unittests/CodeGen/ReloadLinkModulesTest.cpp b/clang/unittests/CodeGen/ReloadLinkModulesTest.cpp new file mode 100644 index 0000000000000..0162dc8e8a7e3 --- /dev/null +++ b/clang/unittests/CodeGen/ReloadLinkModulesTest.cpp @@ -0,0 +1,137 @@ +//=== unittests/CodeGen/ReloadLinkModulesTest.cpp - reloadLinkModules test ===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Tests CodeGenAction::reloadLinkModules. BackendConsumer::LinkInModules() +// consumes the -mlink-builtin-bitcode modules (it clears the list after +// linking), so a BackendConsumer that is reused across translation units -- +// as in clang-repl's incremental compilation -- would link the bitcode into +// the first module only. reloadLinkModules() reloads and reseeds those modules +// so each subsequent translation unit links them again. +// +//===----------------------------------------------------------------------===// + +#include "../../lib/CodeGen/BackendConsumer.h" + +#include "clang/Basic/CodeGenOptions.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendOptions.h" + +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TargetParser/Host.h" + +#include "gtest/gtest.h" + +using namespace clang; +using namespace llvm; + +namespace { + +// Writes a bitcode module defining `i32 @linked_fn()` to a temporary file and +// returns its path. The temporary is registered for removal by the caller. +static std::string writeLinkBitcodeFile(SmallVectorImpl<char> &PathStorage) { + LLVMContext Ctx; + auto M = std::make_unique<llvm::Module>("linked", Ctx); + + auto *FT = + llvm::FunctionType::get(llvm::Type::getInt32Ty(Ctx), /*isVarArg=*/false); + auto *F = Function::Create(FT, GlobalValue::ExternalLinkage, "linked_fn", *M); + auto *BB = BasicBlock::Create(Ctx, "entry", F); + IRBuilder<> B(BB); + B.CreateRet(ConstantInt::get(llvm::Type::getInt32Ty(Ctx), 42)); + + int FD = -1; + std::error_code EC = + sys::fs::createTemporaryFile("reload-link", "bc", FD, PathStorage); + EXPECT_FALSE(EC) << EC.message(); + { + raw_fd_ostream OS(FD, /*shouldClose=*/true); + WriteBitcodeToFile(*M, OS); + } + return std::string(PathStorage.data(), PathStorage.size()); +} + +// Returns true if the module contains a (non-declaration) definition of +// `linked_fn`, i.e. the bitcode library was linked in. +static bool hasLinkedFn(llvm::Module &M) { + Function *F = M.getFunction("linked_fn"); + return F && !F->isDeclaration(); +} + +TEST(ReloadLinkModulesTest, ReloadReseedsConsumedLinkModules) { + // A temporary bitcode file to be linked in via -mlink-builtin-bitcode. + SmallString<128> BCPath; + std::string BCFile = writeLinkBitcodeFile(BCPath); + FileRemover BCRemover(BCPath); + + // The LLVM context is owned by the test so that the scratch modules below + // live in the same context as the modules the action loads. + LLVMContext Context; + + CompilerInstance Compiler; + Compiler.getCodeGenOpts().LinkBitcodeFiles.push_back( + {BCFile, /*PropagateAttrs=*/false, /*Internalize=*/false, + /*LinkFlags=*/0}); + + Compiler.setVirtualFileSystem(vfs::getRealFileSystem()); + Compiler.createDiagnostics(); + + Triple HostTriple(Triple::normalize(sys::getProcessTriple())); + Compiler.getTargetOpts().Triple = HostTriple.getTriple(); + Compiler.setTarget(TargetInfo::CreateTargetInfo(Compiler.getDiagnostics(), + Compiler.getTargetOpts())); + ASSERT_TRUE(Compiler.hasTarget()); + + // EmitLLVMOnlyAction builds the module in memory and emits no output file, + // which is what clang-repl uses. + EmitLLVMOnlyAction Action(&Context); + + auto Buffer = MemoryBuffer::getMemBuffer("", "reload-link-input.c"); + FrontendInputFile Input(Buffer->getMemBufferRef(), InputKind(Language::C)); + + // BeginSourceFile creates the BackendConsumer, which loads the link modules. + // It does not parse (that happens in Execute), which is all we need. + ASSERT_TRUE(Action.BeginSourceFile(Compiler, Input)); + ASSERT_NE(Action.BEConsumer, nullptr); + + // First translation unit: the link module is present and gets linked in. + auto M1 = std::make_unique<llvm::Module>("tu1", Context); + ASSERT_FALSE(Action.BEConsumer->LinkInModules(M1.get())); + EXPECT_TRUE(hasLinkedFn(*M1)) << "first TU should link in the bitcode"; + + // Second translation unit without reloading: LinkInModules consumed the list, + // so nothing is linked in. This is the bug reloadLinkModules exists to fix. + auto M2 = std::make_unique<llvm::Module>("tu2", Context); + ASSERT_FALSE(Action.BEConsumer->LinkInModules(M2.get())); + EXPECT_FALSE(hasLinkedFn(*M2)) + << "link modules should have been consumed by the first TU"; + + // After reloading, the link module is reseeded and links in again. + Action.reloadLinkModules(Compiler); + auto M3 = std::make_unique<llvm::Module>("tu3", Context); + ASSERT_FALSE(Action.BEConsumer->LinkInModules(M3.get())); + EXPECT_TRUE(hasLinkedFn(*M3)) + << "reloadLinkModules should reseed the link modules"; + + Action.EndSourceFile(); +} + +} // namespace _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
