1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
|
//===-- llvm-lto2: test harness for the resolution-based LTO interface ----===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This program takes in a list of bitcode files, links them and performs
// link-time optimization according to the provided symbol resolutions using the
// resolution-based LTO interface, and outputs one or more object files.
//
// This program is intended to eventually replace llvm-lto which uses the legacy
// LTO interface.
//
//===----------------------------------------------------------------------===//
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Remarks/HotnessThresholdParser.h"
#include "llvm/Support/Caching.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/PluginLoader.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Threading.h"
#include <atomic>
using namespace llvm;
using namespace lto;
static codegen::RegisterCodeGenFlags CGF;
static cl::opt<char>
OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
"(default = '-O2')"),
cl::Prefix, cl::ZeroOrMore, cl::init('2'));
static cl::opt<char> CGOptLevel(
"cg-opt-level",
cl::desc("Codegen optimization level (0, 1, 2 or 3, default = '2')"),
cl::init('2'));
static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
cl::desc("<input bitcode files>"));
static cl::opt<std::string> OutputFilename("o", cl::Required,
cl::desc("Output filename"),
cl::value_desc("filename"));
static cl::opt<std::string> CacheDir("cache-dir", cl::desc("Cache Directory"),
cl::value_desc("directory"));
static cl::opt<std::string> OptPipeline("opt-pipeline",
cl::desc("Optimizer Pipeline"),
cl::value_desc("pipeline"));
static cl::opt<std::string> AAPipeline("aa-pipeline",
cl::desc("Alias Analysis Pipeline"),
cl::value_desc("aapipeline"));
static cl::opt<bool> SaveTemps("save-temps", cl::desc("Save temporary files"));
static cl::opt<bool>
ThinLTODistributedIndexes("thinlto-distributed-indexes", cl::init(false),
cl::desc("Write out individual index and "
"import files for the "
"distributed backend case"));
// Default to using all available threads in the system, but using only one
// thread per core (no SMT).
// Use -thinlto-threads=all to use hardware_concurrency() instead, which means
// to use all hardware threads or cores in the system.
static cl::opt<std::string> Threads("thinlto-threads");
static cl::list<std::string> SymbolResolutions(
"r",
cl::desc("Specify a symbol resolution: filename,symbolname,resolution\n"
"where \"resolution\" is a sequence (which may be empty) of the\n"
"following characters:\n"
" p - prevailing: the linker has chosen this definition of the\n"
" symbol\n"
" l - local: the definition of this symbol is unpreemptable at\n"
" runtime and is known to be in this linkage unit\n"
" x - externally visible: the definition of this symbol is\n"
" visible outside of the LTO unit\n"
"A resolution for each symbol must be specified."),
cl::ZeroOrMore);
static cl::opt<std::string> OverrideTriple(
"override-triple",
cl::desc("Replace target triples in input files with this triple"));
static cl::opt<std::string> DefaultTriple(
"default-triple",
cl::desc(
"Replace unspecified target triples in input files with this triple"));
static cl::opt<bool> RemarksWithHotness(
"pass-remarks-with-hotness",
cl::desc("With PGO, include profile count in optimization remarks"),
cl::Hidden);
cl::opt<Optional<uint64_t>, false, remarks::HotnessThresholdParser>
RemarksHotnessThreshold(
"pass-remarks-hotness-threshold",
cl::desc("Minimum profile count required for an "
"optimization remark to be output."
" Use 'auto' to apply the threshold from profile summary."),
cl::value_desc("uint or 'auto'"), cl::init(0), cl::Hidden);
static cl::opt<std::string>
RemarksFilename("pass-remarks-output",
cl::desc("Output filename for pass remarks"),
cl::value_desc("filename"));
static cl::opt<std::string>
RemarksPasses("pass-remarks-filter",
cl::desc("Only record optimization remarks from passes whose "
"names match the given regular expression"),
cl::value_desc("regex"));
static cl::opt<std::string> RemarksFormat(
"pass-remarks-format",
cl::desc("The format used for serializing remarks (default: YAML)"),
cl::value_desc("format"), cl::init("yaml"));
static cl::opt<std::string>
SamplePGOFile("lto-sample-profile-file",
cl::desc("Specify a SamplePGO profile file"));
static cl::opt<std::string>
CSPGOFile("lto-cspgo-profile-file",
cl::desc("Specify a context sensitive PGO profile file"));
static cl::opt<bool>
RunCSIRInstr("lto-cspgo-gen",
cl::desc("Run PGO context sensitive IR instrumentation"),
cl::init(false), cl::Hidden);
static cl::opt<bool>
UseNewPM("use-new-pm",
cl::desc("Run LTO passes using the new pass manager"),
cl::init(LLVM_ENABLE_NEW_PASS_MANAGER), cl::Hidden);
static cl::opt<bool>
DebugPassManager("debug-pass-manager", cl::init(false), cl::Hidden,
cl::desc("Print pass management debugging information"));
static cl::opt<std::string>
StatsFile("stats-file", cl::desc("Filename to write statistics to"));
static cl::list<std::string>
PassPlugins("load-pass-plugin",
cl::desc("Load passes from plugin library"));
static cl::opt<bool> EnableFreestanding(
"lto-freestanding",
cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"),
cl::init(false), cl::Hidden);
static void check(Error E, std::string Msg) {
if (!E)
return;
handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) {
errs() << "llvm-lto2: " << Msg << ": " << EIB.message().c_str() << '\n';
});
exit(1);
}
template <typename T> static T check(Expected<T> E, std::string Msg) {
if (E)
return std::move(*E);
check(E.takeError(), Msg);
return T();
}
static void check(std::error_code EC, std::string Msg) {
check(errorCodeToError(EC), Msg);
}
template <typename T> static T check(ErrorOr<T> E, std::string Msg) {
if (E)
return std::move(*E);
check(E.getError(), Msg);
return T();
}
static int usage() {
errs() << "Available subcommands: dump-symtab run\n";
return 1;
}
static int run(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv, "Resolution-based LTO test harness");
// FIXME: Workaround PR30396 which means that a symbol can appear
// more than once if it is defined in module-level assembly and
// has a GV declaration. We allow (file, symbol) pairs to have multiple
// resolutions and apply them in the order observed.
std::map<std::pair<std::string, std::string>, std::list<SymbolResolution>>
CommandLineResolutions;
for (std::string R : SymbolResolutions) {
StringRef Rest = R;
StringRef FileName, SymbolName;
std::tie(FileName, Rest) = Rest.split(',');
if (Rest.empty()) {
llvm::errs() << "invalid resolution: " << R << '\n';
return 1;
}
std::tie(SymbolName, Rest) = Rest.split(',');
SymbolResolution Res;
for (char C : Rest) {
if (C == 'p')
Res.Prevailing = true;
else if (C == 'l')
Res.FinalDefinitionInLinkageUnit = true;
else if (C == 'x')
Res.VisibleToRegularObj = true;
else if (C == 'r')
Res.LinkerRedefined = true;
else {
llvm::errs() << "invalid character " << C << " in resolution: " << R
<< '\n';
return 1;
}
}
CommandLineResolutions[{std::string(FileName), std::string(SymbolName)}]
.push_back(Res);
}
std::vector<std::unique_ptr<MemoryBuffer>> MBs;
Config Conf;
Conf.CPU = codegen::getMCPU();
Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple());
Conf.MAttrs = codegen::getMAttrs();
if (auto RM = codegen::getExplicitRelocModel())
Conf.RelocModel = RM.getValue();
Conf.CodeModel = codegen::getExplicitCodeModel();
Conf.DebugPassManager = DebugPassManager;
if (SaveTemps)
check(Conf.addSaveTemps(OutputFilename + "."),
"Config::addSaveTemps failed");
// Optimization remarks.
Conf.RemarksFilename = RemarksFilename;
Conf.RemarksPasses = RemarksPasses;
Conf.RemarksWithHotness = RemarksWithHotness;
Conf.RemarksHotnessThreshold = RemarksHotnessThreshold;
Conf.RemarksFormat = RemarksFormat;
Conf.SampleProfile = SamplePGOFile;
Conf.CSIRProfile = CSPGOFile;
Conf.RunCSIRInstr = RunCSIRInstr;
// Run a custom pipeline, if asked for.
Conf.OptPipeline = OptPipeline;
Conf.AAPipeline = AAPipeline;
Conf.OptLevel = OptLevel - '0';
Conf.UseNewPM = UseNewPM;
Conf.Freestanding = EnableFreestanding;
for (auto &PluginFN : PassPlugins)
Conf.PassPlugins.push_back(PluginFN);
switch (CGOptLevel) {
case '0':
Conf.CGOptLevel = CodeGenOpt::None;
break;
case '1':
Conf.CGOptLevel = CodeGenOpt::Less;
break;
case '2':
Conf.CGOptLevel = CodeGenOpt::Default;
break;
case '3':
Conf.CGOptLevel = CodeGenOpt::Aggressive;
break;
default:
llvm::errs() << "invalid cg optimization level: " << CGOptLevel << '\n';
return 1;
}
if (auto FT = codegen::getExplicitFileType())
Conf.CGFileType = FT.getValue();
Conf.OverrideTriple = OverrideTriple;
Conf.DefaultTriple = DefaultTriple;
Conf.StatsFile = StatsFile;
Conf.PTO.LoopVectorization = Conf.OptLevel > 1;
Conf.PTO.SLPVectorization = Conf.OptLevel > 1;
ThinBackend Backend;
if (ThinLTODistributedIndexes)
Backend = createWriteIndexesThinBackend(/* OldPrefix */ "",
/* NewPrefix */ "",
/* ShouldEmitImportsFiles */ true,
/* LinkedObjectsFile */ nullptr,
/* OnWrite */ {});
else
Backend = createInProcessThinBackend(
llvm::heavyweight_hardware_concurrency(Threads));
// Track whether we hit an error; in particular, in the multi-threaded case,
// we can't exit() early because the rest of the threads wouldn't have had a
// change to be join-ed, and that would result in a "terminate called without
// an active exception". Altogether, this results in nondeterministic
// behavior. Instead, we don't exit in the multi-threaded case, but we make
// sure to report the error and then at the end (after joining cleanly)
// exit(1).
std::atomic<bool> HasErrors;
std::atomic_init(&HasErrors, false);
Conf.DiagHandler = [&](const DiagnosticInfo &DI) {
DiagnosticPrinterRawOStream DP(errs());
DI.print(DP);
errs() << '\n';
if (DI.getSeverity() == DS_Error)
HasErrors = true;
};
LTO Lto(std::move(Conf), std::move(Backend));
for (std::string F : InputFilenames) {
std::unique_ptr<MemoryBuffer> MB = check(MemoryBuffer::getFile(F), F);
std::unique_ptr<InputFile> Input =
check(InputFile::create(MB->getMemBufferRef()), F);
std::vector<SymbolResolution> Res;
for (const InputFile::Symbol &Sym : Input->symbols()) {
auto I = CommandLineResolutions.find({F, std::string(Sym.getName())});
// If it isn't found, look for ".", which would have been added
// (followed by a hash) when the symbol was promoted during module
// splitting if it was defined in one part and used in the other.
// Try looking up the symbol name before the suffix.
if (I == CommandLineResolutions.end()) {
auto SplitName = Sym.getName().rsplit(".");
I = CommandLineResolutions.find({F, std::string(SplitName.first)});
}
if (I == CommandLineResolutions.end()) {
llvm::errs() << argv[0] << ": missing symbol resolution for " << F
<< ',' << Sym.getName() << '\n';
HasErrors = true;
} else {
Res.push_back(I->second.front());
I->second.pop_front();
if (I->second.empty())
CommandLineResolutions.erase(I);
}
}
if (HasErrors)
continue;
MBs.push_back(std::move(MB));
check(Lto.add(std::move(Input), Res), F);
}
if (!CommandLineResolutions.empty()) {
HasErrors = true;
for (auto UnusedRes : CommandLineResolutions)
llvm::errs() << argv[0] << ": unused symbol resolution for "
<< UnusedRes.first.first << ',' << UnusedRes.first.second
<< '\n';
}
if (HasErrors)
return 1;
auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
std::string Path = OutputFilename + "." + utostr(Task);
std::error_code EC;
auto S = std::make_unique<raw_fd_ostream>(Path, EC, sys::fs::OF_None);
check(EC, Path);
return std::make_unique<CachedFileStream>(std::move(S), Path);
};
auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
*AddStream(Task)->OS << MB->getBuffer();
};
FileCache Cache;
if (!CacheDir.empty())
Cache = check(localCache("ThinLTO", "Thin", CacheDir, AddBuffer),
"failed to create cache");
check(Lto.run(AddStream, Cache), "LTO::run failed");
return static_cast<int>(HasErrors);
}
static int dumpSymtab(int argc, char **argv) {
for (StringRef F : make_range(argv + 1, argv + argc)) {
std::unique_ptr<MemoryBuffer> MB =
check(MemoryBuffer::getFile(F), std::string(F));
BitcodeFileContents BFC =
check(getBitcodeFileContents(*MB), std::string(F));
if (BFC.Symtab.size() >= sizeof(irsymtab::storage::Header)) {
auto *Hdr = reinterpret_cast<const irsymtab::storage::Header *>(
BFC.Symtab.data());
outs() << "version: " << Hdr->Version << '\n';
if (Hdr->Version == irsymtab::storage::Header::kCurrentVersion)
outs() << "producer: " << Hdr->Producer.get(BFC.StrtabForSymtab)
<< '\n';
}
std::unique_ptr<InputFile> Input =
check(InputFile::create(MB->getMemBufferRef()), std::string(F));
outs() << "target triple: " << Input->getTargetTriple() << '\n';
Triple TT(Input->getTargetTriple());
outs() << "source filename: " << Input->getSourceFileName() << '\n';
if (TT.isOSBinFormatCOFF())
outs() << "linker opts: " << Input->getCOFFLinkerOpts() << '\n';
if (TT.isOSBinFormatELF()) {
outs() << "dependent libraries:";
for (auto L : Input->getDependentLibraries())
outs() << " \"" << L << "\"";
outs() << '\n';
}
ArrayRef<std::pair<StringRef, Comdat::SelectionKind>> ComdatTable =
Input->getComdatTable();
for (const InputFile::Symbol &Sym : Input->symbols()) {
switch (Sym.getVisibility()) {
case GlobalValue::HiddenVisibility:
outs() << 'H';
break;
case GlobalValue::ProtectedVisibility:
outs() << 'P';
break;
case GlobalValue::DefaultVisibility:
outs() << 'D';
break;
}
auto PrintBool = [&](char C, bool B) { outs() << (B ? C : '-'); };
PrintBool('U', Sym.isUndefined());
PrintBool('C', Sym.isCommon());
PrintBool('W', Sym.isWeak());
PrintBool('I', Sym.isIndirect());
PrintBool('O', Sym.canBeOmittedFromSymbolTable());
PrintBool('T', Sym.isTLS());
PrintBool('X', Sym.isExecutable());
outs() << ' ' << Sym.getName() << '\n';
if (Sym.isCommon())
outs() << " size " << Sym.getCommonSize() << " align "
<< Sym.getCommonAlignment() << '\n';
int Comdat = Sym.getComdatIndex();
if (Comdat != -1) {
outs() << " comdat ";
switch (ComdatTable[Comdat].second) {
case Comdat::Any:
outs() << "any";
break;
case Comdat::ExactMatch:
outs() << "exactmatch";
break;
case Comdat::Largest:
outs() << "largest";
break;
case Comdat::NoDeduplicate:
outs() << "nodeduplicate";
break;
case Comdat::SameSize:
outs() << "samesize";
break;
}
outs() << ' ' << ComdatTable[Comdat].first << '\n';
}
if (TT.isOSBinFormatCOFF() && Sym.isWeak() && Sym.isIndirect())
outs() << " fallback " << Sym.getCOFFWeakExternalFallback() << '\n';
if (!Sym.getSectionName().empty())
outs() << " section " << Sym.getSectionName() << "\n";
}
outs() << '\n';
}
return 0;
}
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
// FIXME: This should use llvm::cl subcommands, but it isn't currently
// possible to pass an argument not associated with a subcommand to a
// subcommand (e.g. -use-new-pm).
if (argc < 2)
return usage();
StringRef Subcommand = argv[1];
// Ensure that argv[0] is correct after adjusting argv/argc.
argv[1] = argv[0];
if (Subcommand == "dump-symtab")
return dumpSymtab(argc - 1, argv + 1);
if (Subcommand == "run")
return run(argc - 1, argv + 1);
return usage();
}
|