334 lines · cpp
1//===- bolt/tools/driver/llvm-bolt.cpp - Feedback-directed optimizer ------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This is a binary optimizer that will take 'perf' output and change10// basic block layout for better performance (a.k.a. branch straightening),11// plus some other optimizations that are better performed on a binary.12//13//===----------------------------------------------------------------------===//14 15#include "bolt/Profile/DataAggregator.h"16#include "bolt/Rewrite/MachORewriteInstance.h"17#include "bolt/Rewrite/RewriteInstance.h"18#include "bolt/Utils/CommandLineOpts.h"19#include "llvm/MC/TargetRegistry.h"20#include "llvm/Object/Binary.h"21#include "llvm/Support/CommandLine.h"22#include "llvm/Support/Errc.h"23#include "llvm/Support/Error.h"24#include "llvm/Support/ManagedStatic.h"25#include "llvm/Support/Path.h"26#include "llvm/Support/PrettyStackTrace.h"27#include "llvm/Support/Signals.h"28#include "llvm/Support/TargetSelect.h"29 30#define DEBUG_TYPE "bolt"31 32using namespace llvm;33using namespace object;34using namespace bolt;35 36namespace opts {37 38static cl::OptionCategory *BoltCategories[] = {&BoltCategory,39 &BoltOptCategory,40 &BoltRelocCategory,41 &BoltInstrCategory,42 &BoltOutputCategory};43 44static cl::OptionCategory *BoltDiffCategories[] = {&BoltDiffCategory};45 46static cl::OptionCategory *Perf2BoltCategories[] = {&AggregatorCategory,47 &BoltOutputCategory};48 49static cl::opt<std::string> InputFilename(cl::Positional,50 cl::desc("<executable>"),51 cl::Required, cl::cat(BoltCategory),52 cl::sub(cl::SubCommand::getAll()));53 54static cl::opt<std::string>55InputDataFilename("data",56 cl::desc("<data file>"),57 cl::Optional,58 cl::cat(BoltCategory));59 60static cl::alias61BoltProfile("b",62 cl::desc("alias for -data"),63 cl::aliasopt(InputDataFilename),64 cl::cat(BoltCategory));65 66static cl::opt<std::string>67 LogFile("log-file",68 cl::desc("redirect journaling to a file instead of stdout/stderr"),69 cl::Hidden, cl::cat(BoltCategory));70 71static cl::opt<std::string>72InputDataFilename2("data2",73 cl::desc("<data file>"),74 cl::Optional,75 cl::cat(BoltCategory));76 77static cl::opt<std::string>78InputFilename2(79 cl::Positional,80 cl::desc("<executable>"),81 cl::Optional,82 cl::cat(BoltDiffCategory));83 84} // namespace opts85 86static StringRef ToolName;87 88static void report_error(StringRef Message, std::error_code EC) {89 assert(EC);90 errs() << ToolName << ": '" << Message << "': " << EC.message() << ".\n";91 exit(1);92}93 94static void report_error(StringRef Message, Error E) {95 assert(E);96 errs() << ToolName << ": '" << Message << "': " << toString(std::move(E))97 << ".\n";98 exit(1);99}100 101static void printBoltRevision(llvm::raw_ostream &OS) {102 OS << "BOLT revision " << BoltRevision << "\n";103}104 105void perf2boltMode(int argc, char **argv) {106 cl::HideUnrelatedOptions(ArrayRef(opts::Perf2BoltCategories));107 cl::AddExtraVersionPrinter(printBoltRevision);108 cl::ParseCommandLineOptions(109 argc, argv,110 "perf2bolt - BOLT data aggregator\n"111 "\nEXAMPLE: perf2bolt -p=perf.data executable -o data.fdata\n");112 if (opts::PerfData.empty()) {113 errs() << ToolName << ": expected -perfdata=<filename> option.\n";114 exit(1);115 }116 if (!opts::InputDataFilename.empty()) {117 errs() << ToolName << ": unknown -data option.\n";118 exit(1);119 }120 if (!sys::fs::exists(opts::PerfData))121 report_error(opts::PerfData, errc::no_such_file_or_directory);122 if (!DataAggregator::checkPerfDataMagic(opts::PerfData)) {123 errs() << ToolName << ": '" << opts::PerfData124 << "': expected valid perf.data file.\n";125 exit(1);126 }127 if (opts::OutputFilename.empty()) {128 errs() << ToolName << ": expected -o=<output file> option.\n";129 exit(1);130 }131 opts::AggregateOnly = true;132 opts::ShowDensity = true;133}134 135void boltDiffMode(int argc, char **argv) {136 cl::HideUnrelatedOptions(ArrayRef(opts::BoltDiffCategories));137 cl::AddExtraVersionPrinter(printBoltRevision);138 cl::ParseCommandLineOptions(139 argc, argv,140 "llvm-boltdiff - BOLT binary diff tool\n"141 "\nEXAMPLE: llvm-boltdiff -data=a.fdata -data2=b.fdata exec1 exec2\n");142 if (opts::InputDataFilename2.empty()) {143 errs() << ToolName << ": expected -data2=<filename> option.\n";144 exit(1);145 }146 if (opts::InputDataFilename.empty()) {147 errs() << ToolName << ": expected -data=<filename> option.\n";148 exit(1);149 }150 if (opts::InputFilename2.empty()) {151 errs() << ToolName << ": expected second binary name.\n";152 exit(1);153 }154 if (opts::InputFilename.empty()) {155 errs() << ToolName << ": expected binary.\n";156 exit(1);157 }158 opts::DiffOnly = true;159}160 161void boltMode(int argc, char **argv) {162 cl::HideUnrelatedOptions(ArrayRef(opts::BoltCategories));163 // Register the target printer for --version.164 cl::AddExtraVersionPrinter(printBoltRevision);165 cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);166 167 cl::ParseCommandLineOptions(argc, argv,168 "BOLT - Binary Optimization and Layout Tool\n");169 170 if (opts::OutputFilename.empty()) {171 errs() << ToolName << ": expected -o=<output file> option.\n";172 exit(1);173 }174}175 176int main(int argc, char **argv) {177 // Print a stack trace if we signal out.178 sys::PrintStackTraceOnErrorSignal(argv[0]);179 PrettyStackTraceProgram X(argc, argv);180 181 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.182 183 std::string ToolPath = llvm::sys::fs::getMainExecutable(argv[0], nullptr);184 185 // Initialize targets and assembly printers/parsers.186#define BOLT_TARGET(target) \187 LLVMInitialize##target##TargetInfo(); \188 LLVMInitialize##target##TargetMC(); \189 LLVMInitialize##target##AsmParser(); \190 LLVMInitialize##target##Disassembler(); \191 LLVMInitialize##target##Target(); \192 LLVMInitialize##target##AsmPrinter();193 194#include "bolt/Core/TargetConfig.def"195 196 ToolName = argv[0];197 198 if (llvm::sys::path::filename(ToolName).starts_with("perf2bolt"))199 perf2boltMode(argc, argv);200 else if (llvm::sys::path::filename(ToolName).starts_with("llvm-boltdiff"))201 boltDiffMode(argc, argv);202 else203 boltMode(argc, argv);204 205 if (!sys::fs::exists(opts::InputFilename))206 report_error(opts::InputFilename, errc::no_such_file_or_directory);207 208 // Initialize journaling streams209 raw_ostream *BOLTJournalOut = &outs();210 raw_ostream *BOLTJournalErr = &errs();211 // RAII obj to keep log file open throughout execution212 std::unique_ptr<raw_fd_ostream> LogFileStream;213 if (!opts::LogFile.empty()) {214 std::error_code LogEC;215 LogFileStream = std::make_unique<raw_fd_ostream>(216 opts::LogFile, LogEC, sys::fs::OpenFlags::OF_None);217 if (LogEC) {218 errs() << "BOLT-ERROR: cannot open requested log file for writing: "219 << LogEC.message() << "\n";220 exit(1);221 }222 BOLTJournalOut = LogFileStream.get();223 BOLTJournalErr = LogFileStream.get();224 }225 226 // Attempt to open the binary.227 if (!opts::DiffOnly) {228 Expected<OwningBinary<Binary>> BinaryOrErr =229 createBinary(opts::InputFilename);230 if (Error E = BinaryOrErr.takeError())231 report_error(opts::InputFilename, std::move(E));232 Binary &Binary = *BinaryOrErr.get().getBinary();233 234 if (auto *e = dyn_cast<ELFObjectFileBase>(&Binary)) {235 auto RIOrErr = RewriteInstance::create(e, argc, argv, ToolPath,236 *BOLTJournalOut, *BOLTJournalErr);237 if (Error E = RIOrErr.takeError())238 report_error(opts::InputFilename, std::move(E));239 RewriteInstance &RI = *RIOrErr.get();240 241 if (opts::AggregateOnly && !RI.getBinaryContext().isAArch64() &&242 opts::ArmSPE) {243 errs() << ToolName << ": -spe is available only on AArch64.\n";244 exit(1);245 }246 247 if (!opts::PerfData.empty()) {248 if (!opts::AggregateOnly) {249 errs() << ToolName250 << ": WARNING: reading perf data directly is unsupported, "251 "please use "252 "-aggregate-only or perf2bolt.\n!!! Proceed on your own "253 "risk. !!!\n";254 }255 if (Error E = RI.setProfile(opts::PerfData))256 report_error(opts::PerfData, std::move(E));257 }258 if (!opts::InputDataFilename.empty()) {259 if (Error E = RI.setProfile(opts::InputDataFilename))260 report_error(opts::InputDataFilename, std::move(E));261 }262 if (opts::AggregateOnly && opts::PerfData.empty()) {263 errs() << ToolName << ": missing required -perfdata option.\n";264 exit(1);265 }266 267 if (Error E = RI.run())268 report_error(opts::InputFilename, std::move(E));269 } else if (auto *O = dyn_cast<MachOObjectFile>(&Binary)) {270 auto MachORIOrErr = MachORewriteInstance::create(O, ToolPath);271 if (Error E = MachORIOrErr.takeError())272 report_error(opts::InputFilename, std::move(E));273 MachORewriteInstance &MachORI = *MachORIOrErr.get();274 275 if (!opts::InputDataFilename.empty())276 if (Error E = MachORI.setProfile(opts::InputDataFilename))277 report_error(opts::InputDataFilename, std::move(E));278 279 MachORI.run();280 } else {281 report_error(opts::InputFilename, object_error::invalid_file_type);282 }283 284 return EXIT_SUCCESS;285 }286 287 // Bolt-diff288 Expected<OwningBinary<Binary>> BinaryOrErr1 =289 createBinary(opts::InputFilename);290 Expected<OwningBinary<Binary>> BinaryOrErr2 =291 createBinary(opts::InputFilename2);292 if (Error E = BinaryOrErr1.takeError())293 report_error(opts::InputFilename, std::move(E));294 if (Error E = BinaryOrErr2.takeError())295 report_error(opts::InputFilename2, std::move(E));296 Binary &Binary1 = *BinaryOrErr1.get().getBinary();297 Binary &Binary2 = *BinaryOrErr2.get().getBinary();298 if (auto *ELFObj1 = dyn_cast<ELFObjectFileBase>(&Binary1)) {299 if (auto *ELFObj2 = dyn_cast<ELFObjectFileBase>(&Binary2)) {300 auto RI1OrErr = RewriteInstance::create(ELFObj1, argc, argv, ToolPath);301 if (Error E = RI1OrErr.takeError())302 report_error(opts::InputFilename, std::move(E));303 RewriteInstance &RI1 = *RI1OrErr.get();304 if (Error E = RI1.setProfile(opts::InputDataFilename))305 report_error(opts::InputDataFilename, std::move(E));306 auto RI2OrErr = RewriteInstance::create(ELFObj2, argc, argv, ToolPath);307 if (Error E = RI2OrErr.takeError())308 report_error(opts::InputFilename2, std::move(E));309 RewriteInstance &RI2 = *RI2OrErr.get();310 if (Error E = RI2.setProfile(opts::InputDataFilename2))311 report_error(opts::InputDataFilename2, std::move(E));312 outs() << "BOLT-DIFF: *** Analyzing binary 1: " << opts::InputFilename313 << "\n";314 outs() << "BOLT-DIFF: *** Binary 1 fdata: " << opts::InputDataFilename315 << "\n";316 if (Error E = RI1.run())317 report_error(opts::InputFilename, std::move(E));318 outs() << "BOLT-DIFF: *** Analyzing binary 2: " << opts::InputFilename2319 << "\n";320 outs() << "BOLT-DIFF: *** Binary 2 fdata: "321 << opts::InputDataFilename2 << "\n";322 if (Error E = RI2.run())323 report_error(opts::InputFilename2, std::move(E));324 RI1.compare(RI2);325 } else {326 report_error(opts::InputFilename2, object_error::invalid_file_type);327 }328 } else {329 report_error(opts::InputFilename, object_error::invalid_file_type);330 }331 332 return EXIT_SUCCESS;333}334