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
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::alias
61BoltProfile("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 opts
85
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(status: 1);
92}
93
94static void report_error(StringRef Message, Error E) {
95 assert(E);
96 errs() << ToolName << ": '" << Message << "': " << toString(E: std::move(E))
97 << ".\n";
98 exit(status: 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(Categories: ArrayRef(opts::Perf2BoltCategories));
107 cl::AddExtraVersionPrinter(func: printBoltRevision);
108 cl::ParseCommandLineOptions(
109 argc, argv,
110 Overview: "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(status: 1);
115 }
116 if (!opts::InputDataFilename.empty()) {
117 errs() << ToolName << ": unknown -data option.\n";
118 exit(status: 1);
119 }
120 if (!sys::fs::exists(Path: opts::PerfData))
121 report_error(Message: opts::PerfData, EC: errc::no_such_file_or_directory);
122 if (!DataAggregator::checkPerfDataMagic(FileName: opts::PerfData)) {
123 errs() << ToolName << ": '" << opts::PerfData
124 << "': expected valid perf.data file.\n";
125 exit(status: 1);
126 }
127 if (opts::OutputFilename.empty()) {
128 errs() << ToolName << ": expected -o=<output file> option.\n";
129 exit(status: 1);
130 }
131 opts::AggregateOnly = true;
132 opts::ShowDensity = true;
133}
134
135void boltDiffMode(int argc, char **argv) {
136 cl::HideUnrelatedOptions(Categories: ArrayRef(opts::BoltDiffCategories));
137 cl::AddExtraVersionPrinter(func: printBoltRevision);
138 cl::ParseCommandLineOptions(
139 argc, argv,
140 Overview: "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(status: 1);
145 }
146 if (opts::InputDataFilename.empty()) {
147 errs() << ToolName << ": expected -data=<filename> option.\n";
148 exit(status: 1);
149 }
150 if (opts::InputFilename2.empty()) {
151 errs() << ToolName << ": expected second binary name.\n";
152 exit(status: 1);
153 }
154 if (opts::InputFilename.empty()) {
155 errs() << ToolName << ": expected binary.\n";
156 exit(status: 1);
157 }
158 opts::DiffOnly = true;
159}
160
161void boltMode(int argc, char **argv) {
162 cl::HideUnrelatedOptions(Categories: ArrayRef(opts::BoltCategories));
163 // Register the target printer for --version.
164 cl::AddExtraVersionPrinter(func: printBoltRevision);
165 cl::AddExtraVersionPrinter(func: TargetRegistry::printRegisteredTargetsForVersion);
166
167 cl::ParseCommandLineOptions(argc, argv,
168 Overview: "BOLT - Binary Optimization and Layout Tool\n");
169
170 if (opts::OutputFilename.empty()) {
171 errs() << ToolName << ": expected -o=<output file> option.\n";
172 exit(status: 1);
173 }
174}
175
176int main(int argc, char **argv) {
177 // Print a stack trace if we signal out.
178 sys::PrintStackTraceOnErrorSignal(Argv0: 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(argv0: argv[0], MainExecAddr: 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(path: ToolName).starts_with(Prefix: "perf2bolt"))
199 perf2boltMode(argc, argv);
200 else if (llvm::sys::path::filename(path: ToolName).starts_with(Prefix: "llvm-boltdiff"))
201 boltDiffMode(argc, argv);
202 else
203 boltMode(argc, argv);
204
205 if (!sys::fs::exists(Path: opts::InputFilename))
206 report_error(Message: opts::InputFilename, EC: errc::no_such_file_or_directory);
207
208 // Initialize journaling streams
209 raw_ostream *BOLTJournalOut = &outs();
210 raw_ostream *BOLTJournalErr = &errs();
211 // RAII obj to keep log file open throughout execution
212 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 args&: opts::LogFile, args&: LogEC, args: 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(status: 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(Path: opts::InputFilename);
230 if (Error E = BinaryOrErr.takeError())
231 report_error(Message: opts::InputFilename, E: std::move(E));
232 Binary &Binary = *BinaryOrErr.get().getBinary();
233
234 if (auto *e = dyn_cast<ELFObjectFileBase>(Val: &Binary)) {
235 auto RIOrErr = RewriteInstance::create(File: e, Argc: argc, Argv: argv, ToolPath,
236 Stdout&: *BOLTJournalOut, Stderr&: *BOLTJournalErr);
237 if (Error E = RIOrErr.takeError())
238 report_error(Message: opts::InputFilename, E: std::move(E));
239 RewriteInstance &RI = *RIOrErr.get();
240 if (!opts::PerfData.empty()) {
241 if (!opts::AggregateOnly) {
242 errs() << ToolName
243 << ": WARNING: reading perf data directly is unsupported, "
244 "please use "
245 "-aggregate-only or perf2bolt.\n!!! Proceed on your own "
246 "risk. !!!\n";
247 }
248 if (Error E = RI.setProfile(opts::PerfData))
249 report_error(Message: opts::PerfData, E: std::move(E));
250 }
251 if (!opts::InputDataFilename.empty()) {
252 if (Error E = RI.setProfile(opts::InputDataFilename))
253 report_error(Message: opts::InputDataFilename, E: std::move(E));
254 }
255 if (opts::AggregateOnly && opts::PerfData.empty()) {
256 errs() << ToolName << ": missing required -perfdata option.\n";
257 exit(status: 1);
258 }
259
260 if (Error E = RI.run())
261 report_error(Message: opts::InputFilename, E: std::move(E));
262 } else if (auto *O = dyn_cast<MachOObjectFile>(Val: &Binary)) {
263 auto MachORIOrErr = MachORewriteInstance::create(InputFile: O, ToolPath);
264 if (Error E = MachORIOrErr.takeError())
265 report_error(Message: opts::InputFilename, E: std::move(E));
266 MachORewriteInstance &MachORI = *MachORIOrErr.get();
267
268 if (!opts::InputDataFilename.empty())
269 if (Error E = MachORI.setProfile(opts::InputDataFilename))
270 report_error(Message: opts::InputDataFilename, E: std::move(E));
271
272 MachORI.run();
273 } else {
274 report_error(Message: opts::InputFilename, EC: object_error::invalid_file_type);
275 }
276
277 return EXIT_SUCCESS;
278 }
279
280 // Bolt-diff
281 Expected<OwningBinary<Binary>> BinaryOrErr1 =
282 createBinary(Path: opts::InputFilename);
283 Expected<OwningBinary<Binary>> BinaryOrErr2 =
284 createBinary(Path: opts::InputFilename2);
285 if (Error E = BinaryOrErr1.takeError())
286 report_error(Message: opts::InputFilename, E: std::move(E));
287 if (Error E = BinaryOrErr2.takeError())
288 report_error(Message: opts::InputFilename2, E: std::move(E));
289 Binary &Binary1 = *BinaryOrErr1.get().getBinary();
290 Binary &Binary2 = *BinaryOrErr2.get().getBinary();
291 if (auto *ELFObj1 = dyn_cast<ELFObjectFileBase>(Val: &Binary1)) {
292 if (auto *ELFObj2 = dyn_cast<ELFObjectFileBase>(Val: &Binary2)) {
293 auto RI1OrErr = RewriteInstance::create(File: ELFObj1, Argc: argc, Argv: argv, ToolPath);
294 if (Error E = RI1OrErr.takeError())
295 report_error(Message: opts::InputFilename, E: std::move(E));
296 RewriteInstance &RI1 = *RI1OrErr.get();
297 if (Error E = RI1.setProfile(opts::InputDataFilename))
298 report_error(Message: opts::InputDataFilename, E: std::move(E));
299 auto RI2OrErr = RewriteInstance::create(File: ELFObj2, Argc: argc, Argv: argv, ToolPath);
300 if (Error E = RI2OrErr.takeError())
301 report_error(Message: opts::InputFilename2, E: std::move(E));
302 RewriteInstance &RI2 = *RI2OrErr.get();
303 if (Error E = RI2.setProfile(opts::InputDataFilename2))
304 report_error(Message: opts::InputDataFilename2, E: std::move(E));
305 outs() << "BOLT-DIFF: *** Analyzing binary 1: " << opts::InputFilename
306 << "\n";
307 outs() << "BOLT-DIFF: *** Binary 1 fdata: " << opts::InputDataFilename
308 << "\n";
309 if (Error E = RI1.run())
310 report_error(Message: opts::InputFilename, E: std::move(E));
311 outs() << "BOLT-DIFF: *** Analyzing binary 2: " << opts::InputFilename2
312 << "\n";
313 outs() << "BOLT-DIFF: *** Binary 2 fdata: "
314 << opts::InputDataFilename2 << "\n";
315 if (Error E = RI2.run())
316 report_error(Message: opts::InputFilename2, E: std::move(E));
317 RI1.compare(RI2);
318 } else {
319 report_error(Message: opts::InputFilename2, EC: object_error::invalid_file_type);
320 }
321 } else {
322 report_error(Message: opts::InputFilename, EC: object_error::invalid_file_type);
323 }
324
325 return EXIT_SUCCESS;
326}
327

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of bolt/tools/driver/llvm-bolt.cpp