xref: /llvm-project/bolt/tools/driver/llvm-bolt.cpp (revision 6ee5ff95abe6ba6dfc4ca3a07a27b796fbf3664e)
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