Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / bolt / tools / driver / llvm-bolt.cpp
blob5a3af6a44b522a86d8a32e2d48c653442c27595d
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.
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"
32 using namespace llvm;
33 using namespace object;
34 using namespace bolt;
36 namespace opts {
38 static cl::OptionCategory *BoltCategories[] = {&BoltCategory,
39 &BoltOptCategory,
40 &BoltRelocCategory,
41 &BoltInstrCategory,
42 &BoltOutputCategory};
44 static cl::OptionCategory *BoltDiffCategories[] = {&BoltDiffCategory};
46 static cl::OptionCategory *Perf2BoltCategories[] = {&AggregatorCategory,
47 &BoltOutputCategory};
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>"),
57 cl::Optional,
58 cl::cat(BoltCategory));
60 static cl::alias
61 BoltProfile("b",
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>"),
69 cl::Optional,
70 cl::cat(BoltCategory));
72 static cl::opt<std::string>
73 InputFilename2(
74 cl::Positional,
75 cl::desc("<executable>"),
76 cl::Optional,
77 cl::cat(BoltDiffCategory));
79 } // namespace opts
81 static StringRef ToolName;
83 static void report_error(StringRef Message, std::error_code EC) {
84 assert(EC);
85 errs() << ToolName << ": '" << Message << "': " << EC.message() << ".\n";
86 exit(1);
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);
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(
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);
111 if (!opts::InputDataFilename.empty()) {
112 errs() << ToolName << ": unknown -data option.\n";
113 exit(1);
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);
122 if (opts::OutputFilename.empty()) {
123 errs() << ToolName << ": expected -o=<output file> option.\n";
124 exit(1);
126 opts::AggregateOnly = true;
129 void boltDiffMode(int argc, char **argv) {
130 cl::HideUnrelatedOptions(ArrayRef(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);
140 if (opts::InputDataFilename.empty()) {
141 errs() << ToolName << ": expected -data=<filename> option.\n";
142 exit(1);
144 if (opts::InputFilename2.empty()) {
145 errs() << ToolName << ": expected second binary name.\n";
146 exit(1);
148 if (opts::InputFilename.empty()) {
149 errs() << ToolName << ": expected binary.\n";
150 exit(1);
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";
166 exit(1);
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());
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();
198 ToolName = argv[0];
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);
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) {
225 errs() << ToolName
226 << ": WARNING: reading perf data directly is unsupported, "
227 "please use "
228 "-aggregate-only or perf2bolt.\n!!! Proceed on your own "
229 "risk. !!!\n";
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";
240 exit(1);
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));
255 MachORI.run();
256 } else {
257 report_error(opts::InputFilename, object_error::invalid_file_type);
260 return EXIT_SUCCESS;
263 // Bolt-diff
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
289 << "\n";
290 outs() << "BOLT-DIFF: *** Binary 1 fdata: " << opts::InputDataFilename
291 << "\n";
292 if (Error E = RI1.run())
293 report_error(opts::InputFilename, std::move(E));
294 outs() << "BOLT-DIFF: *** Analyzing binary 2: " << opts::InputFilename2
295 << "\n";
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));
300 RI1.compare(RI2);
301 } else {
302 report_error(opts::InputFilename2, object_error::invalid_file_type);
304 } else {
305 report_error(opts::InputFilename, object_error::invalid_file_type);
308 return EXIT_SUCCESS;