[llvm-branch-commits] [lld] [llvm] release/23.x: [CMake] Added missing LLVM_PTHREAD_LIB dependency. (#211041) (PR #211206)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Jul 22 02:02:26 PDT 2026
https://github.com/llvmbot created https://github.com/llvm/llvm-project/pull/211206
Backport 914d5a1 4f5675a
Requested by: @bd1976bris
>From 991ae5e9947cded7819668d1a6410ef7466701c9 Mon Sep 17 00:00:00 2001
From: Ben Dunbobbin <Ben.Dunbobbin at sony.com>
Date: Tue, 21 Jul 2026 10:53:46 +0100
Subject: [PATCH 1/2] [DTLTO] Overlap temporary file removal (#209423)
Deleting the temporary files produced by the DTLTO pipeline can be
expensive on Windows hosts. For a Clang link (Debug build with
sanitizers and instrumentation) using an optimized toolchain (PGO
non-LTO, llvmorg-22.1.0) on a Windows 11 Pro (Build 26200), AMD Family
25 @ ~4.5 GHz, 16 cores/32 threads, 64 GB RAM machine, the mean duration
of the "Remove DTLTO temporary files" time trace scope was 1267.789 ms
(measured over 10 runs).
This patch performs the deletions on a background thread, allowing them
to overlap with the tail of the link to hide this cost.
This is a re-implementation of the asynchronous cleanup idea from
https://github.com/llvm/llvm-project/pull/186988, which had to be
reverted in https://github.com/llvm/llvm-project/pull/189043 because
cleanup was not guaranteed to complete before LLD invoked
timeTraceProfilerCleanup(). In certain cases timeTraceProfilerCleanup()
was called before temporary file deletion had completed in LLD, which
caused memory leaks that were flagged by sanitizer builds.
Note that the DTLTO implementation has been refactored heavily since
https://github.com/llvm/llvm-project/pull/186988, so this is a
re-implementation along the same lines.
To solve the ordering issue, LLD now calls a hook that defaults to a
no-op, and DTLTO overrides it to drain the background deletion work.
LLD calls this hook before time-trace write/cleanup.
(cherry picked from commit 914d5a1841dedf2148e21bd6de3b92409fe79285)
---
lld/COFF/Driver.cpp | 3 ++
lld/COFF/LTO.cpp | 5 +++
lld/COFF/LTO.h | 1 +
lld/COFF/SymbolTable.cpp | 5 +++
lld/COFF/SymbolTable.h | 2 +
lld/ELF/Config.h | 1 +
lld/ELF/Driver.cpp | 9 +++++
lld/ELF/LTO.cpp | 5 +++
lld/ELF/LTO.h | 1 +
llvm/include/llvm/DTLTO/DTLTO.h | 24 ++++++++++-
llvm/include/llvm/LTO/LTO.h | 8 ++++
llvm/lib/DTLTO/DTLTO.cpp | 64 ++++++++++++++++++++++--------
llvm/tools/llvm-lto2/llvm-lto2.cpp | 1 +
13 files changed, 112 insertions(+), 17 deletions(-)
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index dc4903a5fe126..2d70193187ef1 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2960,6 +2960,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// Write the result.
writeResult(ctx);
+ // LTO cleanup may create time trace events. Wait for it to complete before
+ // writing the time trace data.
+ ctx.forEachSymtab([](SymbolTable &symtab) { symtab.waitForLTOCleanup(); });
// Stop early so we can print the results.
rootTimer.stop();
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 0329f6c2e9cea..71413b0315c08 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -156,6 +156,11 @@ BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) {
BitcodeCompiler::~BitcodeCompiler() = default;
+void BitcodeCompiler::waitForLTOCleanup() {
+ if (ltoObj)
+ ltoObj->waitForCleanup();
+}
+
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..f097d53b9a15f 100644
--- a/lld/COFF/LTO.h
+++ b/lld/COFF/LTO.h
@@ -46,6 +46,7 @@ class BitcodeCompiler {
void add(BitcodeFile &f);
std::vector<InputFile *> compile();
void setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs);
+ void waitForLTOCleanup();
private:
std::unique_ptr<llvm::lto::LTO> ltoObj;
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index df540be800e41..ea5bdbc0be489 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -1511,4 +1511,9 @@ void SymbolTable::compileBitcodeFiles() {
}
}
+void SymbolTable::waitForLTOCleanup() {
+ if (lto)
+ lto->waitForLTOCleanup();
+}
+
} // namespace lld::coff
diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index aadd366c7d39f..12d2e8cb23f8e 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -109,6 +109,8 @@ class SymbolTable {
// added and before the writer writes results to a file.
void compileBitcodeFiles();
+ void waitForLTOCleanup();
+
// Creates an Undefined symbol and marks it as live.
Symbol *addGCRoot(StringRef sym, bool aliasEC = false);
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 863f3223094bb..1426aab12758f 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -215,6 +215,7 @@ class LinkerDriver {
void createFiles(llvm::opt::InputArgList &args);
void loadFiles();
void inferMachineType();
+ void waitForLTOCleanup();
template <class ELFT> void link(llvm::opt::InputArgList &args);
template <class ELFT> void compileBitcodeFiles(bool skipLinkedOutput);
// True if we are in --whole-archive and --no-whole-archive.
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 77821e8c813ef..51d9e32419f9f 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -625,6 +625,11 @@ constexpr const char *saveTempsValues[] = {
LinkerDriver::LinkerDriver(Ctx &ctx) : ctx(ctx) {}
+void LinkerDriver::waitForLTOCleanup() {
+ if (lto)
+ lto->waitForLTOCleanup();
+}
+
void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
ELFOptTable parser;
opt::InputArgList args = parser.parse(ctx, argsArr.slice(1));
@@ -707,6 +712,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
invokeELFT(link, args);
}
+ // LTO cleanup may create time trace events. Wait for it to complete before
+ // writing the time trace data.
+ waitForLTOCleanup();
+
if (ctx.arg.timeTraceEnabled) {
checkError(ctx.e, timeTraceProfilerWrite(
args.getLastArgValue(OPT_time_trace_eq).str(),
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index e40575bffec62..a11681133373d 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -231,6 +231,11 @@ BitcodeCompiler::BitcodeCompiler(Ctx &ctx) : ctx(ctx) {
BitcodeCompiler::~BitcodeCompiler() = default;
+void BitcodeCompiler::waitForLTOCleanup() {
+ if (ltoObj)
+ ltoObj->waitForCleanup();
+}
+
void BitcodeCompiler::add(BitcodeFile &f) {
lto::InputFile &obj = *f.obj;
bool isExec = !ctx.arg.shared && !ctx.arg.relocatable;
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index c8cb2156d90ca..fdbeb32030eac 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -44,6 +44,7 @@ class BitcodeCompiler {
void add(BitcodeFile &f);
SmallVector<std::unique_ptr<InputFile>, 0> compile();
void setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs);
+ void waitForLTOCleanup();
private:
Ctx &ctx;
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index 2d532bc384685..13b83ec6e0770 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -19,10 +19,12 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/LTO/LTO.h"
-#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Signals.h"
+#include "llvm/Support/ThreadPool.h"
#include <functional>
+#include <string>
+#include <utility>
#include <vector>
namespace llvm {
@@ -94,6 +96,12 @@ class LLVM_ABI DTLTO : public LTO {
/// Cache) for each task identifier.
virtual Error run(AddStreamFn AddStream, FileCache Cache = {}) override;
+ /// Wait for LTO cleanup. Clients may call this after run() once subsequent
+ /// linking work that can overlap with cleanup is complete. Cleanup may emit
+ /// time trace events, so this must be called before time trace data is
+ /// finalized.
+ void waitForCleanup() override;
+
private:
/// DTLTO archive support.
///
@@ -356,6 +364,20 @@ class LLVM_ABI DTLTO : public LTO {
// Cleanup files list.
std::vector<std::string> CleanupList;
+ // There can be many temporary files to remove. Performing deletion in the
+ // background can save a few seconds on Windows hosts.
+ struct BackgroundDeletion : DefaultThreadPool {
+ BackgroundDeletion();
+ ~BackgroundDeletion();
+
+ void removeFiles(std::vector<std::string> &&Files, const Config &Conf);
+ void waitForTasks();
+
+ std::vector<std::string> Warnings;
+ };
+
+ BackgroundDeletion BackgroundDeleter;
+
// Record a file for cleanup and register signal-time removal if requested.
void addToCleanup(StringRef Filename) {
CleanupList.push_back(Filename.str());
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index 32aa671183890..cea66f99b99bb 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -439,6 +439,14 @@ class LLVM_ABI LTO {
/// Cache) for each task identifier.
virtual Error run(AddStreamFn AddStream, FileCache Cache = {});
+ /// Wait for cleanup work started by run() to finish.
+ ///
+ /// A client may delay this call to overlap asynchronous cleanup with later
+ /// linking work, but must call it before finalizing time trace data because
+ /// cleanup may emit time trace events. Most LTO implementations have no
+ /// asynchronous cleanup.
+ virtual void waitForCleanup() {}
+
/// Static method that returns a list of libcall symbols that can be generated
/// by LTO but might not be visible from bitcode symbol table.
static SmallVector<const char *> getRuntimeLibcallSymbols(const Triple &TT);
diff --git a/llvm/lib/DTLTO/DTLTO.cpp b/llvm/lib/DTLTO/DTLTO.cpp
index bca05fa53e889..1d42d09d0820a 100644
--- a/llvm/lib/DTLTO/DTLTO.cpp
+++ b/llvm/lib/DTLTO/DTLTO.cpp
@@ -27,27 +27,59 @@
#include "llvm/Support/raw_ostream.h"
#include <string>
+#include <system_error>
+#include <utility>
+#include <vector>
using namespace llvm;
+// Experimentation showed that serial deletion is most efficient, hence
+// a single thread.
+lto::DTLTO::BackgroundDeletion::BackgroundDeletion()
+ : DefaultThreadPool(hardware_concurrency(1)) {}
+
+lto::DTLTO::BackgroundDeletion::~BackgroundDeletion() { waitForTasks(); }
+
+void lto::DTLTO::BackgroundDeletion::waitForTasks() {
+ wait();
+ for (const std::string &Warning : Warnings)
+ errs() << "warning: could not remove the file " << Warning << "\n";
+ Warnings.clear();
+}
+
+void lto::DTLTO::BackgroundDeletion::removeFiles(
+ std::vector<std::string> &&Files, const Config &Conf) {
+ if (Files.empty())
+ return;
+
+ async([this, Files = std::move(Files), TTE = Conf.TimeTraceEnabled,
+ TTG = Conf.TimeTraceGranularity] {
+ if (LLVM_ENABLE_THREADS && TTE)
+ timeTraceProfilerInitialize(TTG, "Remove DTLTO temporary files");
+ {
+ TimeTraceScope TimeScope("Remove DTLTO temporary files");
+ for (const std::string &Path : Files) {
+ std::error_code EC = sys::fs::remove(Path, true);
+ if (!EC ||
+ EC == std::make_error_code(std::errc::no_such_file_or_directory))
+ continue;
+
+ Warnings.emplace_back("'" + Path + "': " + EC.message());
+ }
+ }
+ if (LLVM_ENABLE_THREADS && TTE)
+ timeTraceProfilerFinishThread();
+ });
+}
+
+void lto::DTLTO::waitForCleanup() { BackgroundDeleter.waitForTasks(); }
+
// Remove temporary files created to enable distribution.
void lto::DTLTO::cleanup() {
- if (!SaveTemps) {
- // Remove one file, report error if any.
- auto removeFile = [](StringRef FileName) -> void {
- std::error_code EC = sys::fs::remove(FileName, true);
- if (EC &&
- EC != std::make_error_code(std::errc::no_such_file_or_directory))
- errs() << "warning: could not remove the file '" << FileName
- << "': " << EC.message() << "\n";
- };
-
- TimeTraceScope JobScope("Remove DTLTO temporary files");
- for (const auto &Name : CleanupList)
- removeFile(Name);
- // Clean the CleanupList for safety.
- CleanupList.clear();
- }
+ if (SaveTemps)
+ return;
+
+ BackgroundDeleter.removeFiles(std::move(CleanupList), Conf);
}
// Runs the DTLTO thin link phase, producing per-module summary indices,
diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index 4bcf04df89ac9..7dd7d9ea67150 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -541,6 +541,7 @@ static int run(int argc, char **argv) {
"failed to create cache");
check(Lto->run(AddStream, Cache), "LTO::run failed");
+ Lto->waitForCleanup();
return static_cast<int>(HasErrors);
}
>From 95b945af56ded82a0b25b10de754d8520ff70d2e Mon Sep 17 00:00:00 2001
From: Slava Zakharin <szakharin at nvidia.com>
Date: Tue, 21 Jul 2026 10:37:35 -0700
Subject: [PATCH 2/2] [CMake] Added missing LLVM_PTHREAD_LIB dependency.
(#211041)
Required after #209423.
(cherry picked from commit 4f5675a0500f9ccc60dcbabb57e1c4dc88c40a84)
---
llvm/lib/DTLTO/CMakeLists.txt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/llvm/lib/DTLTO/CMakeLists.txt b/llvm/lib/DTLTO/CMakeLists.txt
index 09c8c20b6a475..f40f570932108 100644
--- a/llvm/lib/DTLTO/CMakeLists.txt
+++ b/llvm/lib/DTLTO/CMakeLists.txt
@@ -3,6 +3,9 @@ add_llvm_component_library(LLVMDTLTO
DTLTO.cpp
DTLTODistributionDriver.cpp
+ LINK_LIBS
+ ${LLVM_PTHREAD_LIB}
+
LINK_COMPONENTS
BinaryFormat
Core
More information about the llvm-branch-commits
mailing list