1 //===- bolt/tools/driver/llvm-bolt.cpp - Feedback-directed optimizer ------===//
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
7 //===----------------------------------------------------------------------===//
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.
13 //===----------------------------------------------------------------------===//
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"
30 #define DEBUG_TYPE "bolt"
33 using namespace object
;
38 static cl::OptionCategory
*BoltCategories
[] = {&BoltCategory
,
44 static cl::OptionCategory
*BoltDiffCategories
[] = {&BoltDiffCategory
};
46 static cl::OptionCategory
*Perf2BoltCategories
[] = {&AggregatorCategory
,
49 static cl::opt
<std::string
> InputFilename(cl::Positional
,
50 cl::desc("<executable>"),
51 cl::Required
, cl::cat(BoltCategory
),
52 cl::sub(cl::SubCommand::getAll()));
54 static cl::opt
<std::string
>
55 InputDataFilename("data",
56 cl::desc("<data file>"),
58 cl::cat(BoltCategory
));
62 cl::desc("alias for -data"),
63 cl::aliasopt(InputDataFilename
),
64 cl::cat(BoltCategory
));
68 cl::desc("redirect journaling to a file instead of stdout/stderr"),
69 cl::Hidden
, cl::cat(BoltCategory
));
71 static cl::opt
<std::string
>
72 InputDataFilename2("data2",
73 cl::desc("<data file>"),
75 cl::cat(BoltCategory
));
77 static cl::opt
<std::string
>
80 cl::desc("<executable>"),
82 cl::cat(BoltDiffCategory
));
86 static StringRef ToolName
;
88 static void report_error(StringRef Message
, std::error_code EC
) {
90 errs() << ToolName
<< ": '" << Message
<< "': " << EC
.message() << ".\n";
94 static void report_error(StringRef Message
, Error E
) {
96 errs() << ToolName
<< ": '" << Message
<< "': " << toString(std::move(E
))
101 static void printBoltRevision(llvm::raw_ostream
&OS
) {
102 OS
<< "BOLT revision " << BoltRevision
<< "\n";
105 void perf2boltMode(int argc
, char **argv
) {
106 cl::HideUnrelatedOptions(ArrayRef(opts::Perf2BoltCategories
));
107 cl::AddExtraVersionPrinter(printBoltRevision
);
108 cl::ParseCommandLineOptions(
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";
116 if (!opts::InputDataFilename
.empty()) {
117 errs() << ToolName
<< ": unknown -data option.\n";
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::PerfData
124 << "': expected valid perf.data file.\n";
127 if (opts::OutputFilename
.empty()) {
128 errs() << ToolName
<< ": expected -o=<output file> option.\n";
131 opts::AggregateOnly
= true;
134 void boltDiffMode(int argc
, char **argv
) {
135 cl::HideUnrelatedOptions(ArrayRef(opts::BoltDiffCategories
));
136 cl::AddExtraVersionPrinter(printBoltRevision
);
137 cl::ParseCommandLineOptions(
139 "llvm-boltdiff - BOLT binary diff tool\n"
140 "\nEXAMPLE: llvm-boltdiff -data=a.fdata -data2=b.fdata exec1 exec2\n");
141 if (opts::InputDataFilename2
.empty()) {
142 errs() << ToolName
<< ": expected -data2=<filename> option.\n";
145 if (opts::InputDataFilename
.empty()) {
146 errs() << ToolName
<< ": expected -data=<filename> option.\n";
149 if (opts::InputFilename2
.empty()) {
150 errs() << ToolName
<< ": expected second binary name.\n";
153 if (opts::InputFilename
.empty()) {
154 errs() << ToolName
<< ": expected binary.\n";
157 opts::DiffOnly
= true;
160 void boltMode(int argc
, char **argv
) {
161 cl::HideUnrelatedOptions(ArrayRef(opts::BoltCategories
));
162 // Register the target printer for --version.
163 cl::AddExtraVersionPrinter(printBoltRevision
);
164 cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion
);
166 cl::ParseCommandLineOptions(argc
, argv
,
167 "BOLT - Binary Optimization and Layout Tool\n");
169 if (opts::OutputFilename
.empty()) {
170 errs() << ToolName
<< ": expected -o=<output file> option.\n";
175 static std::string
GetExecutablePath(const char *Argv0
) {
176 SmallString
<256> ExecutablePath(Argv0
);
177 // Do a PATH lookup if Argv0 isn't a valid path.
178 if (!llvm::sys::fs::exists(ExecutablePath
))
179 if (llvm::ErrorOr
<std::string
> P
=
180 llvm::sys::findProgramByName(ExecutablePath
))
182 return std::string(ExecutablePath
);
185 int main(int argc
, char **argv
) {
186 // Print a stack trace if we signal out.
187 sys::PrintStackTraceOnErrorSignal(argv
[0]);
188 PrettyStackTraceProgram
X(argc
, argv
);
190 llvm_shutdown_obj Y
; // Call llvm_shutdown() on exit.
192 std::string ToolPath
= GetExecutablePath(argv
[0]);
194 // Initialize targets and assembly printers/parsers.
195 llvm::InitializeAllTargetInfos();
196 llvm::InitializeAllTargetMCs();
197 llvm::InitializeAllAsmParsers();
198 llvm::InitializeAllDisassemblers();
200 llvm::InitializeAllTargets();
201 llvm::InitializeAllAsmPrinters();
205 if (llvm::sys::path::filename(ToolName
) == "perf2bolt")
206 perf2boltMode(argc
, argv
);
207 else if (llvm::sys::path::filename(ToolName
) == "llvm-boltdiff")
208 boltDiffMode(argc
, argv
);
210 boltMode(argc
, argv
);
212 if (!sys::fs::exists(opts::InputFilename
))
213 report_error(opts::InputFilename
, errc::no_such_file_or_directory
);
215 // Initialize journaling streams
216 raw_ostream
*BOLTJournalOut
= &outs();
217 raw_ostream
*BOLTJournalErr
= &errs();
218 // RAII obj to keep log file open throughout execution
219 std::unique_ptr
<raw_fd_ostream
> LogFileStream
;
220 if (!opts::LogFile
.empty()) {
221 std::error_code LogEC
;
222 LogFileStream
= std::make_unique
<raw_fd_ostream
>(
223 opts::LogFile
, LogEC
, sys::fs::OpenFlags::OF_None
);
225 errs() << "BOLT-ERROR: cannot open requested log file for writing: "
226 << LogEC
.message() << "\n";
229 BOLTJournalOut
= LogFileStream
.get();
230 BOLTJournalErr
= LogFileStream
.get();
233 // Attempt to open the binary.
234 if (!opts::DiffOnly
) {
235 Expected
<OwningBinary
<Binary
>> BinaryOrErr
=
236 createBinary(opts::InputFilename
);
237 if (Error E
= BinaryOrErr
.takeError())
238 report_error(opts::InputFilename
, std::move(E
));
239 Binary
&Binary
= *BinaryOrErr
.get().getBinary();
241 if (auto *e
= dyn_cast
<ELFObjectFileBase
>(&Binary
)) {
242 auto RIOrErr
= RewriteInstance::create(e
, argc
, argv
, ToolPath
,
243 *BOLTJournalOut
, *BOLTJournalErr
);
244 if (Error E
= RIOrErr
.takeError())
245 report_error(opts::InputFilename
, std::move(E
));
246 RewriteInstance
&RI
= *RIOrErr
.get();
247 if (!opts::PerfData
.empty()) {
248 if (!opts::AggregateOnly
) {
250 << ": WARNING: reading perf data directly is unsupported, "
252 "-aggregate-only or perf2bolt.\n!!! Proceed on your own "
255 if (Error E
= RI
.setProfile(opts::PerfData
))
256 report_error(opts::PerfData
, std::move(E
));
258 if (!opts::InputDataFilename
.empty()) {
259 if (Error E
= RI
.setProfile(opts::InputDataFilename
))
260 report_error(opts::InputDataFilename
, std::move(E
));
262 if (opts::AggregateOnly
&& opts::PerfData
.empty()) {
263 errs() << ToolName
<< ": missing required -perfdata option.\n";
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();
275 if (!opts::InputDataFilename
.empty())
276 if (Error E
= MachORI
.setProfile(opts::InputDataFilename
))
277 report_error(opts::InputDataFilename
, std::move(E
));
281 report_error(opts::InputFilename
, object_error::invalid_file_type
);
288 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::InputFilename
314 outs() << "BOLT-DIFF: *** Binary 1 fdata: " << opts::InputDataFilename
316 if (Error E
= RI1
.run())
317 report_error(opts::InputFilename
, std::move(E
));
318 outs() << "BOLT-DIFF: *** Analyzing binary 2: " << opts::InputFilename2
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
));
326 report_error(opts::InputFilename2
, object_error::invalid_file_type
);
329 report_error(opts::InputFilename
, object_error::invalid_file_type
);