Skip to content

Commit 22f1273

Browse files
committed
[ThinLTO][ELF] Add --thinlto-emit-index-files option
Allows ThinLTO indices to be written to disk on-the-fly/as-part-of “normal” linker execution. Previously ThinLTO indices could be written via --thinlto-index-only but that would cause the linker to exit early. For MLGO specifically, this enables saving the ThinLTO index files without having to restart the linker to collect data only available at later stages (i.e. output of --save-temps) of the linker's execution. Note, this option does not currently work with: --thinlto-object-suffix-replace, as this is intended to be used to consume minimized IR bitcode files while --thinlto-emit-index-files is intended to be run together with InProcessThinLTO (which cannot parse minimized IR). --thinlto-prefix-replace support is left unimplemented but can be implemented if needed Differential Revision: https://reviews.llvm.org/D127777
1 parent 8c6da76 commit 22f1273

File tree

7 files changed

+193
-35
lines changed

7 files changed

+193
-35
lines changed

lld/ELF/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ struct Configuration {
221221
bool target1Rel;
222222
bool trace;
223223
bool thinLTOEmitImportsFiles;
224+
bool thinLTOEmitIndexFiles;
224225
bool thinLTOIndexOnly;
225226
bool timeTraceEnabled;
226227
bool tocOptimize;

lld/ELF/Driver.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,13 +1175,24 @@ static void readConfigs(opt::InputArgList &args) {
11751175
parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
11761176
"--thinlto-cache-policy: invalid cache policy");
11771177
config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files);
1178+
config->thinLTOEmitIndexFiles = args.hasArg(OPT_thinlto_emit_index_files) ||
1179+
args.hasArg(OPT_thinlto_index_only) ||
1180+
args.hasArg(OPT_thinlto_index_only_eq);
11781181
config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
11791182
args.hasArg(OPT_thinlto_index_only_eq);
11801183
config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq);
11811184
config->thinLTOObjectSuffixReplace =
11821185
getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq);
11831186
config->thinLTOPrefixReplace =
11841187
getOldNewOptions(args, OPT_thinlto_prefix_replace_eq);
1188+
if (config->thinLTOEmitIndexFiles && !config->thinLTOIndexOnly) {
1189+
if (args.hasArg(OPT_thinlto_object_suffix_replace_eq))
1190+
error("--thinlto-object-suffix-replace is not supported with "
1191+
"--thinlto-emit-index-files");
1192+
else if (args.hasArg(OPT_thinlto_prefix_replace_eq))
1193+
error("--thinlto-prefix-replace is not supported with "
1194+
"--thinlto-emit-index-files");
1195+
}
11851196
config->thinLTOModulesToCompile =
11861197
args::getStrings(args, OPT_thinlto_single_module_eq);
11871198
config->timeTraceEnabled = args.hasArg(OPT_time_trace);

lld/ELF/LTO.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,15 +191,17 @@ BitcodeCompiler::BitcodeCompiler() {
191191

192192
// Initialize ltoObj.
193193
lto::ThinBackend backend;
194+
auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
194195
if (config->thinLTOIndexOnly) {
195-
auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
196196
backend = lto::createWriteIndexesThinBackend(
197197
std::string(config->thinLTOPrefixReplace.first),
198198
std::string(config->thinLTOPrefixReplace.second),
199199
config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
200200
} else {
201201
backend = lto::createInProcessThinBackend(
202-
llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
202+
llvm::heavyweight_hardware_concurrency(config->thinLTOJobs),
203+
onIndexWrite, config->thinLTOEmitIndexFiles,
204+
config->thinLTOEmitImportsFiles);
203205
}
204206

205207
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
@@ -224,7 +226,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
224226
lto::InputFile &obj = *f.obj;
225227
bool isExec = !config->shared && !config->relocatable;
226228

227-
if (config->thinLTOIndexOnly)
229+
if (config->thinLTOEmitIndexFiles)
228230
thinIndices.insert(obj.getName());
229231

230232
ArrayRef<Symbol *> syms = f.getSymbols();
@@ -339,9 +341,10 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
339341
}
340342
}
341343

342-
if (config->thinLTOIndexOnly) {
344+
if (config->thinLTOEmitIndexFiles)
343345
thinLTOCreateEmptyIndexFiles();
344346

347+
if (config->thinLTOIndexOnly) {
345348
if (!config->ltoObjPath.empty())
346349
saveBuffer(buf[0], config->ltoObjPath);
347350

lld/ELF/Options.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ def thinlto_cache_dir: JJ<"thinlto-cache-dir=">,
596596
HelpText<"Path to ThinLTO cached object file directory">;
597597
defm thinlto_cache_policy: EEq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
598598
def thinlto_emit_imports_files: FF<"thinlto-emit-imports-files">;
599+
def thinlto_emit_index_files: FF<"thinlto-emit-index-files">;
599600
def thinlto_index_only: FF<"thinlto-index-only">;
600601
def thinlto_index_only_eq: JJ<"thinlto-index-only=">;
601602
def thinlto_jobs: JJ<"thinlto-jobs=">,
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
; REQUIRES: x86
2+
3+
;; Mostly copied/updated from thinlto-index-only.ll
4+
;; First ensure that the ThinLTO handling in lld handles
5+
;; bitcode without summary sections gracefully and generates index file.
6+
; RUN: rm -rf %t.dir && mkdir %t.dir && cd %t.dir
7+
; RUN: llvm-as %s -o 1.o
8+
; RUN: llvm-as %p/Inputs/thinlto.ll -o 2.o
9+
; RUN: ld.lld --thinlto-emit-index-files -shared 1.o 2.o -o 3
10+
; RUN: ls 2.o.thinlto.bc
11+
; RUN: ls 3
12+
; RUN: ld.lld -shared 1.o 2.o -o 3
13+
; RUN: llvm-nm 3 | FileCheck %s --check-prefix=NM
14+
15+
;; Basic ThinLTO tests.
16+
; RUN: opt -module-summary %s -o 1.o
17+
; RUN: opt -module-summary %p/Inputs/thinlto.ll -o 2.o
18+
; RUN: opt -module-summary %p/Inputs/thinlto_empty.ll -o 3.o
19+
20+
;; Ensure lld generates an index and also a binary if requested.
21+
; RUN: ld.lld --thinlto-emit-index-files -shared 1.o 2.o -o 4
22+
; RUN: llvm-bcanalyzer -dump 1.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND1
23+
; RUN: llvm-bcanalyzer -dump 2.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND2
24+
; RUN: ls 4
25+
26+
;; Ensure lld generates an index and not a binary if both emit-index and index-only are present.
27+
; RUN: ld.lld --thinlto-emit-index-files --thinlto-index-only -shared 1.o 2.o -o 5
28+
; RUN: not ls 5
29+
30+
;; Ensure lld generates an index even if the file is wrapped in --start-lib/--end-lib
31+
; RUN: rm -f 2.o.thinlto.bc
32+
; RUN: ld.lld --thinlto-emit-index-files -shared 1.o 3.o --start-lib 2.o --end-lib -o 6
33+
; RUN: llvm-dis < 2.o.thinlto.bc | grep -q '\^0 = module:'
34+
; RUN: ls 6
35+
36+
;; Test that LLD generates an empty index even for lazy object file that is not added to link.
37+
;; Test that LLD also generates empty imports file with the --thinlto-emit-imports-files option.
38+
; RUN: rm -f 1.o.thinlto.bc 1.o.imports
39+
; RUN: ld.lld --thinlto-emit-index-files -shared 2.o --start-lib 1.o --end-lib \
40+
; RUN: --thinlto-emit-imports-files -o 7
41+
; RUN: ls 7
42+
; RUN: ls 1.o.thinlto.bc
43+
; RUN: ls 1.o.imports
44+
45+
;; Ensure LLD generates an empty index for each bitcode file even if all bitcode files are lazy.
46+
; RUN: rm -f 1.o.thinlto.bc
47+
; RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux-gnu /dev/null -o dummy.o
48+
; RUN: ld.lld --thinlto-emit-index-files -shared dummy.o --start-lib 1.o --end-lib -o 8
49+
; RUN: ls 8
50+
; RUN: ls 1.o.thinlto.bc
51+
52+
;; Test that LLD errors out when run with suffix replacement, or prefix replacement
53+
; RUN: not ld.lld --thinlto-emit-index-files -shared 2.o --start-lib 1.o --end-lib \
54+
; RUN: --thinlto-prefix-replace="abc;xyz" 2>&1 | FileCheck %s --check-prefix=ERR1
55+
; ERR1: --thinlto-prefix-replace is not supported with --thinlto-emit-index-files
56+
57+
; RUN: not ld.lld --thinlto-emit-index-files -shared 2.o --start-lib 1.o --end-lib \
58+
; RUN: --thinlto-object-suffix-replace="abc;xyz" 2>&1 | FileCheck %s --check-prefix=ERR2
59+
; ERR2: --thinlto-object-suffix-replace is not supported with --thinlto-emit-index-files
60+
61+
;; But not when passed with index only as well
62+
; RUN: ld.lld --thinlto-emit-index-files -shared 2.o --start-lib 1.o --end-lib \
63+
; RUN: --thinlto-prefix-replace="abc;xyz" --thinlto-index-only
64+
65+
; RUN: ld.lld --thinlto-emit-index-files -shared 2.o --start-lib 1.o --end-lib \
66+
; RUN: --thinlto-object-suffix-replace="abc;xyz" --thinlto-index-only
67+
68+
; NM: T f
69+
70+
;; The backend index for this module contains summaries from itself and
71+
;; Inputs/thinlto.ll, as it imports from the latter.
72+
; BACKEND1: <MODULE_STRTAB_BLOCK
73+
; BACKEND1-NEXT: <ENTRY {{.*}} record string = '1.o'
74+
; BACKEND1-NEXT: <ENTRY {{.*}} record string = '2.o'
75+
; BACKEND1-NEXT: </MODULE_STRTAB_BLOCK
76+
; BACKEND1: <GLOBALVAL_SUMMARY_BLOCK
77+
; BACKEND1: <VERSION
78+
; BACKEND1: <FLAGS
79+
; BACKEND1: <VALUE_GUID op0={{1|2}} op1={{-3706093650706652785|-5300342847281564238}}
80+
; BACKEND1: <VALUE_GUID op0={{1|2}} op1={{-3706093650706652785|-5300342847281564238}}
81+
; BACKEND1: <COMBINED
82+
; BACKEND1: <COMBINED
83+
; BACKEND1: </GLOBALVAL_SUMMARY_BLOCK
84+
85+
;; The backend index for Input/thinlto.ll contains summaries from itself only,
86+
;; as it does not import anything.
87+
; BACKEND2: <MODULE_STRTAB_BLOCK
88+
; BACKEND2-NEXT: <ENTRY {{.*}} record string = '2.o'
89+
; BACKEND2-NEXT: </MODULE_STRTAB_BLOCK
90+
; BACKEND2-NEXT: <GLOBALVAL_SUMMARY_BLOCK
91+
; BACKEND2-NEXT: <VERSION
92+
; BACKEND2-NEXT: <FLAGS
93+
; BACKEND2-NEXT: <VALUE_GUID op0=1 op1=-5300342847281564238
94+
; BACKEND2-NEXT: <COMBINED
95+
; BACKEND2-NEXT: <BLOCK_COUNT op0=2/>
96+
; BACKEND2-NEXT: </GLOBALVAL_SUMMARY_BLOCK
97+
98+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
99+
target triple = "x86_64-unknown-linux-gnu"
100+
101+
declare void @g(...)
102+
103+
define void @f() {
104+
entry:
105+
call void (...) @g()
106+
ret void
107+
}

llvm/include/llvm/LTO/LTO.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,17 @@ using ThinBackend = std::function<std::unique_ptr<ThinBackendProc>(
197197

198198
/// This ThinBackend runs the individual backend jobs in-process.
199199
/// The default value means to use one job per hardware core (not hyper-thread).
200-
ThinBackend createInProcessThinBackend(ThreadPoolStrategy Parallelism);
200+
/// OnWrite is callback which receives module identifier and notifies LTO user
201+
/// that index file for the module (and optionally imports file) was created.
202+
/// ShouldEmitIndexFiles being true will write sharded ThinLTO index files
203+
/// to the same path as the input module, with suffix ".thinlto.bc"
204+
/// ShouldEmitImportsFiles is true it also writes a list of imported files to a
205+
/// similar path with ".imports" appended instead.
206+
using IndexWriteCallback = std::function<void(const std::string &)>;
207+
ThinBackend createInProcessThinBackend(ThreadPoolStrategy Parallelism,
208+
IndexWriteCallback OnWrite = nullptr,
209+
bool ShouldEmitIndexFiles = false,
210+
bool ShouldEmitImportsFiles = false);
201211

202212
/// This ThinBackend writes individual module indexes to files, instead of
203213
/// running the individual backend jobs. This backend is for distributed builds
@@ -212,7 +222,6 @@ ThinBackend createInProcessThinBackend(ThreadPoolStrategy Parallelism);
212222
/// the final ThinLTO linking. Can be nullptr.
213223
/// OnWrite is callback which receives module identifier and notifies LTO user
214224
/// that index file for the module (and optionally imports file) was created.
215-
using IndexWriteCallback = std::function<void(const std::string &)>;
216225
ThinBackend createWriteIndexesThinBackend(std::string OldPrefix,
217226
std::string NewPrefix,
218227
bool ShouldEmitImportsFiles,

llvm/lib/LTO/LTO.cpp

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,12 +1161,16 @@ class lto::ThinBackendProc {
11611161
const Config &Conf;
11621162
ModuleSummaryIndex &CombinedIndex;
11631163
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries;
1164+
lto::IndexWriteCallback OnWrite;
1165+
bool ShouldEmitImportsFiles;
11641166

11651167
public:
11661168
ThinBackendProc(const Config &Conf, ModuleSummaryIndex &CombinedIndex,
1167-
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries)
1169+
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
1170+
lto::IndexWriteCallback OnWrite, bool ShouldEmitImportsFiles)
11681171
: Conf(Conf), CombinedIndex(CombinedIndex),
1169-
ModuleToDefinedGVSummaries(ModuleToDefinedGVSummaries) {}
1172+
ModuleToDefinedGVSummaries(ModuleToDefinedGVSummaries),
1173+
OnWrite(OnWrite), ShouldEmitImportsFiles(ShouldEmitImportsFiles) {}
11701174

11711175
virtual ~ThinBackendProc() = default;
11721176
virtual Error start(
@@ -1177,6 +1181,30 @@ class lto::ThinBackendProc {
11771181
MapVector<StringRef, BitcodeModule> &ModuleMap) = 0;
11781182
virtual Error wait() = 0;
11791183
virtual unsigned getThreadCount() = 0;
1184+
1185+
// Write sharded indices and (optionally) imports to disk
1186+
Error emitFiles(const FunctionImporter::ImportMapTy &ImportList,
1187+
llvm::StringRef ModulePath,
1188+
const std::string &NewModulePath) {
1189+
std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex;
1190+
std::error_code EC;
1191+
gatherImportedSummariesForModule(ModulePath, ModuleToDefinedGVSummaries,
1192+
ImportList, ModuleToSummariesForIndex);
1193+
1194+
raw_fd_ostream OS(NewModulePath + ".thinlto.bc", EC,
1195+
sys::fs::OpenFlags::OF_None);
1196+
if (EC)
1197+
return errorCodeToError(EC);
1198+
writeIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex);
1199+
1200+
if (ShouldEmitImportsFiles) {
1201+
EC = EmitImportsFiles(ModulePath, NewModulePath + ".imports",
1202+
ModuleToSummariesForIndex);
1203+
if (EC)
1204+
return errorCodeToError(EC);
1205+
}
1206+
return Error::success();
1207+
}
11801208
};
11811209

11821210
namespace {
@@ -1190,15 +1218,19 @@ class InProcessThinBackend : public ThinBackendProc {
11901218
Optional<Error> Err;
11911219
std::mutex ErrMu;
11921220

1221+
bool ShouldEmitIndexFiles;
1222+
11931223
public:
11941224
InProcessThinBackend(
11951225
const Config &Conf, ModuleSummaryIndex &CombinedIndex,
11961226
ThreadPoolStrategy ThinLTOParallelism,
11971227
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
1198-
AddStreamFn AddStream, FileCache Cache)
1199-
: ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries),
1228+
AddStreamFn AddStream, FileCache Cache, lto::IndexWriteCallback OnWrite,
1229+
bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles)
1230+
: ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries,
1231+
OnWrite, ShouldEmitImportsFiles),
12001232
BackendThreadPool(ThinLTOParallelism), AddStream(std::move(AddStream)),
1201-
Cache(std::move(Cache)) {
1233+
Cache(std::move(Cache)), ShouldEmitIndexFiles(ShouldEmitIndexFiles) {
12021234
for (auto &Name : CombinedIndex.cfiFunctionDefs())
12031235
CfiFunctionDefs.insert(
12041236
GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name)));
@@ -1227,6 +1259,11 @@ class InProcessThinBackend : public ThinBackendProc {
12271259

12281260
auto ModuleID = BM.getModuleIdentifier();
12291261

1262+
if (ShouldEmitIndexFiles) {
1263+
if (auto E = emitFiles(ImportList, ModuleID, ModuleID.str()))
1264+
return E;
1265+
}
1266+
12301267
if (!Cache || !CombinedIndex.modulePaths().count(ModuleID) ||
12311268
all_of(CombinedIndex.getModuleHash(ModuleID),
12321269
[](uint32_t V) { return V == 0; }))
@@ -1285,6 +1322,9 @@ class InProcessThinBackend : public ThinBackendProc {
12851322
},
12861323
BM, std::ref(CombinedIndex), std::ref(ImportList), std::ref(ExportList),
12871324
std::ref(ResolvedODR), std::ref(DefinedGlobals), std::ref(ModuleMap));
1325+
1326+
if (OnWrite)
1327+
OnWrite(std::string(ModulePath));
12881328
return Error::success();
12891329
}
12901330

@@ -1302,13 +1342,16 @@ class InProcessThinBackend : public ThinBackendProc {
13021342
};
13031343
} // end anonymous namespace
13041344

1305-
ThinBackend lto::createInProcessThinBackend(ThreadPoolStrategy Parallelism) {
1345+
ThinBackend lto::createInProcessThinBackend(ThreadPoolStrategy Parallelism,
1346+
lto::IndexWriteCallback OnWrite,
1347+
bool ShouldEmitIndexFiles,
1348+
bool ShouldEmitImportsFiles) {
13061349
return [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex,
13071350
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
13081351
AddStreamFn AddStream, FileCache Cache) {
13091352
return std::make_unique<InProcessThinBackend>(
13101353
Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, AddStream,
1311-
Cache);
1354+
Cache, OnWrite, ShouldEmitIndexFiles, ShouldEmitImportsFiles);
13121355
};
13131356
}
13141357

@@ -1335,20 +1378,18 @@ std::string lto::getThinLTOOutputFile(const std::string &Path,
13351378
namespace {
13361379
class WriteIndexesThinBackend : public ThinBackendProc {
13371380
std::string OldPrefix, NewPrefix;
1338-
bool ShouldEmitImportsFiles;
13391381
raw_fd_ostream *LinkedObjectsFile;
1340-
lto::IndexWriteCallback OnWrite;
13411382

13421383
public:
13431384
WriteIndexesThinBackend(
13441385
const Config &Conf, ModuleSummaryIndex &CombinedIndex,
13451386
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
13461387
std::string OldPrefix, std::string NewPrefix, bool ShouldEmitImportsFiles,
13471388
raw_fd_ostream *LinkedObjectsFile, lto::IndexWriteCallback OnWrite)
1348-
: ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries),
1389+
: ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries,
1390+
OnWrite, ShouldEmitImportsFiles),
13491391
OldPrefix(OldPrefix), NewPrefix(NewPrefix),
1350-
ShouldEmitImportsFiles(ShouldEmitImportsFiles),
1351-
LinkedObjectsFile(LinkedObjectsFile), OnWrite(OnWrite) {}
1392+
LinkedObjectsFile(LinkedObjectsFile) {}
13521393

13531394
Error start(
13541395
unsigned Task, BitcodeModule BM,
@@ -1363,23 +1404,8 @@ class WriteIndexesThinBackend : public ThinBackendProc {
13631404
if (LinkedObjectsFile)
13641405
*LinkedObjectsFile << NewModulePath << '\n';
13651406

1366-
std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex;
1367-
gatherImportedSummariesForModule(ModulePath, ModuleToDefinedGVSummaries,
1368-
ImportList, ModuleToSummariesForIndex);
1369-
1370-
std::error_code EC;
1371-
raw_fd_ostream OS(NewModulePath + ".thinlto.bc", EC,
1372-
sys::fs::OpenFlags::OF_None);
1373-
if (EC)
1374-
return errorCodeToError(EC);
1375-
writeIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex);
1376-
1377-
if (ShouldEmitImportsFiles) {
1378-
EC = EmitImportsFiles(ModulePath, NewModulePath + ".imports",
1379-
ModuleToSummariesForIndex);
1380-
if (EC)
1381-
return errorCodeToError(EC);
1382-
}
1407+
if (auto E = emitFiles(ImportList, ModulePath, NewModulePath))
1408+
return E;
13831409

13841410
if (OnWrite)
13851411
OnWrite(std::string(ModulePath));

0 commit comments

Comments
 (0)