1//===-- llvm-lto2: test harness for the resolution-based LTO interface ----===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
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.
12//
13// This program is intended to eventually replace llvm-lto which uses the legacy
14// LTO interface.
15//
16//===----------------------------------------------------------------------===//
17
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"
31#include <atomic>
32
33using namespace llvm;
34using namespace lto;
35
36static codegen::RegisterCodeGenFlags CGF;
37
38static cl::opt<char>
39    OptLevel("O",
40             cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
41                      "(default = '-O2')"),
42             cl::Prefix, cl::init('2'));
43
44static cl::opt<char> CGOptLevel(
45    "cg-opt-level",
46    cl::desc("Codegen optimization level (0, 1, 2 or 3, default = '2')"),
47    cl::init('2'));
48
49static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
50                                            cl::desc("<input bitcode files>"));
51
52static cl::opt<std::string> OutputFilename("o", cl::Required,
53                                           cl::desc("Output filename"),
54                                           cl::value_desc("filename"));
55
56static cl::opt<std::string> CacheDir("cache-dir", cl::desc("Cache Directory"),
57                                     cl::value_desc("directory"));
58
59static cl::opt<std::string> OptPipeline("opt-pipeline",
60                                        cl::desc("Optimizer Pipeline"),
61                                        cl::value_desc("pipeline"));
62
63static cl::opt<std::string> AAPipeline("aa-pipeline",
64                                       cl::desc("Alias Analysis Pipeline"),
65                                       cl::value_desc("aapipeline"));
66
67static cl::opt<bool> SaveTemps("save-temps", cl::desc("Save temporary files"));
68
69static cl::list<std::string> SelectSaveTemps(
70    "select-save-temps",
71    cl::value_desc("One, or multiple of: "
72                   "resolution,preopt,promote,internalize,import,opt,precodegen"
73                   ",combinedindex"),
74    cl::desc("Save selected temporary files. Cannot be specified together with "
75             "-save-temps"),
76    cl::CommaSeparated);
77
78constexpr const char *SaveTempsValues[] = {
79    "resolution", "preopt", "promote",    "internalize",
80    "import",     "opt",    "precodegen", "combinedindex"};
81
82static cl::opt<bool>
83    ThinLTODistributedIndexes("thinlto-distributed-indexes",
84                              cl::desc("Write out individual index and "
85                                       "import files for the "
86                                       "distributed backend case"));
87
88static cl::opt<bool>
89    ThinLTOEmitIndexes("thinlto-emit-indexes",
90                       cl::desc("Write out individual index files via "
91                                "InProcessThinLTO"));
92
93static cl::opt<bool>
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"));
99
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.
104static cl::opt<std::string> Threads("thinlto-threads");
105
106static cl::list<std::string> SymbolResolutions(
107    "r",
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"
112             "     symbol\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"));
118
119static cl::opt<std::string> OverrideTriple(
120    "override-triple",
121    cl::desc("Replace target triples in input files with this triple"));
122
123static cl::opt<std::string> DefaultTriple(
124    "default-triple",
125    cl::desc(
126        "Replace unspecified target triples in input files with this triple"));
127
128static cl::opt<bool> RemarksWithHotness(
129    "pass-remarks-with-hotness",
130    cl::desc("With PGO, include profile count in optimization remarks"),
131    cl::Hidden);
132
133cl::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);
140
141static cl::opt<std::string>
142    RemarksFilename("pass-remarks-output",
143                    cl::desc("Output filename for pass remarks"),
144                    cl::value_desc("filename"));
145
146static 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"));
151
152static 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"));
156
157static cl::opt<std::string>
158    SamplePGOFile("lto-sample-profile-file",
159                  cl::desc("Specify a SamplePGO profile file"));
160
161static cl::opt<std::string>
162    CSPGOFile("lto-cspgo-profile-file",
163              cl::desc("Specify a context sensitive PGO profile file"));
164
165static cl::opt<bool>
166    RunCSIRInstr("lto-cspgo-gen",
167                 cl::desc("Run PGO context sensitive IR instrumentation"),
168                 cl::Hidden);
169
170static cl::opt<bool>
171    DebugPassManager("debug-pass-manager", cl::Hidden,
172                     cl::desc("Print pass management debugging information"));
173
174static cl::opt<std::string>
175    StatsFile("stats-file", cl::desc("Filename to write statistics to"));
176
177static cl::list<std::string>
178    PassPlugins("load-pass-plugin",
179                cl::desc("Load passes from plugin library"));
180
181static cl::opt<std::string> UnifiedLTOMode("unified-lto", cl::Optional,
182                                           cl::desc("Set LTO mode"),
183                                           cl::value_desc("mode"));
184
185static cl::opt<bool> EnableFreestanding(
186    "lto-freestanding",
187    cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"),
188    cl::Hidden);
189
190static void check(Error E, std::string Msg) {
191  if (!E)
192    return;
193  handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) {
194    errs() << "llvm-lto2: " << Msg << ": " << EIB.message().c_str() << '\n';
195  });
196  exit(1);
197}
198
199template <typename T> static T check(Expected<T> E, std::string Msg) {
200  if (E)
201    return std::move(*E);
202  check(E.takeError(), Msg);
203  return T();
204}
205
206static void check(std::error_code EC, std::string Msg) {
207  check(errorCodeToError(EC), Msg);
208}
209
210template <typename T> static T check(ErrorOr<T> E, std::string Msg) {
211  if (E)
212    return std::move(*E);
213  check(E.getError(), Msg);
214  return T();
215}
216
217static int usage() {
218  errs() << "Available subcommands: dump-symtab run\n";
219  return 1;
220}
221
222static int run(int argc, char **argv) {
223  cl::ParseCommandLineOptions(argc, argv, "Resolution-based LTO test harness");
224
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) {
232    StringRef Rest = R;
233    StringRef FileName, SymbolName;
234    std::tie(FileName, Rest) = Rest.split(',');
235    if (Rest.empty()) {
236      llvm::errs() << "invalid resolution: " << R << '\n';
237      return 1;
238    }
239    std::tie(SymbolName, Rest) = Rest.split(',');
240    SymbolResolution Res;
241    for (char C : Rest) {
242      if (C == 'p')
243        Res.Prevailing = true;
244      else if (C == 'l')
245        Res.FinalDefinitionInLinkageUnit = true;
246      else if (C == 'x')
247        Res.VisibleToRegularObj = true;
248      else if (C == 'r')
249        Res.LinkerRedefined = true;
250      else {
251        llvm::errs() << "invalid character " << C << " in resolution: " << R
252                     << '\n';
253        return 1;
254      }
255    }
256    CommandLineResolutions[{std::string(FileName), std::string(SymbolName)}]
257        .push_back(Res);
258  }
259
260  std::vector<std::unique_ptr<MemoryBuffer>> MBs;
261
262  Config Conf;
263
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();
270
271  Conf.DebugPassManager = DebugPassManager;
272
273  if (SaveTemps && !SelectSaveTemps.empty()) {
274    llvm::errs() << "-save-temps cannot be specified with -select-save-temps\n";
275    return 1;
276  }
277  if (SaveTemps || !SelectSaveTemps.empty()) {
278    DenseSet<StringRef> SaveTempsArgs;
279    for (auto &S : SelectSaveTemps)
280      if (is_contained(SaveTempsValues, S))
281        SaveTempsArgs.insert(S);
282      else {
283        llvm::errs() << ("invalid -select-save-temps argument: " + S) << '\n';
284        return 1;
285      }
286    check(Conf.addSaveTemps(OutputFilename + ".", false, SaveTempsArgs),
287          "Config::addSaveTemps failed");
288  }
289
290  // Optimization remarks.
291  Conf.RemarksFilename = RemarksFilename;
292  Conf.RemarksPasses = RemarksPasses;
293  Conf.RemarksWithHotness = RemarksWithHotness;
294  Conf.RemarksHotnessThreshold = RemarksHotnessThreshold;
295  Conf.RemarksFormat = RemarksFormat;
296
297  Conf.SampleProfile = SamplePGOFile;
298  Conf.CSIRProfile = CSPGOFile;
299  Conf.RunCSIRInstr = RunCSIRInstr;
300
301  // Run a custom pipeline, if asked for.
302  Conf.OptPipeline = OptPipeline;
303  Conf.AAPipeline = AAPipeline;
304
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;
311  } else {
312    llvm::errs() << "invalid cg optimization level: " << CGOptLevel << '\n';
313    return 1;
314  }
315
316  if (auto FT = codegen::getExplicitFileType())
317    Conf.CGFileType = *FT;
318
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;
324
325  ThinBackend Backend;
326  if (ThinLTODistributedIndexes)
327    Backend = createWriteIndexesThinBackend(/*OldPrefix=*/"",
328                                            /*NewPrefix=*/"",
329                                            /*NativeObjectPrefix=*/"",
330                                            ThinLTOEmitImports,
331                                            /*LinkedObjectsFile=*/nullptr,
332                                            /*OnWrite=*/{});
333  else
334    Backend = createInProcessThinBackend(
335        llvm::heavyweight_hardware_concurrency(Threads),
336        /* OnWrite */ {}, ThinLTOEmitIndexes, ThinLTOEmitImports);
337
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)
344  // exit(1).
345  std::atomic<bool> HasErrors;
346  std::atomic_init(&HasErrors, false);
347  Conf.DiagHandler = [&](const DiagnosticInfo &DI) {
348    DiagnosticPrinterRawOStream DP(errs());
349    DI.print(DP);
350    errs() << '\n';
351    if (DI.getSeverity() == DS_Error)
352      HasErrors = true;
353  };
354
355  LTO::LTOKind LTOMode = LTO::LTOK_Default;
356
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";
365    return 1;
366  }
367
368  LTO Lto(std::move(Conf), std::move(Backend), 1, LTOMode);
369
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);
374
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)});
385      }
386      if (I == CommandLineResolutions.end()) {
387        llvm::errs() << argv[0] << ": missing symbol resolution for " << F
388                     << ',' << Sym.getName() << '\n';
389        HasErrors = true;
390      } else {
391        Res.push_back(I->second.front());
392        I->second.pop_front();
393        if (I->second.empty())
394          CommandLineResolutions.erase(I);
395      }
396    }
397
398    if (HasErrors)
399      continue;
400
401    MBs.push_back(std::move(MB));
402    check(Lto.add(std::move(Input), Res), F);
403  }
404
405  if (!CommandLineResolutions.empty()) {
406    HasErrors = true;
407    for (auto UnusedRes : CommandLineResolutions)
408      llvm::errs() << argv[0] << ": unused symbol resolution for "
409                   << UnusedRes.first.first << ',' << UnusedRes.first.second
410                   << '\n';
411  }
412  if (HasErrors)
413    return 1;
414
415  auto AddStream =
416      [&](size_t Task,
417          const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> {
418    std::string Path = OutputFilename + "." + utostr(Task);
419
420    std::error_code EC;
421    auto S = std::make_unique<raw_fd_ostream>(Path, EC, sys::fs::OF_None);
422    check(EC, Path);
423    return std::make_unique<CachedFileStream>(std::move(S), Path);
424  };
425
426  auto AddBuffer = [&](size_t Task, const Twine &ModuleName,
427                       std::unique_ptr<MemoryBuffer> MB) {
428    *AddStream(Task, ModuleName)->OS << MB->getBuffer();
429  };
430
431  FileCache Cache;
432  if (!CacheDir.empty())
433    Cache = check(localCache("ThinLTO", "Thin", CacheDir, AddBuffer),
434                  "failed to create cache");
435
436  check(Lto.run(AddStream, Cache), "LTO::run failed");
437  return static_cast<int>(HasErrors);
438}
439
440static 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));
446
447    if (BFC.Symtab.size() >= sizeof(irsymtab::storage::Header)) {
448      auto *Hdr = reinterpret_cast<const irsymtab::storage::Header *>(
449          BFC.Symtab.data());
450      outs() << "version: " << Hdr->Version << '\n';
451      if (Hdr->Version == irsymtab::storage::Header::kCurrentVersion)
452        outs() << "producer: " << Hdr->Producer.get(BFC.StrtabForSymtab)
453               << '\n';
454    }
455
456    std::unique_ptr<InputFile> Input =
457        check(InputFile::create(MB->getMemBufferRef()), std::string(F));
458
459    outs() << "target triple: " << Input->getTargetTriple() << '\n';
460    Triple TT(Input->getTargetTriple());
461
462    outs() << "source filename: " << Input->getSourceFileName() << '\n';
463
464    if (TT.isOSBinFormatCOFF())
465      outs() << "linker opts: " << Input->getCOFFLinkerOpts() << '\n';
466
467    if (TT.isOSBinFormatELF()) {
468      outs() << "dependent libraries:";
469      for (auto L : Input->getDependentLibraries())
470        outs() << " \"" << L << "\"";
471      outs() << '\n';
472    }
473
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:
479        outs() << 'H';
480        break;
481      case GlobalValue::ProtectedVisibility:
482        outs() << 'P';
483        break;
484      case GlobalValue::DefaultVisibility:
485        outs() << 'D';
486        break;
487      }
488
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';
498
499      if (Sym.isCommon())
500        outs() << "         size " << Sym.getCommonSize() << " align "
501               << Sym.getCommonAlignment() << '\n';
502
503      int Comdat = Sym.getComdatIndex();
504      if (Comdat != -1) {
505        outs() << "         comdat ";
506        switch (ComdatTable[Comdat].second) {
507        case Comdat::Any:
508          outs() << "any";
509          break;
510        case Comdat::ExactMatch:
511          outs() << "exactmatch";
512          break;
513        case Comdat::Largest:
514          outs() << "largest";
515          break;
516        case Comdat::NoDeduplicate:
517          outs() << "nodeduplicate";
518          break;
519        case Comdat::SameSize:
520          outs() << "samesize";
521          break;
522        }
523        outs() << ' ' << ComdatTable[Comdat].first << '\n';
524      }
525
526      if (TT.isOSBinFormatCOFF() && Sym.isWeak() && Sym.isIndirect())
527        outs() << "         fallback " << Sym.getCOFFWeakExternalFallback() << '\n';
528
529      if (!Sym.getSectionName().empty())
530        outs() << "         section " << Sym.getSectionName() << "\n";
531    }
532
533    outs() << '\n';
534  }
535
536  return 0;
537}
538
539int main(int argc, char **argv) {
540  InitLLVM X(argc, argv);
541  InitializeAllTargets();
542  InitializeAllTargetMCs();
543  InitializeAllAsmPrinters();
544  InitializeAllAsmParsers();
545
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).
549  if (argc < 2)
550    return usage();
551
552  StringRef Subcommand = argv[1];
553  // Ensure that argv[0] is correct after adjusting argv/argc.
554  argv[1] = argv[0];
555  if (Subcommand == "dump-symtab")
556    return dumpSymtab(argc - 1, argv + 1);
557  if (Subcommand == "run")
558    return run(argc - 1, argv + 1);
559  return usage();
560}
561