Skip to content

[ThinLTO][NFC] Refactor ThinBackend #110461

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 83 additions & 8 deletions llvm/include/llvm/LTO/LTO.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "llvm/Support/Caching.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/thread.h"
#include "llvm/Transforms/IPO/FunctionAttrs.h"
#include "llvm/Transforms/IPO/FunctionImport.h"
Expand Down Expand Up @@ -105,7 +106,6 @@ void updateMemProfAttributes(Module &Mod, const ModuleSummaryIndex &Index);

class LTO;
struct SymbolResolution;
class ThinBackendProc;

/// An input file. This is a symbol table wrapper that only exposes the
/// information that an LTO client should need in order to do symbol resolution.
Expand Down Expand Up @@ -194,14 +194,90 @@ class InputFile {
}
};

/// A ThinBackend defines what happens after the thin-link phase during ThinLTO.
/// The details of this type definition aren't important; clients can only
/// create a ThinBackend using one of the create*ThinBackend() functions below.
using ThinBackend = std::function<std::unique_ptr<ThinBackendProc>(
using IndexWriteCallback = std::function<void(const std::string &)>;

/// This class defines the interface to the ThinLTO backend.
class ThinBackendProc {
protected:
const Config &Conf;
ModuleSummaryIndex &CombinedIndex;
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries;
IndexWriteCallback OnWrite;
bool ShouldEmitImportsFiles;
DefaultThreadPool BackendThreadPool;
std::optional<Error> Err;
std::mutex ErrMu;

public:
ThinBackendProc(
const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
lto::IndexWriteCallback OnWrite, bool ShouldEmitImportsFiles,
ThreadPoolStrategy ThinLTOParallelism)
: Conf(Conf), CombinedIndex(CombinedIndex),
ModuleToDefinedGVSummaries(ModuleToDefinedGVSummaries),
OnWrite(OnWrite), ShouldEmitImportsFiles(ShouldEmitImportsFiles),
BackendThreadPool(ThinLTOParallelism) {}

virtual ~ThinBackendProc() = default;
virtual 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) = 0;
Error wait() {
BackendThreadPool.wait();
if (Err)
return std::move(*Err);
return Error::success();
}
unsigned getThreadCount() { return BackendThreadPool.getMaxConcurrency(); }
virtual bool isSensitiveToInputOrder() { return false; }

// Write sharded indices and (optionally) imports to disk
Error emitFiles(const FunctionImporter::ImportMapTy &ImportList,
llvm::StringRef ModulePath,
const std::string &NewModulePath) const;
};

/// This callable defines the behavior of a ThinLTO backend after the thin-link
/// phase. It accepts a configuration \p C, a combined module summary index
/// \p CombinedIndex, a map of module identifiers to global variable summaries
/// \p ModuleToDefinedGVSummaries, a function to add output streams \p
/// AddStream, and a file cache \p Cache. It returns a unique pointer to a
/// ThinBackendProc, which can be used to launch backends in parallel.
using ThinBackendFunction = std::function<std::unique_ptr<ThinBackendProc>(
const Config &C, ModuleSummaryIndex &CombinedIndex,
DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache)>;

/// This type defines the behavior following the thin-link phase during ThinLTO.
/// It encapsulates a backend function and a strategy for thread pool
/// parallelism. Clients should use one of the provided create*ThinBackend()
/// functions to instantiate a ThinBackend. Parallelism defines the thread pool
/// strategy to be used for processing.
struct ThinBackend {
ThinBackend(ThinBackendFunction Func, ThreadPoolStrategy Parallelism)
: Func(std::move(Func)), Parallelism(std::move(Parallelism)) {}
ThinBackend() = default;

std::unique_ptr<ThinBackendProc> operator()(
const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache) {
assert(isValid() && "Invalid backend function");
return Func(Conf, CombinedIndex, ModuleToDefinedGVSummaries,
std::move(AddStream), std::move(Cache));
}
ThreadPoolStrategy getParallelism() const { return Parallelism; }
bool isValid() const { return static_cast<bool>(Func); }

private:
ThinBackendFunction Func = nullptr;
ThreadPoolStrategy Parallelism;
};

/// This ThinBackend runs the individual backend jobs in-process.
/// The default value means to use one job per hardware core (not hyper-thread).
/// OnWrite is callback which receives module identifier and notifies LTO user
Expand All @@ -210,7 +286,6 @@ using ThinBackend = std::function<std::unique_ptr<ThinBackendProc>(
/// to the same path as the input module, with suffix ".thinlto.bc"
/// ShouldEmitImportsFiles is true it also writes a list of imported files to a
/// similar path with ".imports" appended instead.
using IndexWriteCallback = std::function<void(const std::string &)>;
ThinBackend createInProcessThinBackend(ThreadPoolStrategy Parallelism,
IndexWriteCallback OnWrite = nullptr,
bool ShouldEmitIndexFiles = false,
Expand Down Expand Up @@ -276,7 +351,7 @@ class LTO {
/// this constructor.
/// FIXME: We do currently require the DiagHandler field to be set in Conf.
/// Until that is fixed, a Config argument is required.
LTO(Config Conf, ThinBackend Backend = nullptr,
LTO(Config Conf, ThinBackend Backend = {},
unsigned ParallelCodeGenParallelismLevel = 1,
LTOKind LTOMode = LTOK_Default);
~LTO();
Expand Down
106 changes: 33 additions & 73 deletions llvm/lib/LTO/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,10 +578,10 @@ LTO::RegularLTOState::RegularLTOState(unsigned ParallelCodeGenParallelismLevel,
CombinedModule->IsNewDbgInfoFormat = UseNewDbgInfoFormat;
}

LTO::ThinLTOState::ThinLTOState(ThinBackend Backend)
: Backend(Backend), CombinedIndex(/*HaveGVs*/ false) {
if (!Backend)
this->Backend =
LTO::ThinLTOState::ThinLTOState(ThinBackend BackendParam)
: Backend(std::move(BackendParam)), CombinedIndex(/*HaveGVs*/ false) {
if (!Backend.isValid())
Backend =
createInProcessThinBackend(llvm::heavyweight_hardware_concurrency());
}

Expand Down Expand Up @@ -1368,75 +1368,33 @@ SmallVector<const char *> LTO::getRuntimeLibcallSymbols(const Triple &TT) {
return LibcallSymbols;
}

/// This class defines the interface to the ThinLTO backend.
class lto::ThinBackendProc {
protected:
const Config &Conf;
ModuleSummaryIndex &CombinedIndex;
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries;
lto::IndexWriteCallback OnWrite;
bool ShouldEmitImportsFiles;
DefaultThreadPool BackendThreadPool;
std::optional<Error> Err;
std::mutex ErrMu;
Error ThinBackendProc::emitFiles(
const FunctionImporter::ImportMapTy &ImportList, llvm::StringRef ModulePath,
const std::string &NewModulePath) const {
ModuleToSummariesForIndexTy ModuleToSummariesForIndex;
GVSummaryPtrSet DeclarationSummaries;

public:
ThinBackendProc(
const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
lto::IndexWriteCallback OnWrite, bool ShouldEmitImportsFiles,
ThreadPoolStrategy ThinLTOParallelism)
: Conf(Conf), CombinedIndex(CombinedIndex),
ModuleToDefinedGVSummaries(ModuleToDefinedGVSummaries),
OnWrite(OnWrite), ShouldEmitImportsFiles(ShouldEmitImportsFiles),
BackendThreadPool(ThinLTOParallelism) {}

virtual ~ThinBackendProc() = default;
virtual 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) = 0;
Error wait() {
BackendThreadPool.wait();
if (Err)
return std::move(*Err);
return Error::success();
}
unsigned getThreadCount() { return BackendThreadPool.getMaxConcurrency(); }
virtual bool isSensitiveToInputOrder() { return false; }

// Write sharded indices and (optionally) imports to disk
Error emitFiles(const FunctionImporter::ImportMapTy &ImportList,
llvm::StringRef ModulePath,
const std::string &NewModulePath) const {
ModuleToSummariesForIndexTy ModuleToSummariesForIndex;
GVSummaryPtrSet DeclarationSummaries;

std::error_code EC;
gatherImportedSummariesForModule(ModulePath, ModuleToDefinedGVSummaries,
ImportList, ModuleToSummariesForIndex,
DeclarationSummaries);

raw_fd_ostream OS(NewModulePath + ".thinlto.bc", EC,
sys::fs::OpenFlags::OF_None);
if (EC)
return createFileError("cannot open " + NewModulePath + ".thinlto.bc",
EC);

writeIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex,
&DeclarationSummaries);

if (ShouldEmitImportsFiles) {
Error ImportFilesError = EmitImportsFiles(
ModulePath, NewModulePath + ".imports", ModuleToSummariesForIndex);
if (ImportFilesError)
return ImportFilesError;
}
return Error::success();
std::error_code EC;
gatherImportedSummariesForModule(ModulePath, ModuleToDefinedGVSummaries,
ImportList, ModuleToSummariesForIndex,
DeclarationSummaries);

raw_fd_ostream OS(NewModulePath + ".thinlto.bc", EC,
sys::fs::OpenFlags::OF_None);
if (EC)
return createFileError("cannot open " + NewModulePath + ".thinlto.bc", EC);

writeIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex,
&DeclarationSummaries);

if (ShouldEmitImportsFiles) {
Error ImportFilesError = EmitImportsFiles(
ModulePath, NewModulePath + ".imports", ModuleToSummariesForIndex);
if (ImportFilesError)
return ImportFilesError;
}
};
return Error::success();
}

namespace {
class InProcessThinBackend : public ThinBackendProc {
Expand Down Expand Up @@ -1561,7 +1519,7 @@ ThinBackend lto::createInProcessThinBackend(ThreadPoolStrategy Parallelism,
lto::IndexWriteCallback OnWrite,
bool ShouldEmitIndexFiles,
bool ShouldEmitImportsFiles) {
return
auto Func =
[=](const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache) {
Expand All @@ -1570,6 +1528,7 @@ ThinBackend lto::createInProcessThinBackend(ThreadPoolStrategy Parallelism,
AddStream, Cache, OnWrite, ShouldEmitIndexFiles,
ShouldEmitImportsFiles);
};
return ThinBackend(Func, Parallelism);
}

StringLiteral lto::getThinLTODefaultCPU(const Triple &TheTriple) {
Expand Down Expand Up @@ -1681,7 +1640,7 @@ ThinBackend lto::createWriteIndexesThinBackend(
std::string NewPrefix, std::string NativeObjectPrefix,
bool ShouldEmitImportsFiles, raw_fd_ostream *LinkedObjectsFile,
IndexWriteCallback OnWrite) {
return
auto Func =
[=](const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache) {
Expand All @@ -1690,6 +1649,7 @@ ThinBackend lto::createWriteIndexesThinBackend(
OldPrefix, NewPrefix, NativeObjectPrefix, ShouldEmitImportsFiles,
LinkedObjectsFile, OnWrite);
};
return ThinBackend(Func, Parallelism);
}

Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
Expand Down
Loading