1 //===-- llvm-lto2: test harness for the resolution-based LTO interface ----===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This program takes in a list of bitcode files, links them and performs
10 // link-time optimization according to the provided symbol resolutions using the
11 // resolution-based LTO interface, and outputs one or more object files.
13 // This program is intended to eventually replace llvm-lto which uses the legacy
16 //===----------------------------------------------------------------------===//
18 #include "llvm/Bitcode/BitcodeReader.h"
19 #include "llvm/CodeGen/CommandFlags.h"
20 #include "llvm/IR/DiagnosticPrinter.h"
21 #include "llvm/LTO/LTO.h"
22 #include "llvm/Passes/PassPlugin.h"
23 #include "llvm/Remarks/HotnessThresholdParser.h"
24 #include "llvm/Support/Caching.h"
25 #include "llvm/Support/CommandLine.h"
26 #include "llvm/Support/FileSystem.h"
27 #include "llvm/Support/InitLLVM.h"
28 #include "llvm/Support/PluginLoader.h"
29 #include "llvm/Support/TargetSelect.h"
30 #include "llvm/Support/Threading.h"
36 static codegen::RegisterCodeGenFlags CGF
;
40 cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
42 cl::Prefix
, cl::init('2'));
44 static cl::opt
<char> CGOptLevel(
46 cl::desc("Codegen optimization level (0, 1, 2 or 3, default = '2')"),
49 static cl::list
<std::string
> InputFilenames(cl::Positional
, cl::OneOrMore
,
50 cl::desc("<input bitcode files>"));
52 static cl::opt
<std::string
> OutputFilename("o", cl::Required
,
53 cl::desc("Output filename"),
54 cl::value_desc("filename"));
56 static cl::opt
<std::string
> CacheDir("cache-dir", cl::desc("Cache Directory"),
57 cl::value_desc("directory"));
59 static cl::opt
<std::string
> OptPipeline("opt-pipeline",
60 cl::desc("Optimizer Pipeline"),
61 cl::value_desc("pipeline"));
63 static cl::opt
<std::string
> AAPipeline("aa-pipeline",
64 cl::desc("Alias Analysis Pipeline"),
65 cl::value_desc("aapipeline"));
67 static cl::opt
<bool> SaveTemps("save-temps", cl::desc("Save temporary files"));
69 static cl::list
<std::string
> SelectSaveTemps(
71 cl::value_desc("One, or multiple of: "
72 "resolution,preopt,promote,internalize,import,opt,precodegen"
74 cl::desc("Save selected temporary files. Cannot be specified together with "
78 constexpr const char *SaveTempsValues
[] = {
79 "resolution", "preopt", "promote", "internalize",
80 "import", "opt", "precodegen", "combinedindex"};
83 ThinLTODistributedIndexes("thinlto-distributed-indexes",
84 cl::desc("Write out individual index and "
85 "import files for the "
86 "distributed backend case"));
89 ThinLTOEmitIndexes("thinlto-emit-indexes",
90 cl::desc("Write out individual index files via "
94 ThinLTOEmitImports("thinlto-emit-imports",
95 cl::desc("Write out individual imports files via "
96 "InProcessThinLTO. Has no effect unless "
97 "specified with -thinlto-emit-indexes or "
98 "-thinlto-distributed-indexes"));
100 // Default to using all available threads in the system, but using only one
101 // thread per core (no SMT).
102 // Use -thinlto-threads=all to use hardware_concurrency() instead, which means
103 // to use all hardware threads or cores in the system.
104 static cl::opt
<std::string
> Threads("thinlto-threads");
106 static cl::list
<std::string
> SymbolResolutions(
108 cl::desc("Specify a symbol resolution: filename,symbolname,resolution\n"
109 "where \"resolution\" is a sequence (which may be empty) of the\n"
110 "following characters:\n"
111 " p - prevailing: the linker has chosen this definition of the\n"
113 " l - local: the definition of this symbol is unpreemptable at\n"
114 " runtime and is known to be in this linkage unit\n"
115 " x - externally visible: the definition of this symbol is\n"
116 " visible outside of the LTO unit\n"
117 "A resolution for each symbol must be specified"));
119 static cl::opt
<std::string
> OverrideTriple(
121 cl::desc("Replace target triples in input files with this triple"));
123 static cl::opt
<std::string
> DefaultTriple(
126 "Replace unspecified target triples in input files with this triple"));
128 static cl::opt
<bool> RemarksWithHotness(
129 "pass-remarks-with-hotness",
130 cl::desc("With PGO, include profile count in optimization remarks"),
133 cl::opt
<std::optional
<uint64_t>, false, remarks::HotnessThresholdParser
>
134 RemarksHotnessThreshold(
135 "pass-remarks-hotness-threshold",
136 cl::desc("Minimum profile count required for an "
137 "optimization remark to be output."
138 " Use 'auto' to apply the threshold from profile summary."),
139 cl::value_desc("uint or 'auto'"), cl::init(0), cl::Hidden
);
141 static cl::opt
<std::string
>
142 RemarksFilename("pass-remarks-output",
143 cl::desc("Output filename for pass remarks"),
144 cl::value_desc("filename"));
146 static cl::opt
<std::string
>
147 RemarksPasses("pass-remarks-filter",
148 cl::desc("Only record optimization remarks from passes whose "
149 "names match the given regular expression"),
150 cl::value_desc("regex"));
152 static cl::opt
<std::string
> RemarksFormat(
153 "pass-remarks-format",
154 cl::desc("The format used for serializing remarks (default: YAML)"),
155 cl::value_desc("format"), cl::init("yaml"));
157 static cl::opt
<std::string
>
158 SamplePGOFile("lto-sample-profile-file",
159 cl::desc("Specify a SamplePGO profile file"));
161 static cl::opt
<std::string
>
162 CSPGOFile("lto-cspgo-profile-file",
163 cl::desc("Specify a context sensitive PGO profile file"));
166 RunCSIRInstr("lto-cspgo-gen",
167 cl::desc("Run PGO context sensitive IR instrumentation"),
171 DebugPassManager("debug-pass-manager", cl::Hidden
,
172 cl::desc("Print pass management debugging information"));
174 static cl::opt
<std::string
>
175 StatsFile("stats-file", cl::desc("Filename to write statistics to"));
177 static cl::list
<std::string
>
178 PassPlugins("load-pass-plugin",
179 cl::desc("Load passes from plugin library"));
181 static cl::opt
<std::string
> UnifiedLTOMode("unified-lto", cl::Optional
,
182 cl::desc("Set LTO mode"),
183 cl::value_desc("mode"));
185 static cl::opt
<bool> EnableFreestanding(
187 cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"),
190 static void check(Error E
, std::string Msg
) {
193 handleAllErrors(std::move(E
), [&](ErrorInfoBase
&EIB
) {
194 errs() << "llvm-lto2: " << Msg
<< ": " << EIB
.message().c_str() << '\n';
199 template <typename T
> static T
check(Expected
<T
> E
, std::string Msg
) {
201 return std::move(*E
);
202 check(E
.takeError(), Msg
);
206 static void check(std::error_code EC
, std::string Msg
) {
207 check(errorCodeToError(EC
), Msg
);
210 template <typename T
> static T
check(ErrorOr
<T
> E
, std::string Msg
) {
212 return std::move(*E
);
213 check(E
.getError(), Msg
);
218 errs() << "Available subcommands: dump-symtab run\n";
222 static int run(int argc
, char **argv
) {
223 cl::ParseCommandLineOptions(argc
, argv
, "Resolution-based LTO test harness");
225 // FIXME: Workaround PR30396 which means that a symbol can appear
226 // more than once if it is defined in module-level assembly and
227 // has a GV declaration. We allow (file, symbol) pairs to have multiple
228 // resolutions and apply them in the order observed.
229 std::map
<std::pair
<std::string
, std::string
>, std::list
<SymbolResolution
>>
230 CommandLineResolutions
;
231 for (std::string R
: SymbolResolutions
) {
233 StringRef FileName
, SymbolName
;
234 std::tie(FileName
, Rest
) = Rest
.split(',');
236 llvm::errs() << "invalid resolution: " << R
<< '\n';
239 std::tie(SymbolName
, Rest
) = Rest
.split(',');
240 SymbolResolution Res
;
241 for (char C
: Rest
) {
243 Res
.Prevailing
= true;
245 Res
.FinalDefinitionInLinkageUnit
= true;
247 Res
.VisibleToRegularObj
= true;
249 Res
.LinkerRedefined
= true;
251 llvm::errs() << "invalid character " << C
<< " in resolution: " << R
256 CommandLineResolutions
[{std::string(FileName
), std::string(SymbolName
)}]
260 std::vector
<std::unique_ptr
<MemoryBuffer
>> MBs
;
264 Conf
.CPU
= codegen::getMCPU();
265 Conf
.Options
= codegen::InitTargetOptionsFromCodeGenFlags(Triple());
266 Conf
.MAttrs
= codegen::getMAttrs();
267 if (auto RM
= codegen::getExplicitRelocModel())
268 Conf
.RelocModel
= *RM
;
269 Conf
.CodeModel
= codegen::getExplicitCodeModel();
271 Conf
.DebugPassManager
= DebugPassManager
;
273 if (SaveTemps
&& !SelectSaveTemps
.empty()) {
274 llvm::errs() << "-save-temps cannot be specified with -select-save-temps\n";
277 if (SaveTemps
|| !SelectSaveTemps
.empty()) {
278 DenseSet
<StringRef
> SaveTempsArgs
;
279 for (auto &S
: SelectSaveTemps
)
280 if (is_contained(SaveTempsValues
, S
))
281 SaveTempsArgs
.insert(S
);
283 llvm::errs() << ("invalid -select-save-temps argument: " + S
) << '\n';
286 check(Conf
.addSaveTemps(OutputFilename
+ ".", false, SaveTempsArgs
),
287 "Config::addSaveTemps failed");
290 // Optimization remarks.
291 Conf
.RemarksFilename
= RemarksFilename
;
292 Conf
.RemarksPasses
= RemarksPasses
;
293 Conf
.RemarksWithHotness
= RemarksWithHotness
;
294 Conf
.RemarksHotnessThreshold
= RemarksHotnessThreshold
;
295 Conf
.RemarksFormat
= RemarksFormat
;
297 Conf
.SampleProfile
= SamplePGOFile
;
298 Conf
.CSIRProfile
= CSPGOFile
;
299 Conf
.RunCSIRInstr
= RunCSIRInstr
;
301 // Run a custom pipeline, if asked for.
302 Conf
.OptPipeline
= OptPipeline
;
303 Conf
.AAPipeline
= AAPipeline
;
305 Conf
.OptLevel
= OptLevel
- '0';
306 Conf
.Freestanding
= EnableFreestanding
;
307 for (auto &PluginFN
: PassPlugins
)
308 Conf
.PassPlugins
.push_back(PluginFN
);
309 if (auto Level
= CodeGenOpt::parseLevel(CGOptLevel
)) {
310 Conf
.CGOptLevel
= *Level
;
312 llvm::errs() << "invalid cg optimization level: " << CGOptLevel
<< '\n';
316 if (auto FT
= codegen::getExplicitFileType())
317 Conf
.CGFileType
= *FT
;
319 Conf
.OverrideTriple
= OverrideTriple
;
320 Conf
.DefaultTriple
= DefaultTriple
;
321 Conf
.StatsFile
= StatsFile
;
322 Conf
.PTO
.LoopVectorization
= Conf
.OptLevel
> 1;
323 Conf
.PTO
.SLPVectorization
= Conf
.OptLevel
> 1;
326 if (ThinLTODistributedIndexes
)
327 Backend
= createWriteIndexesThinBackend(/*OldPrefix=*/"",
329 /*NativeObjectPrefix=*/"",
331 /*LinkedObjectsFile=*/nullptr,
334 Backend
= createInProcessThinBackend(
335 llvm::heavyweight_hardware_concurrency(Threads
),
336 /* OnWrite */ {}, ThinLTOEmitIndexes
, ThinLTOEmitImports
);
338 // Track whether we hit an error; in particular, in the multi-threaded case,
339 // we can't exit() early because the rest of the threads wouldn't have had a
340 // change to be join-ed, and that would result in a "terminate called without
341 // an active exception". Altogether, this results in nondeterministic
342 // behavior. Instead, we don't exit in the multi-threaded case, but we make
343 // sure to report the error and then at the end (after joining cleanly)
345 std::atomic
<bool> HasErrors
;
346 std::atomic_init(&HasErrors
, false);
347 Conf
.DiagHandler
= [&](const DiagnosticInfo
&DI
) {
348 DiagnosticPrinterRawOStream
DP(errs());
351 if (DI
.getSeverity() == DS_Error
)
355 LTO::LTOKind LTOMode
= LTO::LTOK_Default
;
357 if (UnifiedLTOMode
== "full") {
358 LTOMode
= LTO::LTOK_UnifiedRegular
;
359 } else if (UnifiedLTOMode
== "thin") {
360 LTOMode
= LTO::LTOK_UnifiedThin
;
361 } else if (UnifiedLTOMode
== "default") {
362 LTOMode
= LTO::LTOK_Default
;
363 } else if (!UnifiedLTOMode
.empty()) {
364 llvm::errs() << "invalid LTO mode\n";
368 LTO
Lto(std::move(Conf
), std::move(Backend
), 1, LTOMode
);
370 for (std::string F
: InputFilenames
) {
371 std::unique_ptr
<MemoryBuffer
> MB
= check(MemoryBuffer::getFile(F
), F
);
372 std::unique_ptr
<InputFile
> Input
=
373 check(InputFile::create(MB
->getMemBufferRef()), F
);
375 std::vector
<SymbolResolution
> Res
;
376 for (const InputFile::Symbol
&Sym
: Input
->symbols()) {
377 auto I
= CommandLineResolutions
.find({F
, std::string(Sym
.getName())});
378 // If it isn't found, look for ".", which would have been added
379 // (followed by a hash) when the symbol was promoted during module
380 // splitting if it was defined in one part and used in the other.
381 // Try looking up the symbol name before the suffix.
382 if (I
== CommandLineResolutions
.end()) {
383 auto SplitName
= Sym
.getName().rsplit(".");
384 I
= CommandLineResolutions
.find({F
, std::string(SplitName
.first
)});
386 if (I
== CommandLineResolutions
.end()) {
387 llvm::errs() << argv
[0] << ": missing symbol resolution for " << F
388 << ',' << Sym
.getName() << '\n';
391 Res
.push_back(I
->second
.front());
392 I
->second
.pop_front();
393 if (I
->second
.empty())
394 CommandLineResolutions
.erase(I
);
401 MBs
.push_back(std::move(MB
));
402 check(Lto
.add(std::move(Input
), Res
), F
);
405 if (!CommandLineResolutions
.empty()) {
407 for (auto UnusedRes
: CommandLineResolutions
)
408 llvm::errs() << argv
[0] << ": unused symbol resolution for "
409 << UnusedRes
.first
.first
<< ',' << UnusedRes
.first
.second
417 const Twine
&ModuleName
) -> std::unique_ptr
<CachedFileStream
> {
418 std::string Path
= OutputFilename
+ "." + utostr(Task
);
421 auto S
= std::make_unique
<raw_fd_ostream
>(Path
, EC
, sys::fs::OF_None
);
423 return std::make_unique
<CachedFileStream
>(std::move(S
), Path
);
426 auto AddBuffer
= [&](size_t Task
, const Twine
&ModuleName
,
427 std::unique_ptr
<MemoryBuffer
> MB
) {
428 *AddStream(Task
, ModuleName
)->OS
<< MB
->getBuffer();
432 if (!CacheDir
.empty())
433 Cache
= check(localCache("ThinLTO", "Thin", CacheDir
, AddBuffer
),
434 "failed to create cache");
436 check(Lto
.run(AddStream
, Cache
), "LTO::run failed");
437 return static_cast<int>(HasErrors
);
440 static int dumpSymtab(int argc
, char **argv
) {
441 for (StringRef F
: make_range(argv
+ 1, argv
+ argc
)) {
442 std::unique_ptr
<MemoryBuffer
> MB
=
443 check(MemoryBuffer::getFile(F
), std::string(F
));
444 BitcodeFileContents BFC
=
445 check(getBitcodeFileContents(*MB
), std::string(F
));
447 if (BFC
.Symtab
.size() >= sizeof(irsymtab::storage::Header
)) {
448 auto *Hdr
= reinterpret_cast<const irsymtab::storage::Header
*>(
450 outs() << "version: " << Hdr
->Version
<< '\n';
451 if (Hdr
->Version
== irsymtab::storage::Header::kCurrentVersion
)
452 outs() << "producer: " << Hdr
->Producer
.get(BFC
.StrtabForSymtab
)
456 std::unique_ptr
<InputFile
> Input
=
457 check(InputFile::create(MB
->getMemBufferRef()), std::string(F
));
459 outs() << "target triple: " << Input
->getTargetTriple() << '\n';
460 Triple
TT(Input
->getTargetTriple());
462 outs() << "source filename: " << Input
->getSourceFileName() << '\n';
464 if (TT
.isOSBinFormatCOFF())
465 outs() << "linker opts: " << Input
->getCOFFLinkerOpts() << '\n';
467 if (TT
.isOSBinFormatELF()) {
468 outs() << "dependent libraries:";
469 for (auto L
: Input
->getDependentLibraries())
470 outs() << " \"" << L
<< "\"";
474 ArrayRef
<std::pair
<StringRef
, Comdat::SelectionKind
>> ComdatTable
=
475 Input
->getComdatTable();
476 for (const InputFile::Symbol
&Sym
: Input
->symbols()) {
477 switch (Sym
.getVisibility()) {
478 case GlobalValue::HiddenVisibility
:
481 case GlobalValue::ProtectedVisibility
:
484 case GlobalValue::DefaultVisibility
:
489 auto PrintBool
= [&](char C
, bool B
) { outs() << (B
? C
: '-'); };
490 PrintBool('U', Sym
.isUndefined());
491 PrintBool('C', Sym
.isCommon());
492 PrintBool('W', Sym
.isWeak());
493 PrintBool('I', Sym
.isIndirect());
494 PrintBool('O', Sym
.canBeOmittedFromSymbolTable());
495 PrintBool('T', Sym
.isTLS());
496 PrintBool('X', Sym
.isExecutable());
497 outs() << ' ' << Sym
.getName() << '\n';
500 outs() << " size " << Sym
.getCommonSize() << " align "
501 << Sym
.getCommonAlignment() << '\n';
503 int Comdat
= Sym
.getComdatIndex();
505 outs() << " comdat ";
506 switch (ComdatTable
[Comdat
].second
) {
510 case Comdat::ExactMatch
:
511 outs() << "exactmatch";
513 case Comdat::Largest
:
516 case Comdat::NoDeduplicate
:
517 outs() << "nodeduplicate";
519 case Comdat::SameSize
:
520 outs() << "samesize";
523 outs() << ' ' << ComdatTable
[Comdat
].first
<< '\n';
526 if (TT
.isOSBinFormatCOFF() && Sym
.isWeak() && Sym
.isIndirect())
527 outs() << " fallback " << Sym
.getCOFFWeakExternalFallback() << '\n';
529 if (!Sym
.getSectionName().empty())
530 outs() << " section " << Sym
.getSectionName() << "\n";
539 int main(int argc
, char **argv
) {
540 InitLLVM
X(argc
, argv
);
541 InitializeAllTargets();
542 InitializeAllTargetMCs();
543 InitializeAllAsmPrinters();
544 InitializeAllAsmParsers();
546 // FIXME: This should use llvm::cl subcommands, but it isn't currently
547 // possible to pass an argument not associated with a subcommand to a
548 // subcommand (e.g. -use-new-pm).
552 StringRef Subcommand
= argv
[1];
553 // Ensure that argv[0] is correct after adjusting argv/argc.
555 if (Subcommand
== "dump-symtab")
556 return dumpSymtab(argc
- 1, argv
+ 1);
557 if (Subcommand
== "run")
558 return run(argc
- 1, argv
+ 1);