[llvm] [DTLTO] Fix handling of multi-module bitcode inputs (PR #174624)

Ben Dunbobbin via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 6 10:39:19 PST 2026


https://github.com/bd1976bris updated https://github.com/llvm/llvm-project/pull/174624

>From fd8cdd4823921285691816887376aef2ba6f3a7a Mon Sep 17 00:00:00 2001
From: Ben <ben.dunbobbin at sony.com>
Date: Tue, 6 Jan 2026 15:30:02 +0000
Subject: [PATCH] [DTLTO] Fix handling of multi-module bitcode inputs

This change fixes two issues when processing multi-module bitcode
files in DTLTO:

1. The DTLTO archive handling code incorrectly uses
   getSingleBitcodeModule(), which asserts when the bitcode file
   contains more than one module.

2. The temporary file containing the contents of an input archive
   member was not emitted for multi-module bitcode files. This was
   due to incorrect logic for recording whether a bitcode input
   contains any ThinLTO modules. In a typical multi-module bitcode
   file, the first module is a ThinLTO module while a subsequent
   auxiliary module is non-ThinLTO. When modules are processed in
   order, the auxiliary module causes the entire bitcode file to be
   classified as non-ThinLTO, and the archive-member emission logic
   then incorrectly skips it.

In addition, this patch adds a test that verifies that multi-module
bitcode files can be successfully linked with DTLTO. The test
reproduces both issues as they existed prior to this change.

SIE Tracker: TOOLCHAIN-21008
---
 cross-project-tests/dtlto/multimodule.test | 42 ++++++++++++++++++++++
 llvm/include/llvm/LTO/LTO.h                |  2 ++
 llvm/lib/DTLTO/DTLTO.cpp                   |  2 +-
 llvm/lib/LTO/LTO.cpp                       |  4 ++-
 4 files changed, 48 insertions(+), 2 deletions(-)
 create mode 100644 cross-project-tests/dtlto/multimodule.test

diff --git a/cross-project-tests/dtlto/multimodule.test b/cross-project-tests/dtlto/multimodule.test
new file mode 100644
index 0000000000000..b371a26ace399
--- /dev/null
+++ b/cross-project-tests/dtlto/multimodule.test
@@ -0,0 +1,42 @@
+REQUIRES: x86-registered-target,ld.lld,llvm-ar
+
+# Test that a DTLTO link succeeds with a multi-module (via -fsplit-lto-unit)
+# bitcode file. We use an archive, as archive member inputs exercise more of
+# the DTLTO specific code than other input file types.
+
+RUN: rm -rf %t && split-file %s %t && cd %t
+
+RUN: %clang -O2 --target=x86_64-linux-gnu -flto=thin -c usebar.cc \
+RUN:   -fno-rtti -fno-exceptions -fsplit-lto-unit 
+
+# Sanity check that multi-module bitcode was produced.
+RUN: not llvm-modextract -n 2 usebar.o -o - 2>&1 \
+RUN:   | FileCheck %s --check-prefix=TWO
+TWO: bitcode file contains 2 module(s)
+
+# Create an archive.
+RUN: llvm-ar rcs usebar.a usebar.o
+
+# Build with DTLTO.
+RUN: %clang -O2 --target=x86_64-linux-gnu -flto=thin -fuse-ld=lld \
+RUN:   -nostdlib -shared -Wl,--whole-archive,--allow-shlib-undefined usebar.a \
+RUN:   -fthinlto-distributor=%python \
+RUN:   -Xthinlto-distributor=%llvm_src_root/utils/dtlto/local.py \
+RUN:   -Wl,--save-temps
+
+RUN: ls | sort | FileCheck %s
+
+# DTLTO JSON file - confirms DTLTO occurred.
+CHECK: .dist-file.json
+
+# .native.o exists - confirms archive member usebar.o participated in DTLTO.
+CHECK: {{^}}usebar.a(usebar.o
+CHECK-SAME: .native.o
+
+#--- usebar.cc
+// Minimal C++ input to exercise multi-module emission with -fsplit-lto-unit.
+struct A { virtual int foo(); };
+int bar(A *a);
+
+struct B : A { int foo() { return 2; } };
+int use() { static B b; return bar(&b); }
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index 819be1909ec12..ea80a25ac4ed0 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -194,6 +194,8 @@ class InputFile {
 
   // Returns the only BitcodeModule from InputFile.
   LLVM_ABI BitcodeModule &getSingleBitcodeModule();
+  // Returns the primary BitcodeModule from InputFile.
+  LLVM_ABI BitcodeModule &getPrimaryBitcodeModule();
   // Returns the memory buffer reference for this input file.
   MemoryBufferRef getFileBuffer() const { return MbRef; }
   // Returns true if this input file is a member of an archive.
diff --git a/llvm/lib/DTLTO/DTLTO.cpp b/llvm/lib/DTLTO/DTLTO.cpp
index 7ba4bfd80b6ab..d41544277dd45 100644
--- a/llvm/lib/DTLTO/DTLTO.cpp
+++ b/llvm/lib/DTLTO/DTLTO.cpp
@@ -146,7 +146,7 @@ lto::DTLTO::addInput(std::unique_ptr<lto::InputFile> InputPtr) {
     return Input;
 
   SmallString<64> NewModuleId;
-  BitcodeModule &BM = Input->getSingleBitcodeModule();
+  BitcodeModule &BM = Input->getPrimaryBitcodeModule();
 
   // Check if the archive is a thin archive.
   Expected<bool> IsThin = isThinArchive(ArchivePath);
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index ff6762ebb59be..4d69ffeda1280 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -606,6 +606,8 @@ BitcodeModule &InputFile::getSingleBitcodeModule() {
   return Mods[0];
 }
 
+BitcodeModule &InputFile::getPrimaryBitcodeModule() { return Mods[0]; }
+
 LTO::RegularLTOState::RegularLTOState(unsigned ParallelCodeGenParallelismLevel,
                                       const Config &Conf)
     : ParallelCodeGenParallelismLevel(ParallelCodeGenParallelismLevel),
@@ -804,7 +806,7 @@ LTO::addModule(InputFile &Input, ArrayRef<SymbolResolution> InputRes,
   // If any of the modules inside of a input bitcode file was compiled with
   // ThinLTO, we assume that the whole input file also was compiled with
   // ThinLTO.
-  Input.IsThinLTO = IsThinLTO;
+  Input.IsThinLTO |= IsThinLTO;
 
   auto ModSyms = Input.module_symbols(ModI);
   addModuleToGlobalRes(ModSyms, Res,



More information about the llvm-commits mailing list