12f09f445SMaksim Panchenko //===- bolt/tools/driver/llvm-bolt.cpp - Feedback-directed optimizer ------===// 2a34c753fSRafael Auler // 3a34c753fSRafael Auler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a34c753fSRafael Auler // See https://llvm.org/LICENSE.txt for license information. 5a34c753fSRafael Auler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a34c753fSRafael Auler // 7a34c753fSRafael Auler //===----------------------------------------------------------------------===// 8a34c753fSRafael Auler // 9a34c753fSRafael Auler // This is a binary optimizer that will take 'perf' output and change 10a34c753fSRafael Auler // basic block layout for better performance (a.k.a. branch straightening), 11a34c753fSRafael Auler // plus some other optimizations that are better performed on a binary. 12a34c753fSRafael Auler // 13a34c753fSRafael Auler //===----------------------------------------------------------------------===// 14a34c753fSRafael Auler 15a34c753fSRafael Auler #include "bolt/Profile/DataAggregator.h" 16a34c753fSRafael Auler #include "bolt/Rewrite/MachORewriteInstance.h" 17a34c753fSRafael Auler #include "bolt/Rewrite/RewriteInstance.h" 18a34c753fSRafael Auler #include "bolt/Utils/CommandLineOpts.h" 19a34c753fSRafael Auler #include "llvm/MC/TargetRegistry.h" 20a34c753fSRafael Auler #include "llvm/Object/Binary.h" 21a34c753fSRafael Auler #include "llvm/Support/CommandLine.h" 22290e4823Sserge-sans-paille #include "llvm/Support/Errc.h" 23a34c753fSRafael Auler #include "llvm/Support/Error.h" 24a34c753fSRafael Auler #include "llvm/Support/ManagedStatic.h" 25a34c753fSRafael Auler #include "llvm/Support/Path.h" 26a34c753fSRafael Auler #include "llvm/Support/PrettyStackTrace.h" 27a34c753fSRafael Auler #include "llvm/Support/Signals.h" 28a34c753fSRafael Auler #include "llvm/Support/TargetSelect.h" 29a34c753fSRafael Auler 30a34c753fSRafael Auler #define DEBUG_TYPE "bolt" 31a34c753fSRafael Auler 32a34c753fSRafael Auler using namespace llvm; 33a34c753fSRafael Auler using namespace object; 34a34c753fSRafael Auler using namespace bolt; 35a34c753fSRafael Auler 36a34c753fSRafael Auler namespace opts { 37a34c753fSRafael Auler 38a34c753fSRafael Auler static cl::OptionCategory *BoltCategories[] = {&BoltCategory, 39a34c753fSRafael Auler &BoltOptCategory, 40a34c753fSRafael Auler &BoltRelocCategory, 41a34c753fSRafael Auler &BoltInstrCategory, 42a34c753fSRafael Auler &BoltOutputCategory}; 43a34c753fSRafael Auler 44a34c753fSRafael Auler static cl::OptionCategory *BoltDiffCategories[] = {&BoltDiffCategory}; 45a34c753fSRafael Auler 46a34c753fSRafael Auler static cl::OptionCategory *Perf2BoltCategories[] = {&AggregatorCategory, 47a34c753fSRafael Auler &BoltOutputCategory}; 48a34c753fSRafael Auler 4920e9d4caSVladislav Khmelevsky static cl::opt<std::string> InputFilename(cl::Positional, 5020e9d4caSVladislav Khmelevsky cl::desc("<executable>"), 5120e9d4caSVladislav Khmelevsky cl::Required, cl::cat(BoltCategory), 52f7872cdcSNicolai Hähnle cl::sub(cl::SubCommand::getAll())); 5320e9d4caSVladislav Khmelevsky 54a34c753fSRafael Auler static cl::opt<std::string> 55a34c753fSRafael Auler InputDataFilename("data", 56a34c753fSRafael Auler cl::desc("<data file>"), 57a34c753fSRafael Auler cl::Optional, 58a34c753fSRafael Auler cl::cat(BoltCategory)); 59a34c753fSRafael Auler 60a34c753fSRafael Auler static cl::alias 61a34c753fSRafael Auler BoltProfile("b", 62a34c753fSRafael Auler cl::desc("alias for -data"), 63a34c753fSRafael Auler cl::aliasopt(InputDataFilename), 64a34c753fSRafael Auler cl::cat(BoltCategory)); 65a34c753fSRafael Auler 6652cf0711SAmir Ayupov cl::opt<std::string> 6752cf0711SAmir Ayupov LogFile("log-file", 6852cf0711SAmir Ayupov cl::desc("redirect journaling to a file instead of stdout/stderr"), 6952cf0711SAmir Ayupov cl::Hidden, cl::cat(BoltCategory)); 7052cf0711SAmir Ayupov 71a34c753fSRafael Auler static cl::opt<std::string> 72a34c753fSRafael Auler InputDataFilename2("data2", 73a34c753fSRafael Auler cl::desc("<data file>"), 74a34c753fSRafael Auler cl::Optional, 75a34c753fSRafael Auler cl::cat(BoltCategory)); 76a34c753fSRafael Auler 77a34c753fSRafael Auler static cl::opt<std::string> 78a34c753fSRafael Auler InputFilename2( 79a34c753fSRafael Auler cl::Positional, 80a34c753fSRafael Auler cl::desc("<executable>"), 81a34c753fSRafael Auler cl::Optional, 82a34c753fSRafael Auler cl::cat(BoltDiffCategory)); 83a34c753fSRafael Auler 84a34c753fSRafael Auler } // namespace opts 85a34c753fSRafael Auler 86a34c753fSRafael Auler static StringRef ToolName; 87a34c753fSRafael Auler 88a34c753fSRafael Auler static void report_error(StringRef Message, std::error_code EC) { 89a34c753fSRafael Auler assert(EC); 90a34c753fSRafael Auler errs() << ToolName << ": '" << Message << "': " << EC.message() << ".\n"; 91a34c753fSRafael Auler exit(1); 92a34c753fSRafael Auler } 93a34c753fSRafael Auler 94a34c753fSRafael Auler static void report_error(StringRef Message, Error E) { 95a34c753fSRafael Auler assert(E); 96a34c753fSRafael Auler errs() << ToolName << ": '" << Message << "': " << toString(std::move(E)) 97a34c753fSRafael Auler << ".\n"; 98a34c753fSRafael Auler exit(1); 99a34c753fSRafael Auler } 100a34c753fSRafael Auler 101a34c753fSRafael Auler static void printBoltRevision(llvm::raw_ostream &OS) { 102a34c753fSRafael Auler OS << "BOLT revision " << BoltRevision << "\n"; 103a34c753fSRafael Auler } 104a34c753fSRafael Auler 105a34c753fSRafael Auler void perf2boltMode(int argc, char **argv) { 106984b800aSserge-sans-paille cl::HideUnrelatedOptions(ArrayRef(opts::Perf2BoltCategories)); 107a34c753fSRafael Auler cl::AddExtraVersionPrinter(printBoltRevision); 108a34c753fSRafael Auler cl::ParseCommandLineOptions( 109a34c753fSRafael Auler argc, argv, 110a34c753fSRafael Auler "perf2bolt - BOLT data aggregator\n" 111a34c753fSRafael Auler "\nEXAMPLE: perf2bolt -p=perf.data executable -o data.fdata\n"); 112a34c753fSRafael Auler if (opts::PerfData.empty()) { 113a34c753fSRafael Auler errs() << ToolName << ": expected -perfdata=<filename> option.\n"; 114a34c753fSRafael Auler exit(1); 115a34c753fSRafael Auler } 116a34c753fSRafael Auler if (!opts::InputDataFilename.empty()) { 117a34c753fSRafael Auler errs() << ToolName << ": unknown -data option.\n"; 118a34c753fSRafael Auler exit(1); 119a34c753fSRafael Auler } 120a34c753fSRafael Auler if (!sys::fs::exists(opts::PerfData)) 121a34c753fSRafael Auler report_error(opts::PerfData, errc::no_such_file_or_directory); 122a34c753fSRafael Auler if (!DataAggregator::checkPerfDataMagic(opts::PerfData)) { 123a34c753fSRafael Auler errs() << ToolName << ": '" << opts::PerfData 124a34c753fSRafael Auler << "': expected valid perf.data file.\n"; 125a34c753fSRafael Auler exit(1); 126a34c753fSRafael Auler } 127a34c753fSRafael Auler if (opts::OutputFilename.empty()) { 128a34c753fSRafael Auler errs() << ToolName << ": expected -o=<output file> option.\n"; 129a34c753fSRafael Auler exit(1); 130a34c753fSRafael Auler } 131a34c753fSRafael Auler opts::AggregateOnly = true; 132*6ee5ff95SAmir Ayupov opts::ShowDensity = true; 133a34c753fSRafael Auler } 134a34c753fSRafael Auler 135a34c753fSRafael Auler void boltDiffMode(int argc, char **argv) { 136984b800aSserge-sans-paille cl::HideUnrelatedOptions(ArrayRef(opts::BoltDiffCategories)); 137a34c753fSRafael Auler cl::AddExtraVersionPrinter(printBoltRevision); 138a34c753fSRafael Auler cl::ParseCommandLineOptions( 139a34c753fSRafael Auler argc, argv, 140a34c753fSRafael Auler "llvm-boltdiff - BOLT binary diff tool\n" 141a34c753fSRafael Auler "\nEXAMPLE: llvm-boltdiff -data=a.fdata -data2=b.fdata exec1 exec2\n"); 142a34c753fSRafael Auler if (opts::InputDataFilename2.empty()) { 143a34c753fSRafael Auler errs() << ToolName << ": expected -data2=<filename> option.\n"; 144a34c753fSRafael Auler exit(1); 145a34c753fSRafael Auler } 146a34c753fSRafael Auler if (opts::InputDataFilename.empty()) { 147a34c753fSRafael Auler errs() << ToolName << ": expected -data=<filename> option.\n"; 148a34c753fSRafael Auler exit(1); 149a34c753fSRafael Auler } 150a34c753fSRafael Auler if (opts::InputFilename2.empty()) { 151a34c753fSRafael Auler errs() << ToolName << ": expected second binary name.\n"; 152a34c753fSRafael Auler exit(1); 153a34c753fSRafael Auler } 154a34c753fSRafael Auler if (opts::InputFilename.empty()) { 155a34c753fSRafael Auler errs() << ToolName << ": expected binary.\n"; 156a34c753fSRafael Auler exit(1); 157a34c753fSRafael Auler } 158a34c753fSRafael Auler opts::DiffOnly = true; 159a34c753fSRafael Auler } 160a34c753fSRafael Auler 161a34c753fSRafael Auler void boltMode(int argc, char **argv) { 162984b800aSserge-sans-paille cl::HideUnrelatedOptions(ArrayRef(opts::BoltCategories)); 163a34c753fSRafael Auler // Register the target printer for --version. 164a34c753fSRafael Auler cl::AddExtraVersionPrinter(printBoltRevision); 165a34c753fSRafael Auler cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); 166a34c753fSRafael Auler 167a34c753fSRafael Auler cl::ParseCommandLineOptions(argc, argv, 168a34c753fSRafael Auler "BOLT - Binary Optimization and Layout Tool\n"); 169a34c753fSRafael Auler 170a34c753fSRafael Auler if (opts::OutputFilename.empty()) { 171a34c753fSRafael Auler errs() << ToolName << ": expected -o=<output file> option.\n"; 172a34c753fSRafael Auler exit(1); 173a34c753fSRafael Auler } 174a34c753fSRafael Auler } 175a34c753fSRafael Auler 1765c2ae5f4SVladislav Khmelevsky static std::string GetExecutablePath(const char *Argv0) { 1775c2ae5f4SVladislav Khmelevsky SmallString<256> ExecutablePath(Argv0); 178a34c753fSRafael Auler // Do a PATH lookup if Argv0 isn't a valid path. 179a34c753fSRafael Auler if (!llvm::sys::fs::exists(ExecutablePath)) 180a34c753fSRafael Auler if (llvm::ErrorOr<std::string> P = 181a34c753fSRafael Auler llvm::sys::findProgramByName(ExecutablePath)) 182a34c753fSRafael Auler ExecutablePath = *P; 1836da4a7a8SKazu Hirata return std::string(ExecutablePath); 184a34c753fSRafael Auler } 185a34c753fSRafael Auler 186a34c753fSRafael Auler int main(int argc, char **argv) { 187a34c753fSRafael Auler // Print a stack trace if we signal out. 188a34c753fSRafael Auler sys::PrintStackTraceOnErrorSignal(argv[0]); 189a34c753fSRafael Auler PrettyStackTraceProgram X(argc, argv); 190a34c753fSRafael Auler 191a34c753fSRafael Auler llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 192a34c753fSRafael Auler 193a34c753fSRafael Auler std::string ToolPath = GetExecutablePath(argv[0]); 194a34c753fSRafael Auler 195a34c753fSRafael Auler // Initialize targets and assembly printers/parsers. 196a34c753fSRafael Auler llvm::InitializeAllTargetInfos(); 197a34c753fSRafael Auler llvm::InitializeAllTargetMCs(); 198a34c753fSRafael Auler llvm::InitializeAllAsmParsers(); 199a34c753fSRafael Auler llvm::InitializeAllDisassemblers(); 200a34c753fSRafael Auler 201a34c753fSRafael Auler llvm::InitializeAllTargets(); 202a34c753fSRafael Auler llvm::InitializeAllAsmPrinters(); 203a34c753fSRafael Auler 204a34c753fSRafael Auler ToolName = argv[0]; 205a34c753fSRafael Auler 2063c4f0090SAmir Ayupov if (llvm::sys::path::filename(ToolName).starts_with("perf2bolt")) 207a34c753fSRafael Auler perf2boltMode(argc, argv); 2083c4f0090SAmir Ayupov else if (llvm::sys::path::filename(ToolName).starts_with("llvm-boltdiff")) 209a34c753fSRafael Auler boltDiffMode(argc, argv); 210a34c753fSRafael Auler else 211a34c753fSRafael Auler boltMode(argc, argv); 212a34c753fSRafael Auler 213a34c753fSRafael Auler if (!sys::fs::exists(opts::InputFilename)) 214a34c753fSRafael Auler report_error(opts::InputFilename, errc::no_such_file_or_directory); 215a34c753fSRafael Auler 21652cf0711SAmir Ayupov // Initialize journaling streams 21752cf0711SAmir Ayupov raw_ostream *BOLTJournalOut = &outs(); 21852cf0711SAmir Ayupov raw_ostream *BOLTJournalErr = &errs(); 21952cf0711SAmir Ayupov // RAII obj to keep log file open throughout execution 22052cf0711SAmir Ayupov std::unique_ptr<raw_fd_ostream> LogFileStream; 22152cf0711SAmir Ayupov if (!opts::LogFile.empty()) { 22252cf0711SAmir Ayupov std::error_code LogEC; 22352cf0711SAmir Ayupov LogFileStream = std::make_unique<raw_fd_ostream>( 22452cf0711SAmir Ayupov opts::LogFile, LogEC, sys::fs::OpenFlags::OF_None); 22552cf0711SAmir Ayupov if (LogEC) { 22652cf0711SAmir Ayupov errs() << "BOLT-ERROR: cannot open requested log file for writing: " 22752cf0711SAmir Ayupov << LogEC.message() << "\n"; 22852cf0711SAmir Ayupov exit(1); 22952cf0711SAmir Ayupov } 23052cf0711SAmir Ayupov BOLTJournalOut = LogFileStream.get(); 23152cf0711SAmir Ayupov BOLTJournalErr = LogFileStream.get(); 23252cf0711SAmir Ayupov } 23352cf0711SAmir Ayupov 234a34c753fSRafael Auler // Attempt to open the binary. 235a34c753fSRafael Auler if (!opts::DiffOnly) { 236a34c753fSRafael Auler Expected<OwningBinary<Binary>> BinaryOrErr = 237a34c753fSRafael Auler createBinary(opts::InputFilename); 238a34c753fSRafael Auler if (Error E = BinaryOrErr.takeError()) 239a34c753fSRafael Auler report_error(opts::InputFilename, std::move(E)); 240a34c753fSRafael Auler Binary &Binary = *BinaryOrErr.get().getBinary(); 241a34c753fSRafael Auler 242a34c753fSRafael Auler if (auto *e = dyn_cast<ELFObjectFileBase>(&Binary)) { 24352cf0711SAmir Ayupov auto RIOrErr = RewriteInstance::create(e, argc, argv, ToolPath, 24452cf0711SAmir Ayupov *BOLTJournalOut, *BOLTJournalErr); 24532d2473aSAmir Ayupov if (Error E = RIOrErr.takeError()) 24632d2473aSAmir Ayupov report_error(opts::InputFilename, std::move(E)); 24732d2473aSAmir Ayupov RewriteInstance &RI = *RIOrErr.get(); 248a34c753fSRafael Auler if (!opts::PerfData.empty()) { 249a34c753fSRafael Auler if (!opts::AggregateOnly) { 250a34c753fSRafael Auler errs() << ToolName 25140c2e0faSMaksim Panchenko << ": WARNING: reading perf data directly is unsupported, " 25240c2e0faSMaksim Panchenko "please use " 25340c2e0faSMaksim Panchenko "-aggregate-only or perf2bolt.\n!!! Proceed on your own " 25440c2e0faSMaksim Panchenko "risk. !!!\n"; 255a34c753fSRafael Auler } 256a34c753fSRafael Auler if (Error E = RI.setProfile(opts::PerfData)) 257a34c753fSRafael Auler report_error(opts::PerfData, std::move(E)); 258a34c753fSRafael Auler } 259a34c753fSRafael Auler if (!opts::InputDataFilename.empty()) { 260a34c753fSRafael Auler if (Error E = RI.setProfile(opts::InputDataFilename)) 261a34c753fSRafael Auler report_error(opts::InputDataFilename, std::move(E)); 262a34c753fSRafael Auler } 263a34c753fSRafael Auler if (opts::AggregateOnly && opts::PerfData.empty()) { 264a34c753fSRafael Auler errs() << ToolName << ": missing required -perfdata option.\n"; 265a34c753fSRafael Auler exit(1); 266a34c753fSRafael Auler } 267a34c753fSRafael Auler 268af6e66f4SAmir Ayupov if (Error E = RI.run()) 269af6e66f4SAmir Ayupov report_error(opts::InputFilename, std::move(E)); 270a34c753fSRafael Auler } else if (auto *O = dyn_cast<MachOObjectFile>(&Binary)) { 27116492a61SAmir Ayupov auto MachORIOrErr = MachORewriteInstance::create(O, ToolPath); 27232d2473aSAmir Ayupov if (Error E = MachORIOrErr.takeError()) 27332d2473aSAmir Ayupov report_error(opts::InputFilename, std::move(E)); 27432d2473aSAmir Ayupov MachORewriteInstance &MachORI = *MachORIOrErr.get(); 275a34c753fSRafael Auler 276a34c753fSRafael Auler if (!opts::InputDataFilename.empty()) 277a34c753fSRafael Auler if (Error E = MachORI.setProfile(opts::InputDataFilename)) 278a34c753fSRafael Auler report_error(opts::InputDataFilename, std::move(E)); 279a34c753fSRafael Auler 280a34c753fSRafael Auler MachORI.run(); 281a34c753fSRafael Auler } else { 282a34c753fSRafael Auler report_error(opts::InputFilename, object_error::invalid_file_type); 283a34c753fSRafael Auler } 284a34c753fSRafael Auler 285a34c753fSRafael Auler return EXIT_SUCCESS; 286a34c753fSRafael Auler } 287a34c753fSRafael Auler 288a34c753fSRafael Auler // Bolt-diff 289a34c753fSRafael Auler Expected<OwningBinary<Binary>> BinaryOrErr1 = 290a34c753fSRafael Auler createBinary(opts::InputFilename); 291a34c753fSRafael Auler Expected<OwningBinary<Binary>> BinaryOrErr2 = 292a34c753fSRafael Auler createBinary(opts::InputFilename2); 293a34c753fSRafael Auler if (Error E = BinaryOrErr1.takeError()) 294a34c753fSRafael Auler report_error(opts::InputFilename, std::move(E)); 295a34c753fSRafael Auler if (Error E = BinaryOrErr2.takeError()) 296a34c753fSRafael Auler report_error(opts::InputFilename2, std::move(E)); 297a34c753fSRafael Auler Binary &Binary1 = *BinaryOrErr1.get().getBinary(); 298a34c753fSRafael Auler Binary &Binary2 = *BinaryOrErr2.get().getBinary(); 299a34c753fSRafael Auler if (auto *ELFObj1 = dyn_cast<ELFObjectFileBase>(&Binary1)) { 300a34c753fSRafael Auler if (auto *ELFObj2 = dyn_cast<ELFObjectFileBase>(&Binary2)) { 30116492a61SAmir Ayupov auto RI1OrErr = RewriteInstance::create(ELFObj1, argc, argv, ToolPath); 30232d2473aSAmir Ayupov if (Error E = RI1OrErr.takeError()) 30332d2473aSAmir Ayupov report_error(opts::InputFilename, std::move(E)); 30432d2473aSAmir Ayupov RewriteInstance &RI1 = *RI1OrErr.get(); 305a34c753fSRafael Auler if (Error E = RI1.setProfile(opts::InputDataFilename)) 306a34c753fSRafael Auler report_error(opts::InputDataFilename, std::move(E)); 30716492a61SAmir Ayupov auto RI2OrErr = RewriteInstance::create(ELFObj2, argc, argv, ToolPath); 30832d2473aSAmir Ayupov if (Error E = RI2OrErr.takeError()) 30932d2473aSAmir Ayupov report_error(opts::InputFilename2, std::move(E)); 31032d2473aSAmir Ayupov RewriteInstance &RI2 = *RI2OrErr.get(); 311a34c753fSRafael Auler if (Error E = RI2.setProfile(opts::InputDataFilename2)) 312a34c753fSRafael Auler report_error(opts::InputDataFilename2, std::move(E)); 313a34c753fSRafael Auler outs() << "BOLT-DIFF: *** Analyzing binary 1: " << opts::InputFilename 314a34c753fSRafael Auler << "\n"; 315a34c753fSRafael Auler outs() << "BOLT-DIFF: *** Binary 1 fdata: " << opts::InputDataFilename 316a34c753fSRafael Auler << "\n"; 317af6e66f4SAmir Ayupov if (Error E = RI1.run()) 318af6e66f4SAmir Ayupov report_error(opts::InputFilename, std::move(E)); 319a34c753fSRafael Auler outs() << "BOLT-DIFF: *** Analyzing binary 2: " << opts::InputFilename2 320a34c753fSRafael Auler << "\n"; 321a34c753fSRafael Auler outs() << "BOLT-DIFF: *** Binary 2 fdata: " 322a34c753fSRafael Auler << opts::InputDataFilename2 << "\n"; 323af6e66f4SAmir Ayupov if (Error E = RI2.run()) 324af6e66f4SAmir Ayupov report_error(opts::InputFilename2, std::move(E)); 325a34c753fSRafael Auler RI1.compare(RI2); 326a34c753fSRafael Auler } else { 327a34c753fSRafael Auler report_error(opts::InputFilename2, object_error::invalid_file_type); 328a34c753fSRafael Auler } 329a34c753fSRafael Auler } else { 330a34c753fSRafael Auler report_error(opts::InputFilename, object_error::invalid_file_type); 331a34c753fSRafael Auler } 332a34c753fSRafael Auler 333a34c753fSRafael Auler return EXIT_SUCCESS; 334a34c753fSRafael Auler } 335