[lld] [llvm] [DTLTO] Do not serialize inputs that hit in the ThinLTO cache (PR #204104)
Ben Dunbobbin via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 24 03:31:41 PDT 2026
https://github.com/bd1976bris updated https://github.com/llvm/llvm-project/pull/204104
>From 9c153e0b0534703bd27ff40fc633a50d707a0810 Mon Sep 17 00:00:00 2001
From: Ben <ben.dunbobbin at sony.com>
Date: Mon, 15 Jun 2026 23:37:03 +0100
Subject: [PATCH 1/3] [DTLTO] Do not serialize inputs that hit in the ThinLTO
cache
To handle bitcode inputs that are not in individual files on disk,
such as members of non-thin archives, DTLTO serializes those inputs to
temporary individual bitcode files.
This patch changes LLVM to serialize only uncached input modules and
any modules they import from.
For a link of Clang 22 (debug build with sanitizers and
instrumentation), I performed measurements with and without this patch
for an optimized toolchain (PGO non-LTO, based on recent main commit
c264e07). The measurements were run on:
Windows 11 Pro build 26200, AMD Family 25 at approximately 4.5 GHz,
16 cores / 32 threads, and 64 GB RAM.
Ubuntu 24.04.3 LTS, Ryzen 9 5950X with 32 threads, and 62 GiB RAM.
There was no difference in serialization time when the cache was
disabled.
When the cache was enabled and all compilations hit in the cache,
serialization was eliminated, as was the time spent deleting the
previously serialized temporary files, which are no longer created.
Mean wall-clock time improved by about 10% on both machines in this
scenario.
---
.../dtlto/cache-serialization.test | 91 +++++++++++++++++++
llvm/include/llvm/DTLTO/DTLTO.h | 21 +++--
llvm/lib/DTLTO/DTLTOInputFiles.cpp | 21 ++++-
3 files changed, 119 insertions(+), 14 deletions(-)
create mode 100644 cross-project-tests/dtlto/cache-serialization.test
diff --git a/cross-project-tests/dtlto/cache-serialization.test b/cross-project-tests/dtlto/cache-serialization.test
new file mode 100644
index 0000000000000..52005751891db
--- /dev/null
+++ b/cross-project-tests/dtlto/cache-serialization.test
@@ -0,0 +1,91 @@
+REQUIRES: ld.lld
+
+# Show that DTLTO input file serialization interacts correctly with the
+# ThinLTO cache.
+#
+# To handle bitcode inputs that are not in individual files on disk, such as
+# members of non-thin archives, DTLTO serializes such input bitcode to a
+# temporary individual file on disk. This serialization should only be
+# performed if the backend compilation is required. A backend compilation is
+# only required if there is a miss in the ThinLTO cache for that module, or a
+# module is imported by a module that has missed in the ThinLTO cache.
+#
+# Test DTLTO input serialization across three cases:
+# 1. None of the backend compilations hit in the ThinLTO cache.
+# 2. All of the backend compilations hit in the ThinLTO cache.
+# 3. Some backend compilations hit in the ThinLTO cache and some miss.
+
+RUN: rm -rf %t && split-file %s %t && cd %t
+
+RUN: %clang --target=x86_64-linux-gnu -O2 -flto=thin -c t1.c t2.c
+RUN: llvm-ar rcs t1.a t1.o
+RUN: llvm-ar rcs t2.a t2.o
+
+RUN: mkdir cache.dir
+
+DEFINE: %{link} = \
+DEFINE: %clang --target=x86_64-linux-gnu -fuse-ld=lld -shared -nostdlib \
+DEFINE: -O2 -flto=thin \
+DEFINE: -fthinlto-distributor=%python \
+DEFINE: -Xthinlto-distributor=%llvm_src_root/utils/dtlto/local.py \
+DEFINE: -Wl,--thinlto-cache-dir=cache.dir \
+DEFINE: -Wl,--save-temps
+
+# 1. None of the backend compilations hit in the ThinLTO cache.
+#
+# Both t1.o and t2.o require backend compilation, so both should be serialized.
+# Each backend compilation should produce:
+# * a native object
+# * a thinlto.bc file
+# * a serialized temporary bitcode file (an extracted archive member)
+RUN: mkdir none
+RUN: %{link} -Wl,--whole-archive t1.a t2.a -o none/t.elf
+RUN: ls none | sort | FileCheck %s --check-prefixes=JSON,ELF,NONE
+JSON: {{^}}t.[[#PID:]].dist-file.json{{$}}
+ELF: {{^}}t.elf.resolution.txt{{$}}
+NONE-NEXT: {{^}}t1.a(t1.o at [[#T1_OFFSET:]]).1.[[#%X,HEXPID:]].1.[[#PID]].native.o{{$}}
+NONE-NEXT: {{^}}t1.a(t1.o at [[#T1_OFFSET]]).1.[[#%X,HEXPID]].1.[[#PID]].native.o.thinlto.bc{{$}}
+NONE-NEXT: {{^}}t1.a(t1.o at [[#T1_OFFSET]]).1.[[#%X,HEXPID]].o{{$}}
+NONE-NEXT: {{^}}t2.a(t2.o at [[#T2_OFFSET:]]).2.[[#%X,HEXPID]].2.[[#PID]].native.o{{$}}
+NONE-NEXT: {{^}}t2.a(t2.o at [[#T2_OFFSET]]).2.[[#%X,HEXPID]].2.[[#PID]].native.o.thinlto.bc{{$}}
+NONE-NEXT: {{^}}t2.a(t2.o at [[#T2_OFFSET]]).2.[[#%X,HEXPID]].o{{$}}
+NONE-NOT: {{.}}
+
+# 2. All of the backend compilations hit in the ThinLTO cache.
+#
+# Both t1.o and t2.o are satisfied from the ThinLTO cache. No backend
+# compilation runs, so there should be no .native.o files, no .thinlto.bc files,
+# and no serialized archive-member bitcode files.
+RUN: mkdir all
+RUN: %{link} -Wl,--whole-archive t1.a t2.a -o all/t.elf
+RUN: ls all | sort | FileCheck %s --check-prefixes=ELF,ALL
+ALL-NOT: {{.}}
+
+# 3. Some backend compilations hit in the ThinLTO cache and some miss.
+#
+# t1.o and t2.o are cached, but the new t3.o misses the cache and imports from
+# t1.o:
+# * t3.o must be compiled and serialized
+# * cached t1.o must be serialized because t3.o imports from it.
+# * cached t2.o should not be serialized or compiled.
+RUN: %clang --target=x86_64-linux-gnu -O2 -flto=thin -c t3.c
+RUN: llvm-ar rcs t3.a t3.o
+RUN: mkdir some
+RUN: %{link} -Wl,--whole-archive t1.a t2.a t3.a -o some/t.elf
+RUN: ls some | sort | FileCheck %s --check-prefixes=JSON,ELF,SOME
+SOME-NEXT: {{^}}t1.a(t1.o at [[#T1_OFFSET:]]).1.[[#%X,HEXPID:]].o{{$}}
+SOME-NEXT: {{^}}t3.a(t3.o at [[#T3_OFFSET:]]).3.[[#%X,HEXPID]].3.[[#PID]].native.o{{$}}
+SOME-NEXT: {{^}}t3.a(t3.o at [[#T3_OFFSET]]).3.[[#%X,HEXPID]].3.[[#PID]].native.o.thinlto.bc{{$}}
+SOME-NEXT: {{^}}t3.a(t3.o at [[#T3_OFFSET]]).3.[[#%X,HEXPID]].o{{$}}
+SOME-NOT: {{.}}
+
+#--- t1.c
+__attribute__((retain)) int t1(int x) { return x; }
+
+#--- t2.c
+extern int t1(int);
+__attribute__((retain)) int t2(int x) { return t1(x) + x; }
+
+#--- t3.c
+extern int t1(int);
+__attribute__((retain)) int t3(int x) { return t1(x) + x; }
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index 54e4a414dae2a..756ed31d83e61 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -38,11 +38,11 @@ namespace lto {
/// to DTLTO distributors.
///
/// Input files are kept until the pipeline has determined per-module ThinLTO
-/// participation. addInput() performs: (1) register the input; (2) on Windows,
-/// normalize module ID for standalone bitcode; (3) for thin archive members,
-/// set module ID to the on-disk member path; (4) for other archives and FatLTO,
-/// set module ID to a unique path and serialize content in
-/// serializeLTOInputs().
+/// participation and cache status. addInput() performs: (1) register the
+/// input; (2) on Windows, normalize module ID for standalone bitcode; (3) for
+/// thin archive members, set module ID to the on-disk member path; (4) for
+/// other archives and FatLTO, set module ID to a unique path whose content is
+/// serialized later by serializeLTOInputs().
class LLVM_ABI DTLTO : public LTO {
using Base = LTO;
@@ -98,16 +98,17 @@ class LLVM_ABI DTLTO : public LTO {
virtual Error run(AddStreamFn AddStream, FileCache Cache = {}) override;
private:
- /// DTLTO archives support.
+ /// DTLTO archive support.
///
/// Save the contents of ThinLTO-enabled input files that must be serialized
/// for distribution, such as archive members and FatLTO objects, to
/// individual bitcode files named after the module ID.
///
- /// Must be called after all input files are added but before optimization
- /// begins. If a file with that name already exists, it is likely a leftover
- /// from a previously terminated linker process and can be safely overwritten.
- Error serializeLTOInputs();
+ /// Must be called after all input files are added and cache hits are known,
+ /// but before optimization begins. Existing files are overwritten because
+ /// they are likely leftovers from a previously terminated linker process and
+ /// can be safely replaced.
+ LLVM_ABI Error serializeLTOInputs();
// Remove temporary files created to enable distribution.
void cleanup() override;
diff --git a/llvm/lib/DTLTO/DTLTOInputFiles.cpp b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
index 8751cb14d7a06..a74c391d30009 100644
--- a/llvm/lib/DTLTO/DTLTOInputFiles.cpp
+++ b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
@@ -14,6 +14,7 @@
#include "llvm/DTLTO/DTLTO.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -204,18 +205,30 @@ lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) {
// Save the contents of ThinLTO-enabled input files that must be serialized for
// distribution.
Error lto::DTLTO::serializeLTOInputs() {
+ DenseSet<StringRef> RequiredModuleIDs;
+ for (const Job &J : Jobs) {
+ if (J.Cached)
+ continue;
+ RequiredModuleIDs.insert(J.ModuleID);
+ for (StringRef ImportPath : J.ImportsFilesList)
+ RequiredModuleIDs.insert(ImportPath);
+ }
+
for (auto &Input : InputFiles) {
if (!Input->isThinLTO() || !Input->getSerializeForDistribution())
continue;
+
// Save the content of the input file to a file named after the module ID.
- StringRef ModuleId = Input->getName();
- TimeTraceScope TimeScope("Serialize bitcode input for DTLTO", ModuleId);
+ StringRef ModuleID = Input->getName();
+ if (!RequiredModuleIDs.contains(ModuleID))
+ continue;
+ TimeTraceScope TimeScope("Serialize bitcode input for DTLTO", ModuleID);
MemoryBufferRef Buf = Input->getFileBuffer();
- if (Error Err = save(Buf.getBuffer(), ModuleId))
+ if (Error Err = save(Buf.getBuffer(), ModuleID))
return Err;
// Cleanup this file on abnormal process exit.
if (!SaveTemps)
- addToCleanup(ModuleId);
+ addToCleanup(ModuleID);
}
return Error::success();
}
>From e5a5062b468a57b542a58b6cfb1ca4d2cb262a75 Mon Sep 17 00:00:00 2001
From: Ben <ben.dunbobbin at sony.com>
Date: Wed, 17 Jun 2026 10:52:49 +0100
Subject: [PATCH 2/3] Address DTLTO input serialization review comments
Track input module IDs to serialize while preparing each DTLTO job,
recording only non-cached jobs and their imports. This avoids looping
over Jobs again in serializeLTOInputs().
Rename the set to InputModuleIDsToSerialize to better reflect that it
filters input files whose bitcode must be written for distribution.
---
llvm/include/llvm/DTLTO/DTLTO.h | 10 ++++++++--
llvm/lib/DTLTO/DTLTO.cpp | 6 ++++++
llvm/lib/DTLTO/DTLTOInputFiles.cpp | 12 +-----------
3 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index 756ed31d83e61..6b09b0e4794d9 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -16,6 +16,7 @@
#ifndef LLVM_DTLTO_DTLTO_H
#define LLVM_DTLTO_DTLTO_H
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Support/ManagedStatic.h"
@@ -164,6 +165,8 @@ class LLVM_ABI DTLTO : public LTO {
private:
// Backend compilation jobs, one per module.
SmallVector<Job> Jobs;
+ // Input module IDs that must be serialized to individual files.
+ DenseSet<StringRef> InputModuleIDsToSerialize;
// Task index offset for first ThinLTO job.
unsigned ThinLTOTaskOffset;
// Optional cache for native objects.
@@ -211,10 +214,13 @@ class LLVM_ABI DTLTO : public LTO {
/// distributor will skip this job. On a cache miss, J.CacheAddStream is
/// set for later use when storing the compiled object.
///
- /// 4. Writes the per-module summary index to disk only on cache miss. The
+ /// 4. Records the module ID and imported module IDs that must be serialized
+ /// to individual files.
+ ///
+ /// 5. Writes the per-module summary index to disk only on cache miss. The
/// remote compiler will read this via -fthinlto-index=.
///
- /// 5. Registers the job's temporary files for removal on abnormal process
+ /// 6. Registers the job's temporary files for removal on abnormal process
/// exit when SaveTemps is false (only for files that will be created).
///
/// \param ModulePath The module identifier (bitcode path) for the ThinLTO
diff --git a/llvm/lib/DTLTO/DTLTO.cpp b/llvm/lib/DTLTO/DTLTO.cpp
index 162f4a60ab9f9..6805746f64978 100644
--- a/llvm/lib/DTLTO/DTLTO.cpp
+++ b/llvm/lib/DTLTO/DTLTO.cpp
@@ -154,6 +154,10 @@ Error lto::DTLTO::prepareDtltoJob(StringRef ModulePath, unsigned Task) {
if (Error Err = checkCacheHit(J))
return Err;
if (!J.Cached) {
+ InputModuleIDsToSerialize.insert(J.ModuleID);
+ for (StringRef ImportPath : J.ImportsFilesList)
+ InputModuleIDsToSerialize.insert(ImportPath);
+
TimeTraceScope JobScope("Emit individual index for DTLTO",
J.SummaryIndexPath);
if (Error Err = save(SummaryIndexFiles[Task], J.SummaryIndexPath))
@@ -225,6 +229,8 @@ Error lto::DTLTO::prepareDtltoJobs() {
auto &ModuleMap =
ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap;
+ InputModuleIDsToSerialize.clear();
+
if (ModuleMap.empty())
return Error::success();
diff --git a/llvm/lib/DTLTO/DTLTOInputFiles.cpp b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
index a74c391d30009..c89fbe4575f6a 100644
--- a/llvm/lib/DTLTO/DTLTOInputFiles.cpp
+++ b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
@@ -14,7 +14,6 @@
#include "llvm/DTLTO/DTLTO.h"
-#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -205,22 +204,13 @@ lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) {
// Save the contents of ThinLTO-enabled input files that must be serialized for
// distribution.
Error lto::DTLTO::serializeLTOInputs() {
- DenseSet<StringRef> RequiredModuleIDs;
- for (const Job &J : Jobs) {
- if (J.Cached)
- continue;
- RequiredModuleIDs.insert(J.ModuleID);
- for (StringRef ImportPath : J.ImportsFilesList)
- RequiredModuleIDs.insert(ImportPath);
- }
-
for (auto &Input : InputFiles) {
if (!Input->isThinLTO() || !Input->getSerializeForDistribution())
continue;
// Save the content of the input file to a file named after the module ID.
StringRef ModuleID = Input->getName();
- if (!RequiredModuleIDs.contains(ModuleID))
+ if (!InputModuleIDsToSerialize.contains(ModuleID))
continue;
TimeTraceScope TimeScope("Serialize bitcode input for DTLTO", ModuleID);
MemoryBufferRef Buf = Input->getFileBuffer();
>From a50da94952907c0a200aeab0401b208d7f700ee7 Mon Sep 17 00:00:00 2001
From: Ben <ben.dunbobbin at sony.com>
Date: Wed, 24 Jun 2026 11:26:50 +0100
Subject: [PATCH 3/3] [DTLTO] Use extraction terminology
Change the terminology used for DTLTO from "serialization" to
"extraction".
Update the DTLTO time-trace scope and tests to match the new wording, and
rename/update related lld and cross-project DTLTO cache tests.
I have also improved some unrelated wording in the comments and
removed a redundant comment block from the DTLTO class comment.
---
.../dtlto/cache-extraction.test | 101 ++++++++++++++++++
.../dtlto/cache-serialization.test | 91 ----------------
lld/test/ELF/dtlto/timetrace.test | 4 +-
llvm/include/llvm/DTLTO/DTLTO.h | 30 +++---
llvm/include/llvm/LTO/LTO.h | 23 ++--
llvm/lib/DTLTO/DTLTO.cpp | 8 +-
llvm/lib/DTLTO/DTLTOInputFiles.cpp | 15 ++-
7 files changed, 139 insertions(+), 133 deletions(-)
create mode 100644 cross-project-tests/dtlto/cache-extraction.test
delete mode 100644 cross-project-tests/dtlto/cache-serialization.test
diff --git a/cross-project-tests/dtlto/cache-extraction.test b/cross-project-tests/dtlto/cache-extraction.test
new file mode 100644
index 0000000000000..58428f7bb15eb
--- /dev/null
+++ b/cross-project-tests/dtlto/cache-extraction.test
@@ -0,0 +1,101 @@
+REQUIRES: ld.lld
+
+# Show that DTLTO input file extraction interacts correctly with the
+# ThinLTO cache.
+#
+# To handle bitcode inputs that are not in individual files on disk, such as
+# members of non-thin archives, DTLTO extracts such input bitcode to a
+# temporary individual file on disk. This extraction should only be
+# performed if the backend compilation is required. A backend compilation is
+# only required if there is a miss in the ThinLTO cache for that module, or a
+# module is imported by a module that has missed in the ThinLTO cache.
+#
+# Test DTLTO input extraction across three cases:
+# 1. None of the backend compilations hit in the ThinLTO cache.
+# 2. All of the backend compilations hit in the ThinLTO cache.
+# 3. Some backend compilations hit in the ThinLTO cache and some miss.
+
+RUN: rm -rf %t && split-file %s %t && cd %t
+
+RUN: %clang --target=x86_64-linux-gnu -O2 -flto=thin -c f1.c f2.c
+RUN: llvm-ar rcs f1.a f1.o
+RUN: llvm-ar rcs f2.a f2.o
+
+RUN: mkdir cache.dir
+
+DEFINE: %{link} = \
+DEFINE: %clang --target=x86_64-linux-gnu -fuse-ld=lld -shared -nostdlib \
+DEFINE: -O2 -flto=thin \
+DEFINE: -fthinlto-distributor=%python \
+DEFINE: -Xthinlto-distributor=%llvm_src_root/utils/dtlto/local.py \
+DEFINE: -Wl,--thinlto-cache-dir=cache.dir \
+DEFINE: -Wl,--save-temps
+
+# 1. None of the backend compilations hit in the ThinLTO cache.
+#
+# Both f1.o and f2.o require backend compilation, so both should be extracted.
+# Each backend compilation should produce:
+# * a native object
+# * a thinlto.bc file
+# * a temporary bitcode file (extracted archive member)
+RUN: mkdir none
+RUN: %{link} -Wl,--whole-archive f1.a f2.a -o none/out.elf
+RUN: ls none | sort | FileCheck %s --check-prefix=NONE
+NONE-NOT: {{^}}f1
+NONE-NOT: {{^}}f2
+NONE-NOT: {{^}}f3
+NONE: {{^}}f1.a(f1.o at [[#F1_OFFSET:]]).1.[[#%X,HEXPID:]].1.[[#PID:]].native.o{{$}}
+NONE-NEXT: {{^}}f1.a(f1.o at [[#F1_OFFSET]]).1.[[#%X,HEXPID]].1.[[#PID]].native.o.thinlto.bc{{$}}
+NONE-NEXT: {{^}}f1.a(f1.o at [[#F1_OFFSET]]).1.[[#%X,HEXPID]].o{{$}}
+NONE-NEXT: {{^}}f2.a(f2.o at [[#F2_OFFSET:]]).2.[[#%X,HEXPID]].2.[[#PID]].native.o{{$}}
+NONE-NEXT: {{^}}f2.a(f2.o at [[#F2_OFFSET]]).2.[[#%X,HEXPID]].2.[[#PID]].native.o.thinlto.bc{{$}}
+NONE-NEXT: {{^}}f2.a(f2.o at [[#F2_OFFSET]]).2.[[#%X,HEXPID]].o{{$}}
+NONE-NOT: {{^}}f1
+NONE-NOT: {{^}}f2
+NONE-NOT: {{^}}f3
+
+# 2. All of the backend compilations hit in the ThinLTO cache.
+#
+# Both f1.o and f2.o are satisfied from the ThinLTO cache. No backend
+# compilation runs, so there should be no .native.o files, no .thinlto.bc files,
+# and no extracted archive-member bitcode files.
+RUN: mkdir all
+RUN: %{link} -Wl,--whole-archive f1.a f2.a -o all/out.elf
+RUN: ls all | sort | FileCheck %s --check-prefix=ALL
+ALL-NOT: {{^}}f1
+ALL-NOT: {{^}}f2
+ALL-NOT: {{^}}f3
+
+# 3. Some backend compilations hit in the ThinLTO cache and some miss.
+#
+# f1.o and f2.o are cached, but the new f3.o misses the cache and imports
+# from f1.o:
+# * f3.o must be compiled and extracted
+# * cached f1.o must be extracted because f3.o imports from it.
+# * cached f2.o should not be extracted or compiled.
+RUN: %clang --target=x86_64-linux-gnu -O2 -flto=thin -c f3.c
+RUN: llvm-ar rcs f3.a f3.o
+RUN: mkdir some
+RUN: %{link} -Wl,--whole-archive f1.a f2.a f3.a -o some/out.elf
+RUN: ls some | sort | FileCheck %s --check-prefix=SOME
+SOME-NOT: {{^}}f1
+SOME-NOT: {{^}}f2
+SOME-NOT: {{^}}f3
+SOME: {{^}}f1.a(f1.o at [[#F1_OFFSET:]]).1.[[#%X,HEXPID:]].o{{$}}
+SOME-NEXT: {{^}}f3.a(f3.o at [[#F3_OFFSET:]]).3.[[#%X,HEXPID]].3.[[#PID:]].native.o{{$}}
+SOME-NEXT: {{^}}f3.a(f3.o at [[#F3_OFFSET]]).3.[[#%X,HEXPID]].3.[[#PID]].native.o.thinlto.bc{{$}}
+SOME-NEXT: {{^}}f3.a(f3.o at [[#F3_OFFSET]]).3.[[#%X,HEXPID]].o{{$}}
+SOME-NOT: {{^}}f1
+SOME-NOT: {{^}}f2
+SOME-NOT: {{^}}f3
+
+#--- f1.c
+__attribute__((retain)) int f1(int x) { return x; }
+
+#--- f2.c
+extern int f1(int);
+__attribute__((retain)) int f2(int x) { return f1(x) + x; }
+
+#--- f3.c
+extern int f1(int);
+__attribute__((retain)) int f3(int x) { return f1(x) + x; }
diff --git a/cross-project-tests/dtlto/cache-serialization.test b/cross-project-tests/dtlto/cache-serialization.test
deleted file mode 100644
index 52005751891db..0000000000000
--- a/cross-project-tests/dtlto/cache-serialization.test
+++ /dev/null
@@ -1,91 +0,0 @@
-REQUIRES: ld.lld
-
-# Show that DTLTO input file serialization interacts correctly with the
-# ThinLTO cache.
-#
-# To handle bitcode inputs that are not in individual files on disk, such as
-# members of non-thin archives, DTLTO serializes such input bitcode to a
-# temporary individual file on disk. This serialization should only be
-# performed if the backend compilation is required. A backend compilation is
-# only required if there is a miss in the ThinLTO cache for that module, or a
-# module is imported by a module that has missed in the ThinLTO cache.
-#
-# Test DTLTO input serialization across three cases:
-# 1. None of the backend compilations hit in the ThinLTO cache.
-# 2. All of the backend compilations hit in the ThinLTO cache.
-# 3. Some backend compilations hit in the ThinLTO cache and some miss.
-
-RUN: rm -rf %t && split-file %s %t && cd %t
-
-RUN: %clang --target=x86_64-linux-gnu -O2 -flto=thin -c t1.c t2.c
-RUN: llvm-ar rcs t1.a t1.o
-RUN: llvm-ar rcs t2.a t2.o
-
-RUN: mkdir cache.dir
-
-DEFINE: %{link} = \
-DEFINE: %clang --target=x86_64-linux-gnu -fuse-ld=lld -shared -nostdlib \
-DEFINE: -O2 -flto=thin \
-DEFINE: -fthinlto-distributor=%python \
-DEFINE: -Xthinlto-distributor=%llvm_src_root/utils/dtlto/local.py \
-DEFINE: -Wl,--thinlto-cache-dir=cache.dir \
-DEFINE: -Wl,--save-temps
-
-# 1. None of the backend compilations hit in the ThinLTO cache.
-#
-# Both t1.o and t2.o require backend compilation, so both should be serialized.
-# Each backend compilation should produce:
-# * a native object
-# * a thinlto.bc file
-# * a serialized temporary bitcode file (an extracted archive member)
-RUN: mkdir none
-RUN: %{link} -Wl,--whole-archive t1.a t2.a -o none/t.elf
-RUN: ls none | sort | FileCheck %s --check-prefixes=JSON,ELF,NONE
-JSON: {{^}}t.[[#PID:]].dist-file.json{{$}}
-ELF: {{^}}t.elf.resolution.txt{{$}}
-NONE-NEXT: {{^}}t1.a(t1.o at [[#T1_OFFSET:]]).1.[[#%X,HEXPID:]].1.[[#PID]].native.o{{$}}
-NONE-NEXT: {{^}}t1.a(t1.o at [[#T1_OFFSET]]).1.[[#%X,HEXPID]].1.[[#PID]].native.o.thinlto.bc{{$}}
-NONE-NEXT: {{^}}t1.a(t1.o at [[#T1_OFFSET]]).1.[[#%X,HEXPID]].o{{$}}
-NONE-NEXT: {{^}}t2.a(t2.o at [[#T2_OFFSET:]]).2.[[#%X,HEXPID]].2.[[#PID]].native.o{{$}}
-NONE-NEXT: {{^}}t2.a(t2.o at [[#T2_OFFSET]]).2.[[#%X,HEXPID]].2.[[#PID]].native.o.thinlto.bc{{$}}
-NONE-NEXT: {{^}}t2.a(t2.o at [[#T2_OFFSET]]).2.[[#%X,HEXPID]].o{{$}}
-NONE-NOT: {{.}}
-
-# 2. All of the backend compilations hit in the ThinLTO cache.
-#
-# Both t1.o and t2.o are satisfied from the ThinLTO cache. No backend
-# compilation runs, so there should be no .native.o files, no .thinlto.bc files,
-# and no serialized archive-member bitcode files.
-RUN: mkdir all
-RUN: %{link} -Wl,--whole-archive t1.a t2.a -o all/t.elf
-RUN: ls all | sort | FileCheck %s --check-prefixes=ELF,ALL
-ALL-NOT: {{.}}
-
-# 3. Some backend compilations hit in the ThinLTO cache and some miss.
-#
-# t1.o and t2.o are cached, but the new t3.o misses the cache and imports from
-# t1.o:
-# * t3.o must be compiled and serialized
-# * cached t1.o must be serialized because t3.o imports from it.
-# * cached t2.o should not be serialized or compiled.
-RUN: %clang --target=x86_64-linux-gnu -O2 -flto=thin -c t3.c
-RUN: llvm-ar rcs t3.a t3.o
-RUN: mkdir some
-RUN: %{link} -Wl,--whole-archive t1.a t2.a t3.a -o some/t.elf
-RUN: ls some | sort | FileCheck %s --check-prefixes=JSON,ELF,SOME
-SOME-NEXT: {{^}}t1.a(t1.o at [[#T1_OFFSET:]]).1.[[#%X,HEXPID:]].o{{$}}
-SOME-NEXT: {{^}}t3.a(t3.o at [[#T3_OFFSET:]]).3.[[#%X,HEXPID]].3.[[#PID]].native.o{{$}}
-SOME-NEXT: {{^}}t3.a(t3.o at [[#T3_OFFSET]]).3.[[#%X,HEXPID]].3.[[#PID]].native.o.thinlto.bc{{$}}
-SOME-NEXT: {{^}}t3.a(t3.o at [[#T3_OFFSET]]).3.[[#%X,HEXPID]].o{{$}}
-SOME-NOT: {{.}}
-
-#--- t1.c
-__attribute__((retain)) int t1(int x) { return x; }
-
-#--- t2.c
-extern int t1(int);
-__attribute__((retain)) int t2(int x) { return t1(x) + x; }
-
-#--- t3.c
-extern int t1(int);
-__attribute__((retain)) int t3(int x) { return t1(x) + x; }
diff --git a/lld/test/ELF/dtlto/timetrace.test b/lld/test/ELF/dtlto/timetrace.test
index f43f51284b5c4..664ffe68f6fcd 100644
--- a/lld/test/ELF/dtlto/timetrace.test
+++ b/lld/test/ELF/dtlto/timetrace.test
@@ -33,13 +33,13 @@ RUN: %python filter_order_and_pprint.py %t.json | FileCheck %s
CHECK: "name": "Add input for DTLTO"
CHECK: "name": "Add input for DTLTO"
CHECK: "name": "Remove DTLTO temporary files"
-CHECK: "name": "Serialize bitcode input for DTLTO"
+CHECK: "name": "Extract bitcode input for DTLTO"
CHECK-SAME: "detail": "t1.a(t1.bc at [[#ARCHIVE_OFFSET:]]).1.[[PID:[A-F0-9]+]].o"
CHECK: "name": "Total Add input for DTLTO"
CHECK-SAME: "count": 2,
CHECK: "name": "Total Remove DTLTO temporary files"
CHECK-SAME: "count": 1,
-CHECK: "name": "Total Serialize bitcode input for DTLTO"
+CHECK: "name": "Total Extract bitcode input for DTLTO"
CHECK-SAME: "count": 1,
#--- t1.ll
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index 6b09b0e4794d9..2d532bc384685 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -28,22 +28,19 @@
namespace llvm {
namespace lto {
-/// Prepares inputs for Distributed ThinLTO so that backend compilations can use
+/// Prepares inputs for Distributed ThinLTO so that backend compilations use
/// individual bitcode paths and consistent module IDs.
///
/// Each input must exist as an individual bitcode file on disk and be loadable
-/// via its ModuleID. Archive members and FatLTO objects do not satisfy that by
-/// default; this class writes bitcode out when needed and updates ModuleID.
-/// On Windows, module IDs are normalized to remove short 8.3 path components
+/// via its ModuleID. Archive members and FatLTO objects do not satisfy this
+/// requirement. For these inputs, this class extracts the individual bitcode
+/// to an individual temporary file, and updates ModuleID to that path. On
+/// Windows, module IDs are normalized to remove short 8.3 path components
/// that are machine-local and break distribution; other normalization is left
/// to DTLTO distributors.
///
-/// Input files are kept until the pipeline has determined per-module ThinLTO
-/// participation and cache status. addInput() performs: (1) register the
-/// input; (2) on Windows, normalize module ID for standalone bitcode; (3) for
-/// thin archive members, set module ID to the on-disk member path; (4) for
-/// other archives and FatLTO, set module ID to a unique path whose content is
-/// serialized later by serializeLTOInputs().
+/// Input files are kept alive until the pipeline has determined per-module
+/// ThinLTO participation and cache status, see addInput() for details.
class LLVM_ABI DTLTO : public LTO {
using Base = LTO;
@@ -83,8 +80,7 @@ class LLVM_ABI DTLTO : public LTO {
/// (normalized on Windows) to the member file on disk.
/// 4. For archive members and FatLTO objects, overwrite the module ID with a
/// unique path (normalized on Windows) naming a file that will contain the
- /// member content. The file is created and populated later (see
- /// serializeInputs()).
+ /// content. The file is created/populated later (see extractLTOInputs()).
Expected<std::shared_ptr<InputFile>>
addInput(std::unique_ptr<InputFile> InputPtr) override;
@@ -101,7 +97,7 @@ class LLVM_ABI DTLTO : public LTO {
private:
/// DTLTO archive support.
///
- /// Save the contents of ThinLTO-enabled input files that must be serialized
+ /// Save the contents of ThinLTO-enabled input files that must be extracted
/// for distribution, such as archive members and FatLTO objects, to
/// individual bitcode files named after the module ID.
///
@@ -109,7 +105,7 @@ class LLVM_ABI DTLTO : public LTO {
/// but before optimization begins. Existing files are overwritten because
/// they are likely leftovers from a previously terminated linker process and
/// can be safely replaced.
- LLVM_ABI Error serializeLTOInputs();
+ LLVM_ABI Error extractLTOInputs();
// Remove temporary files created to enable distribution.
void cleanup() override;
@@ -165,8 +161,8 @@ class LLVM_ABI DTLTO : public LTO {
private:
// Backend compilation jobs, one per module.
SmallVector<Job> Jobs;
- // Input module IDs that must be serialized to individual files.
- DenseSet<StringRef> InputModuleIDsToSerialize;
+ // Input module IDs that must be extracted to individual files.
+ DenseSet<StringRef> InputModuleIDsToExtract;
// Task index offset for first ThinLTO job.
unsigned ThinLTOTaskOffset;
// Optional cache for native objects.
@@ -214,7 +210,7 @@ class LLVM_ABI DTLTO : public LTO {
/// distributor will skip this job. On a cache miss, J.CacheAddStream is
/// set for later use when storing the compiled object.
///
- /// 4. Records the module ID and imported module IDs that must be serialized
+ /// 4. Records the module ID and imported module IDs that must be extracted
/// to individual files.
///
/// 5. Writes the per-module summary index to disk only on cache miss. The
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index d6b6864047eac..0951039b310bd 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -134,11 +134,12 @@ class InputFile {
MemoryBufferRef MbRef;
bool IsFatLTOObject = false;
- // For distributed compilation, each input must exist as an individual bitcode
- // file on disk and be identified by its ModuleID. Archive members and FatLTO
- // objects violate this. So, in these cases we flag that the bitcode must be
- // written out to a new standalone file.
- bool SerializeForDistribution = false;
+ // For distributed compilation, each input must exist as an individual
+ // bitcode file on disk identified by its ModuleID. For archive members and
+ // FatLTO objects, the input bitcode is a sub-section of a larger file. In
+ // these cases we flag that the bitcode must be written to a temporary
+ // standalone file. Effectively, extracted from its container.
+ bool ExtractForDistribution = false;
bool IsThinLTO = false;
StringRef ArchivePath;
StringRef MemberName;
@@ -212,12 +213,12 @@ class InputFile {
LLVM_ABI BitcodeModule &getPrimaryBitcodeModule();
// Returns the memory buffer reference for this input file.
MemoryBufferRef getFileBuffer() const { return MbRef; }
- // Returns true if this input should be serialized to disk for distribution.
- // See the comment on SerializeForDistribution for details.
- bool getSerializeForDistribution() const { return SerializeForDistribution; }
- // Mark whether this input should be serialized to disk for distribution.
- // See the comment on SerializeForDistribution for details.
- void setSerializeForDistribution(bool SFD) { SerializeForDistribution = SFD; }
+ // Returns true if this input should be extracted to disk for distribution.
+ // See the comment on ExtractForDistribution for details.
+ bool getExtractForDistribution() const { return ExtractForDistribution; }
+ // Mark whether this input should be extracted to disk for distribution.
+ // See the comment on ExtractForDistribution for details.
+ void setExtractForDistribution(bool EFD) { ExtractForDistribution = EFD; }
// Returns true if this bitcode came from a FatLTO object.
bool isFatLTOObject() const { return IsFatLTOObject; }
// Mark this bitcode as coming from a FatLTO object.
diff --git a/llvm/lib/DTLTO/DTLTO.cpp b/llvm/lib/DTLTO/DTLTO.cpp
index 6805746f64978..bca05fa53e889 100644
--- a/llvm/lib/DTLTO/DTLTO.cpp
+++ b/llvm/lib/DTLTO/DTLTO.cpp
@@ -90,7 +90,7 @@ LLVM_ABI Error lto::DTLTO::run(AddStreamFn AddStream, FileCache CacheParam) {
if (Error Err = prepareDtltoJobs())
return Err;
- if (Error Err = serializeLTOInputs())
+ if (Error Err = extractLTOInputs())
return Err;
if (Error Err = performCodegen())
return Err;
@@ -154,9 +154,9 @@ Error lto::DTLTO::prepareDtltoJob(StringRef ModulePath, unsigned Task) {
if (Error Err = checkCacheHit(J))
return Err;
if (!J.Cached) {
- InputModuleIDsToSerialize.insert(J.ModuleID);
+ InputModuleIDsToExtract.insert(J.ModuleID);
for (StringRef ImportPath : J.ImportsFilesList)
- InputModuleIDsToSerialize.insert(ImportPath);
+ InputModuleIDsToExtract.insert(ImportPath);
TimeTraceScope JobScope("Emit individual index for DTLTO",
J.SummaryIndexPath);
@@ -229,7 +229,7 @@ Error lto::DTLTO::prepareDtltoJobs() {
auto &ModuleMap =
ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap;
- InputModuleIDsToSerialize.clear();
+ InputModuleIDsToExtract.clear();
if (ModuleMap.empty())
return Error::success();
diff --git a/llvm/lib/DTLTO/DTLTOInputFiles.cpp b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
index c89fbe4575f6a..b940bad4b8635 100644
--- a/llvm/lib/DTLTO/DTLTOInputFiles.cpp
+++ b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
@@ -163,8 +163,7 @@ lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) {
}
// For a member of a thin archive that is not a FatLTO object, there is an
- // existing file on disk that can be used, so we can avoid having to
- // serialize.
+ // existing file on disk that can be used, so we can avoid having to extract.
Expected<bool> UseThinMember =
Input->isFatLTOObject() ? false : isThinArchive(ArchivePath);
if (!UseThinMember)
@@ -179,7 +178,7 @@ lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) {
}
// A new file on disk will be needed for archive members and FatLTO objects.
- Input->setSerializeForDistribution(true);
+ Input->setExtractForDistribution(true);
// Get the normalized output directory, if we haven't already.
if (LinkerOutputDir.empty()) {
@@ -201,18 +200,18 @@ lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) {
return Input;
}
-// Save the contents of ThinLTO-enabled input files that must be serialized for
+// Save the contents of ThinLTO-enabled input files that must be extracted for
// distribution.
-Error lto::DTLTO::serializeLTOInputs() {
+Error lto::DTLTO::extractLTOInputs() {
for (auto &Input : InputFiles) {
- if (!Input->isThinLTO() || !Input->getSerializeForDistribution())
+ if (!Input->isThinLTO() || !Input->getExtractForDistribution())
continue;
// Save the content of the input file to a file named after the module ID.
StringRef ModuleID = Input->getName();
- if (!InputModuleIDsToSerialize.contains(ModuleID))
+ if (!InputModuleIDsToExtract.contains(ModuleID))
continue;
- TimeTraceScope TimeScope("Serialize bitcode input for DTLTO", ModuleID);
+ TimeTraceScope TimeScope("Extract bitcode input for DTLTO", ModuleID);
MemoryBufferRef Buf = Input->getFileBuffer();
if (Error Err = save(Buf.getBuffer(), ModuleID))
return Err;
More information about the llvm-commits
mailing list