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
));
66 static cl::opt
<std::string
>
67 InputDataFilename2("data2",
68 cl::desc("<data file>"),
70 cl::cat(BoltCategory
));
72 static cl::opt
<std::string
>
75 cl::desc("<executable>"),
77 cl::cat(BoltDiffCategory
));
81 static StringRef ToolName
;
83 static void report_error(StringRef Message
, std::error_code EC
) {
85 errs() << ToolName
<< ": '" << Message
<< "': " << EC
.message() << ".\n";
89 static void report_error(StringRef Message
, Error E
) {
91 errs() << ToolName
<< ": '" << Message
<< "': " << toString(std::move(E
))
96 static void printBoltRevision(llvm::raw_ostream
&OS
) {
97 OS
<< "BOLT revision " << BoltRevision
<< "\n";
100 void perf2boltMode(int argc
, char **argv
) {
101 cl::HideUnrelatedOptions(ArrayRef(opts::Perf2BoltCategories
));
102 cl::AddExtraVersionPrinter(printBoltRevision
);
103 cl::ParseCommandLineOptions(
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";
111 if (!opts::InputDataFilename
.empty()) {
112 errs() << ToolName
<< ": unknown -data option.\n";
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";
122 if (opts::OutputFilename
.empty()) {
123 errs() << ToolName
<< ": expected -o=<output file> option.\n";
126 opts::AggregateOnly
= true;
129 void boltDiffMode(int argc
, char **argv
) {
130 cl::HideUnrelatedOptions(ArrayRef(opts::BoltDiffCategories
));
131 cl::AddExtraVersionPrinter(printBoltRevision
);
132 cl::ParseCommandLineOptions(
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";
140 if (opts::InputDataFilename
.empty()) {
141 errs() << ToolName
<< ": expected -data=<filename> option.\n";
144 if (opts::InputFilename2
.empty()) {
145 errs() << ToolName
<< ": expected second binary name.\n";
148 if (opts::InputFilename
.empty()) {
149 errs() << ToolName
<< ": expected binary.\n";
152 opts::DiffOnly
= true;
155 void boltMode(int argc
, char **argv
) {
156 cl::HideUnrelatedOptions(ArrayRef(opts::BoltCategories
));
157 // Register the target printer for --version.
158 cl::AddExtraVersionPrinter(printBoltRevision
);
159 cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion
);
161 cl::ParseCommandLineOptions(argc
, argv
,
162 "BOLT - Binary Optimization and Layout Tool\n");
164 if (opts::OutputFilename
.empty()) {
165 errs() << ToolName
<< ": expected -o=<output file> option.\n";
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
))
177 return std::string(ExecutablePath
.str());
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
);
185 llvm_shutdown_obj Y
; // Call llvm_shutdown() on exit.
187 std::string ToolPath
= GetExecutablePath(argv
[0]);
189 // Initialize targets and assembly printers/parsers.
190 llvm::InitializeAllTargetInfos();
191 llvm::InitializeAllTargetMCs();
192 llvm::InitializeAllAsmParsers();
193 llvm::InitializeAllDisassemblers();
195 llvm::InitializeAllTargets();
196 llvm::InitializeAllAsmPrinters();
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
);
205 boltMode(argc
, argv
);
207 if (!sys::fs::exists(opts::InputFilename
))
208 report_error(opts::InputFilename
, errc::no_such_file_or_directory
);
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();
218 if (auto *e
= dyn_cast
<ELFObjectFileBase
>(&Binary
)) {
219 auto RIOrErr
= RewriteInstance::create(e
, argc
, argv
, ToolPath
);
220 if (Error E
= RIOrErr
.takeError())
221 report_error(opts::InputFilename
, std::move(E
));
222 RewriteInstance
&RI
= *RIOrErr
.get();
223 if (!opts::PerfData
.empty()) {
224 if (!opts::AggregateOnly
) {
226 << ": WARNING: reading perf data directly is unsupported, "
228 "-aggregate-only or perf2bolt.\n!!! Proceed on your own "
231 if (Error E
= RI
.setProfile(opts::PerfData
))
232 report_error(opts::PerfData
, std::move(E
));
234 if (!opts::InputDataFilename
.empty()) {
235 if (Error E
= RI
.setProfile(opts::InputDataFilename
))
236 report_error(opts::InputDataFilename
, std::move(E
));
238 if (opts::AggregateOnly
&& opts::PerfData
.empty()) {
239 errs() << ToolName
<< ": missing required -perfdata option.\n";
243 if (Error E
= RI
.run())
244 report_error(opts::InputFilename
, std::move(E
));
245 } else if (auto *O
= dyn_cast
<MachOObjectFile
>(&Binary
)) {
246 auto MachORIOrErr
= MachORewriteInstance::create(O
, ToolPath
);
247 if (Error E
= MachORIOrErr
.takeError())
248 report_error(opts::InputFilename
, std::move(E
));
249 MachORewriteInstance
&MachORI
= *MachORIOrErr
.get();
251 if (!opts::InputDataFilename
.empty())
252 if (Error E
= MachORI
.setProfile(opts::InputDataFilename
))
253 report_error(opts::InputDataFilename
, std::move(E
));
257 report_error(opts::InputFilename
, object_error::invalid_file_type
);
264 Expected
<OwningBinary
<Binary
>> BinaryOrErr1
=
265 createBinary(opts::InputFilename
);
266 Expected
<OwningBinary
<Binary
>> BinaryOrErr2
=
267 createBinary(opts::InputFilename2
);
268 if (Error E
= BinaryOrErr1
.takeError())
269 report_error(opts::InputFilename
, std::move(E
));
270 if (Error E
= BinaryOrErr2
.takeError())
271 report_error(opts::InputFilename2
, std::move(E
));
272 Binary
&Binary1
= *BinaryOrErr1
.get().getBinary();
273 Binary
&Binary2
= *BinaryOrErr2
.get().getBinary();
274 if (auto *ELFObj1
= dyn_cast
<ELFObjectFileBase
>(&Binary1
)) {
275 if (auto *ELFObj2
= dyn_cast
<ELFObjectFileBase
>(&Binary2
)) {
276 auto RI1OrErr
= RewriteInstance::create(ELFObj1
, argc
, argv
, ToolPath
);
277 if (Error E
= RI1OrErr
.takeError())
278 report_error(opts::InputFilename
, std::move(E
));
279 RewriteInstance
&RI1
= *RI1OrErr
.get();
280 if (Error E
= RI1
.setProfile(opts::InputDataFilename
))
281 report_error(opts::InputDataFilename
, std::move(E
));
282 auto RI2OrErr
= RewriteInstance::create(ELFObj2
, argc
, argv
, ToolPath
);
283 if (Error E
= RI2OrErr
.takeError())
284 report_error(opts::InputFilename2
, std::move(E
));
285 RewriteInstance
&RI2
= *RI2OrErr
.get();
286 if (Error E
= RI2
.setProfile(opts::InputDataFilename2
))
287 report_error(opts::InputDataFilename2
, std::move(E
));
288 outs() << "BOLT-DIFF: *** Analyzing binary 1: " << opts::InputFilename
290 outs() << "BOLT-DIFF: *** Binary 1 fdata: " << opts::InputDataFilename
292 if (Error E
= RI1
.run())
293 report_error(opts::InputFilename
, std::move(E
));
294 outs() << "BOLT-DIFF: *** Analyzing binary 2: " << opts::InputFilename2
296 outs() << "BOLT-DIFF: *** Binary 2 fdata: "
297 << opts::InputDataFilename2
<< "\n";
298 if (Error E
= RI2
.run())
299 report_error(opts::InputFilename2
, std::move(E
));
302 report_error(opts::InputFilename2
, object_error::invalid_file_type
);
305 report_error(opts::InputFilename
, object_error::invalid_file_type
);