[clang] [lld] [llvm] [DTLTO] Refactor the DTLTO code. (PR #192629)
Konstantin Belochapka via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 2 22:09:20 PDT 2026
https://github.com/kbelochapka updated https://github.com/llvm/llvm-project/pull/192629
>From c6ae223391b31617aa9a051c6630652aacb392c3 Mon Sep 17 00:00:00 2001
From: Konstantin Belochapka <konstantin.belochapka at sony.com>
Date: Tue, 3 Mar 2026 00:58:28 -0800
Subject: [PATCH 01/11] [DTLTO] Refactor the DTLTO code.
DTLTO implementation code has been moved from `llvm/lib/LTO/` to `llvm/lib/DTLTO/`.
This refactor does not change any externally visible behavior,
so existing DTLTO tests and documentation remain valid.
The move was done to reduce code duplication, improve maintainability,
and make it easier to adopt future performance improvements.
---
cross-project-tests/dtlto/dtlto-cache.test | 4 +-
cross-project-tests/dtlto/savetemps-lock.test | 4 +-
lld/COFF/LTO.cpp | 19 +-
lld/ELF/LTO.cpp | 17 +-
lld/test/ELF/dtlto/timetrace.test | 4 +-
llvm/include/llvm/DTLTO/DTLTO.h | 399 +++++++++++--
llvm/include/llvm/LTO/Config.h | 17 +
llvm/include/llvm/LTO/LTO.h | 55 +-
llvm/lib/DTLTO/DTLTO.cpp | 444 ++++++++++++--
llvm/lib/LTO/LTO.cpp | 552 +++---------------
llvm/test/ThinLTO/X86/dtlto/timetrace.ll | 6 +
llvm/tools/llvm-lto2/CMakeLists.txt | 1 +
llvm/tools/llvm-lto2/llvm-lto2.cpp | 27 +-
13 files changed, 906 insertions(+), 643 deletions(-)
diff --git a/cross-project-tests/dtlto/dtlto-cache.test b/cross-project-tests/dtlto/dtlto-cache.test
index 5dd67a50ab2c3..82c13eb80dca0 100644
--- a/cross-project-tests/dtlto/dtlto-cache.test
+++ b/cross-project-tests/dtlto/dtlto-cache.test
@@ -31,8 +31,8 @@ RUN: -Wl,--thinlto-remote-compiler=%clang \
RUN: -Wl,--thinlto-cache-dir=cache.dir \
RUN: -Wl,--save-temps
-# Check that there are no backend compilation jobs occurred.
-RUN: grep -wo args populate2.*.dist-file.json | wc -l | grep -qx "\s*1"
+# At this point we expect full cache hits. Distributor will not be invoked and a Json file will not be created.
+RUN: not ls cache.dir/*.dist-file.json
RUN: ls cache.dir | count 3
RUN: %clang -O0 --target=x86_64-linux-gnu -flto=thin -c foo.c -o foo.O0.o
diff --git a/cross-project-tests/dtlto/savetemps-lock.test b/cross-project-tests/dtlto/savetemps-lock.test
index 822744b1dfc4f..17ada42a5ef14 100644
--- a/cross-project-tests/dtlto/savetemps-lock.test
+++ b/cross-project-tests/dtlto/savetemps-lock.test
@@ -30,12 +30,12 @@ RUN: | FileCheck %s --implicit-check-not=warning
CHECK-DAG: Lock any files in the output directory.
CHECK-DAG: warning: could not remove the file 'locked{{/|\\}}t.[[#PID:]].dist-file.json': {{.*}}
# Filename composition: <archive>(<member> at <offset>).<task>.<pid>.<task>.<pid>.native.o.
+CHECK-DAG: warning: could not remove the file 'locked{{/|\\}}t.a(t1.o at [[#T1_OFFSET:]]).1.[[#%X,HEXPID:]].o': {{.*}}
CHECK-DAG: warning: could not remove the file 'locked{{/|\\}}t.a(t1.o at [[#T1_OFFSET:]]).1.[[#%X,HEXPID:]].1.[[#PID]].native.o': {{.*}}
CHECK-DAG: warning: could not remove the file 'locked{{/|\\}}t.a(t1.o at [[#T1_OFFSET]]).1.[[#%X,HEXPID]].1.[[#PID]].native.o.thinlto.bc': {{.*}}
+CHECK-DAG: warning: could not remove the file 'locked{{/|\\}}t.a(t2.o at [[#T2_OFFSET:]]).2.[[#%X,HEXPID]].o': {{.*}}
CHECK-DAG: warning: could not remove the file 'locked{{/|\\}}t.a(t2.o at [[#T2_OFFSET:]]).2.[[#%X,HEXPID]].2.[[#PID]].native.o': {{.*}}
CHECK-DAG: warning: could not remove the file 'locked{{/|\\}}t.a(t2.o at [[#T2_OFFSET]]).2.[[#%X,HEXPID]].2.[[#PID]].native.o.thinlto.bc': {{.*}}
-CHECK-DAG: warning: could not remove temporary DTLTO input file 'locked{{/|\\}}t.a(t1.o at [[#T1_OFFSET]]).1.[[#%X,HEXPID]].o': {{.*}}
-CHECK-DAG: warning: could not remove temporary DTLTO input file 'locked{{/|\\}}t.a(t2.o at [[#T2_OFFSET]]).2.[[#%X,HEXPID]].o': {{.*}}
#--- t1.c
__attribute__((retain)) int t1(int x) { return x; }
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index aa47ff26c6b4a..123d6c51620c6 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -126,17 +126,7 @@ BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) {
// Initialize ltoObj.
lto::ThinBackend backend;
- if (!ctx.config.dtltoDistributor.empty()) {
- backend = lto::createOutOfProcessThinBackend(
- llvm::hardware_concurrency(ctx.config.thinLTOJobs),
- /*OnWrite=*/nullptr,
- /*ShouldEmitIndexFiles=*/false,
- /*ShouldEmitImportFiles=*/false, ctx.config.outputFile,
- ctx.config.dtltoDistributor, ctx.config.dtltoDistributorArgs,
- ctx.config.dtltoCompiler, ctx.config.dtltoCompilerPrependArgs,
- ctx.config.dtltoCompilerArgs, !ctx.config.saveTempsArgs.empty(),
- createAddBufferFn(files, file_names));
- } else if (ctx.config.thinLTOIndexOnly) {
+ if (ctx.config.thinLTOIndexOnly || !ctx.config.dtltoDistributor.empty()) {
auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
backend = lto::createWriteIndexesThinBackend(
llvm::hardware_concurrency(ctx.config.thinLTOJobs),
@@ -155,7 +145,12 @@ BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) {
else
ltoObj = std::make_unique<lto::DTLTO>(
createConfig(), backend, ctx.config.ltoPartitions,
- llvm::lto::LTO::LTOKind::LTOK_Default, ctx.config.outputFile,
+ llvm::lto::LTO::LTOKind::LTOK_Default, nullptr,
+ ctx.config.thinLTOEmitImportsFiles, ctx.config.thinLTOIndexOnly,
+ ctx.config.outputFile, ctx.config.dtltoDistributor,
+ ctx.config.dtltoDistributorArgs, ctx.config.dtltoCompiler,
+ ctx.config.dtltoCompilerPrependArgs, ctx.config.dtltoCompilerArgs,
+ createAddBufferFn(files, file_names),
!ctx.config.saveTempsArgs.empty());
}
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 27be8859a3f65..fbab04d6b704f 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -184,21 +184,13 @@ BitcodeCompiler::BitcodeCompiler(Ctx &ctx) : ctx(ctx) {
// Initialize ltoObj.
lto::ThinBackend backend;
auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
- if (ctx.arg.thinLTOIndexOnly) {
+ if (ctx.arg.thinLTOIndexOnly || !ctx.arg.dtltoDistributor.empty()) {
backend = lto::createWriteIndexesThinBackend(
llvm::hardware_concurrency(ctx.arg.thinLTOJobs),
std::string(ctx.arg.thinLTOPrefixReplaceOld),
std::string(ctx.arg.thinLTOPrefixReplaceNew),
std::string(ctx.arg.thinLTOPrefixReplaceNativeObject),
ctx.arg.thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
- } else if (!ctx.arg.dtltoDistributor.empty()) {
- backend = lto::createOutOfProcessThinBackend(
- llvm::hardware_concurrency(ctx.arg.thinLTOJobs), onIndexWrite,
- ctx.arg.thinLTOEmitIndexFiles, ctx.arg.thinLTOEmitImportsFiles,
- ctx.arg.outputFile, ctx.arg.dtltoDistributor,
- ctx.arg.dtltoDistributorArgs, ctx.arg.dtltoCompiler,
- ctx.arg.dtltoCompilerPrependArgs, ctx.arg.dtltoCompilerArgs,
- !ctx.arg.saveTempsArgs.empty(), createAddBufferFn(files, filenames));
} else {
backend = lto::createInProcessThinBackend(
llvm::heavyweight_hardware_concurrency(ctx.arg.thinLTOJobs),
@@ -210,6 +202,7 @@ BitcodeCompiler::BitcodeCompiler(Ctx &ctx) : ctx(ctx) {
llvm::lto::LTO::LTOKind::LTOK_UnifiedThin,
llvm::lto::LTO::LTOKind::LTOK_UnifiedRegular,
llvm::lto::LTO::LTOKind::LTOK_Default};
+
if (ctx.arg.dtltoDistributor.empty())
ltoObj = std::make_unique<lto::LTO>(createConfig(ctx), backend,
ctx.arg.ltoPartitions,
@@ -217,7 +210,11 @@ BitcodeCompiler::BitcodeCompiler(Ctx &ctx) : ctx(ctx) {
else
ltoObj = std::make_unique<lto::DTLTO>(
createConfig(ctx), backend, ctx.arg.ltoPartitions,
- ltoModes[ctx.arg.ltoKind], ctx.arg.outputFile,
+ ltoModes[ctx.arg.ltoKind], onIndexWrite, ctx.arg.thinLTOEmitIndexFiles,
+ ctx.arg.thinLTOEmitImportsFiles, ctx.arg.outputFile,
+ ctx.arg.dtltoDistributor, ctx.arg.dtltoDistributorArgs,
+ ctx.arg.dtltoCompiler, ctx.arg.dtltoCompilerPrependArgs,
+ ctx.arg.dtltoCompilerArgs, createAddBufferFn(files, filenames),
!ctx.arg.saveTempsArgs.empty());
// Initialize usedStartStop.
if (ctx.bitcodeFiles.empty())
diff --git a/lld/test/ELF/dtlto/timetrace.test b/lld/test/ELF/dtlto/timetrace.test
index 4567b0a1d4b02..f43f51284b5c4 100644
--- a/lld/test/ELF/dtlto/timetrace.test
+++ b/lld/test/ELF/dtlto/timetrace.test
@@ -32,12 +32,12 @@ RUN: %python filter_order_and_pprint.py %t.json | FileCheck %s
## Check that DTLTO add input file events are recorded.
CHECK: "name": "Add input for DTLTO"
CHECK: "name": "Add input for DTLTO"
-CHECK: "name": "Remove temporary inputs for DTLTO"
+CHECK: "name": "Remove DTLTO temporary files"
CHECK: "name": "Serialize 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 temporary inputs for DTLTO"
+CHECK: "name": "Total Remove DTLTO temporary files"
CHECK-SAME: "count": 1,
CHECK: "name": "Total Serialize bitcode input for DTLTO"
CHECK-SAME: "count": 1,
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index 5a8566b71bc16..248b86674e6bd 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -1,83 +1,402 @@
-//===- DTLTO.h - Distributed ThinLTO functions and classes ----*- C++ -*-===//
+//===- DTLTO.h - Distributed ThinLTO implementation -----------------------===//
//
// 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
//
-//===---------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===//
+//
+// \file
+// Declarations for Distributed ThinLTO, including the DTLTO class and the
+// distribution driver. The implementation focuses on preparing input files for
+// distribution.
+//
+//===----------------------------------------------------------------------===//
#ifndef LLVM_DTLTO_DTLTO_H
#define LLVM_DTLTO_DTLTO_H
+#include "llvm/ADT/SmallString.h"
#include "llvm/LTO/LTO.h"
-#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Signals.h"
+
+#include <functional>
+#include <vector>
namespace llvm {
namespace lto {
-// The purpose of this class is to prepare inputs so that distributed ThinLTO
-// backend compilations can succeed.
-//
-// For distributed compilation, each input must exist as an individual bitcode
-// file on disk and be loadable via its ModuleID. This requirement is not met
-// for archive members, as an archive is a collection of files rather than a
-// standalone file. Similarly, for FatLTO objects, the bitcode is stored in a
-// section of the containing ELF object file. To address this, the class ensures
-// that an individual bitcode file exists for each input (by writing it out if
-// necessary) and that the ModuleID is updated to point to it. Module IDs are
-// also normalized on Windows to remove short 8.3 form paths that cannot be
-// loaded on remote machines.
-//
-// The class ensures that lto::InputFile objects are preserved until enough of
-// the LTO pipeline has executed to determine the required per-module
-// information, such as whether a module will participate in ThinLTO.
+class DTLTO;
+
+/// Prepares inputs for Distributed ThinLTO so that backend compilations can 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
+/// 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. 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
+/// handleArchiveInputs().
class DTLTO : public LTO {
using Base = LTO;
public:
LLVM_ABI DTLTO(Config Conf, ThinBackend Backend,
unsigned ParallelCodeGenParallelismLevel, LTOKind LTOMode,
- StringRef LinkerOutputFile, bool SaveTemps)
+ IndexWriteCallback OnWrite, bool EmitIndexFiles,
+ bool EmitImportsFiles, StringRef LinkerOutputFile,
+ StringRef Distributor, ArrayRef<StringRef> DistributorArgs,
+ StringRef RemoteCompiler,
+ ArrayRef<StringRef> RemoteCompilerPrependArgs,
+ ArrayRef<StringRef> RemoteCompilerArgs,
+ AddBufferFn AddBufferArg, bool SaveTempsArg)
: Base(std::move(Conf), Backend, ParallelCodeGenParallelismLevel,
LTOMode),
- LinkerOutputFile(LinkerOutputFile), SaveTemps(SaveTemps) {
+ OnWriteCb(OnWrite), ShouldEmitIndexFiles(EmitIndexFiles),
+ ShouldEmitImportFiles(EmitImportsFiles),
+ DistributorParams{Distributor, DistributorArgs,
+ RemoteCompiler, RemoteCompilerPrependArgs,
+ RemoteCompilerArgs, LinkerOutputFile},
+ AddBuffer(AddBufferArg), SaveTemps(SaveTempsArg) {
assert(!LinkerOutputFile.empty() && "expected a valid linker output file");
}
- // Add an input file and prepare it for distribution.
+ /// Add an input file and prepare it for distribution.
+ ///
+ /// This function performs the following tasks:
+ /// 1. Add the input file to the LTO object's list of input files.
+ /// 2. For individual bitcode file inputs on Windows only, overwrite the
+ /// module
+ /// ID with a normalized path to remove short 8.3 form components.
+ /// 3. For thin archive members, overwrite the module ID with the path
+ /// (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()).
LLVM_ABI Expected<std::shared_ptr<InputFile>>
addInput(std::unique_ptr<InputFile> InputPtr) override;
-protected:
- // 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.
- LLVM_ABI llvm::Error serializeInputsForDistribution() override;
+ /// Runs the DTLTO pipeline. This function calls the supplied AddStream
+ /// function to add native object files to the link.
+ ///
+ /// The Cache parameter is optional. If supplied, it will be used to cache
+ /// native object files and add them to the link.
+ ///
+ /// The client will receive at most one callback (via either AddStream or
+ /// Cache) for each task identifier.
+ LLVM_ABI virtual Error run(AddStreamFn AddStream,
+ FileCache Cache = {}) override;
+
+private:
+ /// DTLTO archives 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.
+ LLVM_ABI Error handleArchiveInputs();
+
+ // Remove temporary files created to enable distribution.
+ LLVM_ABI void cleanup();
+
+public:
+ // Mutable and const accessors to the LTO configuration object.
+ Config &getConfig() { return Conf; }
+ const Config &getConfig() const { return Conf; }
- LLVM_ABI void cleanup() override;
+ // Set the LTO kind.
+ void setLTOMode(LTOKind Knd) { LTOMode = Knd; }
+ // Replace the ThinLTO backend (e.g. WriteIndexesThinBackend for the thin
+ // link).
+ void setThinBackend(ThinBackend Backend) { ThinLTO.Backend = Backend; }
private:
- // Bump allocator for a purpose of saving updated module IDs.
+ // Bump allocator for saving updated module IDs.
BumpPtrAllocator PtrAlloc;
+ // String saver backed by PtrAlloc.
StringSaver Saver{PtrAlloc};
- /// The output file to which this LTO invocation will contribute.
- StringRef LinkerOutputFile;
+ using SString = SmallString<128>;
- /// The normalized output directory, derived from LinkerOutputFile.
- StringRef LinkerOutputDir;
+ // Function pointer that defines the callback to add a pre-existing file.
+ AddBufferFn AddBuffer;
+ // Count of jobs that hit the cache.
+ std::atomic<size_t> CachedJobs{0};
+ // Normalized output directory from LinkerOutputFile.
+ SString LinkerOutputDir;
+ // Keep temporary files when true.
+ bool SaveTemps = false;
- /// Controls preservation of any created temporary files.
- bool SaveTemps;
+public:
+ struct Job {
+ // Task index (combines RegularLTO parallel codegen offset with module
+ // index).
+ unsigned Task;
+ // Module identifier (bitcode path) for the ThinLTO module.
+ StringRef ModuleID;
+ // Native object path.
+ StringRef NativeObjectPath;
+ // Per-module summary index path.
+ StringRef SummaryIndexPath;
+ // Per-module imports list path.
+ StringRef ImportsPath;
+ // Bitcode files from which this module imports.
+ ArrayRef<std::string> ImportsFiles;
+ // Cache key from thin link.
+ std::string CacheKey;
+ // On cache miss, stream used to store the compiled object in the cache.
+ AddStreamFn CacheAddStream;
+ // Set when the object was already supplied via the cache callback.
+ bool Cached = false;
+ };
- // Array of input bitcode files for LTO.
- std::vector<std::shared_ptr<lto::InputFile>> InputFiles;
+private:
+ // Backend compilation jobs, one per module.
+ SmallVector<Job> Jobs;
+ // Task index offset for first ThinLTO job.
+ unsigned ThinLTOTaskOffset;
+ // Optional cache for native objects.
+ FileCache Cache;
+ // Keep summary index files when true.
+ bool ShouldEmitIndexFiles = false;
+ // Keep summary import files when true.
+ bool ShouldEmitImportFiles = false;
+ // On index file write callback.
+ IndexWriteCallback OnWriteCb;
- // Cache of whether a path refers to a thin archive.
- StringMap<bool> ArchiveIsThinCache;
+ /// Probes the LTO cache for a compiled native object for the given job.
+ ///
+ /// If no cache is configured (Cache.isValid() is false), returns immediately
+ /// without modifying the job.
+ ///
+ /// Otherwise, looks up the cache using J.CacheKey. On a cache hit, the cached
+ /// object has already been passed to the linker via the Cache callback, so
+ /// J.Cached is set to true, CachedJobs is incremented, and the distributor
+ /// can skip this job. On a cache miss, the cache returns an AddStreamFn; we
+ /// store it in J.CacheAddStream for use when storing the freshly compiled
+ /// object after the distributor runs.
+ ///
+ /// \param J The job to check. Must have Task, CacheKey, and ModuleID set.
+ /// On return, J.Cached and J.CacheAddStream may be updated.
+ ///
+ /// \returns Error::success() on success, or an Error from the cache lookup.
+ Error checkCacheHit(Job &J);
+
+ /// Prepares a single DTLTO backend compilation job for a ThinLTO module.
+ ///
+ /// Called once per module during performCodegen(). This function:
+ ///
+ /// 1. Computes output paths for the native object and summary index files.
+ /// Both are placed in the linker output directory with names of the form
+ /// stem.Task.UID.native.o and stem.Task.UID.thinlto.bc, where stem is
+ /// derived from ModulePath.
+ ///
+ /// 2. Initializes the Job struct with Task, ModuleID (ModulePath), paths,
+ /// ImportsFiles and CacheKey from thin link results, and default values
+ /// for CacheAddStream and Cached.
+ ///
+ /// 3. Calls checkCacheHit() to probe the cache. On a cache hit, J.Cached is
+ /// set and the cached object has already been passed to the linker; the
+ /// 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
+ /// remote compiler will read this via -fthinlto-index=.
+ ///
+ /// 5. 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
+ /// module.
+ /// \param Task The task index (combines RegularLTO.ParallelCodeGen
+ /// parallelism offset with the module index).
+ ///
+ /// \returns Error::success() on success, or an Error from saveBuffer() or
+ /// checkCacheHit().
+ Error prepareDtltoJob(StringRef ModulePath, unsigned Task);
+
+ /// Initializes DTLTO state and prepares a job for each ThinLTO module.
+ ///
+ /// Sets task offset, target triple, UID, and Jobs. For each module, calls
+ /// prepareDtltoJob() to assign output paths, check the cache, and write
+ /// summary index shards to disk when needed.
+ ///
+ /// \returns Error::success() on success, or an Error from prepareDtltoJob.
+ Error prepareDtltoJobs();
- // Determines if the file at the given path is a thin archive.
+ /// Runs the DTLTO code generation phase. Must be invoked after thinLink().
+ ///
+ /// Builds Clang options, emits a JSON manifest describing compilation jobs,
+ /// and invokes the distributor to compile ThinLTO modules remotely. Cache
+ /// hits are skipped; the distributor runs only when there are uncached jobs.
+ ///
+ /// \returns Error::success() on success, or an Error on manifest or
+ /// distributor failure.
+ Error performCodegen();
+
+ /// Adds compiled object files to the link for each non-cached job.
+ ///
+ /// Loads each native object from disk, then either writes it to the cache
+ /// (which adds it to the link via the cache callback) or passes it to
+ /// AddStreamFunc directly when caching is disabled.
+ ///
+ /// \returns Error::success() on success, or an Error if a file cannot be read
+ /// or a cache stream cannot be obtained.
+ Error addObjectFilesToLink();
+
+ // Determines if a file at the given path is a thin archive file.
+ //
+ // Uses a cache to avoid repeatedly reading the same file; reads only the
+ // header (magic bytes) to identify the archive type.
Expected<bool> isThinArchive(const StringRef ArchivePath);
+
+ // Unique ID for this link (process ID as string).
+ std::string UID;
+
+ // Input files registered for this link (same order as addInput).
+ std::vector<std::shared_ptr<lto::InputFile>> InputFiles;
+ // Cache for isThinArchive() results keyed by archive path.
+ StringMap<bool> ArchiveIsThinCache;
+ // Callback used by run() to add native objects to the link.
+ AddStreamFn AddStreamFunc = nullptr;
+ // Per-task summary index shards from the thin link (in-memory buffers).
+ std::vector<SmallString<0>> SummaryIndexFiles;
+ // Per-task imported bitcode paths from the thin link.
+ std::vector<std::vector<std::string>> ImportsFilesLists;
+ // Per-task cache keys for incremental builds from the thin link.
+ std::vector<std::string> CacheKeysList;
+
+ /// Runs the DTLTO thin link phase, producing per-module summary indices,
+ /// import lists, and cache keys for distribution.
+ ///
+ /// This function configures a WriteIndexesThinBackend and invokes the base
+ /// LTO run, which performs the thin link. The thin link resolves cross-module
+ /// references and produces:
+ ///
+ /// - SummaryIndexFiles: per-module summary index shards (in-memory buffers)
+ /// - ImportsFilesLists: per-module lists of imported bitcode files
+ /// - CacheKeysList: per-module cache keys for incremental builds
+ /// - ModuleNames: per-module identifiers
+ ///
+ /// The Config callbacks (GetSummaryIndexStreamFunc, GetCacheKeysListRefFunc,
+ /// GetImportsListRefFunc) are installed so the WriteIndexesThinBackend
+ /// populates these arrays. performCodegen() later uses them to prepare
+ /// backend jobs.
+ ///
+ /// \returns Error::success() if the thin link completes, or an Error from
+ /// Base::run().
+ Error performThinLink();
+
+ /// Derive a set of Clang options that will be shared/common for all DTLTO
+ /// backend compilations. We are intentionally minimal here as these options
+ /// must remain synchronized with the behavior of Clang. DTLTO does not
+ /// support all the features available with in-process LTO. More features are
+ /// expected to be added over time. Users can specify Clang options directly
+ /// if a feature is not supported. Note that explicitly specified options that
+ /// imply additional input or output file dependencies must be communicated to
+ /// the distribution system, potentially by setting extra options on the
+ /// distributor program.
+ void buildCommonRemoteCompilerOptions();
+
+public:
+ // Parameters and shared state for DistributorDriver class.
+ struct DistributionDriverParams {
+ LLVM_ABI
+ DistributionDriverParams() = default;
+ DistributionDriverParams(StringRef DistributorArg,
+ ArrayRef<StringRef> DistributorArgsArg,
+ StringRef RemoteCompilerArg,
+ ArrayRef<StringRef> RemoteCompilerPrependArgsArg,
+ ArrayRef<StringRef> RemoteCompilerArgsArg,
+ StringRef LinkerOutputFileArg)
+ : DistributorPath(DistributorArg), DistributorArgs(DistributorArgsArg),
+ RemoteCompiler(RemoteCompilerArg),
+ RemoteCompilerPrependArgs(RemoteCompilerPrependArgsArg),
+ RemoteCompilerArgs(RemoteCompilerArgsArg),
+ LinkerOutputFile(LinkerOutputFileArg) {}
+
+ // Output linker file path.
+ SString LinkerOutputFile;
+ // Path to the distributor executable.
+ SString DistributorPath;
+ // Arguments passed to the distributor.
+ ArrayRef<StringRef> DistributorArgs;
+ // Compiler executabl invoked by the distributor (e.g., Clang).
+ SString RemoteCompiler;
+ // Options prepended to remote compiler args.
+ ArrayRef<StringRef> RemoteCompilerPrependArgs;
+ // User-supplied options passed to remote compiler.
+ ArrayRef<StringRef> RemoteCompilerArgs;
+
+ // Common Clang options for all compilation jobs.
+ SmallVector<StringRef, 0> CodegenOptions;
+ // Input paths shared across compilation jobs.
+ DenseSet<StringRef> CommonInputs;
+ // Target triple for compilations.
+ Triple TargetTriple;
+ };
+
+private:
+ // Distributor configuration class instance.
+ DistributionDriverParams DistributorParams;
+
+ // Cleanup files list.
+ std::vector<std::string> CleanupList;
+
+ // Record a file for cleanup and register signal-time removal if requested.
+ void addToCleanup(StringRef Filename) {
+ CleanupList.push_back(Filename.str());
+ sys::RemoveFileOnSignal(Filename);
+ }
+};
+
+namespace {
+constexpr StringRef BCError = "DTLTO backend compilation: ";
+}
+
+class DistributionDriver {
+public:
+ LLVM_ABI
+ DistributionDriver(DTLTO::DistributionDriverParams &ParamsArg,
+ ArrayRef<DTLTO::Job> JobsArg, bool SaveTempsArg,
+ std::function<void(StringRef)> AddToClenupArg)
+ : Params{ParamsArg}, Jobs{JobsArg}, SaveTemps{SaveTempsArg},
+ AddToCleanup{AddToClenupArg} {};
+
+private:
+ DTLTO::DistributionDriverParams &Params;
+ // Keep temporary files when true.
+ bool SaveTemps = false;
+ std::function<void(StringRef)> AddToCleanup;
+ ArrayRef<DTLTO::Job> Jobs;
+ SmallString<128> DistributorJsonFile;
+
+ // Generates a JSON file describing the compilations
+ Error emitJson();
+ // Saves JSON file on a filesystem.
+ Error saveJson();
+
+public:
+ /// Invokes the distributor to compile bitcode modules remotely.
+ ///
+ /// Runs the distributor with the
+ /// JSON manifest path; the distributor spawns remote compiler processes.
+ ///
+ /// \returns Error::success() on success, or an Error if the distributor
+ /// fails.
+ Error operator()();
};
} // namespace lto
diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h
index 2aeb902bcfccf..33d93c21c755a 100644
--- a/llvm/include/llvm/LTO/Config.h
+++ b/llvm/include/llvm/LTO/Config.h
@@ -292,6 +292,23 @@ struct Config {
LLVM_ABI Error addSaveTemps(std::string OutputFileName,
bool UseInputModulePath = false,
const DenseSet<StringRef> &SaveTempsArgs = {});
+
+ /// Called by WriteIndexesThinBackend when it needs to write a bitcode
+ /// module's summary index. The callback should return a stream to write
+ /// the index into. If the callback returns nullptr, the backend falls back
+ /// to writing the summary index to a file.
+ std::function<std::unique_ptr<raw_pwrite_stream>(size_t Task)>
+ OnSummaryIndexStoreCb;
+ /// Called by WriteIndexesThinBackend when it needs to store a bitcode
+ /// module's imports list. The callback should return a vector that the
+ /// backend will populate with the imported module paths. If not set, the
+ /// backend writes the imports list to a file instead.
+ std::function<std::vector<std::string> &(size_t Task)> OnImportsListStoreCb;
+ /// Called by WriteIndexesThinBackend when it needs to store a bitcode
+ /// module's cache key. The callback should return a string that the backend
+ /// will fill with the computed cache key. If not set, the cache key is
+ /// discarded.
+ std::function<std::string &(size_t Task)> OnCacheKeyStoreCb;
};
struct LTOLLVMDiagnosticHandler : public DiagnosticHandler {
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index aba2661e81c47..0cf51c3eaa351 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -287,16 +287,23 @@ class ThinBackendProc {
virtual bool isSensitiveToInputOrder() { return false; }
// Write sharded indices and (optionally) imports to disk
- LLVM_ABI Error emitFiles(const FunctionImporter::ImportMapTy &ImportList,
+ LLVM_ABI Error emitFiles(const FunctionImporter::ImportMapTy &ImportList, unsigned Task,
StringRef ModulePath,
const std::string &NewModulePath) const;
// Write sharded indices to SummaryPath, (optionally) imports to disk, and
- // (optionally) record imports in ImportsFiles.
+ // (optionally) record imports in ImportsFiles. When cache key parameters are
+ // provided and GetCacheKeysListRefFunc is set, also computes and stores the
+ // LTO cache key for the module.
LLVM_ABI Error emitFiles(
- const FunctionImporter::ImportMapTy &ImportList, StringRef ModulePath,
+ const FunctionImporter::ImportMapTy &ImportList, unsigned Task, StringRef ModulePath,
const std::string &NewModulePath, StringRef SummaryPath,
- std::optional<std::reference_wrapper<ImportsFilesContainer>> ImportsFiles)
+ std::optional<std::reference_wrapper<ImportsFilesContainer>> ImportsFiles,
+ const FunctionImporter::ExportSetTy *ExportList = nullptr,
+ const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> *ResolvedODR =
+ nullptr,
+ const DenseSet<GlobalValue::GUID> *CfiFunctionDefs = nullptr,
+ const DenseSet<GlobalValue::GUID> *CfiFunctionDecls = nullptr)
const;
};
@@ -351,35 +358,6 @@ LLVM_ABI ThinBackend createInProcessThinBackend(
ThreadPoolStrategy Parallelism, IndexWriteCallback OnWrite = nullptr,
bool ShouldEmitIndexFiles = false, bool ShouldEmitImportsFiles = false);
-/// This ThinBackend generates the index shards and then runs the individual
-/// backend jobs via an external process. It takes the same parameters as the
-/// InProcessThinBackend; however, these parameters only control the behavior
-/// when generating the index files for the modules. Additionally:
-/// LinkerOutputFile is a string that should identify this LTO invocation in
-/// the context of a wider build. It's used for naming to aid the user in
-/// identifying activity related to a specific LTO invocation.
-/// Distributor specifies the path to a process to invoke to manage the backend
-/// job execution.
-/// DistributorArgs specifies a list of arguments to be applied to the
-/// distributor.
-/// RemoteCompiler specifies the path to a Clang executable to be invoked for
-/// the backend jobs.
-/// RemoteCompilerPrependArgs specifies a list of prepend arguments to be
-/// applied to the backend compilations.
-/// RemoteCompilerArgs specifies a list of arguments to be applied to the
-/// backend compilations.
-/// SaveTemps is a debugging tool that prevents temporary files created by this
-/// backend from being cleaned up.
-/// AddBuffer is used to add a pre-existing native object buffer to the link.
-LLVM_ABI ThinBackend createOutOfProcessThinBackend(
- ThreadPoolStrategy Parallelism, IndexWriteCallback OnWrite,
- bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles,
- StringRef LinkerOutputFile, StringRef Distributor,
- ArrayRef<StringRef> DistributorArgs, StringRef RemoteCompiler,
- ArrayRef<StringRef> RemoteCompilerPrependArgs,
- ArrayRef<StringRef> RemoteCompilerArgs, bool SaveTemps,
- AddBufferFn AddBuffer);
-
/// This ThinBackend writes individual module indexes to files, instead of
/// running the individual backend jobs. This backend is for distributed builds
/// where separate processes will invoke the real backends.
@@ -467,7 +445,7 @@ class LTO {
///
/// The client will receive at most one callback (via either AddStream or
/// Cache) for each task identifier.
- LLVM_ABI Error run(AddStreamFn AddStream, FileCache Cache = {});
+ LLVM_ABI virtual Error run(AddStreamFn AddStream, FileCache Cache = {});
/// Static method that returns a list of libcall symbols that can be generated
/// by LTO but might not be visible from bitcode symbol table.
@@ -483,13 +461,9 @@ class LTO {
getLibFuncSymbols(const Triple &TT, llvm::StringSaver &Saver);
protected:
- // Called at the start of run().
- virtual Error serializeInputsForDistribution() { return Error::success(); }
-
// Called before returning from run().
virtual void cleanup();
-private:
Config Conf;
struct RegularLTOState {
@@ -547,6 +521,7 @@ class LTO {
DenseMap<GlobalValue::GUID, StringRef> PrevailingModuleForGUID;
} ThinLTO;
+private:
// The global resolution for a particular (mangled) symbol name. This is in
// particular necessary to track whether each symbol can be internalized.
// Because any input file may introduce a new cross-partition reference, we
@@ -648,9 +623,11 @@ class LTO {
mutable bool CalledGetMaxTasks = false;
+protected:
// LTO mode when using Unified LTO.
LTOKind LTOMode;
+private:
// Use Optional to distinguish false from not yet initialized.
std::optional<bool> EnableSplitLTOUnit;
@@ -687,6 +664,8 @@ class LTO {
addInput(std::unique_ptr<lto::InputFile> InputPtr) {
return std::shared_ptr<lto::InputFile>(InputPtr.release());
}
+
+
};
/// The resolution for a symbol. The linker must provide a SymbolResolution for
diff --git a/llvm/lib/DTLTO/DTLTO.cpp b/llvm/lib/DTLTO/DTLTO.cpp
index b5e233fe0ef54..94bb75c6eb7c1 100644
--- a/llvm/lib/DTLTO/DTLTO.cpp
+++ b/llvm/lib/DTLTO/DTLTO.cpp
@@ -1,4 +1,4 @@
-//===- Dtlto.cpp - Distributed ThinLTO implementation --------------------===//
+//===- DTLTO.cpp - Distributed ThinLTO implementation ---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -14,6 +14,8 @@
#include "llvm/DTLTO/DTLTO.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -21,10 +23,10 @@
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Archive.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/JSON.h"
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
#ifdef _WIN32
@@ -67,7 +69,7 @@ Expected<StringRef> normalizePath(StringRef Path, StringSaver &Saver) {
if (Path.empty())
return Path;
SmallString<256> Expanded;
- if (std::error_code EC = llvm::sys::windows::makeLongFormPath(Path, Expanded))
+ if (std::error_code EC = sys::windows::makeLongFormPath(Path, Expanded))
return createStringError(inconvertibleErrorCode(),
"Normalization failed for path %s: %s",
Path.str().c_str(), EC.message().c_str());
@@ -88,8 +90,9 @@ SmallString<256> computeThinArchiveMemberPath(StringRef ArchivePath,
if (sys::path::is_relative(MemberName)) {
MemberPath = sys::path::parent_path(ArchivePath);
sys::path::append(MemberPath, MemberName);
- } else
+ } else {
MemberPath = MemberName;
+ }
sys::path::remove_dots(MemberPath, /*remove_dot_dot=*/true);
return MemberPath;
}
@@ -97,10 +100,6 @@ SmallString<256> computeThinArchiveMemberPath(StringRef ArchivePath,
} // namespace
// Determines if a file at the given path is a thin archive file.
-//
-// This function uses a cache to avoid repeatedly reading the same file.
-// It reads only the header portion (magic bytes) of the file to identify
-// the archive type.
Expected<bool> lto::DTLTO::isThinArchive(const StringRef ArchivePath) {
// Return cached result if available.
auto Cached = ArchiveIsThinCache.find(ArchivePath);
@@ -141,17 +140,6 @@ Expected<bool> lto::DTLTO::isThinArchive(const StringRef ArchivePath) {
}
// Add an input file and prepare it for distribution.
-//
-// This function performs the following tasks:
-// 1. Add the input file to the LTO object's list of input files.
-// 2. For individual bitcode file inputs on Windows only, overwrite the module
-// ID with a normalized path to remove short 8.3 form components.
-// 3. For thin archive members, overwrite the module ID with the path
-// (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()).
Expected<std::shared_ptr<lto::InputFile>>
lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) {
TimeTraceScope TimeScope("Add input for DTLTO");
@@ -205,7 +193,8 @@ lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) {
// Get the normalized output directory, if we haven't already.
if (LinkerOutputDir.empty()) {
- auto N = normalizePath(sys::path::parent_path(LinkerOutputFile), Saver);
+ auto N = normalizePath(
+ sys::path::parent_path(DistributorParams.LinkerOutputFile), Saver);
if (!N)
return N.takeError();
LinkerOutputDir = *N;
@@ -223,43 +212,416 @@ lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) {
}
// 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.
-llvm::Error lto::DTLTO::serializeInputsForDistribution() {
+// distribution.
+Error lto::DTLTO::handleArchiveInputs() {
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);
+ MemoryBufferRef Buf = Input->getFileBuffer();
+ if (Error Err = save(Buf.getBuffer(), ModuleId))
+ return Err;
// Cleanup this file on abnormal process exit.
if (!SaveTemps)
- llvm::sys::RemoveFileOnSignal(ModuleId);
- if (Error EC = save(Input.get(), ModuleId))
- return EC;
+ addToCleanup(ModuleId);
}
-
return Error::success();
}
-// Remove serialized inputs created to enable distribution.
+// Remove temporary files created to enable distribution.
void lto::DTLTO::cleanup() {
if (!SaveTemps) {
- TimeTraceScope TimeScope("Remove temporary inputs for DTLTO");
- for (auto &Input : InputFiles) {
- if (!Input->getSerializeForDistribution())
- continue;
- std::error_code EC =
- sys::fs::remove(Input->getName(), /*IgnoreNonExisting=*/true);
+ // 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 temporary DTLTO input file '"
- << Input->getName() << "': " << EC.message() << "\n";
+ errs() << "warning: could not remove the file '" << FileName
+ << "': " << EC.message() << "\n";
+ };
+
+ TimeTraceScope JobScope("Remove DTLTO temporary files");
+ for (const auto &Name : CleanupList) {
+ removeFile(Name);
+ }
+ }
+ // Base::cleanup();
+}
+
+// Runs the DTLTO thin link phase, producing per-module summary indices,
+// import lists, and cache keys for distribution.
+Error lto::DTLTO::performThinLink() {
+ auto ThinIndexBackend = lto::createWriteIndexesThinBackend(
+ hardware_concurrency(), "", "", "", true, nullptr, nullptr);
+ setThinBackend(ThinIndexBackend);
+ setLTOMode(lto::LTO::LTOKind::LTOK_UnifiedThin);
+
+ size_t NumTasks = getMaxTasks();
+ SummaryIndexFiles.resize(NumTasks);
+ ImportsFilesLists.resize(NumTasks);
+ CacheKeysList.resize(NumTasks);
+
+ lto::Config &Cfg = getConfig();
+ Cfg.OnSummaryIndexStoreCb =
+ [&](size_t task) -> std::unique_ptr<raw_svector_ostream> {
+ return std::make_unique<raw_svector_ostream>(SummaryIndexFiles[task]);
+ };
+ Cfg.OnCacheKeyStoreCb = [&](size_t task) -> std::string & {
+ return CacheKeysList[task];
+ };
+ Cfg.OnImportsListStoreCb = [&](size_t task) -> std::vector<std::string> & {
+ return ImportsFilesLists[task];
+ };
+
+ return Base::run(AddStreamFunc, {});
+}
+
+// Runs the DTLTO pipeline.
+LLVM_ABI Error lto::DTLTO::run(AddStreamFn AddStream, FileCache CacheParam) {
+ scope_exit CleanUp([this]() { cleanup(); });
+
+ AddStreamFunc = AddStream;
+ Cache = std::move(CacheParam);
+ Conf.Dtlto = 1;
+ UID = itostr(sys::Process::getProcessId());
+
+ if (Error Err = performThinLink())
+ return Err;
+
+ ThinLTOTaskOffset = RegularLTO.ParallelCodeGenParallelismLevel;
+ DistributorParams.TargetTriple = RegularLTO.CombinedModule->getTargetTriple();
+
+ if (Error Err = prepareDtltoJobs())
+ return Err;
+ if (Error Err = handleArchiveInputs())
+ return Err;
+ if (Error Err = performCodegen())
+ return Err;
+ if (Error Err = addObjectFilesToLink())
+ return Err;
+ return Error::success();
+}
+
+// Probes the LTO cache for a compiled native object for the given job.
+Error lto::DTLTO::checkCacheHit(Job &J) {
+ if (!Cache.isValid())
+ return Error::success();
+
+ auto CacheAddStreamExp = Cache(J.Task, J.CacheKey, J.ModuleID);
+ if (Error Err = CacheAddStreamExp.takeError())
+ return Err;
+ AddStreamFn &CacheAddStream = *CacheAddStreamExp;
+ // If CacheAddStream is null, we have a cache hit and at this point
+ // object file is already passed back to the linker.
+ if (!CacheAddStream) {
+ J.Cached = true; // Cache hit, mark the job as cached.
+ CachedJobs.fetch_add(1);
+ } else {
+ // If CacheAddStream is not null, we have a cache miss and we need to
+ // run the backend for codegen. Save cache 'add stream'
+ // function for a later use.
+ J.CacheAddStream = std::move(CacheAddStream);
+ }
+ return Error::success();
+}
+
+// Prepares a single DTLTO backend compilation job for a ThinLTO module.
+Error lto::DTLTO::prepareDtltoJob(StringRef ModulePath, unsigned Task) {
+ assert(Task >= ThinLTOTaskOffset && Task - ThinLTOTaskOffset < Jobs.size() &&
+ "Task index out of range for Jobs");
+ assert(Task < SummaryIndexFiles.size() && "Task index out of range");
+
+ SString ObjFilePath =
+ sys::path::parent_path(DistributorParams.LinkerOutputFile);
+ sys::path::append(ObjFilePath, sys::path::stem(ModulePath) + "." +
+ itostr(Task) + "." + UID + ".native.o");
+
+ SString SummaryIndexPathStr = ObjFilePath;
+ SummaryIndexPathStr += ".thinlto.bc";
+ SString ImportsPathStr = ModulePath;
+ ImportsPathStr += ".imports";
+
+ Job &J = Jobs[Task - ThinLTOTaskOffset];
+ J = {Task,
+ ModulePath,
+ Saver.save(ObjFilePath.str()),
+ Saver.save(SummaryIndexPathStr.str()),
+ Saver.save(ImportsPathStr.str()),
+ ImportsFilesLists[Task],
+ CacheKeysList[Task],
+ nullptr,
+ false};
+
+ if (Error Err = checkCacheHit(J))
+ return Err;
+ if (!J.Cached) {
+ TimeTraceScope JobScope("Emit individual index for DTLTO",
+ J.SummaryIndexPath);
+ if (Error Err = save(SummaryIndexFiles[Task], J.SummaryIndexPath))
+ return Err;
+ }
+ if (OnWriteCb)
+ OnWriteCb(J.SummaryIndexPath.str());
+
+ if (ShouldEmitImportFiles)
+ if (Error Err = save(join(ImportsFilesLists[Task], "\n"), J.ImportsPath))
+ return Err;
+
+ if (!SaveTemps) {
+ if (!J.Cached)
+ addToCleanup(J.NativeObjectPath.str());
+ if (!ShouldEmitIndexFiles)
+ addToCleanup(J.SummaryIndexPath.str());
+ if (!ShouldEmitImportFiles)
+ addToCleanup(J.ImportsPath.str());
+ }
+ return Error::success();
+}
+
+// Derive a set of Clang options that will be shared/common for all DTLTO
+// backend compilations.
+void lto::DTLTO::buildCommonRemoteCompilerOptions() {
+ const lto::Config &C = getConfig();
+ auto &Ops = DistributorParams.CodegenOptions;
+
+ Ops.push_back(Saver.save("-O" + Twine(C.OptLevel)));
+
+ if (C.Options.EmitAddrsig)
+ Ops.push_back("-faddrsig");
+ if (C.Options.FunctionSections)
+ Ops.push_back("-ffunction-sections");
+ if (C.Options.DataSections)
+ Ops.push_back("-fdata-sections");
+
+ if (C.RelocModel == Reloc::PIC_)
+ // Clang doesn't have -fpic for all triples.
+ if (!DistributorParams.TargetTriple.isOSBinFormatCOFF())
+ Ops.push_back("-fpic");
+
+ // Turn on/off warnings about profile cfg mismatch (default on)
+ // --lto-pgo-warn-mismatch.
+ if (!C.PGOWarnMismatch) {
+ Ops.push_back("-mllvm");
+ Ops.push_back("-no-pgo-warn-mismatch");
+ }
+
+ // Enable sample-based profile guided optimizations.
+ // Sample profile file path --lto-sample-profile=<value>.
+ if (!C.SampleProfile.empty()) {
+ Ops.push_back(Saver.save("-fprofile-sample-use=" + Twine(C.SampleProfile)));
+ DistributorParams.CommonInputs.insert(C.SampleProfile);
+ }
+
+ // We don't know which of options will be used by Clang.
+ Ops.push_back("-Wno-unused-command-line-argument");
+
+ // Forward any supplied options.
+ if (!DistributorParams.RemoteCompilerArgs.empty())
+ for (auto &a : DistributorParams.RemoteCompilerArgs)
+ Ops.push_back(a);
+}
+
+// Initializes DTLTO state and prepares a job for each ThinLTO module.
+Error lto::DTLTO::prepareDtltoJobs() {
+ auto &ModuleMap =
+ ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap;
+
+ if (ModuleMap.empty())
+ return Error::success();
+
+ Jobs.resize(ModuleMap.size());
+
+ for (auto [I, Mod] : enumerate(ModuleMap))
+ if (Error E = prepareDtltoJob(Mod.first, ThinLTOTaskOffset + I))
+ return E;
+
+ return Error::success();
+}
+
+// Runs the DTLTO code generation phase. Must be invoked after thinLink().
+Error lto::DTLTO::performCodegen() {
+ if (Jobs.empty())
+ return Error::success();
+ // Build common remote compiler options.
+ buildCommonRemoteCompilerOptions();
+
+ DistributionDriver Distributor(DistributorParams, Jobs, SaveTemps,
+ [&](StringRef S) { addToCleanup(S); });
+
+ if (CachedJobs.load() < Jobs.size()) {
+ if (Error E = Distributor())
+ return E;
+ }
+ return Error::success();
+}
+
+// Adds compiled object files to the link for each non-cached job.
+Error lto::DTLTO::addObjectFilesToLink() {
+ TimeTraceScope FilesScope("Add DTLTO files to the link");
+ for (auto &Job : Jobs) {
+ if (!Job.CacheKey.empty() && Job.Cached) {
+ assert(Cache.isValid());
+ continue;
+ }
+ // Load the native object from a file into a memory buffer
+ // and store its contents in the output buffer.
+ auto ObjFileMbOrErr =
+ MemoryBuffer::getFile(Job.NativeObjectPath, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
+ if (std::error_code EC = ObjFileMbOrErr.getError())
+ return make_error<StringError>(
+ BCError + "cannot open native object file: " + Job.NativeObjectPath +
+ ": " + EC.message(),
+ inconvertibleErrorCode());
+
+ MemoryBufferRef ObjFileMbRef = ObjFileMbOrErr->get()->getMemBufferRef();
+ if (Cache.isValid()) {
+ // Cache hits are taken care of earlier. At this point, we could only
+ // have cache misses.
+ assert(Job.CacheAddStream);
+ // Obtain a file stream for a storing a cache entry.
+ auto CachedFileStreamOrErr = Job.CacheAddStream(Job.Task, Job.ModuleID);
+ if (!CachedFileStreamOrErr)
+ return joinErrors(
+ CachedFileStreamOrErr.takeError(),
+ createStringError(inconvertibleErrorCode(),
+ "Cannot get a cache file stream: %s",
+ Job.NativeObjectPath.data()));
+ // Store a file buffer into the cache stream.
+ auto &CacheStream = *(CachedFileStreamOrErr->get());
+ *(CacheStream.OS) << ObjFileMbRef.getBuffer();
+ if (Error Err = CacheStream.commit())
+ return Err;
+ } else {
+ if (AddBuffer) {
+ AddBuffer(Job.Task, Job.ModuleID, std::move(ObjFileMbOrErr.get()));
+ } else {
+ auto StreamOrErr = AddStreamFunc(Job.Task, Job.ModuleID);
+ if (Error Err = StreamOrErr.takeError())
+ return Err;
+ auto &Stream = *StreamOrErr->get();
+ *Stream.OS << ObjFileMbRef.getBuffer();
+ if (Error Err = Stream.commit())
+ return Err;
+ }
}
}
- Base::cleanup();
+ return Error::success();
+}
+
+// Generates a JSON file describing the backend compilations, for the
+// distributor.
+Error lto::DistributionDriver::emitJson() {
+ using json::Array;
+ std::error_code EC;
+ raw_fd_ostream OS(DistributorJsonFile, EC);
+ if (EC)
+ return createStringError(EC, "Error while creating Json file");
+
+ json::OStream JOS(OS);
+ JOS.object([&]() {
+ // Information common to all jobs.
+ JOS.attributeObject("common", [&]() {
+ JOS.attribute("linker_output", Params.LinkerOutputFile);
+
+ JOS.attributeArray("args", [&]() {
+ JOS.value(Params.RemoteCompiler);
+
+ // Forward any supplied prepend options.
+ if (!Params.RemoteCompilerPrependArgs.empty())
+ for (auto &A : Params.RemoteCompilerPrependArgs)
+ JOS.value(A);
+
+ JOS.value("-c");
+
+ JOS.value(std::string("--target=") + Params.TargetTriple.str());
+
+ for (const auto &A : Params.CodegenOptions)
+ JOS.value(A);
+ });
+
+ JOS.attribute("inputs", Array(Params.CommonInputs));
+ });
+
+ // Per-compilation-job information.
+ JOS.attributeArray("jobs", [&]() {
+ for (const auto &J : Jobs) {
+ assert(J.Task != 0);
+ if (J.Cached) {
+ continue;
+ }
+
+ SmallVector<StringRef, 2> Inputs;
+ SmallVector<StringRef, 1> Outputs;
+
+ JOS.object([&]() {
+ JOS.attributeArray("args", [&]() {
+ JOS.value(J.ModuleID);
+ Inputs.push_back(J.ModuleID);
+
+ JOS.value(
+ std::string("-fthinlto-index=" + J.SummaryIndexPath.str()));
+ Inputs.push_back(J.SummaryIndexPath);
+
+ JOS.value("-o");
+ JOS.value(J.NativeObjectPath);
+ Outputs.push_back(J.NativeObjectPath);
+ });
+
+ // Add the bitcode files from which imports will be made. These do
+ // not explicitly appear on the backend compilation command lines
+ // but are recorded in the summary index shards.
+ append_range(Inputs, J.ImportsFiles);
+ JOS.attribute("inputs", Array(Inputs));
+
+ JOS.attribute("outputs", Array(Outputs));
+ });
+ }
+ });
+ });
+
+ return Error::success();
+}
+
+// Saves JSON file on a filesystem.
+Error lto::DistributionDriver::saveJson() {
+ DistributorJsonFile = sys::path::parent_path(Params.LinkerOutputFile);
+ TimeTraceScope TimeScope("Emit DTLTO JSON");
+ sys::path::append(DistributorJsonFile,
+ sys::path::stem(Params.LinkerOutputFile) + "." +
+ itostr(sys::Process::getProcessId()) +
+ ".dist-file.json");
+ if (Error E = emitJson())
+ return make_error<StringError>(
+ BCError + "failed to generate distributor JSON script: " +
+ DistributorJsonFile,
+ errorToErrorCode(std::move(E)));
+
+ // Add JSON file to the cleanup files list.
+ if (!SaveTemps)
+ AddToCleanup(DistributorJsonFile);
+ return Error::success();
+}
+
+// Invokes the distributor to compile uncached ThinLTO modules remotely.
+Error lto::DistributionDriver::operator()() {
+ if (Error E = saveJson())
+ return E;
+
+ TimeTraceScope TimeScope("Execute DTLTO distributor", Params.DistributorPath);
+ SmallVector<StringRef, 3> Args = {Params.DistributorPath};
+ append_range(Args, Params.DistributorArgs);
+ Args.push_back(DistributorJsonFile);
+ std::string ErrMsg;
+ if (sys::ExecuteAndWait(Args[0], Args,
+ /*Env=*/std::nullopt, /*Redirects=*/{},
+ /*SecondsToWait=*/0, /*MemoryLimit=*/0, &ErrMsg)) {
+ return make_error<StringError>(
+ BCError + "distributor execution failed" +
+ (!ErrMsg.empty() ? ": " + ErrMsg + Twine(".") : Twine(".")),
+ inconvertibleErrorCode());
+ }
+ return Error::success();
}
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 95faf3484c456..1e444ca92c443 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -1299,10 +1299,6 @@ Error LTO::checkPartiallySplit() {
}
Error LTO::run(AddStreamFn AddStream, FileCache Cache) {
- llvm::scope_exit CleanUp([this]() { cleanup(); });
-
- if (Error EC = serializeInputsForDistribution())
- return EC;
// Compute "dead" symbols, we don't want to import/export these!
DenseSet<GlobalValue::GUID> GUIDPreservedSymbols;
@@ -1518,17 +1514,22 @@ SmallVector<StringRef> LTO::getLibFuncSymbols(const Triple &TT,
}
Error ThinBackendProc::emitFiles(
- const FunctionImporter::ImportMapTy &ImportList, llvm::StringRef ModulePath,
+ const FunctionImporter::ImportMapTy &ImportList, unsigned Task, llvm::StringRef ModulePath,
const std::string &NewModulePath) const {
- return emitFiles(ImportList, ModulePath, NewModulePath,
+ return emitFiles(ImportList, Task, ModulePath, NewModulePath,
NewModulePath + ".thinlto.bc",
- /*ImportsFiles=*/std::nullopt);
+ /*ImportsFiles=*/std::nullopt, nullptr, nullptr, nullptr,
+ nullptr);
}
Error ThinBackendProc::emitFiles(
- const FunctionImporter::ImportMapTy &ImportList, llvm::StringRef ModulePath,
+ const FunctionImporter::ImportMapTy &ImportList, unsigned Task, llvm::StringRef ModulePath,
const std::string &NewModulePath, StringRef SummaryPath,
- std::optional<std::reference_wrapper<ImportsFilesContainer>> ImportsFiles)
+ std::optional<std::reference_wrapper<ImportsFilesContainer>> ImportsFiles,
+ const FunctionImporter::ExportSetTy *ExportList,
+ const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> *ResolvedODR,
+ const DenseSet<GlobalValue::GUID> *CfiFunctionDefs,
+ const DenseSet<GlobalValue::GUID> *CfiFunctionDecls)
const {
ModuleToSummariesForIndexTy ModuleToSummariesForIndex;
GVSummaryPtrSet DeclarationSummaries;
@@ -1538,18 +1539,32 @@ Error ThinBackendProc::emitFiles(
ImportList, ModuleToSummariesForIndex,
DeclarationSummaries);
- raw_fd_ostream OS(SummaryPath, EC, sys::fs::OpenFlags::OF_None);
- if (EC)
- return createFileError("cannot open " + Twine(SummaryPath), EC);
+ if (Conf.OnSummaryIndexStoreCb) {
+ std::unique_ptr<raw_pwrite_stream> PS = Conf.OnSummaryIndexStoreCb(Task);
+ writeIndexToFile(CombinedIndex, *PS, &ModuleToSummariesForIndex,
+ &DeclarationSummaries);
+ } else {
+ raw_fd_ostream OS(SummaryPath, EC, sys::fs::OpenFlags::OF_None);
+ if (EC)
+ return createFileError("cannot open " + Twine(SummaryPath), EC);
- writeIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex,
- &DeclarationSummaries);
+ writeIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex,
+ &DeclarationSummaries);
+ }
- if (ShouldEmitImportsFiles) {
- Error ImportsFilesError = EmitImportsFiles(
- ModulePath, NewModulePath + ".imports", ModuleToSummariesForIndex);
- if (ImportsFilesError)
- return ImportsFilesError;
+ if (Conf.OnImportsListStoreCb) {
+ std::vector<std::string> &ImportsListRef = Conf.OnImportsListStoreCb(Task);
+ for (const auto &ILI : ModuleToSummariesForIndex) {
+ if (ILI.first != ModulePath)
+ ImportsListRef.push_back(ILI.first);
+ }
+ } else {
+ if (ShouldEmitImportsFiles) {
+ Error ImportsFilesError = EmitImportsFiles(
+ ModulePath, NewModulePath + ".imports", ModuleToSummariesForIndex);
+ if (ImportsFilesError)
+ return ImportsFilesError;
+ }
}
// Optionally, store the imports files.
@@ -1558,6 +1573,19 @@ Error ThinBackendProc::emitFiles(
ModulePath, ModuleToSummariesForIndex,
[&](StringRef M) { ImportsFiles->get().push_back(M.str()); });
+ // Optionally, compute and store the LTO cache key for DTLTO.
+ if (Conf.OnCacheKeyStoreCb && ExportList && ResolvedODR && CfiFunctionDefs &&
+ CfiFunctionDecls) {
+ assert(ModuleToDefinedGVSummaries.count(ModulePath));
+ const GVSummaryMapTy &DefinedGlobals =
+ ModuleToDefinedGVSummaries.find(ModulePath)->second;
+
+ std::string &CacheKeyRef = Conf.OnCacheKeyStoreCb(Task);
+ CacheKeyRef = computeLTOCacheKey(
+ Conf, CombinedIndex, ModulePath, ImportList, *ExportList, *ResolvedODR,
+ DefinedGlobals, *CfiFunctionDefs, *CfiFunctionDecls);
+ }
+
return Error::success();
}
@@ -1632,7 +1660,7 @@ class InProcessThinBackend : public CGThinBackend {
Conf.CodeGenOnly, BitcodeLibFuncs);
};
if (ShouldEmitIndexFiles) {
- if (auto E = emitFiles(ImportList, ModuleID, ModuleID.str()))
+ if (auto E = emitFiles(ImportList, Task, ModuleID, ModuleID.str()))
return E;
}
@@ -1750,7 +1778,7 @@ class FirstRoundThinBackend : public InProcessThinBackend {
// FirstRoundThinBackend. However, these files are not generated for
// SecondRoundThinBackend.
if (ShouldEmitIndexFiles) {
- if (auto E = emitFiles(ImportList, ModuleID, ModuleID.str()))
+ if (auto E = emitFiles(ImportList, Task, ModuleID, ModuleID.str()))
return E;
}
@@ -1927,6 +1955,8 @@ namespace {
class WriteIndexesThinBackend : public ThinBackendProc {
std::string OldPrefix, NewPrefix, NativeObjectPrefix;
raw_fd_ostream *LinkedObjectsFile;
+ DenseSet<GlobalValue::GUID> CfiFunctionDefs;
+ DenseSet<GlobalValue::GUID> CfiFunctionDecls;
public:
WriteIndexesThinBackend(
@@ -1940,7 +1970,12 @@ class WriteIndexesThinBackend : public ThinBackendProc {
OnWrite, ShouldEmitImportsFiles, ThinLTOParallelism),
OldPrefix(OldPrefix), NewPrefix(NewPrefix),
NativeObjectPrefix(NativeObjectPrefix),
- LinkedObjectsFile(LinkedObjectsFile) {}
+ LinkedObjectsFile(LinkedObjectsFile) {
+ auto &Defs = CombinedIndex.cfiFunctionDefs();
+ CfiFunctionDefs.insert(Defs.guid_begin(), Defs.guid_end());
+ auto &Decls = CombinedIndex.cfiFunctionDecls();
+ CfiFunctionDecls.insert(Decls.guid_begin(), Decls.guid_end());
+ }
Error start(
unsigned Task, BitcodeModule BM,
@@ -1963,22 +1998,28 @@ class WriteIndexesThinBackend : public ThinBackendProc {
}
BackendThreadPool.async(
- [this](const StringRef ModulePath,
+ [this](unsigned Task, const StringRef ModulePath,
const FunctionImporter::ImportMapTy &ImportList,
+ const FunctionImporter::ExportSetTy &ExportList,
+ const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>
+ &ResolvedODR,
const std::string &OldPrefix, const std::string &NewPrefix) {
std::string NewModulePath =
getThinLTOOutputFile(ModulePath, OldPrefix, NewPrefix);
- auto E = emitFiles(ImportList, ModulePath, NewModulePath);
+ auto E = emitFiles(ImportList, Task, ModulePath, NewModulePath,
+ NewModulePath + ".thinlto.bc",
+ /*ImportsFiles=*/std::nullopt, &ExportList,
+ &ResolvedODR, &CfiFunctionDefs, &CfiFunctionDecls);
if (E) {
std::unique_lock<std::mutex> L(ErrMu);
if (Err)
Err = joinErrors(std::move(*Err), std::move(E));
else
Err = std::move(E);
- return;
}
},
- ModulePath, ImportList, OldPrefix, NewPrefix);
+ Task, ModulePath, ImportList, ExportList, ResolvedODR, OldPrefix,
+ NewPrefix);
if (OnWrite)
OnWrite(std::string(ModulePath));
@@ -2352,462 +2393,3 @@ std::vector<int> lto::generateModulesOrdering(ArrayRef<BitcodeModule *> R) {
});
return ModulesOrdering;
}
-
-namespace {
-/// This out-of-process backend does not perform code generation when invoked
-/// for each task. Instead, it generates the necessary information (e.g., the
-/// summary index shard, import list, etc.) to enable code generation to be
-/// performed externally, similar to WriteIndexesThinBackend. The backend's
-/// `wait` function then invokes an external distributor process to carry out
-/// the backend compilations.
-class OutOfProcessThinBackend : public CGThinBackend {
- using SString = SmallString<128>;
-
- BumpPtrAllocator Alloc;
- StringSaver Saver{Alloc};
-
- SString LinkerOutputFile;
-
- SString DistributorPath;
- ArrayRef<StringRef> DistributorArgs;
-
- SString RemoteCompiler;
- ArrayRef<StringRef> RemoteCompilerPrependArgs;
- ArrayRef<StringRef> RemoteCompilerArgs;
-
- bool SaveTemps;
-
- SmallVector<StringRef, 0> CodegenOptions;
- DenseSet<StringRef> CommonInputs;
- // Number of the object files that have been already cached.
- std::atomic<size_t> CachedJobs{0};
- // Information specific to individual backend compilation job.
- struct Job {
- unsigned Task;
- StringRef ModuleID;
- StringRef NativeObjectPath;
- StringRef SummaryIndexPath;
- ImportsFilesContainer ImportsFiles;
- std::string CacheKey;
- AddStreamFn CacheAddStream;
- bool Cached = false;
- };
- // The set of backend compilations jobs.
- SmallVector<Job> Jobs;
-
- // A unique string to identify the current link.
- SmallString<8> UID;
-
- // The offset to the first ThinLTO task.
- unsigned ThinLTOTaskOffset;
-
- // The target triple to supply for backend compilations.
- llvm::Triple Triple;
-
- // Cache
- FileCache Cache;
-
- // Callback to add a pre-existing native object buffer to the link.
- AddBufferFn AddBuffer;
-
-public:
- OutOfProcessThinBackend(
- const Config &Conf, ModuleSummaryIndex &CombinedIndex,
- ThreadPoolStrategy ThinLTOParallelism,
- const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
- FileCache CacheFn, lto::IndexWriteCallback OnWrite,
- bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles,
- StringRef LinkerOutputFile, StringRef Distributor,
- ArrayRef<StringRef> DistributorArgs, StringRef RemoteCompiler,
- ArrayRef<StringRef> RemoteCompilerPrependArgs,
- ArrayRef<StringRef> RemoteCompilerArgs, bool SaveTemps,
- AddBufferFn AddBuffer)
- : CGThinBackend(Conf, CombinedIndex, ModuleToDefinedGVSummaries, OnWrite,
- ShouldEmitIndexFiles, ShouldEmitImportsFiles,
- ThinLTOParallelism),
- LinkerOutputFile(LinkerOutputFile), DistributorPath(Distributor),
- DistributorArgs(DistributorArgs), RemoteCompiler(RemoteCompiler),
- RemoteCompilerPrependArgs(RemoteCompilerPrependArgs),
- RemoteCompilerArgs(RemoteCompilerArgs), SaveTemps(SaveTemps),
- Cache(std::move(CacheFn)), AddBuffer(std::move(AddBuffer)) {}
-
- void setup(unsigned ThinLTONumTasks, unsigned ThinLTOTaskOffset,
- llvm::Triple Triple) override {
- UID = itostr(sys::Process::getProcessId());
- Jobs.resize((size_t)ThinLTONumTasks);
- this->ThinLTOTaskOffset = ThinLTOTaskOffset;
- this->Triple = std::move(Triple);
- this->Conf.Dtlto = 1;
- }
-
- virtual Error runThinLTOBackendThread(
- Job &J, const FunctionImporter::ImportMapTy &ImportList,
- const FunctionImporter::ExportSetTy &ExportList,
- const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>
- &ResolvedODR) {
- {
- TimeTraceScope TimeScope("Emit individual index for DTLTO",
- J.SummaryIndexPath);
- if (auto E = emitFiles(ImportList, J.ModuleID, J.ModuleID.str(),
- J.SummaryIndexPath, J.ImportsFiles))
- return E;
- }
-
- if (!Cache.isValid() || !CombinedIndex.modulePaths().count(J.ModuleID) ||
- all_of(CombinedIndex.getModuleHash(J.ModuleID),
- [](uint32_t V) { return V == 0; }))
- // Cache disabled or no entry for this module in the combined index or
- // no module hash.
- return Error::success();
-
- TimeTraceScope TimeScope("Check cache for DTLTO", J.SummaryIndexPath);
- const GVSummaryMapTy &DefinedGlobals =
- ModuleToDefinedGVSummaries.find(J.ModuleID)->second;
-
- // The module may be cached, this helps handling it.
- J.CacheKey = computeLTOCacheKey(Conf, CombinedIndex, J.ModuleID, ImportList,
- ExportList, ResolvedODR, DefinedGlobals,
- CfiFunctionDefs, CfiFunctionDecls);
-
- // The module may be cached, this helps handling it.
- auto CacheAddStreamExp = Cache(J.Task, J.CacheKey, J.ModuleID);
- if (Error Err = CacheAddStreamExp.takeError())
- return Err;
- AddStreamFn &CacheAddStream = *CacheAddStreamExp;
- // If CacheAddStream is null, we have a cache hit and at this point
- // object file is already passed back to the linker.
- if (!CacheAddStream) {
- J.Cached = true; // Cache hit, mark the job as cached.
- CachedJobs.fetch_add(1);
- } else {
- // If CacheAddStream is not null, we have a cache miss and we need to
- // run the backend for codegen. Save cache 'add stream'
- // function for a later use.
- J.CacheAddStream = std::move(CacheAddStream);
- }
- return Error::success();
- }
-
- Error start(
- unsigned Task, BitcodeModule BM,
- const FunctionImporter::ImportMapTy &ImportList,
- const FunctionImporter::ExportSetTy &ExportList,
- const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
- MapVector<StringRef, BitcodeModule> &ModuleMap) override {
-
- StringRef ModulePath = BM.getModuleIdentifier();
-
- SString ObjFilePath = sys::path::parent_path(LinkerOutputFile);
- sys::path::append(ObjFilePath, sys::path::stem(ModulePath) + "." +
- itostr(Task) + "." + UID + ".native.o");
-
- Job &J = Jobs[Task - ThinLTOTaskOffset];
- J = {Task,
- ModulePath,
- Saver.save(ObjFilePath.str()),
- Saver.save(ObjFilePath.str() + ".thinlto.bc"),
- {}, // Filled in by emitFiles below.
- "", /*CacheKey=*/
- nullptr,
- false};
-
- // Cleanup per-job temporary files on abnormal process exit.
- if (!SaveTemps) {
- llvm::sys::RemoveFileOnSignal(J.NativeObjectPath);
- if (!ShouldEmitIndexFiles)
- llvm::sys::RemoveFileOnSignal(J.SummaryIndexPath);
- }
-
- assert(ModuleToDefinedGVSummaries.count(ModulePath));
-
- // The BackendThreadPool is only used here to write the sharded index files
- // (similar to WriteIndexesThinBackend).
- BackendThreadPool.async(
- [=](Job &J, const FunctionImporter::ImportMapTy &ImportList,
- const FunctionImporter::ExportSetTy &ExportList,
- const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>
- &ResolvedODR) {
- if (LLVM_ENABLE_THREADS && Conf.TimeTraceEnabled)
- timeTraceProfilerInitialize(
- Conf.TimeTraceGranularity,
- "Emit individual index and check cache for DTLTO");
- Error E =
- runThinLTOBackendThread(J, ImportList, ExportList, ResolvedODR);
- if (E) {
- std::unique_lock<std::mutex> L(ErrMu);
- if (Err)
- Err = joinErrors(std::move(*Err), std::move(E));
- else
- Err = std::move(E);
- }
- if (LLVM_ENABLE_THREADS && Conf.TimeTraceEnabled)
- timeTraceProfilerFinishThread();
- },
- std::ref(J), std::ref(ImportList), std::ref(ExportList),
- std::ref(ResolvedODR));
-
- return Error::success();
- }
-
- // Derive a set of Clang options that will be shared/common for all DTLTO
- // backend compilations. We are intentionally minimal here as these options
- // must remain synchronized with the behavior of Clang. DTLTO does not support
- // all the features available with in-process LTO. More features are expected
- // to be added over time. Users can specify Clang options directly if a
- // feature is not supported. Note that explicitly specified options that imply
- // additional input or output file dependencies must be communicated to the
- // distribution system, potentially by setting extra options on the
- // distributor program.
- void buildCommonRemoteCompilerOptions() {
- const lto::Config &C = Conf;
- auto &Ops = CodegenOptions;
-
- Ops.push_back(Saver.save("-O" + Twine(C.OptLevel)));
-
- if (C.Options.EmitAddrsig)
- Ops.push_back("-faddrsig");
- if (C.Options.FunctionSections)
- Ops.push_back("-ffunction-sections");
- if (C.Options.DataSections)
- Ops.push_back("-fdata-sections");
-
- if (C.RelocModel == Reloc::PIC_)
- // Clang doesn't have -fpic for all triples.
- if (!Triple.isOSBinFormatCOFF())
- Ops.push_back("-fpic");
-
- // Turn on/off warnings about profile cfg mismatch (default on)
- // --lto-pgo-warn-mismatch.
- if (!C.PGOWarnMismatch) {
- Ops.push_back("-mllvm");
- Ops.push_back("-no-pgo-warn-mismatch");
- }
-
- // Enable sample-based profile guided optimizations.
- // Sample profile file path --lto-sample-profile=<value>.
- if (!C.SampleProfile.empty()) {
- Ops.push_back(
- Saver.save("-fprofile-sample-use=" + Twine(C.SampleProfile)));
- CommonInputs.insert(C.SampleProfile);
- }
-
- // We don't know which of options will be used by Clang.
- Ops.push_back("-Wno-unused-command-line-argument");
-
- // Forward any supplied options.
- if (!RemoteCompilerArgs.empty())
- for (auto &a : RemoteCompilerArgs)
- Ops.push_back(a);
- }
-
- // Generates a JSON file describing the backend compilations, for the
- // distributor.
- bool emitDistributorJson(StringRef DistributorJson) {
- using json::Array;
- std::error_code EC;
- raw_fd_ostream OS(DistributorJson, EC);
- if (EC)
- return false;
-
- json::OStream JOS(OS);
- JOS.object([&]() {
- // Information common to all jobs.
- JOS.attributeObject("common", [&]() {
- JOS.attribute("linker_output", LinkerOutputFile);
-
- JOS.attributeArray("args", [&]() {
- JOS.value(RemoteCompiler);
-
- // Forward any supplied prepend options.
- if (!RemoteCompilerPrependArgs.empty())
- for (auto &A : RemoteCompilerPrependArgs)
- JOS.value(A);
-
- JOS.value("-c");
-
- JOS.value(Saver.save("--target=" + Triple.str()));
-
- for (const auto &A : CodegenOptions)
- JOS.value(A);
- });
-
- JOS.attribute("inputs", Array(CommonInputs));
- });
-
- // Per-compilation-job information.
- JOS.attributeArray("jobs", [&]() {
- for (const auto &J : Jobs) {
- assert(J.Task != 0);
- if (J.Cached) {
- assert(!Cache.getCacheDirectoryPath().empty());
- continue;
- }
-
- SmallVector<StringRef, 2> Inputs;
- SmallVector<StringRef, 1> Outputs;
-
- JOS.object([&]() {
- JOS.attributeArray("args", [&]() {
- JOS.value(J.ModuleID);
- Inputs.push_back(J.ModuleID);
-
- JOS.value(
- Saver.save("-fthinlto-index=" + Twine(J.SummaryIndexPath)));
- Inputs.push_back(J.SummaryIndexPath);
-
- JOS.value("-o");
- JOS.value(J.NativeObjectPath);
- Outputs.push_back(J.NativeObjectPath);
- });
-
- // Add the bitcode files from which imports will be made. These do
- // not explicitly appear on the backend compilation command lines
- // but are recorded in the summary index shards.
- llvm::append_range(Inputs, J.ImportsFiles);
- JOS.attribute("inputs", Array(Inputs));
-
- JOS.attribute("outputs", Array(Outputs));
- });
- }
- });
- });
-
- return true;
- }
-
- void removeFile(StringRef FileName) {
- 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";
- }
-
- Error wait() override {
- // Wait for the information on the required backend compilations to be
- // gathered.
- BackendThreadPool.wait();
- if (Err)
- return std::move(*Err);
-
- llvm::scope_exit CleanPerJobFiles([&] {
- llvm::TimeTraceScope TimeScope("Remove DTLTO temporary files");
- if (!SaveTemps)
- for (auto &Job : Jobs) {
- removeFile(Job.NativeObjectPath);
- if (!ShouldEmitIndexFiles)
- removeFile(Job.SummaryIndexPath);
- }
- });
-
- const StringRef BCError = "DTLTO backend compilation: ";
-
- buildCommonRemoteCompilerOptions();
-
- SString JsonFile = sys::path::parent_path(LinkerOutputFile);
- {
- llvm::TimeTraceScope TimeScope("Emit DTLTO JSON");
- sys::path::append(JsonFile, sys::path::stem(LinkerOutputFile) + "." +
- UID + ".dist-file.json");
- // Cleanup DTLTO JSON file on abnormal process exit.
- if (!SaveTemps)
- llvm::sys::RemoveFileOnSignal(JsonFile);
- if (!emitDistributorJson(JsonFile))
- return make_error<StringError>(
- BCError + "failed to generate distributor JSON script: " + JsonFile,
- inconvertibleErrorCode());
- }
- llvm::scope_exit CleanJson([&] {
- if (!SaveTemps)
- removeFile(JsonFile);
- });
-
- {
- llvm::TimeTraceScope TimeScope("Execute DTLTO distributor",
- DistributorPath);
- // Checks if we have any jobs that don't have corresponding cache entries.
- if (CachedJobs.load() < Jobs.size()) {
- SmallVector<StringRef, 3> Args = {DistributorPath};
- llvm::append_range(Args, DistributorArgs);
- Args.push_back(JsonFile);
- std::string ErrMsg;
- if (sys::ExecuteAndWait(Args[0], Args,
- /*Env=*/std::nullopt, /*Redirects=*/{},
- /*SecondsToWait=*/0, /*MemoryLimit=*/0,
- &ErrMsg)) {
- return make_error<StringError>(
- BCError + "distributor execution failed" +
- (!ErrMsg.empty() ? ": " + ErrMsg + Twine(".") : Twine(".")),
- inconvertibleErrorCode());
- }
- }
- }
-
- {
- llvm::TimeTraceScope FilesScope("Add DTLTO files to the link");
- for (auto &Job : Jobs) {
- if (!Job.CacheKey.empty() && Job.Cached) {
- assert(Cache.isValid());
- continue;
- }
- // Load the native object from a file into a memory buffer
- // and store its contents in the output buffer.
- auto ObjFileMbOrErr =
- MemoryBuffer::getFile(Job.NativeObjectPath, /*IsText=*/false,
- /*RequiresNullTerminator=*/false);
- if (std::error_code EC = ObjFileMbOrErr.getError())
- return make_error<StringError>(
- BCError + "cannot open native object file: " +
- Job.NativeObjectPath + ": " + EC.message(),
- inconvertibleErrorCode());
-
- if (Cache.isValid()) {
- // Cache hits are taken care of earlier. At this point, we could only
- // have cache misses.
- assert(Job.CacheAddStream);
- MemoryBufferRef ObjFileMbRef =
- ObjFileMbOrErr->get()->getMemBufferRef();
- // Obtain a file stream for a storing a cache entry.
- auto CachedFileStreamOrErr =
- Job.CacheAddStream(Job.Task, Job.ModuleID);
- if (!CachedFileStreamOrErr)
- return joinErrors(
- CachedFileStreamOrErr.takeError(),
- createStringError(inconvertibleErrorCode(),
- "Cannot get a cache file stream: %s",
- Job.NativeObjectPath.data()));
- // Store a file buffer into the cache stream.
- auto &CacheStream = *(CachedFileStreamOrErr->get());
- *(CacheStream.OS) << ObjFileMbRef.getBuffer();
- if (Error Err = CacheStream.commit())
- return Err;
- } else {
- AddBuffer(Job.Task, Job.ModuleID, std::move(*ObjFileMbOrErr));
- }
- }
- }
- return Error::success();
- }
-};
-} // end anonymous namespace
-
-ThinBackend lto::createOutOfProcessThinBackend(
- ThreadPoolStrategy Parallelism, lto::IndexWriteCallback OnWrite,
- bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles,
- StringRef LinkerOutputFile, StringRef Distributor,
- ArrayRef<StringRef> DistributorArgs, StringRef RemoteCompiler,
- ArrayRef<StringRef> RemoteCompilerPrependArgs,
- ArrayRef<StringRef> RemoteCompilerArgs, bool SaveTemps,
- AddBufferFn AddBuffer) {
- auto Func =
- [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex,
- const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
- AddStreamFn, FileCache Cache, ArrayRef<StringRef> BitcodeLibFuncs) {
- return std::make_unique<OutOfProcessThinBackend>(
- Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, Cache,
- OnWrite, ShouldEmitIndexFiles, ShouldEmitImportsFiles,
- LinkerOutputFile, Distributor, DistributorArgs, RemoteCompiler,
- RemoteCompilerPrependArgs, RemoteCompilerArgs, SaveTemps,
- AddBuffer);
- };
- return ThinBackend(Func, Parallelism);
-}
diff --git a/llvm/test/ThinLTO/X86/dtlto/timetrace.ll b/llvm/test/ThinLTO/X86/dtlto/timetrace.ll
index cab9fa04a3bb9..73b70188221a1 100644
--- a/llvm/test/ThinLTO/X86/dtlto/timetrace.ll
+++ b/llvm/test/ThinLTO/X86/dtlto/timetrace.ll
@@ -24,6 +24,10 @@ RUN: %python filter_order_and_pprint.py %t.json | FileCheck %s
CHECK-NOT: "name"
CHECK: "name": "Add DTLTO files to the link"
CHECK-SAME: "pid": [[#PID:]],
+CHECK-NEXT: "name": "Add input for DTLTO"
+CHECK-SAME: "pid": [[#PID:]],
+CHECK-NEXT: "name": "Add input for DTLTO"
+CHECK-SAME: "pid": [[#PID:]],
CHECK-NEXT: "name": "Emit DTLTO JSON"
CHECK-NEXT: "name": "Emit individual index for DTLTO"
CHECK-SAME: t1.1.[[#PID]].native.o.thinlto.bc"
@@ -33,6 +37,8 @@ CHECK-NEXT: "name": "Execute DTLTO distributor", "{{.*}}"
CHECK-NEXT: "name": "Remove DTLTO temporary files"
CHECK-NEXT: "name": "Total Add DTLTO files to the link"
CHECK-SAME: "count": 1,
+CHECK-NEXT: "name": "Total Add input for DTLTO"
+CHECK-SAME: "count": 2,
CHECK-NEXT: "name": "Total Emit DTLTO JSON"
CHECK-SAME: "count": 1,
CHECK-NEXT: "name": "Total Emit individual index for DTLTO"
diff --git a/llvm/tools/llvm-lto2/CMakeLists.txt b/llvm/tools/llvm-lto2/CMakeLists.txt
index 60ddce1898a6b..4d0c93680d355 100644
--- a/llvm/tools/llvm-lto2/CMakeLists.txt
+++ b/llvm/tools/llvm-lto2/CMakeLists.txt
@@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
BitReader
CodeGen
Core
+ DTLTO
Linker
LTO
MC
diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index 839324e396850..60d9688385be5 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -19,6 +19,7 @@
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/DTLTO/DTLTO.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Plugins/PassPlugin.h"
#include "llvm/Remarks/HotnessThresholdParser.h"
@@ -443,7 +444,7 @@ static int run(int argc, char **argv) {
};
ThinBackend Backend;
- if (ThinLTODistributedIndexes)
+ if (ThinLTODistributedIndexes || !DTLTODistributor.empty())
Backend = createWriteIndexesThinBackend(llvm::hardware_concurrency(Threads),
/*OldPrefix=*/"",
/*NewPrefix=*/"",
@@ -451,13 +452,7 @@ static int run(int argc, char **argv) {
ThinLTOEmitImports,
/*LinkedObjectsFile=*/nullptr,
/*OnWrite=*/{});
- else if (!DTLTODistributor.empty()) {
- Backend = createOutOfProcessThinBackend(
- llvm::heavyweight_hardware_concurrency(Threads),
- /*OnWrite=*/{}, ThinLTOEmitIndexes, ThinLTOEmitImports, OutputFilename,
- DTLTODistributor, DTLTODistributorArgsSV, DTLTOCompiler,
- DTLTOCompilerPrependArgsSV, DTLTOCompilerArgsSV, SaveTemps, AddBuffer);
- } else
+ else
Backend = createInProcessThinBackend(
llvm::heavyweight_hardware_concurrency(Threads),
/* OnWrite */ {}, ThinLTOEmitIndexes, ThinLTOEmitImports);
@@ -480,7 +475,17 @@ static int run(int argc, char **argv) {
LTO::LTOKind LTOMode = UnifiedLTOMode;
- LTO Lto(std::move(Conf), std::move(Backend), 1, LTOMode);
+ std::unique_ptr<LTO> Lto;
+ if (!DTLTODistributor.empty()) {
+ Lto = std::make_unique<DTLTO>(
+ std::move(Conf), std::move(Backend), 1, LTOMode, nullptr,
+ ThinLTOEmitIndexes, ThinLTOEmitImports, OutputFilename,
+ DTLTODistributor, DTLTODistributorArgsSV, DTLTOCompiler,
+ DTLTOCompilerPrependArgsSV, DTLTOCompilerArgsSV, AddBuffer, SaveTemps);
+ } else {
+ Lto =
+ std::make_unique<LTO>(std::move(Conf), std::move(Backend), 1, LTOMode);
+ }
for (std::string F : InputFilenames) {
std::unique_ptr<MemoryBuffer> MB = check(MemoryBuffer::getFile(F), F);
@@ -514,7 +519,7 @@ static int run(int argc, char **argv) {
continue;
MBs.push_back(std::move(MB));
- check(Lto.add(std::move(Input), Res), F);
+ check(Lto->add(std::move(Input), Res), F);
}
if (!CommandLineResolutions.empty()) {
@@ -535,7 +540,7 @@ static int run(int argc, char **argv) {
Cache = check(localCache("ThinLTO", "Thin", CacheDir, AddBuffer),
"failed to create cache");
- check(Lto.run(AddStream, Cache), "LTO::run failed");
+ check(Lto->run(AddStream, Cache), "LTO::run failed");
return static_cast<int>(HasErrors);
}
>From 956bdaf358727ae69dbf4a1313f02c419e8bb0c8 Mon Sep 17 00:00:00 2001
From: Konstantin Belochapka <konstantin.belochapka at sony.com>
Date: Fri, 17 Apr 2026 04:50:13 -0700
Subject: [PATCH 02/11] [DTLTO] Code refactor - Fix compile errors on Linux.
---
llvm/include/llvm/DTLTO/DTLTO.h | 21 ++++++++++-----------
llvm/lib/DTLTO/DTLTO.cpp | 2 +-
2 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index 248b86674e6bd..4a37e681e2077 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -60,12 +60,12 @@ class DTLTO : public LTO {
AddBufferFn AddBufferArg, bool SaveTempsArg)
: Base(std::move(Conf), Backend, ParallelCodeGenParallelismLevel,
LTOMode),
- OnWriteCb(OnWrite), ShouldEmitIndexFiles(EmitIndexFiles),
- ShouldEmitImportFiles(EmitImportsFiles),
+ AddBuffer(AddBufferArg), SaveTemps(SaveTempsArg),
+ ShouldEmitIndexFiles(EmitIndexFiles),
+ ShouldEmitImportFiles(EmitImportsFiles), OnWriteCb(OnWrite),
DistributorParams{Distributor, DistributorArgs,
RemoteCompiler, RemoteCompilerPrependArgs,
- RemoteCompilerArgs, LinkerOutputFile},
- AddBuffer(AddBufferArg), SaveTemps(SaveTempsArg) {
+ RemoteCompilerArgs, LinkerOutputFile} {
assert(!LinkerOutputFile.empty() && "expected a valid linker output file");
}
@@ -109,7 +109,7 @@ class DTLTO : public LTO {
LLVM_ABI Error handleArchiveInputs();
// Remove temporary files created to enable distribution.
- LLVM_ABI void cleanup();
+ LLVM_ABI void cleanup() override;
public:
// Mutable and const accessors to the LTO configuration object.
@@ -321,11 +321,10 @@ class DTLTO : public LTO {
ArrayRef<StringRef> RemoteCompilerPrependArgsArg,
ArrayRef<StringRef> RemoteCompilerArgsArg,
StringRef LinkerOutputFileArg)
- : DistributorPath(DistributorArg), DistributorArgs(DistributorArgsArg),
- RemoteCompiler(RemoteCompilerArg),
+ : LinkerOutputFile(LinkerOutputFileArg), DistributorPath(DistributorArg),
+ DistributorArgs(DistributorArgsArg), RemoteCompiler(RemoteCompilerArg),
RemoteCompilerPrependArgs(RemoteCompilerPrependArgsArg),
- RemoteCompilerArgs(RemoteCompilerArgsArg),
- LinkerOutputFile(LinkerOutputFileArg) {}
+ RemoteCompilerArgs(RemoteCompilerArgsArg) {}
// Output linker file path.
SString LinkerOutputFile;
@@ -372,8 +371,8 @@ class DistributionDriver {
DistributionDriver(DTLTO::DistributionDriverParams &ParamsArg,
ArrayRef<DTLTO::Job> JobsArg, bool SaveTempsArg,
std::function<void(StringRef)> AddToClenupArg)
- : Params{ParamsArg}, Jobs{JobsArg}, SaveTemps{SaveTempsArg},
- AddToCleanup{AddToClenupArg} {};
+ : Params{ParamsArg}, SaveTemps{SaveTempsArg},
+ AddToCleanup{AddToClenupArg}, Jobs{JobsArg} {};
private:
DTLTO::DistributionDriverParams &Params;
diff --git a/llvm/lib/DTLTO/DTLTO.cpp b/llvm/lib/DTLTO/DTLTO.cpp
index 94bb75c6eb7c1..ed643e97f179d 100644
--- a/llvm/lib/DTLTO/DTLTO.cpp
+++ b/llvm/lib/DTLTO/DTLTO.cpp
@@ -247,7 +247,7 @@ void lto::DTLTO::cleanup() {
removeFile(Name);
}
}
- // Base::cleanup();
+ Base::cleanup();
}
// Runs the DTLTO thin link phase, producing per-module summary indices,
>From 6be8afaa161bf4eb564a1361fab63fe62b0966bf Mon Sep 17 00:00:00 2001
From: Konstantin Belochapka <konstantin.belochapka at sony.com>
Date: Fri, 17 Apr 2026 05:07:47 -0700
Subject: [PATCH 03/11] [DTLTO] Code refactor - Fix format errors.
---
llvm/include/llvm/DTLTO/DTLTO.h | 9 +++++----
llvm/include/llvm/LTO/LTO.h | 18 ++++++++----------
llvm/lib/DTLTO/DTLTO.cpp | 6 ------
llvm/lib/LTO/LTO.cpp | 18 +++++++++---------
llvm/tools/llvm-lto2/llvm-lto2.cpp | 2 +-
5 files changed, 23 insertions(+), 30 deletions(-)
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index 4a37e681e2077..ecc58631e68fa 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -321,8 +321,9 @@ class DTLTO : public LTO {
ArrayRef<StringRef> RemoteCompilerPrependArgsArg,
ArrayRef<StringRef> RemoteCompilerArgsArg,
StringRef LinkerOutputFileArg)
- : LinkerOutputFile(LinkerOutputFileArg), DistributorPath(DistributorArg),
- DistributorArgs(DistributorArgsArg), RemoteCompiler(RemoteCompilerArg),
+ : LinkerOutputFile(LinkerOutputFileArg),
+ DistributorPath(DistributorArg), DistributorArgs(DistributorArgsArg),
+ RemoteCompiler(RemoteCompilerArg),
RemoteCompilerPrependArgs(RemoteCompilerPrependArgsArg),
RemoteCompilerArgs(RemoteCompilerArgsArg) {}
@@ -356,8 +357,8 @@ class DTLTO : public LTO {
// Record a file for cleanup and register signal-time removal if requested.
void addToCleanup(StringRef Filename) {
- CleanupList.push_back(Filename.str());
- sys::RemoveFileOnSignal(Filename);
+ CleanupList.push_back(Filename.str());
+ sys::RemoveFileOnSignal(Filename);
}
};
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index 0cf51c3eaa351..5c0a85ba931e7 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -287,8 +287,8 @@ class ThinBackendProc {
virtual bool isSensitiveToInputOrder() { return false; }
// Write sharded indices and (optionally) imports to disk
- LLVM_ABI Error emitFiles(const FunctionImporter::ImportMapTy &ImportList, unsigned Task,
- StringRef ModulePath,
+ LLVM_ABI Error emitFiles(const FunctionImporter::ImportMapTy &ImportList,
+ unsigned Task, StringRef ModulePath,
const std::string &NewModulePath) const;
// Write sharded indices to SummaryPath, (optionally) imports to disk, and
@@ -296,15 +296,15 @@ class ThinBackendProc {
// provided and GetCacheKeysListRefFunc is set, also computes and stores the
// LTO cache key for the module.
LLVM_ABI Error emitFiles(
- const FunctionImporter::ImportMapTy &ImportList, unsigned Task, StringRef ModulePath,
- const std::string &NewModulePath, StringRef SummaryPath,
+ const FunctionImporter::ImportMapTy &ImportList, unsigned Task,
+ StringRef ModulePath, const std::string &NewModulePath,
+ StringRef SummaryPath,
std::optional<std::reference_wrapper<ImportsFilesContainer>> ImportsFiles,
const FunctionImporter::ExportSetTy *ExportList = nullptr,
- const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> *ResolvedODR =
- nullptr,
+ const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>
+ *ResolvedODR = nullptr,
const DenseSet<GlobalValue::GUID> *CfiFunctionDefs = nullptr,
- const DenseSet<GlobalValue::GUID> *CfiFunctionDecls = nullptr)
- const;
+ const DenseSet<GlobalValue::GUID> *CfiFunctionDecls = nullptr) const;
};
/// This callable defines the behavior of a ThinLTO backend after the thin-link
@@ -664,8 +664,6 @@ class LTO {
addInput(std::unique_ptr<lto::InputFile> InputPtr) {
return std::shared_ptr<lto::InputFile>(InputPtr.release());
}
-
-
};
/// The resolution for a symbol. The linker must provide a SymbolResolution for
diff --git a/llvm/lib/DTLTO/DTLTO.cpp b/llvm/lib/DTLTO/DTLTO.cpp
index ed643e97f179d..5525cc42a15cc 100644
--- a/llvm/lib/DTLTO/DTLTO.cpp
+++ b/llvm/lib/DTLTO/DTLTO.cpp
@@ -54,12 +54,6 @@ Error save(StringRef Buffer, StringRef Path) {
return Error::success();
}
-// Saves the content of Input to Path overwriting any existing file.
-Error save(lto::InputFile *Input, StringRef Path) {
- MemoryBufferRef MB = Input->getFileBuffer();
- return save(MB.getBuffer(), Path);
-}
-
// Normalize and save a path. Aside from expanding Windows 8.3 short paths,
// no other normalization is currently required here. These paths are
// machine-local and break distribution systems; other normalization is
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 1e444ca92c443..3b1134dd34b60 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -1514,8 +1514,8 @@ SmallVector<StringRef> LTO::getLibFuncSymbols(const Triple &TT,
}
Error ThinBackendProc::emitFiles(
- const FunctionImporter::ImportMapTy &ImportList, unsigned Task, llvm::StringRef ModulePath,
- const std::string &NewModulePath) const {
+ const FunctionImporter::ImportMapTy &ImportList, unsigned Task,
+ llvm::StringRef ModulePath, const std::string &NewModulePath) const {
return emitFiles(ImportList, Task, ModulePath, NewModulePath,
NewModulePath + ".thinlto.bc",
/*ImportsFiles=*/std::nullopt, nullptr, nullptr, nullptr,
@@ -1523,14 +1523,14 @@ Error ThinBackendProc::emitFiles(
}
Error ThinBackendProc::emitFiles(
- const FunctionImporter::ImportMapTy &ImportList, unsigned Task, llvm::StringRef ModulePath,
- const std::string &NewModulePath, StringRef SummaryPath,
+ const FunctionImporter::ImportMapTy &ImportList, unsigned Task,
+ llvm::StringRef ModulePath, const std::string &NewModulePath,
+ StringRef SummaryPath,
std::optional<std::reference_wrapper<ImportsFilesContainer>> ImportsFiles,
const FunctionImporter::ExportSetTy *ExportList,
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> *ResolvedODR,
const DenseSet<GlobalValue::GUID> *CfiFunctionDefs,
- const DenseSet<GlobalValue::GUID> *CfiFunctionDecls)
- const {
+ const DenseSet<GlobalValue::GUID> *CfiFunctionDecls) const {
ModuleToSummariesForIndexTy ModuleToSummariesForIndex;
GVSummaryPtrSet DeclarationSummaries;
@@ -2007,9 +2007,9 @@ class WriteIndexesThinBackend : public ThinBackendProc {
std::string NewModulePath =
getThinLTOOutputFile(ModulePath, OldPrefix, NewPrefix);
auto E = emitFiles(ImportList, Task, ModulePath, NewModulePath,
- NewModulePath + ".thinlto.bc",
- /*ImportsFiles=*/std::nullopt, &ExportList,
- &ResolvedODR, &CfiFunctionDefs, &CfiFunctionDecls);
+ NewModulePath + ".thinlto.bc",
+ /*ImportsFiles=*/std::nullopt, &ExportList,
+ &ResolvedODR, &CfiFunctionDefs, &CfiFunctionDecls);
if (E) {
std::unique_lock<std::mutex> L(ErrMu);
if (Err)
diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index 60d9688385be5..2a83cb46ffe4c 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -18,8 +18,8 @@
#include "llvm/ADT/ScopeExit.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/CodeGen/CommandFlags.h"
-#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/DTLTO/DTLTO.h"
+#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Plugins/PassPlugin.h"
#include "llvm/Remarks/HotnessThresholdParser.h"
>From 4469fc41e1139190a24eeec01953f0a61b23d094 Mon Sep 17 00:00:00 2001
From: Konstantin Belochapka <konstantin.belochapka at sony.com>
Date: Fri, 1 May 2026 01:52:22 -0700
Subject: [PATCH 04/11] [DTLTO] Code refactor - addressed Teresa review
comments. #1
---
llvm/include/llvm/DTLTO/DTLTO.h | 2 --
llvm/include/llvm/LTO/Config.h | 2 +-
llvm/lib/DTLTO/DTLTO.cpp | 3 +--
llvm/lib/LTO/LTO.cpp | 1 +
4 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index ecc58631e68fa..3dbb90f261510 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -27,8 +27,6 @@
namespace llvm {
namespace lto {
-class DTLTO;
-
/// Prepares inputs for Distributed ThinLTO so that backend compilations can use
/// individual bitcode paths and consistent module IDs.
///
diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h
index 33d93c21c755a..9fe7a289abfac 100644
--- a/llvm/include/llvm/LTO/Config.h
+++ b/llvm/include/llvm/LTO/Config.h
@@ -295,7 +295,7 @@ struct Config {
/// Called by WriteIndexesThinBackend when it needs to write a bitcode
/// module's summary index. The callback should return a stream to write
- /// the index into. If the callback returns nullptr, the backend falls back
+ /// the index into. If not set, the backend falls back
/// to writing the summary index to a file.
std::function<std::unique_ptr<raw_pwrite_stream>(size_t Task)>
OnSummaryIndexStoreCb;
diff --git a/llvm/lib/DTLTO/DTLTO.cpp b/llvm/lib/DTLTO/DTLTO.cpp
index 5525cc42a15cc..e40c5a82568b0 100644
--- a/llvm/lib/DTLTO/DTLTO.cpp
+++ b/llvm/lib/DTLTO/DTLTO.cpp
@@ -237,9 +237,8 @@ void lto::DTLTO::cleanup() {
};
TimeTraceScope JobScope("Remove DTLTO temporary files");
- for (const auto &Name : CleanupList) {
+ for (const auto &Name : CleanupList)
removeFile(Name);
- }
}
Base::cleanup();
}
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 3b1134dd34b60..331b7d6c5e51e 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -1299,6 +1299,7 @@ Error LTO::checkPartiallySplit() {
}
Error LTO::run(AddStreamFn AddStream, FileCache Cache) {
+ llvm::scope_exit CleanUp([this]() { cleanup(); });
// Compute "dead" symbols, we don't want to import/export these!
DenseSet<GlobalValue::GUID> GUIDPreservedSymbols;
>From 57bd7c73f77c0fa4b52df1be26eea79edcf9baa1 Mon Sep 17 00:00:00 2001
From: Konstantin Belochapka <konstantin.belochapka at sony.com>
Date: Fri, 1 May 2026 04:14:12 -0700
Subject: [PATCH 05/11] [DTLTO] Code refactor - addressed Teresa review
comments. #2
---
lld/COFF/LTO.cpp | 4 ++--
lld/ELF/LTO.cpp | 6 +++---
llvm/include/llvm/DTLTO/DTLTO.h | 14 ++++++--------
llvm/lib/LTO/LTO.cpp | 2 +-
llvm/tools/llvm-lto2/llvm-lto2.cpp | 10 +++++-----
5 files changed, 17 insertions(+), 19 deletions(-)
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 123d6c51620c6..0329f6c2e9cea 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -126,7 +126,7 @@ BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) {
// Initialize ltoObj.
lto::ThinBackend backend;
- if (ctx.config.thinLTOIndexOnly || !ctx.config.dtltoDistributor.empty()) {
+ if (ctx.config.thinLTOIndexOnly) {
auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
backend = lto::createWriteIndexesThinBackend(
llvm::hardware_concurrency(ctx.config.thinLTOJobs),
@@ -144,7 +144,7 @@ BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) {
ctx.config.ltoPartitions);
else
ltoObj = std::make_unique<lto::DTLTO>(
- createConfig(), backend, ctx.config.ltoPartitions,
+ createConfig(), ctx.config.ltoPartitions,
llvm::lto::LTO::LTOKind::LTOK_Default, nullptr,
ctx.config.thinLTOEmitImportsFiles, ctx.config.thinLTOIndexOnly,
ctx.config.outputFile, ctx.config.dtltoDistributor,
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index fbab04d6b704f..e40575bffec62 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -184,7 +184,7 @@ BitcodeCompiler::BitcodeCompiler(Ctx &ctx) : ctx(ctx) {
// Initialize ltoObj.
lto::ThinBackend backend;
auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
- if (ctx.arg.thinLTOIndexOnly || !ctx.arg.dtltoDistributor.empty()) {
+ if (ctx.arg.thinLTOIndexOnly) {
backend = lto::createWriteIndexesThinBackend(
llvm::hardware_concurrency(ctx.arg.thinLTOJobs),
std::string(ctx.arg.thinLTOPrefixReplaceOld),
@@ -209,8 +209,8 @@ BitcodeCompiler::BitcodeCompiler(Ctx &ctx) : ctx(ctx) {
ltoModes[ctx.arg.ltoKind]);
else
ltoObj = std::make_unique<lto::DTLTO>(
- createConfig(ctx), backend, ctx.arg.ltoPartitions,
- ltoModes[ctx.arg.ltoKind], onIndexWrite, ctx.arg.thinLTOEmitIndexFiles,
+ createConfig(ctx), ctx.arg.ltoPartitions, ltoModes[ctx.arg.ltoKind],
+ onIndexWrite, ctx.arg.thinLTOEmitIndexFiles,
ctx.arg.thinLTOEmitImportsFiles, ctx.arg.outputFile,
ctx.arg.dtltoDistributor, ctx.arg.dtltoDistributorArgs,
ctx.arg.dtltoCompiler, ctx.arg.dtltoCompilerPrependArgs,
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index 3dbb90f261510..24d7bb5002fb5 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -47,17 +47,15 @@ class DTLTO : public LTO {
using Base = LTO;
public:
- LLVM_ABI DTLTO(Config Conf, ThinBackend Backend,
- unsigned ParallelCodeGenParallelismLevel, LTOKind LTOMode,
- IndexWriteCallback OnWrite, bool EmitIndexFiles,
- bool EmitImportsFiles, StringRef LinkerOutputFile,
- StringRef Distributor, ArrayRef<StringRef> DistributorArgs,
- StringRef RemoteCompiler,
+ LLVM_ABI DTLTO(Config Conf, unsigned ParallelCodeGenParallelismLevel,
+ LTOKind LTOMode, IndexWriteCallback OnWrite,
+ bool EmitIndexFiles, bool EmitImportsFiles,
+ StringRef LinkerOutputFile, StringRef Distributor,
+ ArrayRef<StringRef> DistributorArgs, StringRef RemoteCompiler,
ArrayRef<StringRef> RemoteCompilerPrependArgs,
ArrayRef<StringRef> RemoteCompilerArgs,
AddBufferFn AddBufferArg, bool SaveTempsArg)
- : Base(std::move(Conf), Backend, ParallelCodeGenParallelismLevel,
- LTOMode),
+ : Base(std::move(Conf), {}, ParallelCodeGenParallelismLevel, LTOMode),
AddBuffer(AddBufferArg), SaveTemps(SaveTempsArg),
ShouldEmitIndexFiles(EmitIndexFiles),
ShouldEmitImportFiles(EmitImportsFiles), OnWriteCb(OnWrite),
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 331b7d6c5e51e..83d92277de1c4 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -1299,7 +1299,7 @@ Error LTO::checkPartiallySplit() {
}
Error LTO::run(AddStreamFn AddStream, FileCache Cache) {
- llvm::scope_exit CleanUp([this]() { cleanup(); });
+ llvm::scope_exit CleanUp([this]() { LTO::cleanup(); });
// Compute "dead" symbols, we don't want to import/export these!
DenseSet<GlobalValue::GUID> GUIDPreservedSymbols;
diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index 2a83cb46ffe4c..843352251ef8d 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -444,7 +444,7 @@ static int run(int argc, char **argv) {
};
ThinBackend Backend;
- if (ThinLTODistributedIndexes || !DTLTODistributor.empty())
+ if (ThinLTODistributedIndexes)
Backend = createWriteIndexesThinBackend(llvm::hardware_concurrency(Threads),
/*OldPrefix=*/"",
/*NewPrefix=*/"",
@@ -478,10 +478,10 @@ static int run(int argc, char **argv) {
std::unique_ptr<LTO> Lto;
if (!DTLTODistributor.empty()) {
Lto = std::make_unique<DTLTO>(
- std::move(Conf), std::move(Backend), 1, LTOMode, nullptr,
- ThinLTOEmitIndexes, ThinLTOEmitImports, OutputFilename,
- DTLTODistributor, DTLTODistributorArgsSV, DTLTOCompiler,
- DTLTOCompilerPrependArgsSV, DTLTOCompilerArgsSV, AddBuffer, SaveTemps);
+ std::move(Conf), 1, LTOMode, nullptr, ThinLTOEmitIndexes,
+ ThinLTOEmitImports, OutputFilename, DTLTODistributor,
+ DTLTODistributorArgsSV, DTLTOCompiler, DTLTOCompilerPrependArgsSV,
+ DTLTOCompilerArgsSV, AddBuffer, SaveTemps);
} else {
Lto =
std::make_unique<LTO>(std::move(Conf), std::move(Backend), 1, LTOMode);
>From 49237be31ebc41ceffd4491a41e3892f939c5598 Mon Sep 17 00:00:00 2001
From: Konstantin Belochapka <konstantin.belochapka at sony.com>
Date: Wed, 20 May 2026 04:31:11 -0700
Subject: [PATCH 06/11] [DTLTO] DTLTO.cpp had been splitted into three sources:
DTLTOInputFiles.cpp, DTLTO.cpp and DTLTODistributionDriver.cpp.
---
llvm/include/llvm/DTLTO/DTLTO.h | 3 +
llvm/lib/DTLTO/CMakeLists.txt | 2 +
llvm/lib/DTLTO/DTLTO.cpp | 311 +--------------------
llvm/lib/DTLTO/DTLTODistributionDriver.cpp | 143 ++++++++++
llvm/lib/DTLTO/DTLTOInputFiles.cpp | 222 +++++++++++++++
5 files changed, 371 insertions(+), 310 deletions(-)
create mode 100644 llvm/lib/DTLTO/DTLTODistributionDriver.cpp
create mode 100644 llvm/lib/DTLTO/DTLTOInputFiles.cpp
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index 24d7bb5002fb5..c9c7b6e6aa82d 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -135,6 +135,9 @@ class DTLTO : public LTO {
// Keep temporary files when true.
bool SaveTemps = false;
+ // Saves the content of Buffer to Path overwriting any existing file.
+ static Error save(StringRef Buffer, StringRef Path);
+
public:
struct Job {
// Task index (combines RegularLTO parallel codegen offset with module
diff --git a/llvm/lib/DTLTO/CMakeLists.txt b/llvm/lib/DTLTO/CMakeLists.txt
index 62a4912c961ea..09c8c20b6a475 100644
--- a/llvm/lib/DTLTO/CMakeLists.txt
+++ b/llvm/lib/DTLTO/CMakeLists.txt
@@ -1,5 +1,7 @@
add_llvm_component_library(LLVMDTLTO
+ DTLTOInputFiles.cpp
DTLTO.cpp
+ DTLTODistributionDriver.cpp
LINK_COMPONENTS
BinaryFormat
diff --git a/llvm/lib/DTLTO/DTLTO.cpp b/llvm/lib/DTLTO/DTLTO.cpp
index e40c5a82568b0..aa446cadd4555 100644
--- a/llvm/lib/DTLTO/DTLTO.cpp
+++ b/llvm/lib/DTLTO/DTLTO.cpp
@@ -5,10 +5,9 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
// \file
// This file implements support functions for Distributed ThinLTO, focusing on
-// preparing input files for distribution.
+// preparing complilation jobs for distribution.
//
//===----------------------------------------------------------------------===//
@@ -19,211 +18,18 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/BinaryFormat/Magic.h"
#include "llvm/LTO/LTO.h"
-#include "llvm/Object/Archive.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/JSON.h"
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
-#ifdef _WIN32
-#include "llvm/Support/Windows/WindowsSupport.h"
-#endif
#include <string>
using namespace llvm;
-namespace {
-
-// Saves the content of Buffer to Path overwriting any existing file.
-Error save(StringRef Buffer, StringRef Path) {
- std::error_code EC;
- raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::OF_None);
- if (EC)
- return createStringError(inconvertibleErrorCode(),
- "Failed to create file %s: %s", Path.data(),
- EC.message().c_str());
- OS.write(Buffer.data(), Buffer.size());
- if (OS.has_error())
- return createStringError(inconvertibleErrorCode(),
- "Failed writing to file %s", Path.data());
- return Error::success();
-}
-
-// Normalize and save a path. Aside from expanding Windows 8.3 short paths,
-// no other normalization is currently required here. These paths are
-// machine-local and break distribution systems; other normalization is
-// handled by the DTLTO distributors.
-Expected<StringRef> normalizePath(StringRef Path, StringSaver &Saver) {
-#if defined(_WIN32)
- if (Path.empty())
- return Path;
- SmallString<256> Expanded;
- if (std::error_code EC = sys::windows::makeLongFormPath(Path, Expanded))
- return createStringError(inconvertibleErrorCode(),
- "Normalization failed for path %s: %s",
- Path.str().c_str(), EC.message().c_str());
- return Saver.save(Expanded.str());
-#else
- return Saver.save(Path);
-#endif
-}
-
-// Compute the file path for a thin archive member.
-//
-// For thin archives, an archive member name is typically a file path relative
-// to the archive file's directory. This function resolves that path.
-SmallString<256> computeThinArchiveMemberPath(StringRef ArchivePath,
- StringRef MemberName) {
- assert(!ArchivePath.empty() && "An archive file path must be non empty.");
- SmallString<256> MemberPath;
- if (sys::path::is_relative(MemberName)) {
- MemberPath = sys::path::parent_path(ArchivePath);
- sys::path::append(MemberPath, MemberName);
- } else {
- MemberPath = MemberName;
- }
- sys::path::remove_dots(MemberPath, /*remove_dot_dot=*/true);
- return MemberPath;
-}
-
-} // namespace
-
-// Determines if a file at the given path is a thin archive file.
-Expected<bool> lto::DTLTO::isThinArchive(const StringRef ArchivePath) {
- // Return cached result if available.
- auto Cached = ArchiveIsThinCache.find(ArchivePath);
- if (Cached != ArchiveIsThinCache.end())
- return Cached->second;
-
- uint64_t FileSize = -1;
- std::error_code EC = sys::fs::file_size(ArchivePath, FileSize);
- if (EC)
- return createStringError(inconvertibleErrorCode(),
- "Failed to get file size from archive %s: %s",
- ArchivePath.data(), EC.message().c_str());
- if (FileSize < sizeof(object::ThinArchiveMagic))
- return createStringError(inconvertibleErrorCode(),
- "Archive file size is too small %s",
- ArchivePath.data());
-
- // Read only the first few bytes containing the magic signature.
- ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFileSlice(
- ArchivePath, sizeof(object::ThinArchiveMagic), 0);
- if ((EC = MBOrErr.getError()))
- return createStringError(inconvertibleErrorCode(),
- "Failed to read from archive %s: %s",
- ArchivePath.data(), EC.message().c_str());
-
- StringRef Buf = (*MBOrErr)->getBuffer();
- if (file_magic::archive != identify_magic(Buf))
- return createStringError(inconvertibleErrorCode(),
- "Unknown format for archive %s",
- ArchivePath.data());
-
- bool IsThin = Buf.starts_with(object::ThinArchiveMagic);
-
- // Cache the result.
- ArchiveIsThinCache[ArchivePath] = IsThin;
-
- return IsThin;
-}
-
-// Add an input file and prepare it for distribution.
-Expected<std::shared_ptr<lto::InputFile>>
-lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) {
- TimeTraceScope TimeScope("Add input for DTLTO");
-
- // Add the input file to the LTO object.
- InputFiles.emplace_back(InputPtr.release());
- auto &Input = InputFiles.back();
- BitcodeModule &BM = Input->getPrimaryBitcodeModule();
-
- auto setIdFromPath = [&](StringRef Path) -> Error {
- auto N = normalizePath(Path, Saver);
- if (!N)
- return N.takeError();
- BM.setModuleIdentifier(*N);
- return Error::success();
- };
-
- StringRef ArchivePath = Input->getArchivePath();
-
- // In most cases, the module ID already points to an individual bitcode file
- // on disk, so no further preparation for distribution is required. However,
- // on Windows we overwite the module ID to expand Windows 8.3 short form
- // paths. These paths are machine-local and break distribution systems; other
- // normalization is handled by the DTLTO distributors.
- if (ArchivePath.empty() && !Input->isFatLTOObject()) {
-#if defined(_WIN32)
- if (Error E = setIdFromPath(Input->getName()))
- return std::move(E);
-#endif
- return Input;
- }
-
- // 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.
- Expected<bool> UseThinMember =
- Input->isFatLTOObject() ? false : isThinArchive(ArchivePath);
- if (!UseThinMember)
- return UseThinMember.takeError();
- if (*UseThinMember) {
- // For thin archives, use the path to the actual member file on disk.
- auto MemberPath =
- computeThinArchiveMemberPath(ArchivePath, Input->getMemberName());
- if (Error E = setIdFromPath(MemberPath))
- return std::move(E);
- return Input;
- }
-
- // A new file on disk will be needed for archive members and FatLTO objects.
- Input->setSerializeForDistribution(true);
-
- // Get the normalized output directory, if we haven't already.
- if (LinkerOutputDir.empty()) {
- auto N = normalizePath(
- sys::path::parent_path(DistributorParams.LinkerOutputFile), Saver);
- if (!N)
- return N.takeError();
- LinkerOutputDir = *N;
- }
-
- // Create a unique path by including the process ID and sequence number in the
- // filename.
- SmallString<256> Id(LinkerOutputDir);
- sys::path::append(Id,
- Twine(sys::path::filename(Input->getName())) + "." +
- std::to_string(InputFiles.size()) /*Sequence number*/ +
- "." + utohexstr(sys::Process::getProcessId()) + ".o");
- BM.setModuleIdentifier(Saver.save(Id.str()));
- return Input;
-}
-
-// Save the contents of ThinLTO-enabled input files that must be serialized for
-// distribution.
-Error lto::DTLTO::handleArchiveInputs() {
- 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);
- MemoryBufferRef Buf = Input->getFileBuffer();
- if (Error Err = save(Buf.getBuffer(), ModuleId))
- return Err;
- // Cleanup this file on abnormal process exit.
- if (!SaveTemps)
- addToCleanup(ModuleId);
- }
- return Error::success();
-}
-
// Remove temporary files created to enable distribution.
void lto::DTLTO::cleanup() {
if (!SaveTemps) {
@@ -503,118 +309,3 @@ Error lto::DTLTO::addObjectFilesToLink() {
}
return Error::success();
}
-
-// Generates a JSON file describing the backend compilations, for the
-// distributor.
-Error lto::DistributionDriver::emitJson() {
- using json::Array;
- std::error_code EC;
- raw_fd_ostream OS(DistributorJsonFile, EC);
- if (EC)
- return createStringError(EC, "Error while creating Json file");
-
- json::OStream JOS(OS);
- JOS.object([&]() {
- // Information common to all jobs.
- JOS.attributeObject("common", [&]() {
- JOS.attribute("linker_output", Params.LinkerOutputFile);
-
- JOS.attributeArray("args", [&]() {
- JOS.value(Params.RemoteCompiler);
-
- // Forward any supplied prepend options.
- if (!Params.RemoteCompilerPrependArgs.empty())
- for (auto &A : Params.RemoteCompilerPrependArgs)
- JOS.value(A);
-
- JOS.value("-c");
-
- JOS.value(std::string("--target=") + Params.TargetTriple.str());
-
- for (const auto &A : Params.CodegenOptions)
- JOS.value(A);
- });
-
- JOS.attribute("inputs", Array(Params.CommonInputs));
- });
-
- // Per-compilation-job information.
- JOS.attributeArray("jobs", [&]() {
- for (const auto &J : Jobs) {
- assert(J.Task != 0);
- if (J.Cached) {
- continue;
- }
-
- SmallVector<StringRef, 2> Inputs;
- SmallVector<StringRef, 1> Outputs;
-
- JOS.object([&]() {
- JOS.attributeArray("args", [&]() {
- JOS.value(J.ModuleID);
- Inputs.push_back(J.ModuleID);
-
- JOS.value(
- std::string("-fthinlto-index=" + J.SummaryIndexPath.str()));
- Inputs.push_back(J.SummaryIndexPath);
-
- JOS.value("-o");
- JOS.value(J.NativeObjectPath);
- Outputs.push_back(J.NativeObjectPath);
- });
-
- // Add the bitcode files from which imports will be made. These do
- // not explicitly appear on the backend compilation command lines
- // but are recorded in the summary index shards.
- append_range(Inputs, J.ImportsFiles);
- JOS.attribute("inputs", Array(Inputs));
-
- JOS.attribute("outputs", Array(Outputs));
- });
- }
- });
- });
-
- return Error::success();
-}
-
-// Saves JSON file on a filesystem.
-Error lto::DistributionDriver::saveJson() {
- DistributorJsonFile = sys::path::parent_path(Params.LinkerOutputFile);
- TimeTraceScope TimeScope("Emit DTLTO JSON");
- sys::path::append(DistributorJsonFile,
- sys::path::stem(Params.LinkerOutputFile) + "." +
- itostr(sys::Process::getProcessId()) +
- ".dist-file.json");
- if (Error E = emitJson())
- return make_error<StringError>(
- BCError + "failed to generate distributor JSON script: " +
- DistributorJsonFile,
- errorToErrorCode(std::move(E)));
-
- // Add JSON file to the cleanup files list.
- if (!SaveTemps)
- AddToCleanup(DistributorJsonFile);
- return Error::success();
-}
-
-// Invokes the distributor to compile uncached ThinLTO modules remotely.
-Error lto::DistributionDriver::operator()() {
- if (Error E = saveJson())
- return E;
-
- TimeTraceScope TimeScope("Execute DTLTO distributor", Params.DistributorPath);
- SmallVector<StringRef, 3> Args = {Params.DistributorPath};
- append_range(Args, Params.DistributorArgs);
- Args.push_back(DistributorJsonFile);
- std::string ErrMsg;
- if (sys::ExecuteAndWait(Args[0], Args,
- /*Env=*/std::nullopt, /*Redirects=*/{},
- /*SecondsToWait=*/0, /*MemoryLimit=*/0, &ErrMsg)) {
- return make_error<StringError>(
- BCError + "distributor execution failed" +
- (!ErrMsg.empty() ? ": " + ErrMsg + Twine(".") : Twine(".")),
- inconvertibleErrorCode());
- }
- return Error::success();
-}
diff --git a/llvm/lib/DTLTO/DTLTODistributionDriver.cpp b/llvm/lib/DTLTO/DTLTODistributionDriver.cpp
new file mode 100644
index 0000000000000..cde2060a47779
--- /dev/null
+++ b/llvm/lib/DTLTO/DTLTODistributionDriver.cpp
@@ -0,0 +1,143 @@
+//===- DTLTODistributionDriver.cpp - DTLTO Distribution Driver ---------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// \file
+// This file implements the Distributed ThinLTO driver that prepares the
+// compilation job descriptions and invokes the external distributor.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DTLTO/DTLTO.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/TimeProfiler.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <string>
+
+using namespace llvm;
+
+// Generates a JSON file describing the backend compilations, for the
+// distributor.
+Error lto::DistributionDriver::emitJson() {
+ using json::Array;
+ std::error_code EC;
+ raw_fd_ostream OS(DistributorJsonFile, EC);
+ if (EC)
+ return createStringError(EC, "Error while creating Json file");
+
+ json::OStream JOS(OS);
+ JOS.object([&]() {
+ // Information common to all jobs.
+ JOS.attributeObject("common", [&]() {
+ JOS.attribute("linker_output", Params.LinkerOutputFile);
+
+ JOS.attributeArray("args", [&]() {
+ JOS.value(Params.RemoteCompiler);
+
+ // Forward any supplied prepend options.
+ if (!Params.RemoteCompilerPrependArgs.empty())
+ for (auto &A : Params.RemoteCompilerPrependArgs)
+ JOS.value(A);
+
+ JOS.value("-c");
+
+ JOS.value(std::string("--target=") + Params.TargetTriple.str());
+
+ for (const auto &A : Params.CodegenOptions)
+ JOS.value(A);
+ });
+
+ JOS.attribute("inputs", Array(Params.CommonInputs));
+ });
+
+ // Per-compilation-job information.
+ JOS.attributeArray("jobs", [&]() {
+ for (const auto &J : Jobs) {
+ assert(J.Task != 0);
+ if (J.Cached) {
+ continue;
+ }
+
+ SmallVector<StringRef, 2> Inputs;
+ SmallVector<StringRef, 1> Outputs;
+
+ JOS.object([&]() {
+ JOS.attributeArray("args", [&]() {
+ JOS.value(J.ModuleID);
+ Inputs.push_back(J.ModuleID);
+
+ JOS.value(
+ std::string("-fthinlto-index=" + J.SummaryIndexPath.str()));
+ Inputs.push_back(J.SummaryIndexPath);
+
+ JOS.value("-o");
+ JOS.value(J.NativeObjectPath);
+ Outputs.push_back(J.NativeObjectPath);
+ });
+
+ // Add the bitcode files from which imports will be made. These do
+ // not explicitly appear on the backend compilation command lines
+ // but are recorded in the summary index shards.
+ append_range(Inputs, J.ImportsFiles);
+ JOS.attribute("inputs", Array(Inputs));
+
+ JOS.attribute("outputs", Array(Outputs));
+ });
+ }
+ });
+ });
+
+ return Error::success();
+}
+
+// Saves JSON file on a filesystem.
+Error lto::DistributionDriver::saveJson() {
+ DistributorJsonFile = sys::path::parent_path(Params.LinkerOutputFile);
+ TimeTraceScope TimeScope("Emit DTLTO JSON");
+ sys::path::append(DistributorJsonFile,
+ sys::path::stem(Params.LinkerOutputFile) + "." +
+ itostr(sys::Process::getProcessId()) +
+ ".dist-file.json");
+ if (Error E = emitJson())
+ return make_error<StringError>(
+ BCError + "failed to generate distributor JSON script: " +
+ DistributorJsonFile,
+ errorToErrorCode(std::move(E)));
+
+ // Add JSON file to the cleanup files list.
+ if (!SaveTemps)
+ AddToCleanup(DistributorJsonFile);
+ return Error::success();
+}
+
+// Invokes the distributor to compile uncached ThinLTO modules remotely.
+Error lto::DistributionDriver::operator()() {
+ if (Error E = saveJson())
+ return E;
+
+ TimeTraceScope TimeScope("Execute DTLTO distributor", Params.DistributorPath);
+ SmallVector<StringRef, 3> Args = {Params.DistributorPath};
+ append_range(Args, Params.DistributorArgs);
+ Args.push_back(DistributorJsonFile);
+ std::string ErrMsg;
+ if (sys::ExecuteAndWait(Args[0], Args,
+ /*Env=*/std::nullopt, /*Redirects=*/{},
+ /*SecondsToWait=*/0, /*MemoryLimit=*/0, &ErrMsg)) {
+ return make_error<StringError>(
+ BCError + "distributor execution failed" +
+ (!ErrMsg.empty() ? ": " + ErrMsg + Twine(".") : Twine(".")),
+ inconvertibleErrorCode());
+ }
+ return Error::success();
+}
diff --git a/llvm/lib/DTLTO/DTLTOInputFiles.cpp b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
new file mode 100644
index 0000000000000..a0b3ee6549fde
--- /dev/null
+++ b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
@@ -0,0 +1,222 @@
+//===- DTLTOInputFiles.cpp - Distributed ThinLTO implementation -----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// \file
+// This file implements support functions for Distributed ThinLTO, focusing on
+// preparing input files for distribution.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DTLTO/DTLTO.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBufferRef.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/TimeProfiler.h"
+#include "llvm/Support/raw_ostream.h"
+#ifdef _WIN32
+#include "llvm/Support/Windows/WindowsSupport.h"
+#endif
+
+#include <string>
+
+using namespace llvm;
+
+
+// Saves the content of Buffer to Path overwriting any existing file.
+Error lto::DTLTO::save(StringRef Buffer, StringRef Path) {
+ std::error_code EC;
+ raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::OF_None);
+ if (EC)
+ return createStringError(inconvertibleErrorCode(),
+ "Failed to create file %s: %s", Path.data(),
+ EC.message().c_str());
+ OS.write(Buffer.data(), Buffer.size());
+ if (OS.has_error())
+ return createStringError(inconvertibleErrorCode(),
+ "Failed writing to file %s", Path.data());
+ return Error::success();
+}
+
+namespace {
+ // Normalize and save a path. Aside from expanding Windows 8.3 short paths,
+// no other normalization is currently required here. These paths are
+// machine-local and break distribution systems; other normalization is
+// handled by the DTLTO distributors.
+Expected<StringRef> normalizePath(StringRef Path, StringSaver &Saver) {
+#if defined(_WIN32)
+ if (Path.empty())
+ return Path;
+ SmallString<256> Expanded;
+ if (std::error_code EC = sys::windows::makeLongFormPath(Path, Expanded))
+ return createStringError(inconvertibleErrorCode(),
+ "Normalization failed for path %s: %s",
+ Path.str().c_str(), EC.message().c_str());
+ return Saver.save(Expanded.str());
+#else
+ return Saver.save(Path);
+#endif
+}
+
+// Compute the file path for a thin archive member.
+//
+// For thin archives, an archive member name is typically a file path relative
+// to the archive file's directory. This function resolves that path.
+SmallString<256> computeThinArchiveMemberPath(StringRef ArchivePath,
+ StringRef MemberName) {
+ assert(!ArchivePath.empty() && "An archive file path must be non empty.");
+ SmallString<256> MemberPath;
+ if (sys::path::is_relative(MemberName)) {
+ MemberPath = sys::path::parent_path(ArchivePath);
+ sys::path::append(MemberPath, MemberName);
+ } else {
+ MemberPath = MemberName;
+ }
+ sys::path::remove_dots(MemberPath, /*remove_dot_dot=*/true);
+ return MemberPath;
+}
+
+} // namespace
+
+// Determines if a file at the given path is a thin archive file.
+Expected<bool> lto::DTLTO::isThinArchive(const StringRef ArchivePath) {
+ // Return cached result if available.
+ auto Cached = ArchiveIsThinCache.find(ArchivePath);
+ if (Cached != ArchiveIsThinCache.end())
+ return Cached->second;
+
+ uint64_t FileSize = -1;
+ std::error_code EC = sys::fs::file_size(ArchivePath, FileSize);
+ if (EC)
+ return createStringError(inconvertibleErrorCode(),
+ "Failed to get file size from archive %s: %s",
+ ArchivePath.data(), EC.message().c_str());
+ if (FileSize < sizeof(object::ThinArchiveMagic))
+ return createStringError(inconvertibleErrorCode(),
+ "Archive file size is too small %s",
+ ArchivePath.data());
+
+ // Read only the first few bytes containing the magic signature.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFileSlice(
+ ArchivePath, sizeof(object::ThinArchiveMagic), 0);
+ if ((EC = MBOrErr.getError()))
+ return createStringError(inconvertibleErrorCode(),
+ "Failed to read from archive %s: %s",
+ ArchivePath.data(), EC.message().c_str());
+
+ StringRef Buf = (*MBOrErr)->getBuffer();
+ if (file_magic::archive != identify_magic(Buf))
+ return createStringError(inconvertibleErrorCode(),
+ "Unknown format for archive %s",
+ ArchivePath.data());
+
+ bool IsThin = Buf.starts_with(object::ThinArchiveMagic);
+
+ // Cache the result.
+ ArchiveIsThinCache[ArchivePath] = IsThin;
+
+ return IsThin;
+}
+
+// Add an input file and prepare it for distribution.
+Expected<std::shared_ptr<lto::InputFile>>
+lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) {
+ TimeTraceScope TimeScope("Add input for DTLTO");
+
+ // Add the input file to the LTO object.
+ InputFiles.emplace_back(InputPtr.release());
+ auto &Input = InputFiles.back();
+ BitcodeModule &BM = Input->getPrimaryBitcodeModule();
+
+ auto setIdFromPath = [&](StringRef Path) -> Error {
+ auto N = normalizePath(Path, Saver);
+ if (!N)
+ return N.takeError();
+ BM.setModuleIdentifier(*N);
+ return Error::success();
+ };
+
+ StringRef ArchivePath = Input->getArchivePath();
+
+ // In most cases, the module ID already points to an individual bitcode file
+ // on disk, so no further preparation for distribution is required. However,
+ // on Windows we overwite the module ID to expand Windows 8.3 short form
+ // paths. These paths are machine-local and break distribution systems; other
+ // normalization is handled by the DTLTO distributors.
+ if (ArchivePath.empty() && !Input->isFatLTOObject()) {
+#if defined(_WIN32)
+ if (Error E = setIdFromPath(Input->getName()))
+ return std::move(E);
+#endif
+ return Input;
+ }
+
+ // 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.
+ Expected<bool> UseThinMember =
+ Input->isFatLTOObject() ? false : isThinArchive(ArchivePath);
+ if (!UseThinMember)
+ return UseThinMember.takeError();
+ if (*UseThinMember) {
+ // For thin archives, use the path to the actual member file on disk.
+ auto MemberPath =
+ computeThinArchiveMemberPath(ArchivePath, Input->getMemberName());
+ if (Error E = setIdFromPath(MemberPath))
+ return std::move(E);
+ return Input;
+ }
+
+ // A new file on disk will be needed for archive members and FatLTO objects.
+ Input->setSerializeForDistribution(true);
+
+ // Get the normalized output directory, if we haven't already.
+ if (LinkerOutputDir.empty()) {
+ auto N = normalizePath(
+ sys::path::parent_path(DistributorParams.LinkerOutputFile), Saver);
+ if (!N)
+ return N.takeError();
+ LinkerOutputDir = *N;
+ }
+
+ // Create a unique path by including the process ID and sequence number in the
+ // filename.
+ SmallString<256> Id(LinkerOutputDir);
+ sys::path::append(Id,
+ Twine(sys::path::filename(Input->getName())) + "." +
+ std::to_string(InputFiles.size()) /*Sequence number*/ +
+ "." + utohexstr(sys::Process::getProcessId()) + ".o");
+ BM.setModuleIdentifier(Saver.save(Id.str()));
+ return Input;
+}
+
+// Save the contents of ThinLTO-enabled input files that must be serialized for
+// distribution.
+Error lto::DTLTO::handleArchiveInputs() {
+ 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);
+ MemoryBufferRef Buf = Input->getFileBuffer();
+ if (Error Err = save(Buf.getBuffer(), ModuleId))
+ return Err;
+ // Cleanup this file on abnormal process exit.
+ if (!SaveTemps)
+ addToCleanup(ModuleId);
+ }
+ return Error::success();
+}
>From c6a3d6867004b9121ad337ff1860c829d285218c Mon Sep 17 00:00:00 2001
From: Konstantin Belochapka <konstantin.belochapka at sony.com>
Date: Sat, 23 May 2026 02:46:40 -0700
Subject: [PATCH 07/11] [DTLTO] Fixed after merge artifacts and fixed source
formatting for DTLTOInputFiles.cpp.
---
clang/test/CMakeLists.txt | 2 +-
llvm/lib/DTLTO/DTLTOInputFiles.cpp | 3 +--
llvm/tools/llvm-lto2/llvm-lto2.cpp | 2 +-
3 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt
index 235e9d0ba4c0a..ad74f549589e1 100644
--- a/clang/test/CMakeLists.txt
+++ b/clang/test/CMakeLists.txt
@@ -256,4 +256,4 @@ endif()
add_subdirectory(Analysis/LifetimeSafety)
-add_dependencies(check-clang-format clang-format-check-format)
+add_dependencies(clang-format-check-format)
diff --git a/llvm/lib/DTLTO/DTLTOInputFiles.cpp b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
index a0b3ee6549fde..ca1402f396cf4 100644
--- a/llvm/lib/DTLTO/DTLTOInputFiles.cpp
+++ b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
@@ -34,7 +34,6 @@
using namespace llvm;
-
// Saves the content of Buffer to Path overwriting any existing file.
Error lto::DTLTO::save(StringRef Buffer, StringRef Path) {
std::error_code EC;
@@ -51,7 +50,7 @@ Error lto::DTLTO::save(StringRef Buffer, StringRef Path) {
}
namespace {
- // Normalize and save a path. Aside from expanding Windows 8.3 short paths,
+// Normalize and save a path. Aside from expanding Windows 8.3 short paths,
// no other normalization is currently required here. These paths are
// machine-local and break distribution systems; other normalization is
// handled by the DTLTO distributors.
diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index 843352251ef8d..4bcf04df89ac9 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -532,7 +532,7 @@ static int run(int argc, char **argv) {
if (HasErrors)
return 1;
- Lto.setBitcodeLibFuncs(
+ Lto->setBitcodeLibFuncs(
SmallVector<StringRef>(BitcodeLibFuncs.begin(), BitcodeLibFuncs.end()));
FileCache Cache;
>From ae4ee1095354047af6d734a74c2317c389b396f2 Mon Sep 17 00:00:00 2001
From: Konstantin Belochapka <konstantin.belochapka at sony.com>
Date: Sat, 23 May 2026 03:44:09 -0700
Subject: [PATCH 08/11] [DTLTO] Temporary fix to make cmake configure phase
work.
---
clang/test/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt
index ad74f549589e1..6cf1e6cc5d8cf 100644
--- a/clang/test/CMakeLists.txt
+++ b/clang/test/CMakeLists.txt
@@ -256,4 +256,4 @@ endif()
add_subdirectory(Analysis/LifetimeSafety)
-add_dependencies(clang-format-check-format)
+add_dependencies(clang-test clang-format-check-format)
>From dcb7ad4a975dfcf97b92316c132944fa7c91b12b Mon Sep 17 00:00:00 2001
From: Konstantin Belochapka <konstantin.belochapka at sony.com>
Date: Sun, 24 May 2026 00:40:48 -0700
Subject: [PATCH 09/11] [DTLTO] Cleaner implementation of emitFiles() in
LTO.cpp.
---
llvm/include/llvm/LTO/LTO.h | 18 +++-------
llvm/lib/LTO/LTO.cpp | 70 +++++++++++++------------------------
2 files changed, 30 insertions(+), 58 deletions(-)
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index 5c0a85ba931e7..395b10a4dcd9f 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -292,19 +292,11 @@ class ThinBackendProc {
const std::string &NewModulePath) const;
// Write sharded indices to SummaryPath, (optionally) imports to disk, and
- // (optionally) record imports in ImportsFiles. When cache key parameters are
- // provided and GetCacheKeysListRefFunc is set, also computes and stores the
- // LTO cache key for the module.
- LLVM_ABI Error emitFiles(
- const FunctionImporter::ImportMapTy &ImportList, unsigned Task,
- StringRef ModulePath, const std::string &NewModulePath,
- StringRef SummaryPath,
- std::optional<std::reference_wrapper<ImportsFilesContainer>> ImportsFiles,
- const FunctionImporter::ExportSetTy *ExportList = nullptr,
- const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>
- *ResolvedODR = nullptr,
- const DenseSet<GlobalValue::GUID> *CfiFunctionDefs = nullptr,
- const DenseSet<GlobalValue::GUID> *CfiFunctionDecls = nullptr) const;
+ // (optionally) record imports in ImportsFiles.
+ LLVM_ABI Error emitFiles(const FunctionImporter::ImportMapTy &ImportList,
+ unsigned Task, StringRef ModulePath,
+ const std::string &NewModulePath,
+ StringRef SummaryPath) const;
};
/// This callable defines the behavior of a ThinLTO backend after the thin-link
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 83d92277de1c4..2277fc4f81bfa 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -1518,20 +1518,13 @@ Error ThinBackendProc::emitFiles(
const FunctionImporter::ImportMapTy &ImportList, unsigned Task,
llvm::StringRef ModulePath, const std::string &NewModulePath) const {
return emitFiles(ImportList, Task, ModulePath, NewModulePath,
- NewModulePath + ".thinlto.bc",
- /*ImportsFiles=*/std::nullopt, nullptr, nullptr, nullptr,
- nullptr);
+ NewModulePath + ".thinlto.bc");
}
Error ThinBackendProc::emitFiles(
const FunctionImporter::ImportMapTy &ImportList, unsigned Task,
llvm::StringRef ModulePath, const std::string &NewModulePath,
- StringRef SummaryPath,
- std::optional<std::reference_wrapper<ImportsFilesContainer>> ImportsFiles,
- const FunctionImporter::ExportSetTy *ExportList,
- const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> *ResolvedODR,
- const DenseSet<GlobalValue::GUID> *CfiFunctionDefs,
- const DenseSet<GlobalValue::GUID> *CfiFunctionDecls) const {
+ StringRef SummaryPath) const {
ModuleToSummariesForIndexTy ModuleToSummariesForIndex;
GVSummaryPtrSet DeclarationSummaries;
@@ -1540,53 +1533,33 @@ Error ThinBackendProc::emitFiles(
ImportList, ModuleToSummariesForIndex,
DeclarationSummaries);
- if (Conf.OnSummaryIndexStoreCb) {
- std::unique_ptr<raw_pwrite_stream> PS = Conf.OnSummaryIndexStoreCb(Task);
- writeIndexToFile(CombinedIndex, *PS, &ModuleToSummariesForIndex,
- &DeclarationSummaries);
- } else {
+ if (!Conf.OnSummaryIndexStoreCb) {
raw_fd_ostream OS(SummaryPath, EC, sys::fs::OpenFlags::OF_None);
if (EC)
return createFileError("cannot open " + Twine(SummaryPath), EC);
writeIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex,
&DeclarationSummaries);
- }
- if (Conf.OnImportsListStoreCb) {
- std::vector<std::string> &ImportsListRef = Conf.OnImportsListStoreCb(Task);
- for (const auto &ILI : ModuleToSummariesForIndex) {
- if (ILI.first != ModulePath)
- ImportsListRef.push_back(ILI.first);
- }
- } else {
if (ShouldEmitImportsFiles) {
Error ImportsFilesError = EmitImportsFiles(
ModulePath, NewModulePath + ".imports", ModuleToSummariesForIndex);
if (ImportsFilesError)
return ImportsFilesError;
}
- }
-
- // Optionally, store the imports files.
- if (ImportsFiles)
- processImportsFiles(
- ModulePath, ModuleToSummariesForIndex,
- [&](StringRef M) { ImportsFiles->get().push_back(M.str()); });
-
- // Optionally, compute and store the LTO cache key for DTLTO.
- if (Conf.OnCacheKeyStoreCb && ExportList && ResolvedODR && CfiFunctionDefs &&
- CfiFunctionDecls) {
- assert(ModuleToDefinedGVSummaries.count(ModulePath));
- const GVSummaryMapTy &DefinedGlobals =
- ModuleToDefinedGVSummaries.find(ModulePath)->second;
+ } else {
+ std::unique_ptr<raw_pwrite_stream> OS = Conf.OnSummaryIndexStoreCb(Task);
+ writeIndexToFile(CombinedIndex, *OS, &ModuleToSummariesForIndex,
+ &DeclarationSummaries);
- std::string &CacheKeyRef = Conf.OnCacheKeyStoreCb(Task);
- CacheKeyRef = computeLTOCacheKey(
- Conf, CombinedIndex, ModulePath, ImportList, *ExportList, *ResolvedODR,
- DefinedGlobals, *CfiFunctionDefs, *CfiFunctionDecls);
+ if (Conf.OnImportsListStoreCb) {
+ std::vector<std::string> &ImportsListRef =
+ Conf.OnImportsListStoreCb(Task);
+ processImportsFiles(
+ ModulePath, ModuleToSummariesForIndex,
+ [&](StringRef M) { ImportsListRef.push_back(M.str()); });
+ }
}
-
return Error::success();
}
@@ -2007,10 +1980,7 @@ class WriteIndexesThinBackend : public ThinBackendProc {
const std::string &OldPrefix, const std::string &NewPrefix) {
std::string NewModulePath =
getThinLTOOutputFile(ModulePath, OldPrefix, NewPrefix);
- auto E = emitFiles(ImportList, Task, ModulePath, NewModulePath,
- NewModulePath + ".thinlto.bc",
- /*ImportsFiles=*/std::nullopt, &ExportList,
- &ResolvedODR, &CfiFunctionDefs, &CfiFunctionDecls);
+ auto E = emitFiles(ImportList, Task, ModulePath, NewModulePath);
if (E) {
std::unique_lock<std::mutex> L(ErrMu);
if (Err)
@@ -2018,6 +1988,16 @@ class WriteIndexesThinBackend : public ThinBackendProc {
else
Err = std::move(E);
}
+ assert(ModuleToDefinedGVSummaries.count(ModulePath));
+ const GVSummaryMapTy &DefinedGlobals =
+ ModuleToDefinedGVSummaries.find(ModulePath)->second;
+
+ if (Conf.OnCacheKeyStoreCb) {
+ std::string &CacheKey = Conf.OnCacheKeyStoreCb(Task);
+ CacheKey = computeLTOCacheKey(
+ Conf, CombinedIndex, ModulePath, ImportList, ExportList,
+ ResolvedODR, DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls);
+ }
},
Task, ModulePath, ImportList, ExportList, ResolvedODR, OldPrefix,
NewPrefix);
>From 83a3f669ac61247f61fac3cfdd16723d6e36a3b3 Mon Sep 17 00:00:00 2001
From: Konstantin Belochapka <konstantin.belochapka at sony.com>
Date: Sun, 31 May 2026 23:41:03 -0700
Subject: [PATCH 10/11] [DTLTO] Code refactor - addressed Teresa review
comments. #3
---
llvm/include/llvm/DTLTO/DTLTO.h | 32 ++++++++-----
llvm/include/llvm/LTO/Config.h | 7 +--
llvm/lib/DTLTO/DTLTO.cpp | 38 +++++++--------
llvm/lib/DTLTO/DTLTODistributionDriver.cpp | 4 +-
llvm/lib/DTLTO/DTLTOInputFiles.cpp | 8 ++--
llvm/lib/LTO/LTO.cpp | 56 ++++++++++++----------
6 files changed, 78 insertions(+), 67 deletions(-)
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index c9c7b6e6aa82d..871b9fe25ef4a 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -1,4 +1,4 @@
-//===- DTLTO.h - Distributed ThinLTO implementation -----------------------===//
+//===- DTLTO.h - Integrated Distributed ThinLTO implementation ------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -7,9 +7,9 @@
//===----------------------------------------------------------------------===//
//
// \file
-// Declarations for Distributed ThinLTO, including the DTLTO class and the
-// distribution driver. The implementation focuses on preparing input files for
-// distribution.
+// Declarations for Integrated Distributed ThinLTO, including the DTLTO class
+// and the distribution driver. The implementation focuses on preparing input
+// files for distribution.
//
//===----------------------------------------------------------------------===//
@@ -42,7 +42,7 @@ namespace lto {
/// 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
-/// handleArchiveInputs().
+/// serializeBitcodeArchiveMembers().
class DTLTO : public LTO {
using Base = LTO;
@@ -55,23 +55,29 @@ class DTLTO : public LTO {
ArrayRef<StringRef> RemoteCompilerPrependArgs,
ArrayRef<StringRef> RemoteCompilerArgs,
AddBufferFn AddBufferArg, bool SaveTempsArg)
- : Base(std::move(Conf), {}, ParallelCodeGenParallelismLevel, LTOMode),
+ : Base(std::move(Conf), writeIndexesBackendInstance(),
+ ParallelCodeGenParallelismLevel, LTOMode),
AddBuffer(AddBufferArg), SaveTemps(SaveTempsArg),
ShouldEmitIndexFiles(EmitIndexFiles),
- ShouldEmitImportFiles(EmitImportsFiles), OnWriteCb(OnWrite),
+ ShouldEmitImportFiles(EmitImportsFiles), OnIndexWriteCb(OnWrite),
DistributorParams{Distributor, DistributorArgs,
RemoteCompiler, RemoteCompilerPrependArgs,
RemoteCompilerArgs, LinkerOutputFile} {
assert(!LinkerOutputFile.empty() && "expected a valid linker output file");
}
+ // Create an instance of WriteIndexesBackend class.
+ static lto::ThinBackend writeIndexesBackendInstance() {
+ return lto::createWriteIndexesThinBackend(hardware_concurrency(), "", "",
+ "", true, nullptr, nullptr);
+ }
+
/// Add an input file and prepare it for distribution.
///
/// This function performs the following tasks:
/// 1. Add the input file to the LTO object's list of input files.
/// 2. For individual bitcode file inputs on Windows only, overwrite the
- /// module
- /// ID with a normalized path to remove short 8.3 form components.
+ /// module ID with a normalized path to remove short 8.3 form components.
/// 3. For thin archive members, overwrite the module ID with the path
/// (normalized on Windows) to the member file on disk.
/// 4. For archive members and FatLTO objects, overwrite the module ID with a
@@ -102,7 +108,7 @@ class DTLTO : public LTO {
/// 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.
- LLVM_ABI Error handleArchiveInputs();
+ LLVM_ABI Error serializeBitcodeArchiveMembers();
// Remove temporary files created to enable distribution.
LLVM_ABI void cleanup() override;
@@ -173,7 +179,7 @@ class DTLTO : public LTO {
// Keep summary import files when true.
bool ShouldEmitImportFiles = false;
// On index file write callback.
- IndexWriteCallback OnWriteCb;
+ IndexWriteCallback OnIndexWriteCb;
/// Probes the LTO cache for a compiled native object for the given job.
///
@@ -273,7 +279,7 @@ class DTLTO : public LTO {
// Per-task summary index shards from the thin link (in-memory buffers).
std::vector<SmallString<0>> SummaryIndexFiles;
// Per-task imported bitcode paths from the thin link.
- std::vector<std::vector<std::string>> ImportsFilesLists;
+ std::vector<std::vector<std::string>> ImportsFiles;
// Per-task cache keys for incremental builds from the thin link.
std::vector<std::string> CacheKeysList;
@@ -285,7 +291,7 @@ class DTLTO : public LTO {
/// references and produces:
///
/// - SummaryIndexFiles: per-module summary index shards (in-memory buffers)
- /// - ImportsFilesLists: per-module lists of imported bitcode files
+ /// - ImportsFiles: per-module lists of imported bitcode files
/// - CacheKeysList: per-module cache keys for incremental builds
/// - ModuleNames: per-module identifiers
///
diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h
index 9fe7a289abfac..f322f753813ff 100644
--- a/llvm/include/llvm/LTO/Config.h
+++ b/llvm/include/llvm/LTO/Config.h
@@ -298,17 +298,18 @@ struct Config {
/// the index into. If not set, the backend falls back
/// to writing the summary index to a file.
std::function<std::unique_ptr<raw_pwrite_stream>(size_t Task)>
- OnSummaryIndexStoreCb;
+ GetSummaryIndexOutputStream;
/// Called by WriteIndexesThinBackend when it needs to store a bitcode
/// module's imports list. The callback should return a vector that the
/// backend will populate with the imported module paths. If not set, the
/// backend writes the imports list to a file instead.
- std::function<std::vector<std::string> &(size_t Task)> OnImportsListStoreCb;
+ std::function<std::vector<std::string> &(size_t Task)>
+ GetImportsListOutputArray;
/// Called by WriteIndexesThinBackend when it needs to store a bitcode
/// module's cache key. The callback should return a string that the backend
/// will fill with the computed cache key. If not set, the cache key is
/// discarded.
- std::function<std::string &(size_t Task)> OnCacheKeyStoreCb;
+ std::function<std::string &(size_t Task)> GetCacheKeyOutputString;
};
struct LTOLLVMDiagnosticHandler : public DiagnosticHandler {
diff --git a/llvm/lib/DTLTO/DTLTO.cpp b/llvm/lib/DTLTO/DTLTO.cpp
index aa446cadd4555..16a5fd1a7c79d 100644
--- a/llvm/lib/DTLTO/DTLTO.cpp
+++ b/llvm/lib/DTLTO/DTLTO.cpp
@@ -1,4 +1,4 @@
-//===- DTLTO.cpp - Distributed ThinLTO implementation ---------------------===//
+//===- DTLTO.cpp - Integrated Distributed ThinLTO implementation ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
// \file
-// This file implements support functions for Distributed ThinLTO, focusing on
-// preparing complilation jobs for distribution.
+// This file implements support functions for Integrated Distributed ThinLTO,
+// focusing on preparing complilation jobs for distribution.
//
//===----------------------------------------------------------------------===//
@@ -45,35 +45,35 @@ void lto::DTLTO::cleanup() {
TimeTraceScope JobScope("Remove DTLTO temporary files");
for (const auto &Name : CleanupList)
removeFile(Name);
+ // Clean the CleanupList for safety.
+ CleanupList.clear();
}
- Base::cleanup();
}
// Runs the DTLTO thin link phase, producing per-module summary indices,
// import lists, and cache keys for distribution.
Error lto::DTLTO::performThinLink() {
- auto ThinIndexBackend = lto::createWriteIndexesThinBackend(
- hardware_concurrency(), "", "", "", true, nullptr, nullptr);
- setThinBackend(ThinIndexBackend);
setLTOMode(lto::LTO::LTOKind::LTOK_UnifiedThin);
size_t NumTasks = getMaxTasks();
SummaryIndexFiles.resize(NumTasks);
- ImportsFilesLists.resize(NumTasks);
+ ImportsFiles.resize(NumTasks);
CacheKeysList.resize(NumTasks);
lto::Config &Cfg = getConfig();
- Cfg.OnSummaryIndexStoreCb =
+ Cfg.GetSummaryIndexOutputStream =
[&](size_t task) -> std::unique_ptr<raw_svector_ostream> {
return std::make_unique<raw_svector_ostream>(SummaryIndexFiles[task]);
};
- Cfg.OnCacheKeyStoreCb = [&](size_t task) -> std::string & {
+ Cfg.GetCacheKeyOutputString = [&](size_t task) -> std::string & {
return CacheKeysList[task];
};
- Cfg.OnImportsListStoreCb = [&](size_t task) -> std::vector<std::string> & {
- return ImportsFilesLists[task];
- };
-
+ if (ShouldEmitImportFiles) {
+ Cfg.GetImportsListOutputArray =
+ [&](size_t task) -> std::vector<std::string> & {
+ return ImportsFiles[task];
+ };
+ }
return Base::run(AddStreamFunc, {});
}
@@ -94,7 +94,7 @@ LLVM_ABI Error lto::DTLTO::run(AddStreamFn AddStream, FileCache CacheParam) {
if (Error Err = prepareDtltoJobs())
return Err;
- if (Error Err = handleArchiveInputs())
+ if (Error Err = serializeBitcodeArchiveMembers())
return Err;
if (Error Err = performCodegen())
return Err;
@@ -148,7 +148,7 @@ Error lto::DTLTO::prepareDtltoJob(StringRef ModulePath, unsigned Task) {
Saver.save(ObjFilePath.str()),
Saver.save(SummaryIndexPathStr.str()),
Saver.save(ImportsPathStr.str()),
- ImportsFilesLists[Task],
+ ImportsFiles[Task],
CacheKeysList[Task],
nullptr,
false};
@@ -161,11 +161,11 @@ Error lto::DTLTO::prepareDtltoJob(StringRef ModulePath, unsigned Task) {
if (Error Err = save(SummaryIndexFiles[Task], J.SummaryIndexPath))
return Err;
}
- if (OnWriteCb)
- OnWriteCb(J.SummaryIndexPath.str());
+ if (OnIndexWriteCb)
+ OnIndexWriteCb(J.SummaryIndexPath.str());
if (ShouldEmitImportFiles)
- if (Error Err = save(join(ImportsFilesLists[Task], "\n"), J.ImportsPath))
+ if (Error Err = save(join(ImportsFiles[Task], "\n"), J.ImportsPath))
return Err;
if (!SaveTemps) {
diff --git a/llvm/lib/DTLTO/DTLTODistributionDriver.cpp b/llvm/lib/DTLTO/DTLTODistributionDriver.cpp
index cde2060a47779..24dff04318a79 100644
--- a/llvm/lib/DTLTO/DTLTODistributionDriver.cpp
+++ b/llvm/lib/DTLTO/DTLTODistributionDriver.cpp
@@ -7,8 +7,8 @@
//===----------------------------------------------------------------------===//
//
// \file
-// This file implements the Distributed ThinLTO driver that prepares the
-// compilation job descriptions and invokes the external distributor.
+// This file implements the Integrated Distributed ThinLTO driver that prepares
+// the compilation job descriptions and invokes the external distributor.
//
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/DTLTO/DTLTOInputFiles.cpp b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
index ca1402f396cf4..b13019e70083f 100644
--- a/llvm/lib/DTLTO/DTLTOInputFiles.cpp
+++ b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
@@ -1,4 +1,4 @@
-//===- DTLTOInputFiles.cpp - Distributed ThinLTO implementation -----------===//
+//===- DTLTOInputFiles.cpp - Integrated Distributed ThinLTO implementation ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -7,8 +7,8 @@
//===----------------------------------------------------------------------===//
//
// \file
-// This file implements support functions for Distributed ThinLTO, focusing on
-// preparing input files for distribution.
+// This file implements support functions for Integrated Distributed ThinLTO,
+// focusing on preparing input files for distribution.
//
//===----------------------------------------------------------------------===//
@@ -203,7 +203,7 @@ 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::handleArchiveInputs() {
+Error lto::DTLTO::serializeBitcodeArchiveMembers() {
for (auto &Input : InputFiles) {
if (!Input->isThinLTO() || !Input->getSerializeForDistribution())
continue;
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 2277fc4f81bfa..dbc0b28002e15 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -1299,6 +1299,8 @@ Error LTO::checkPartiallySplit() {
}
Error LTO::run(AddStreamFn AddStream, FileCache Cache) {
+ // Call the base class cleanup() explicitly since run() may be invoked on a
+ // derived LTO object.
llvm::scope_exit CleanUp([this]() { LTO::cleanup(); });
// Compute "dead" symbols, we don't want to import/export these!
@@ -1532,33 +1534,34 @@ Error ThinBackendProc::emitFiles(
gatherImportedSummariesForModule(ModulePath, ModuleToDefinedGVSummaries,
ImportList, ModuleToSummariesForIndex,
DeclarationSummaries);
-
- if (!Conf.OnSummaryIndexStoreCb) {
- raw_fd_ostream OS(SummaryPath, EC, sys::fs::OpenFlags::OF_None);
+ // Resolve the output stream (either file-backed or callback-provided) for the
+ // index file.
+ std::unique_ptr<raw_pwrite_stream> OS;
+ if (Conf.GetSummaryIndexOutputStream) {
+ OS = Conf.GetSummaryIndexOutputStream(Task);
+ assert(OS && "GetSummaryIndexOutputStream returned null");
+ } else {
+ auto FileOS = std::make_unique<raw_fd_ostream>(SummaryPath, EC,
+ sys::fs::OpenFlags::OF_None);
if (EC)
return createFileError("cannot open " + Twine(SummaryPath), EC);
+ OS = std::move(FileOS);
+ }
- writeIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex,
- &DeclarationSummaries);
-
- if (ShouldEmitImportsFiles) {
- Error ImportsFilesError = EmitImportsFiles(
- ModulePath, NewModulePath + ".imports", ModuleToSummariesForIndex);
- if (ImportsFilesError)
- return ImportsFilesError;
- }
- } else {
- std::unique_ptr<raw_pwrite_stream> OS = Conf.OnSummaryIndexStoreCb(Task);
- writeIndexToFile(CombinedIndex, *OS, &ModuleToSummariesForIndex,
- &DeclarationSummaries);
-
- if (Conf.OnImportsListStoreCb) {
- std::vector<std::string> &ImportsListRef =
- Conf.OnImportsListStoreCb(Task);
- processImportsFiles(
- ModulePath, ModuleToSummariesForIndex,
- [&](StringRef M) { ImportsListRef.push_back(M.str()); });
- }
+ writeIndexToFile(CombinedIndex, *OS, &ModuleToSummariesForIndex,
+ &DeclarationSummaries);
+
+ // Emit imports files if requested, using callback if provided.
+ if (Conf.GetImportsListOutputArray) {
+ std::vector<std::string> &ImportsListRef =
+ Conf.GetImportsListOutputArray(Task);
+ processImportsFiles(
+ ModulePath, ModuleToSummariesForIndex,
+ [&](StringRef M) { ImportsListRef.push_back(M.str()); });
+ } else if (ShouldEmitImportsFiles) {
+ if (Error E = EmitImportsFiles(ModulePath, NewModulePath + ".imports",
+ ModuleToSummariesForIndex))
+ return E;
}
return Error::success();
}
@@ -1992,8 +1995,9 @@ class WriteIndexesThinBackend : public ThinBackendProc {
const GVSummaryMapTy &DefinedGlobals =
ModuleToDefinedGVSummaries.find(ModulePath)->second;
- if (Conf.OnCacheKeyStoreCb) {
- std::string &CacheKey = Conf.OnCacheKeyStoreCb(Task);
+ // DTLTO needs the per-module LTO cache key to probe the cache.
+ if (Conf.GetCacheKeyOutputString) {
+ std::string &CacheKey = Conf.GetCacheKeyOutputString(Task);
CacheKey = computeLTOCacheKey(
Conf, CombinedIndex, ModulePath, ImportList, ExportList,
ResolvedODR, DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls);
>From 25411674ea820c4e44c50efdf8c7d315e2ee1528 Mon Sep 17 00:00:00 2001
From: Konstantin Belochapka <konstantin.belochapka at sony.com>
Date: Mon, 1 Jun 2026 22:14:00 -0700
Subject: [PATCH 11/11] [DTLTO] Code refactor - addressed Teresa review
comments. #4
---
llvm/include/llvm/DTLTO/DTLTO.h | 21 ++++++++-------------
llvm/include/llvm/LTO/LTO.h | 2 +-
llvm/lib/DTLTO/DTLTO.cpp | 12 +++++-------
llvm/lib/DTLTO/DTLTODistributionDriver.cpp | 2 +-
llvm/lib/DTLTO/DTLTOInputFiles.cpp | 2 +-
5 files changed, 16 insertions(+), 23 deletions(-)
diff --git a/llvm/include/llvm/DTLTO/DTLTO.h b/llvm/include/llvm/DTLTO/DTLTO.h
index 871b9fe25ef4a..b1e43c3e9346d 100644
--- a/llvm/include/llvm/DTLTO/DTLTO.h
+++ b/llvm/include/llvm/DTLTO/DTLTO.h
@@ -42,7 +42,7 @@ namespace lto {
/// 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
-/// serializeBitcodeArchiveMembers().
+/// serializeLTOInputs().
class DTLTO : public LTO {
using Base = LTO;
@@ -64,6 +64,7 @@ class DTLTO : public LTO {
RemoteCompiler, RemoteCompilerPrependArgs,
RemoteCompilerArgs, LinkerOutputFile} {
assert(!LinkerOutputFile.empty() && "expected a valid linker output file");
+ LTOMode = lto::LTO::LTOKind::LTOK_UnifiedThin;
}
// Create an instance of WriteIndexesBackend class.
@@ -108,7 +109,7 @@ class DTLTO : public LTO {
/// 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.
- LLVM_ABI Error serializeBitcodeArchiveMembers();
+ LLVM_ABI Error serializeLTOInputs();
// Remove temporary files created to enable distribution.
LLVM_ABI void cleanup() override;
@@ -118,12 +119,6 @@ class DTLTO : public LTO {
Config &getConfig() { return Conf; }
const Config &getConfig() const { return Conf; }
- // Set the LTO kind.
- void setLTOMode(LTOKind Knd) { LTOMode = Knd; }
- // Replace the ThinLTO backend (e.g. WriteIndexesThinBackend for the thin
- // link).
- void setThinBackend(ThinBackend Backend) { ThinLTO.Backend = Backend; }
-
private:
// Bump allocator for saving updated module IDs.
BumpPtrAllocator PtrAlloc;
@@ -158,7 +153,7 @@ class DTLTO : public LTO {
// Per-module imports list path.
StringRef ImportsPath;
// Bitcode files from which this module imports.
- ArrayRef<std::string> ImportsFiles;
+ ArrayRef<std::string> ImportsFilesList;
// Cache key from thin link.
std::string CacheKey;
// On cache miss, stream used to store the compiled object in the cache.
@@ -209,8 +204,8 @@ class DTLTO : public LTO {
/// derived from ModulePath.
///
/// 2. Initializes the Job struct with Task, ModuleID (ModulePath), paths,
- /// ImportsFiles and CacheKey from thin link results, and default values
- /// for CacheAddStream and Cached.
+ /// ImportsFilesList and CacheKey from thin link results, and default
+ /// values for CacheAddStream and Cached.
///
/// 3. Calls checkCacheHit() to probe the cache. On a cache hit, J.Cached is
/// set and the cached object has already been passed to the linker; the
@@ -279,7 +274,7 @@ class DTLTO : public LTO {
// Per-task summary index shards from the thin link (in-memory buffers).
std::vector<SmallString<0>> SummaryIndexFiles;
// Per-task imported bitcode paths from the thin link.
- std::vector<std::vector<std::string>> ImportsFiles;
+ std::vector<std::vector<std::string>> ImportsFilesList;
// Per-task cache keys for incremental builds from the thin link.
std::vector<std::string> CacheKeysList;
@@ -291,7 +286,7 @@ class DTLTO : public LTO {
/// references and produces:
///
/// - SummaryIndexFiles: per-module summary index shards (in-memory buffers)
- /// - ImportsFiles: per-module lists of imported bitcode files
+ /// - ImportsFilesList: per-module lists of imported bitcode files
/// - CacheKeysList: per-module cache keys for incremental builds
/// - ModuleNames: per-module identifiers
///
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index 395b10a4dcd9f..bfb5388c8c7cb 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -292,7 +292,7 @@ class ThinBackendProc {
const std::string &NewModulePath) const;
// Write sharded indices to SummaryPath, (optionally) imports to disk, and
- // (optionally) record imports in ImportsFiles.
+ // (optionally) record imports in ImportsFilesList.
LLVM_ABI Error emitFiles(const FunctionImporter::ImportMapTy &ImportList,
unsigned Task, StringRef ModulePath,
const std::string &NewModulePath,
diff --git a/llvm/lib/DTLTO/DTLTO.cpp b/llvm/lib/DTLTO/DTLTO.cpp
index 16a5fd1a7c79d..6f8d9824911e8 100644
--- a/llvm/lib/DTLTO/DTLTO.cpp
+++ b/llvm/lib/DTLTO/DTLTO.cpp
@@ -53,11 +53,9 @@ void lto::DTLTO::cleanup() {
// Runs the DTLTO thin link phase, producing per-module summary indices,
// import lists, and cache keys for distribution.
Error lto::DTLTO::performThinLink() {
- setLTOMode(lto::LTO::LTOKind::LTOK_UnifiedThin);
-
size_t NumTasks = getMaxTasks();
SummaryIndexFiles.resize(NumTasks);
- ImportsFiles.resize(NumTasks);
+ ImportsFilesList.resize(NumTasks);
CacheKeysList.resize(NumTasks);
lto::Config &Cfg = getConfig();
@@ -71,7 +69,7 @@ Error lto::DTLTO::performThinLink() {
if (ShouldEmitImportFiles) {
Cfg.GetImportsListOutputArray =
[&](size_t task) -> std::vector<std::string> & {
- return ImportsFiles[task];
+ return ImportsFilesList[task];
};
}
return Base::run(AddStreamFunc, {});
@@ -94,7 +92,7 @@ LLVM_ABI Error lto::DTLTO::run(AddStreamFn AddStream, FileCache CacheParam) {
if (Error Err = prepareDtltoJobs())
return Err;
- if (Error Err = serializeBitcodeArchiveMembers())
+ if (Error Err = serializeLTOInputs())
return Err;
if (Error Err = performCodegen())
return Err;
@@ -148,7 +146,7 @@ Error lto::DTLTO::prepareDtltoJob(StringRef ModulePath, unsigned Task) {
Saver.save(ObjFilePath.str()),
Saver.save(SummaryIndexPathStr.str()),
Saver.save(ImportsPathStr.str()),
- ImportsFiles[Task],
+ ImportsFilesList[Task],
CacheKeysList[Task],
nullptr,
false};
@@ -165,7 +163,7 @@ Error lto::DTLTO::prepareDtltoJob(StringRef ModulePath, unsigned Task) {
OnIndexWriteCb(J.SummaryIndexPath.str());
if (ShouldEmitImportFiles)
- if (Error Err = save(join(ImportsFiles[Task], "\n"), J.ImportsPath))
+ if (Error Err = save(join(J.ImportsFilesList, "\n"), J.ImportsPath))
return Err;
if (!SaveTemps) {
diff --git a/llvm/lib/DTLTO/DTLTODistributionDriver.cpp b/llvm/lib/DTLTO/DTLTODistributionDriver.cpp
index 24dff04318a79..b6606d42458e0 100644
--- a/llvm/lib/DTLTO/DTLTODistributionDriver.cpp
+++ b/llvm/lib/DTLTO/DTLTODistributionDriver.cpp
@@ -89,7 +89,7 @@ Error lto::DistributionDriver::emitJson() {
// Add the bitcode files from which imports will be made. These do
// not explicitly appear on the backend compilation command lines
// but are recorded in the summary index shards.
- append_range(Inputs, J.ImportsFiles);
+ append_range(Inputs, J.ImportsFilesList);
JOS.attribute("inputs", Array(Inputs));
JOS.attribute("outputs", Array(Outputs));
diff --git a/llvm/lib/DTLTO/DTLTOInputFiles.cpp b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
index b13019e70083f..8751cb14d7a06 100644
--- a/llvm/lib/DTLTO/DTLTOInputFiles.cpp
+++ b/llvm/lib/DTLTO/DTLTOInputFiles.cpp
@@ -203,7 +203,7 @@ 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::serializeBitcodeArchiveMembers() {
+Error lto::DTLTO::serializeLTOInputs() {
for (auto &Input : InputFiles) {
if (!Input->isThinLTO() || !Input->getSerializeForDistribution())
continue;
More information about the cfe-commits
mailing list