[lld] [codex] [lld][COFF] Emit ThinLTO indexes for unused lazy bitcode (PR #203321)
David Zbarsky via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 11 09:20:49 PDT 2026
https://github.com/dzbarsky updated https://github.com/llvm/llvm-project/pull/203321
>From 8c17ea518d40b3583ff4188a9e52ec8b4fbdc71f Mon Sep 17 00:00:00 2001
From: David Zbarsky <dzbarsky at gmail.com>
Date: Thu, 11 Jun 2026 12:09:19 -0400
Subject: [PATCH] [lld][COFF] Emit ThinLTO indexes for unused lazy bitcode
Distributed ThinLTO build systems determine their expected backend inputs
before the linker resolves /start-lib members. The COFF linker only asked
the LTO API to write index and imports files for selected bitcode inputs.
An unused BitcodeFile left lazy by /start-lib therefore produced neither
file, causing build systems that declare those outputs to fail.
Track lazy BitcodeFile instances and, before moving selected inputs into
the LTO API, emit a ModuleSummaryIndex with
SkipModuleByDistributedBackend and an empty imports file for each input
that remains lazy. This matches the existing ELF, Mach-O, and WebAssembly
behavior and preserves /start-lib archive selection semantics.
Extend thinlto-emit-imports.ll to select one lazy module, leave another
lazy, and verify that the unused module's index tells the distributed
backend to skip it.
---
lld/COFF/COFFLinkerContext.h | 1 +
lld/COFF/Driver.cpp | 9 +++++++--
lld/COFF/LTO.cpp | 27 +++++++++++++++++++++++++++
lld/COFF/LTO.h | 2 ++
lld/test/COFF/thinlto-emit-imports.ll | 6 ++++--
5 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/lld/COFF/COFFLinkerContext.h b/lld/COFF/COFFLinkerContext.h
index 0f8f2062b9f2e..a2273e0ad9ae3 100644
--- a/lld/COFF/COFFLinkerContext.h
+++ b/lld/COFF/COFFLinkerContext.h
@@ -60,6 +60,7 @@ class COFFLinkerContext : public CommonLinkerContext {
}
std::vector<ObjFile *> objFileInstances;
+ std::vector<BitcodeFile *> lazyBitcodeFileInstances;
std::map<std::string, PDBInputFile *> pdbInputFileInstances;
std::vector<ImportFile *> importFileInstances;
std::int64_t consumedInputsSize = 0;
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 024cb2c95cd20..e797471a3d61c 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -12,6 +12,7 @@
#include "DebugTypes.h"
#include "ICF.h"
#include "InputFiles.h"
+#include "LTO.h"
#include "MarkLive.h"
#include "MinGW.h"
#include "SymbolTable.h"
@@ -206,10 +207,12 @@ static bool compatibleMachineType(COFFLinkerContext &ctx, MachineTypes mt) {
void LinkerDriver::addFile(InputFile *file) {
Log(ctx) << "Reading " << toString(file);
if (file->lazy) {
- if (auto *f = dyn_cast<BitcodeFile>(file))
+ if (auto *f = dyn_cast<BitcodeFile>(file)) {
+ ctx.lazyBitcodeFileInstances.push_back(f);
f->parseLazy();
- else
+ } else {
cast<ObjFile>(file)->parseLazy();
+ }
} else {
ctx.consumedInputsSize += file->mb.getBufferSize();
file->parse();
@@ -2770,6 +2773,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// Do LTO by compiling bitcode input files to a set of native COFF files then
// link those files (unless -thinlto-index-only was given, in which case we
// resolve symbols and write indices, but don't generate native code or link).
+ if (config->thinLTOIndexOnly)
+ thinLTOCreateEmptyIndexFiles(ctx);
ltoCompilationDone = true;
ctx.forEachSymtab([](SymbolTable &symtab) { symtab.compileBitcodeFiles(); });
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 0329f6c2e9cea..ae706fa6d9930 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -156,6 +156,33 @@ BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) {
BitcodeCompiler::~BitcodeCompiler() = default;
+// If a lazy BitcodeFile has not been added to the link, emit an empty index
+// file. This matches ELF and GNU gold plugin behavior for distributed builds.
+void lld::coff::thinLTOCreateEmptyIndexFiles(COFFLinkerContext &ctx) {
+ DenseSet<StringRef> linkedBitcodeFiles;
+ ctx.forEachSymtab([&](SymbolTable &symtab) {
+ for (BitcodeFile *f : symtab.bitcodeFileInstances)
+ linkedBitcodeFiles.insert(f->getName());
+ });
+
+ for (BitcodeFile *f : ctx.lazyBitcodeFileInstances) {
+ if (!f->lazy || linkedBitcodeFiles.contains(f->getName()))
+ continue;
+ std::string path = lto::getThinLTOOutputFile(
+ f->obj->getName(), ctx.config.thinLTOPrefixReplaceOld,
+ ctx.config.thinLTOPrefixReplaceNew);
+ std::unique_ptr<raw_fd_ostream> os = openFile(path + ".thinlto.bc");
+ if (!os)
+ continue;
+
+ ModuleSummaryIndex m(/*HaveGVs=*/false);
+ m.setSkipModuleByDistributedBackend();
+ writeIndexToFile(m, *os);
+ if (ctx.config.thinLTOEmitImportsFiles)
+ openFile(path + ".imports");
+ }
+}
+
static void undefine(Symbol *s) { replaceSymbol<Undefined>(s, s->getName()); }
void BitcodeCompiler::add(BitcodeFile &f) {
diff --git a/lld/COFF/LTO.h b/lld/COFF/LTO.h
index 73e855e567b09..d520911afa2ae 100644
--- a/lld/COFF/LTO.h
+++ b/lld/COFF/LTO.h
@@ -38,6 +38,8 @@ class BitcodeFile;
class InputFile;
class COFFLinkerContext;
+void thinLTOCreateEmptyIndexFiles(COFFLinkerContext &ctx);
+
class BitcodeCompiler {
public:
BitcodeCompiler(COFFLinkerContext &ctx);
diff --git a/lld/test/COFF/thinlto-emit-imports.ll b/lld/test/COFF/thinlto-emit-imports.ll
index 26af017b17b2c..51936bc32935f 100644
--- a/lld/test/COFF/thinlto-emit-imports.ll
+++ b/lld/test/COFF/thinlto-emit-imports.ll
@@ -11,7 +11,8 @@
; Ensure lld generates imports files if requested for distributed backends.
; RUN: rm -f %t3.obj.imports %t3.obj.thinlto.bc
; RUN: lld-link -entry:main -thinlto-index-only \
-; RUN: -thinlto-emit-imports-files %t1.obj %t2.obj %t3.obj -out:%t4.exe
+; RUN: -thinlto-emit-imports-files %t1.obj \
+; RUN: -start-lib %t2.obj %t3.obj -end-lib -out:%t4.exe
; The imports file for this module contains the bitcode file for
; Inputs/thinlto.ll
@@ -26,7 +27,8 @@
; RUN: cat %t3.obj.imports | count 0
; The index file should be created even for the input with an empty summary.
-; RUN: ls %t3.obj.thinlto.bc
+; RUN: llvm-dis %t3.obj.thinlto.bc -o - | FileCheck %s --check-prefix=UNUSED
+; UNUSED: ^{{[0-9]+}} = flags: 2
; Ensure lld generates error if unable to write to imports file.
; RUN: rm -f %t3.obj.imports
More information about the llvm-commits
mailing list