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