Files
gps/tools/local/circt/include/llvm/DTLTO/DTLTO.h
T
2026-07-12 11:01:51 +03:00

403 lines
17 KiB
C++

//===- 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.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// \file
// Declarations for Integrated 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/ManagedStatic.h"
#include "llvm/Support/Signals.h"
#include <functional>
#include <vector>
namespace llvm {
namespace lto {
/// 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
/// serializeLTOInputs().
class LLVM_ABI DTLTO : public LTO {
using Base = LTO;
public:
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), writeIndexesBackendInstance(),
ParallelCodeGenParallelismLevel, LTOMode),
AddBuffer(AddBufferArg), SaveTemps(SaveTempsArg),
ShouldEmitIndexFiles(EmitIndexFiles),
ShouldEmitImportFiles(EmitImportsFiles), OnIndexWriteCb(OnWrite),
DistributorParams{Distributor, DistributorArgs,
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.
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.
/// 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<InputFile>>
addInput(std::unique_ptr<InputFile> InputPtr) 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.
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.
Error serializeLTOInputs();
// Remove temporary files created to enable distribution.
void cleanup() override;
public:
// Mutable and const accessors to the LTO configuration object.
Config &getConfig() { return Conf; }
const Config &getConfig() const { return Conf; }
private:
// Bump allocator for saving updated module IDs.
BumpPtrAllocator PtrAlloc;
// String saver backed by PtrAlloc.
StringSaver Saver{PtrAlloc};
using SString = SmallString<128>;
// 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;
// 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
// 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> ImportsFilesList;
// 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;
};
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 OnIndexWriteCb;
/// 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,
/// 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
/// 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();
/// 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>> ImportsFilesList;
// 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)
/// - ImportsFilesList: 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 {
DistributionDriverParams() = default;
DistributionDriverParams(StringRef DistributorArg,
ArrayRef<StringRef> DistributorArgsArg,
StringRef RemoteCompilerArg,
ArrayRef<StringRef> RemoteCompilerPrependArgsArg,
ArrayRef<StringRef> RemoteCompilerArgsArg,
StringRef LinkerOutputFileArg)
: LinkerOutputFile(LinkerOutputFileArg),
DistributorPath(DistributorArg), DistributorArgs(DistributorArgsArg),
RemoteCompiler(RemoteCompilerArg),
RemoteCompilerPrependArgs(RemoteCompilerPrependArgsArg),
RemoteCompilerArgs(RemoteCompilerArgsArg) {}
// 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:
DistributionDriver(DTLTO::DistributionDriverParams &ParamsArg,
ArrayRef<DTLTO::Job> JobsArg, bool SaveTempsArg,
std::function<void(StringRef)> AddToClenupArg)
: Params{ParamsArg}, SaveTemps{SaveTempsArg},
AddToCleanup{AddToClenupArg}, Jobs{JobsArg} {};
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.
LLVM_ABI Error operator()();
};
} // namespace lto
} // namespace llvm
#endif // LLVM_DTLTO_DTLTO_H