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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This is a binary optimizer that will take 'perf' output and change
10 // 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
32 using namespace llvm;
33 using namespace object;
34 using namespace bolt;
35
36 namespace opts {
37
38 static cl::OptionCategory *BoltCategories[] = {&BoltCategory,
39 &BoltOptCategory,
40 &BoltRelocCategory,
41 &BoltInstrCategory,
42 &BoltOutputCategory};
43
44 static cl::OptionCategory *BoltDiffCategories[] = {&BoltDiffCategory};
45
46 static cl::OptionCategory *Perf2BoltCategories[] = {&AggregatorCategory,
47 &BoltOutputCategory};
48
49 static cl::opt<std::string> InputFilename(cl::Positional,
50 cl::desc("<executable>"),
51 cl::Required, cl::cat(BoltCategory),
52 cl::sub(*cl::AllSubCommands));
53
54 static cl::opt<std::string>
55 InputDataFilename("data",
56 cl::desc("<data file>"),
57 cl::Optional,
58 cl::cat(BoltCategory));
59
60 static cl::alias
61 BoltProfile("b",
62 cl::desc("alias for -data"),
63 cl::aliasopt(InputDataFilename),
64 cl::cat(BoltCategory));
65
66 static cl::opt<std::string>
67 InputDataFilename2("data2",
68 cl::desc("<data file>"),
69 cl::Optional,
70 cl::cat(BoltCategory));
71
72 static cl::opt<std::string>
73 InputFilename2(
74 cl::Positional,
75 cl::desc("<executable>"),
76 cl::Optional,
77 cl::cat(BoltDiffCategory));
78
79 } // namespace opts
80
81 static StringRef ToolName;
82
report_error(StringRef Message,std::error_code EC)83 static void report_error(StringRef Message, std::error_code EC) {
84 assert(EC);
85 errs() << ToolName << ": '" << Message << "': " << EC.message() << ".\n";
86 exit(1);
87 }
88
report_error(StringRef Message,Error E)89 static void report_error(StringRef Message, Error E) {
90 assert(E);
91 errs() << ToolName << ": '" << Message << "': " << toString(std::move(E))
92 << ".\n";
93 exit(1);
94 }
95
printBoltRevision(llvm::raw_ostream & OS)96 static void printBoltRevision(llvm::raw_ostream &OS) {
97 OS << "BOLT revision " << BoltRevision << "\n";
98 }
99
perf2boltMode(int argc,char ** argv)100 void perf2boltMode(int argc, char **argv) {
101 cl::HideUnrelatedOptions(makeArrayRef(opts::Perf2BoltCategories));
102 cl::AddExtraVersionPrinter(printBoltRevision);
103 cl::ParseCommandLineOptions(
104 argc, argv,
105 "perf2bolt - BOLT data aggregator\n"
106 "\nEXAMPLE: perf2bolt -p=perf.data executable -o data.fdata\n");
107 if (opts::PerfData.empty()) {
108 errs() << ToolName << ": expected -perfdata=<filename> option.\n";
109 exit(1);
110 }
111 if (!opts::InputDataFilename.empty()) {
112 errs() << ToolName << ": unknown -data option.\n";
113 exit(1);
114 }
115 if (!sys::fs::exists(opts::PerfData))
116 report_error(opts::PerfData, errc::no_such_file_or_directory);
117 if (!DataAggregator::checkPerfDataMagic(opts::PerfData)) {
118 errs() << ToolName << ": '" << opts::PerfData
119 << "': expected valid perf.data file.\n";
120 exit(1);
121 }
122 if (opts::OutputFilename.empty()) {
123 errs() << ToolName << ": expected -o=<output file> option.\n";
124 exit(1);
125 }
126 opts::AggregateOnly = true;
127 }
128
boltDiffMode(int argc,char ** argv)129 void boltDiffMode(int argc, char **argv) {
130 cl::HideUnrelatedOptions(makeArrayRef(opts::BoltDiffCategories));
131 cl::AddExtraVersionPrinter(printBoltRevision);
132 cl::ParseCommandLineOptions(
133 argc, argv,
134 "llvm-boltdiff - BOLT binary diff tool\n"
135 "\nEXAMPLE: llvm-boltdiff -data=a.fdata -data2=b.fdata exec1 exec2\n");
136 if (opts::InputDataFilename2.empty()) {
137 errs() << ToolName << ": expected -data2=<filename> option.\n";
138 exit(1);
139 }
140 if (opts::InputDataFilename.empty()) {
141 errs() << ToolName << ": expected -data=<filename> option.\n";
142 exit(1);
143 }
144 if (opts::InputFilename2.empty()) {
145 errs() << ToolName << ": expected second binary name.\n";
146 exit(1);
147 }
148 if (opts::InputFilename.empty()) {
149 errs() << ToolName << ": expected binary.\n";
150 exit(1);
151 }
152 opts::DiffOnly = true;
153 }
154
boltMode(int argc,char ** argv)155 void boltMode(int argc, char **argv) {
156 cl::HideUnrelatedOptions(makeArrayRef(opts::BoltCategories));
157 // Register the target printer for --version.
158 cl::AddExtraVersionPrinter(printBoltRevision);
159 cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
160
161 cl::ParseCommandLineOptions(argc, argv,
162 "BOLT - Binary Optimization and Layout Tool\n");
163
164 if (opts::OutputFilename.empty()) {
165 errs() << ToolName << ": expected -o=<output file> option.\n";
166 exit(1);
167 }
168 }
169
GetExecutablePath(const char * Argv0)170 static std::string GetExecutablePath(const char *Argv0) {
171 SmallString<256> ExecutablePath(Argv0);
172 // Do a PATH lookup if Argv0 isn't a valid path.
173 if (!llvm::sys::fs::exists(ExecutablePath))
174 if (llvm::ErrorOr<std::string> P =
175 llvm::sys::findProgramByName(ExecutablePath))
176 ExecutablePath = *P;
177 return std::string(ExecutablePath.str());
178 }
179
main(int argc,char ** argv)180 int main(int argc, char **argv) {
181 // Print a stack trace if we signal out.
182 sys::PrintStackTraceOnErrorSignal(argv[0]);
183 PrettyStackTraceProgram X(argc, argv);
184
185 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
186
187 std::string ToolPath = GetExecutablePath(argv[0]);
188
189 // Initialize targets and assembly printers/parsers.
190 llvm::InitializeAllTargetInfos();
191 llvm::InitializeAllTargetMCs();
192 llvm::InitializeAllAsmParsers();
193 llvm::InitializeAllDisassemblers();
194
195 llvm::InitializeAllTargets();
196 llvm::InitializeAllAsmPrinters();
197
198 ToolName = argv[0];
199
200 if (llvm::sys::path::filename(ToolName) == "perf2bolt")
201 perf2boltMode(argc, argv);
202 else if (llvm::sys::path::filename(ToolName) == "llvm-boltdiff")
203 boltDiffMode(argc, argv);
204 else
205 boltMode(argc, argv);
206
207 if (!sys::fs::exists(opts::InputFilename))
208 report_error(opts::InputFilename, errc::no_such_file_or_directory);
209
210 // Attempt to open the binary.
211 if (!opts::DiffOnly) {
212 Expected<OwningBinary<Binary>> BinaryOrErr =
213 createBinary(opts::InputFilename);
214 if (Error E = BinaryOrErr.takeError())
215 report_error(opts::InputFilename, std::move(E));
216 Binary &Binary = *BinaryOrErr.get().getBinary();
217
218 if (auto *e = dyn_cast<ELFObjectFileBase>(&Binary)) {
219 auto RIOrErr =
220 RewriteInstance::createRewriteInstance(e, argc, argv, ToolPath);
221 if (Error E = RIOrErr.takeError())
222 report_error(opts::InputFilename, std::move(E));
223 RewriteInstance &RI = *RIOrErr.get();
224 if (!opts::PerfData.empty()) {
225 if (!opts::AggregateOnly) {
226 errs() << ToolName
227 << ": WARNING: reading perf data directly is unsupported, "
228 "please use "
229 "-aggregate-only or perf2bolt.\n!!! Proceed on your own "
230 "risk. !!!\n";
231 }
232 if (Error E = RI.setProfile(opts::PerfData))
233 report_error(opts::PerfData, std::move(E));
234 }
235 if (!opts::InputDataFilename.empty()) {
236 if (Error E = RI.setProfile(opts::InputDataFilename))
237 report_error(opts::InputDataFilename, std::move(E));
238 }
239 if (opts::AggregateOnly && opts::PerfData.empty()) {
240 errs() << ToolName << ": missing required -perfdata option.\n";
241 exit(1);
242 }
243
244 if (Error E = RI.run())
245 report_error(opts::InputFilename, std::move(E));
246 } else if (auto *O = dyn_cast<MachOObjectFile>(&Binary)) {
247 auto MachORIOrErr =
248 MachORewriteInstance::createMachORewriteInstance(O, ToolPath);
249 if (Error E = MachORIOrErr.takeError())
250 report_error(opts::InputFilename, std::move(E));
251 MachORewriteInstance &MachORI = *MachORIOrErr.get();
252
253 if (!opts::InputDataFilename.empty())
254 if (Error E = MachORI.setProfile(opts::InputDataFilename))
255 report_error(opts::InputDataFilename, std::move(E));
256
257 MachORI.run();
258 } else {
259 report_error(opts::InputFilename, object_error::invalid_file_type);
260 }
261
262 return EXIT_SUCCESS;
263 }
264
265 // Bolt-diff
266 Expected<OwningBinary<Binary>> BinaryOrErr1 =
267 createBinary(opts::InputFilename);
268 Expected<OwningBinary<Binary>> BinaryOrErr2 =
269 createBinary(opts::InputFilename2);
270 if (Error E = BinaryOrErr1.takeError())
271 report_error(opts::InputFilename, std::move(E));
272 if (Error E = BinaryOrErr2.takeError())
273 report_error(opts::InputFilename2, std::move(E));
274 Binary &Binary1 = *BinaryOrErr1.get().getBinary();
275 Binary &Binary2 = *BinaryOrErr2.get().getBinary();
276 if (auto *ELFObj1 = dyn_cast<ELFObjectFileBase>(&Binary1)) {
277 if (auto *ELFObj2 = dyn_cast<ELFObjectFileBase>(&Binary2)) {
278 auto RI1OrErr =
279 RewriteInstance::createRewriteInstance(ELFObj1, argc, argv, ToolPath);
280 if (Error E = RI1OrErr.takeError())
281 report_error(opts::InputFilename, std::move(E));
282 RewriteInstance &RI1 = *RI1OrErr.get();
283 if (Error E = RI1.setProfile(opts::InputDataFilename))
284 report_error(opts::InputDataFilename, std::move(E));
285 auto RI2OrErr =
286 RewriteInstance::createRewriteInstance(ELFObj2, argc, argv, ToolPath);
287 if (Error E = RI2OrErr.takeError())
288 report_error(opts::InputFilename2, std::move(E));
289 RewriteInstance &RI2 = *RI2OrErr.get();
290 if (Error E = RI2.setProfile(opts::InputDataFilename2))
291 report_error(opts::InputDataFilename2, std::move(E));
292 outs() << "BOLT-DIFF: *** Analyzing binary 1: " << opts::InputFilename
293 << "\n";
294 outs() << "BOLT-DIFF: *** Binary 1 fdata: " << opts::InputDataFilename
295 << "\n";
296 if (Error E = RI1.run())
297 report_error(opts::InputFilename, std::move(E));
298 outs() << "BOLT-DIFF: *** Analyzing binary 2: " << opts::InputFilename2
299 << "\n";
300 outs() << "BOLT-DIFF: *** Binary 2 fdata: "
301 << opts::InputDataFilename2 << "\n";
302 if (Error E = RI2.run())
303 report_error(opts::InputFilename2, std::move(E));
304 RI1.compare(RI2);
305 } else {
306 report_error(opts::InputFilename2, object_error::invalid_file_type);
307 }
308 } else {
309 report_error(opts::InputFilename, object_error::invalid_file_type);
310 }
311
312 return EXIT_SUCCESS;
313 }
314