Skip to content

Make WriteIndexesThinBackend multi threaded #109847

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
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
1 change: 1 addition & 0 deletions lld/COFF/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) {
if (ctx.config.thinLTOIndexOnly) {
auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
backend = lto::createWriteIndexesThinBackend(
llvm::hardware_concurrency(ctx.config.thinLTOJobs),
std::string(ctx.config.thinLTOPrefixReplaceOld),
std::string(ctx.config.thinLTOPrefixReplaceNew),
std::string(ctx.config.thinLTOPrefixReplaceNativeObject),
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ BitcodeCompiler::BitcodeCompiler(Ctx &ctx) : ctx(ctx) {
auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
if (ctx.arg.thinLTOIndexOnly) {
backend = lto::createWriteIndexesThinBackend(
llvm::hardware_concurrency(ctx.arg.thinLTOJobs),
std::string(ctx.arg.thinLTOPrefixReplaceOld),
std::string(ctx.arg.thinLTOPrefixReplaceNew),
std::string(ctx.arg.thinLTOPrefixReplaceNativeObject),
Expand Down
1 change: 1 addition & 0 deletions lld/MachO/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ BitcodeCompiler::BitcodeCompiler() {
auto onIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
if (config->thinLTOIndexOnly) {
backend = lto::createWriteIndexesThinBackend(
llvm::hardware_concurrency(config->thinLTOJobs),
std::string(config->thinLTOPrefixReplaceOld),
std::string(config->thinLTOPrefixReplaceNew),
std::string(config->thinLTOPrefixReplaceNativeObject),
Expand Down
2 changes: 1 addition & 1 deletion lld/test/COFF/thinlto-emit-imports.ll
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
; RUN: not lld-link -entry:main -thinlto-index-only \
; RUN: -thinlto-emit-imports-files %t1.obj %t2.obj %t3.obj \
; RUN: -out:%t4.exe 2>&1 | FileCheck -DMSG=%errc_EACCES %s --check-prefix=ERR
; ERR: cannot open {{.*}}3.obj.imports: [[MSG]]
; ERR: 'cannot open {{.*}}3.obj.imports': [[MSG]]

; Ensure lld doesn't generate import files when thinlto-index-only is not enabled
; RUN: rm -f %t1.obj.imports
Expand Down
2 changes: 1 addition & 1 deletion lld/test/ELF/lto/thinlto-cant-write-index.ll
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
; RUN: chmod u-w %t2.o.thinlto.bc
; RUN: not ld.lld --plugin-opt=thinlto-index-only -shared %t1.o %t2.o -o /dev/null 2>&1 | FileCheck -DMSG=%errc_EACCES %s
; RUN: chmod u+w %t2.o.thinlto.bc
; CHECK: cannot open {{.*}}2.o.thinlto.bc: [[MSG]]
; CHECK: 'cannot open {{.*}}2.o.thinlto.bc': [[MSG]]

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
Expand Down
2 changes: 1 addition & 1 deletion lld/test/ELF/lto/thinlto-emit-imports.ll
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
; RUN: touch %t3.o.imports
; RUN: chmod 400 %t3.o.imports
; RUN: not ld.lld --plugin-opt=thinlto-index-only --plugin-opt=thinlto-emit-imports-files -shared %t1.o %t2.o %t3.o -o /dev/null 2>&1 | FileCheck -DMSG=%errc_EACCES %s --check-prefix=ERR
; ERR: cannot open {{.*}}3.o.imports: [[MSG]]
; ERR: 'cannot open {{.*}}3.o.imports': [[MSG]]

; RUN: rm -f %t1.o.imports %t2.o.imports rm -f %t3.o.imports
; RUN: ld.lld --plugin-opt=thinlto-emit-imports-files -shared %t1.o %t2.o %t3.o -o %t4
Expand Down
2 changes: 1 addition & 1 deletion lld/test/MachO/thinlto-emit-imports.ll
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
; RUN: chmod 400 %t3.o.imports
; RUN: not %lld --thinlto-index-only --thinlto-emit-imports-files -dylib %t1.o %t2.o %t3.o -o /dev/null 2>&1 \
; RUN: | FileCheck -DMSG=%errc_EACCES %s --check-prefix=ERR
; ERR: cannot open {{.*}}3.o.imports: [[MSG]]
; ERR: 'cannot open {{.*}}3.o.imports': [[MSG]]

; Ensure lld doesn't generate import files when thinlto-index-only is not enabled
; RUN: rm -f %t1.o.imports
Expand Down
3 changes: 2 additions & 1 deletion llvm/include/llvm/LTO/LTO.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ ThinBackend createInProcessThinBackend(ThreadPoolStrategy Parallelism,
/// the objects with NativeObjectPrefix instead of NewPrefix. OnWrite is
/// callback which receives module identifier and notifies LTO user that index
/// file for the module (and optionally imports file) was created.
ThinBackend createWriteIndexesThinBackend(std::string OldPrefix,
ThinBackend createWriteIndexesThinBackend(ThreadPoolStrategy Parallelism,
std::string OldPrefix,
std::string NewPrefix,
std::string NativeObjectPrefix,
bool ShouldEmitImportsFiles,
Expand Down
12 changes: 12 additions & 0 deletions llvm/include/llvm/Support/Threading.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,18 @@ constexpr bool llvm_is_multithreaded() { return LLVM_ENABLE_THREADS; }
return S;
}

/// Like hardware_concurrency() above, but builds a strategy
/// based on the rules described for get_threadpool_strategy().
/// If \p Num is invalid, returns a default strategy where one thread per
/// hardware core is used.
inline ThreadPoolStrategy hardware_concurrency(StringRef Num) {
std::optional<ThreadPoolStrategy> S =
get_threadpool_strategy(Num, hardware_concurrency());
if (S)
return *S;
return hardware_concurrency();
}

/// Returns an optimal thread strategy to execute specified amount of tasks.
/// This strategy should prevent us from creating too many threads if we
/// occasionaly have an unexpectedly small amount of tasks.
Expand Down
6 changes: 3 additions & 3 deletions llvm/include/llvm/Transforms/IPO/FunctionImport.h
Original file line number Diff line number Diff line change
Expand Up @@ -417,9 +417,9 @@ void gatherImportedSummariesForModule(
GVSummaryPtrSet &DecSummaries);

/// Emit into \p OutputFilename the files module \p ModulePath will import from.
std::error_code
EmitImportsFiles(StringRef ModulePath, StringRef OutputFilename,
const ModuleToSummariesForIndexTy &ModuleToSummariesForIndex);
Error EmitImportsFiles(
StringRef ModulePath, StringRef OutputFilename,
const ModuleToSummariesForIndexTy &ModuleToSummariesForIndex);

/// Based on the information recorded in the summaries during global
/// summary-based analysis:
Expand Down
106 changes: 61 additions & 45 deletions llvm/lib/LTO/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1376,15 +1376,20 @@ class lto::ThinBackendProc {
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries;
lto::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)
lto::IndexWriteCallback OnWrite, bool ShouldEmitImportsFiles,
ThreadPoolStrategy ThinLTOParallelism)
: Conf(Conf), CombinedIndex(CombinedIndex),
ModuleToDefinedGVSummaries(ModuleToDefinedGVSummaries),
OnWrite(OnWrite), ShouldEmitImportsFiles(ShouldEmitImportsFiles) {}
OnWrite(OnWrite), ShouldEmitImportsFiles(ShouldEmitImportsFiles),
BackendThreadPool(ThinLTOParallelism) {}

virtual ~ThinBackendProc() = default;
virtual Error start(
Expand All @@ -1393,13 +1398,19 @@ class lto::ThinBackendProc {
const FunctionImporter::ExportSetTy &ExportList,
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
MapVector<StringRef, BitcodeModule> &ModuleMap) = 0;
virtual Error wait() = 0;
virtual unsigned getThreadCount() = 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 std::string &NewModulePath) const {
ModuleToSummariesForIndexTy ModuleToSummariesForIndex;
GVSummaryPtrSet DeclarationSummaries;

Expand All @@ -1411,32 +1422,29 @@ class lto::ThinBackendProc {
raw_fd_ostream OS(NewModulePath + ".thinlto.bc", EC,
sys::fs::OpenFlags::OF_None);
if (EC)
return errorCodeToError(EC);
return createFileError("cannot open " + NewModulePath + ".thinlto.bc",
EC);

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

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

namespace {
class InProcessThinBackend : public ThinBackendProc {
DefaultThreadPool BackendThreadPool;
AddStreamFn AddStream;
FileCache Cache;
DenseSet<GlobalValue::GUID> CfiFunctionDefs;
DenseSet<GlobalValue::GUID> CfiFunctionDecls;

std::optional<Error> Err;
std::mutex ErrMu;

bool ShouldEmitIndexFiles;

public:
Expand All @@ -1447,9 +1455,9 @@ class InProcessThinBackend : public ThinBackendProc {
AddStreamFn AddStream, FileCache Cache, lto::IndexWriteCallback OnWrite,
bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles)
: ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries,
OnWrite, ShouldEmitImportsFiles),
BackendThreadPool(ThinLTOParallelism), AddStream(std::move(AddStream)),
Cache(std::move(Cache)), ShouldEmitIndexFiles(ShouldEmitIndexFiles) {
OnWrite, ShouldEmitImportsFiles, ThinLTOParallelism),
AddStream(std::move(AddStream)), Cache(std::move(Cache)),
ShouldEmitIndexFiles(ShouldEmitIndexFiles) {
for (auto &Name : CombinedIndex.cfiFunctionDefs())
CfiFunctionDefs.insert(
GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name)));
Expand Down Expand Up @@ -1546,18 +1554,6 @@ class InProcessThinBackend : public ThinBackendProc {
OnWrite(std::string(ModulePath));
return Error::success();
}

Error wait() override {
BackendThreadPool.wait();
if (Err)
return std::move(*Err);
else
return Error::success();
}

unsigned getThreadCount() override {
return BackendThreadPool.getMaxConcurrency();
}
};
} // end anonymous namespace

Expand Down Expand Up @@ -1618,12 +1614,13 @@ class WriteIndexesThinBackend : public ThinBackendProc {
public:
WriteIndexesThinBackend(
const Config &Conf, ModuleSummaryIndex &CombinedIndex,
ThreadPoolStrategy ThinLTOParallelism,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
std::string OldPrefix, std::string NewPrefix,
std::string NativeObjectPrefix, bool ShouldEmitImportsFiles,
raw_fd_ostream *LinkedObjectsFile, lto::IndexWriteCallback OnWrite)
: ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries,
OnWrite, ShouldEmitImportsFiles),
OnWrite, ShouldEmitImportsFiles, ThinLTOParallelism),
OldPrefix(OldPrefix), NewPrefix(NewPrefix),
NativeObjectPrefix(NativeObjectPrefix),
LinkedObjectsFile(LinkedObjectsFile) {}
Expand All @@ -1635,9 +1632,11 @@ class WriteIndexesThinBackend : public ThinBackendProc {
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
MapVector<StringRef, BitcodeModule> &ModuleMap) override {
StringRef ModulePath = BM.getModuleIdentifier();
std::string NewModulePath =
getThinLTOOutputFile(ModulePath, OldPrefix, NewPrefix);

// The contents of this file may be used as input to a native link, and must
// therefore contain the processed modules in a determinstic order that
// match the order they are provided on the command line. For that reason,
// we cannot include this in the asynchronously executed lambda below.
if (LinkedObjectsFile) {
std::string ObjectPrefix =
NativeObjectPrefix.empty() ? NewPrefix : NativeObjectPrefix;
Expand All @@ -1646,33 +1645,49 @@ class WriteIndexesThinBackend : public ThinBackendProc {
*LinkedObjectsFile << LinkedObjectsFilePath << '\n';
}

if (auto E = emitFiles(ImportList, ModulePath, NewModulePath))
return E;
BackendThreadPool.async(
[this](const StringRef ModulePath,
const FunctionImporter::ImportMapTy &ImportList,
const std::string &OldPrefix, const std::string &NewPrefix) {
std::string NewModulePath =
getThinLTOOutputFile(ModulePath, OldPrefix, NewPrefix);
auto E = emitFiles(ImportList, ModulePath, NewModulePath);
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);

if (OnWrite)
OnWrite(std::string(ModulePath));
return Error::success();
}

Error wait() override { return Error::success(); }

// WriteIndexesThinBackend should always return 1 to prevent module
// re-ordering and avoid non-determinism in the final link.
unsigned getThreadCount() override { return 1; }
bool isSensitiveToInputOrder() override {
// The order which modules are written to LinkedObjectsFile should be
// deterministic and match the order they are passed on the command line.
return true;
}
};
} // end anonymous namespace

ThinBackend lto::createWriteIndexesThinBackend(
std::string OldPrefix, std::string NewPrefix,
std::string NativeObjectPrefix, bool ShouldEmitImportsFiles,
raw_fd_ostream *LinkedObjectsFile, IndexWriteCallback OnWrite) {
ThreadPoolStrategy Parallelism, std::string OldPrefix,
std::string NewPrefix, std::string NativeObjectPrefix,
bool ShouldEmitImportsFiles, raw_fd_ostream *LinkedObjectsFile,
IndexWriteCallback OnWrite) {
return
[=](const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache) {
return std::make_unique<WriteIndexesThinBackend>(
Conf, CombinedIndex, ModuleToDefinedGVSummaries, OldPrefix,
NewPrefix, NativeObjectPrefix, ShouldEmitImportsFiles,
Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries,
OldPrefix, NewPrefix, NativeObjectPrefix, ShouldEmitImportsFiles,
LinkedObjectsFile, OnWrite);
};
}
Expand Down Expand Up @@ -1854,7 +1869,8 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
ResolvedODR[Mod.first], ThinLTO.ModuleMap);
};

if (BackendProcess->getThreadCount() == 1) {
if (BackendProcess->getThreadCount() == 1 ||
BackendProcess->isSensitiveToInputOrder()) {
// Process the modules in the order they were provided on the
// command-line. It is important for this codepath to be used for
// WriteIndexesThinBackend, to ensure the emitted LinkedObjectsFile lists
Expand Down
5 changes: 2 additions & 3 deletions llvm/lib/LTO/ThinLTOCodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -837,9 +837,8 @@ void ThinLTOCodeGenerator::emitImports(Module &TheModule, StringRef OutputName,
ModuleIdentifier, ModuleToDefinedGVSummaries,
ImportLists[ModuleIdentifier], ModuleToSummariesForIndex, DecSummaries);

std::error_code EC;
if ((EC = EmitImportsFiles(ModuleIdentifier, OutputName,
ModuleToSummariesForIndex)))
if (Error EC = EmitImportsFiles(ModuleIdentifier, OutputName,
ModuleToSummariesForIndex))
report_fatal_error(Twine("Failed to open ") + OutputName +
" to save imports lists\n");
}
Expand Down
7 changes: 4 additions & 3 deletions llvm/lib/Transforms/IPO/FunctionImport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1553,20 +1553,21 @@ void llvm::gatherImportedSummariesForModule(
}

/// Emit the files \p ModulePath will import from into \p OutputFilename.
std::error_code llvm::EmitImportsFiles(
Error llvm::EmitImportsFiles(
StringRef ModulePath, StringRef OutputFilename,
const ModuleToSummariesForIndexTy &ModuleToSummariesForIndex) {
std::error_code EC;
raw_fd_ostream ImportsOS(OutputFilename, EC, sys::fs::OpenFlags::OF_Text);
if (EC)
return EC;
return createFileError("cannot open " + OutputFilename,
errorCodeToError(EC));
for (const auto &ILI : ModuleToSummariesForIndex)
// The ModuleToSummariesForIndex map includes an entry for the current
// Module (needed for writing out the index files). We don't want to
// include it in the imports file, however, so filter it out.
if (ILI.first != ModulePath)
ImportsOS << ILI.first << "\n";
return std::error_code();
return Error::success();
}

bool llvm::convertToDeclaration(GlobalValue &GV) {
Expand Down
2 changes: 1 addition & 1 deletion llvm/tools/gold/gold-plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,7 @@ static std::unique_ptr<LTO> createLTO(IndexWriteCallback OnIndexWrite,
std::string OldPrefix, NewPrefix;
getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix);
Backend = createWriteIndexesThinBackend(
OldPrefix, NewPrefix,
llvm::hardware_concurrency(options::Parallelism) OldPrefix, NewPrefix,
// TODO: Add support for optional native object path in
// thinlto_prefix_replace option to match lld.
/*NativeObjectPrefix=*/"", options::thinlto_emit_imports_files,
Expand Down
3 changes: 2 additions & 1 deletion llvm/tools/llvm-lto2/llvm-lto2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,8 @@ static int run(int argc, char **argv) {

ThinBackend Backend;
if (ThinLTODistributedIndexes)
Backend = createWriteIndexesThinBackend(/*OldPrefix=*/"",
Backend = createWriteIndexesThinBackend(llvm::hardware_concurrency(Threads),
/*OldPrefix=*/"",
/*NewPrefix=*/"",
/*NativeObjectPrefix=*/"",
ThinLTOEmitImports,
Expand Down
Loading