| 1 | //===- bolt/Utils/CommandLineOpts.cpp - BOLT CLI options ------------------===// |
| 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 | // BOLT CLI options |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "bolt/Utils/CommandLineOpts.h" |
| 14 | #include "VCSVersion.inc" |
| 15 | #include "llvm/Support/Regex.h" |
| 16 | |
| 17 | using namespace llvm; |
| 18 | |
| 19 | namespace llvm { |
| 20 | namespace bolt { |
| 21 | const char *BoltRevision = |
| 22 | #ifdef BOLT_REVISION |
| 23 | BOLT_REVISION; |
| 24 | #else |
| 25 | "<unknown>" ; |
| 26 | #endif |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | namespace opts { |
| 31 | |
| 32 | HeatmapModeKind HeatmapMode = HM_None; |
| 33 | bool BinaryAnalysisMode = false; |
| 34 | |
| 35 | cl::OptionCategory BoltCategory("BOLT generic options" ); |
| 36 | cl::OptionCategory BoltDiffCategory("BOLTDIFF generic options" ); |
| 37 | cl::OptionCategory BoltOptCategory("BOLT optimization options" ); |
| 38 | cl::OptionCategory BoltRelocCategory("BOLT options in relocation mode" ); |
| 39 | cl::OptionCategory BoltOutputCategory("Output options" ); |
| 40 | cl::OptionCategory AggregatorCategory("Data aggregation options" ); |
| 41 | cl::OptionCategory BoltInstrCategory("BOLT instrumentation options" ); |
| 42 | cl::OptionCategory HeatmapCategory("Heatmap options" ); |
| 43 | cl::OptionCategory BinaryAnalysisCategory("BinaryAnalysis options" ); |
| 44 | |
| 45 | cl::opt<unsigned> AlignText("align-text" , |
| 46 | cl::desc("alignment of .text section" ), cl::Hidden, |
| 47 | cl::cat(BoltCategory)); |
| 48 | |
| 49 | cl::opt<unsigned> AlignFunctions( |
| 50 | "align-functions" , |
| 51 | cl::desc("align functions at a given value (relocation mode)" ), |
| 52 | cl::init(Val: 64), cl::cat(BoltOptCategory)); |
| 53 | |
| 54 | cl::opt<bool> |
| 55 | AggregateOnly("aggregate-only" , |
| 56 | cl::desc("exit after writing aggregated data file" ), |
| 57 | cl::Hidden, |
| 58 | cl::cat(AggregatorCategory)); |
| 59 | |
| 60 | cl::opt<unsigned> |
| 61 | BucketsPerLine("line-size" , |
| 62 | cl::desc("number of entries per line (default 256)" ), |
| 63 | cl::init(Val: 256), cl::Optional, cl::cat(HeatmapCategory)); |
| 64 | |
| 65 | cl::opt<bool> |
| 66 | CompactCodeModel("compact-code-model" , |
| 67 | cl::desc("generate code for binaries <128MB on AArch64" ), |
| 68 | cl::init(Val: false), cl::cat(BoltCategory)); |
| 69 | |
| 70 | cl::opt<bool> |
| 71 | DiffOnly("diff-only" , |
| 72 | cl::desc("stop processing once we have enough to compare two binaries" ), |
| 73 | cl::Hidden, |
| 74 | cl::cat(BoltDiffCategory)); |
| 75 | |
| 76 | cl::opt<bool> |
| 77 | EnableBAT("enable-bat" , |
| 78 | cl::desc("write BOLT Address Translation tables" ), |
| 79 | cl::init(Val: false), |
| 80 | cl::ZeroOrMore, |
| 81 | cl::cat(BoltCategory)); |
| 82 | |
| 83 | cl::opt<bool> EqualizeBBCounts( |
| 84 | "equalize-bb-counts" , |
| 85 | cl::desc("use same count for BBs that should have equivalent count (used " |
| 86 | "in non-LBR and shrink wrapping)" ), |
| 87 | cl::ZeroOrMore, cl::init(Val: false), cl::Hidden, cl::cat(BoltOptCategory)); |
| 88 | |
| 89 | llvm::cl::opt<bool> ForcePatch( |
| 90 | "force-patch" , |
| 91 | llvm::cl::desc("force patching of original entry points to ensure " |
| 92 | "execution follows only the new/optimized code." ), |
| 93 | llvm::cl::Hidden, llvm::cl::cat(BoltCategory)); |
| 94 | |
| 95 | cl::opt<bool> RemoveSymtab("remove-symtab" , cl::desc("Remove .symtab section" ), |
| 96 | cl::cat(BoltCategory)); |
| 97 | |
| 98 | cl::opt<unsigned> |
| 99 | ExecutionCountThreshold("execution-count-threshold" , |
| 100 | cl::desc("perform profiling accuracy-sensitive optimizations only if " |
| 101 | "function execution count >= the threshold (default: 0)" ), |
| 102 | cl::init(Val: 0), |
| 103 | cl::ZeroOrMore, |
| 104 | cl::Hidden, |
| 105 | cl::cat(BoltOptCategory)); |
| 106 | |
| 107 | bool HeatmapBlockSpecParser::parse(cl::Option &O, StringRef ArgName, |
| 108 | StringRef Arg, HeatmapBlockSizes &Val) { |
| 109 | // Parses a human-readable suffix into a shift amount or nullopt on error. |
| 110 | auto parseSuffix = [](StringRef Suffix) -> std::optional<unsigned> { |
| 111 | if (Suffix.empty()) |
| 112 | return 0; |
| 113 | if (!Regex{"^[kKmMgG]i?[bB]?$" }.match(String: Suffix)) |
| 114 | return std::nullopt; |
| 115 | // clang-format off |
| 116 | switch (Suffix.front()) { |
| 117 | case 'k': case 'K': return 10; |
| 118 | case 'm': case 'M': return 20; |
| 119 | case 'g': case 'G': return 30; |
| 120 | } |
| 121 | // clang-format on |
| 122 | llvm_unreachable("Unexpected suffix" ); |
| 123 | }; |
| 124 | |
| 125 | SmallVector<StringRef> Sizes; |
| 126 | Arg.split(A&: Sizes, Separator: ','); |
| 127 | unsigned PreviousSize = 0; |
| 128 | for (StringRef Size : Sizes) { |
| 129 | StringRef OrigSize = Size; |
| 130 | unsigned &SizeVal = Val.emplace_back(args: 0); |
| 131 | if (Size.consumeInteger(Radix: 10, Result&: SizeVal)) { |
| 132 | O.error(Message: "'" + OrigSize + "' value can't be parsed as an integer" ); |
| 133 | return true; |
| 134 | } |
| 135 | if (std::optional<unsigned> ShiftAmt = parseSuffix(Size)) { |
| 136 | SizeVal <<= *ShiftAmt; |
| 137 | } else { |
| 138 | O.error(Message: "'" + Size + "' value can't be parsed as a suffix" ); |
| 139 | return true; |
| 140 | } |
| 141 | if (SizeVal <= PreviousSize || (PreviousSize && SizeVal % PreviousSize)) { |
| 142 | O.error(Message: "'" + OrigSize + "' must be a multiple of previous value" ); |
| 143 | return true; |
| 144 | } |
| 145 | PreviousSize = SizeVal; |
| 146 | } |
| 147 | return false; |
| 148 | } |
| 149 | |
| 150 | cl::opt<opts::HeatmapBlockSizes, false, opts::HeatmapBlockSpecParser> |
| 151 | HeatmapBlock( |
| 152 | "block-size" , cl::value_desc("initial_size{,zoom-out_size,...}" ), |
| 153 | cl::desc("heatmap bucket size, optionally followed by zoom-out sizes " |
| 154 | "for coarse-grained heatmaps (default 64B, 4K, 256K)." ), |
| 155 | cl::init(Val: HeatmapBlockSizes{/*Initial*/ 64, /*Zoom-out*/ 4096, 262144}), |
| 156 | cl::cat(HeatmapCategory)); |
| 157 | |
| 158 | cl::opt<unsigned long long> HeatmapMaxAddress( |
| 159 | "max-address" , cl::init(Val: 0xffffffff), |
| 160 | cl::desc("maximum address considered valid for heatmap (default 4GB)" ), |
| 161 | cl::Optional, cl::cat(HeatmapCategory)); |
| 162 | |
| 163 | cl::opt<unsigned long long> HeatmapMinAddress( |
| 164 | "min-address" , cl::init(Val: 0x0), |
| 165 | cl::desc("minimum address considered valid for heatmap (default 0)" ), |
| 166 | cl::Optional, cl::cat(HeatmapCategory)); |
| 167 | |
| 168 | cl::opt<bool> HeatmapPrintMappings( |
| 169 | "print-mappings" , cl::init(Val: false), |
| 170 | cl::desc("print mappings in the legend, between characters/blocks and text " |
| 171 | "sections (default false)" ), |
| 172 | cl::Optional, cl::cat(HeatmapCategory)); |
| 173 | |
| 174 | cl::opt<std::string> HeatmapOutput("heatmap" , |
| 175 | cl::desc("print heatmap to a given file" ), |
| 176 | cl::Optional, cl::cat(HeatmapCategory)); |
| 177 | |
| 178 | cl::opt<bool> HotData("hot-data" , |
| 179 | cl::desc("hot data symbols support (relocation mode)" ), |
| 180 | cl::cat(BoltCategory)); |
| 181 | |
| 182 | cl::opt<bool> HotFunctionsAtEnd( |
| 183 | "hot-functions-at-end" , |
| 184 | cl::desc( |
| 185 | "if reorder-functions is used, order functions putting hottest last" ), |
| 186 | cl::cat(BoltCategory)); |
| 187 | |
| 188 | cl::opt<bool> HotText( |
| 189 | "hot-text" , |
| 190 | cl::desc( |
| 191 | "Generate hot text symbols. Apply this option to a precompiled binary " |
| 192 | "that manually calls into hugify, such that at runtime hugify call " |
| 193 | "will put hot code into 2M pages. This requires relocation." ), |
| 194 | cl::ZeroOrMore, cl::cat(BoltCategory)); |
| 195 | |
| 196 | cl::opt<bool> |
| 197 | Instrument("instrument" , |
| 198 | cl::desc("instrument code to generate accurate profile data" ), |
| 199 | cl::cat(BoltOptCategory)); |
| 200 | |
| 201 | cl::opt<bool> Lite("lite" , cl::desc("skip processing of cold functions" ), |
| 202 | cl::cat(BoltCategory)); |
| 203 | |
| 204 | cl::opt<std::string> |
| 205 | OutputFilename("o" , |
| 206 | cl::desc("<output file>" ), |
| 207 | cl::Optional, |
| 208 | cl::cat(BoltOutputCategory)); |
| 209 | |
| 210 | cl::opt<std::string> PerfData("perfdata" , cl::desc("<data file>" ), cl::Optional, |
| 211 | cl::cat(AggregatorCategory), |
| 212 | cl::sub(cl::SubCommand::getAll())); |
| 213 | |
| 214 | static cl::alias |
| 215 | PerfDataA("p" , |
| 216 | cl::desc("alias for -perfdata" ), |
| 217 | cl::aliasopt(PerfData), |
| 218 | cl::cat(AggregatorCategory)); |
| 219 | |
| 220 | cl::opt<bool> PrintCacheMetrics( |
| 221 | "print-cache-metrics" , |
| 222 | cl::desc("calculate and print various metrics for instruction cache" ), |
| 223 | cl::cat(BoltOptCategory)); |
| 224 | |
| 225 | cl::opt<bool> PrintSections("print-sections" , |
| 226 | cl::desc("print all registered sections" ), |
| 227 | cl::Hidden, cl::cat(BoltCategory)); |
| 228 | |
| 229 | cl::opt<ProfileFormatKind> ProfileFormat( |
| 230 | "profile-format" , |
| 231 | cl::desc( |
| 232 | "format to dump profile output in aggregation mode, default is fdata" ), |
| 233 | cl::init(Val: PF_Fdata), |
| 234 | cl::values(clEnumValN(PF_Fdata, "fdata" , "offset-based plaintext format" ), |
| 235 | clEnumValN(PF_YAML, "yaml" , "dense YAML representation" )), |
| 236 | cl::ZeroOrMore, cl::Hidden, cl::cat(BoltCategory)); |
| 237 | |
| 238 | cl::opt<std::string> SaveProfile("w" , |
| 239 | cl::desc("save recorded profile to a file" ), |
| 240 | cl::cat(BoltOutputCategory)); |
| 241 | |
| 242 | cl::opt<bool> ShowDensity("show-density" , |
| 243 | cl::desc("show profile density details" ), |
| 244 | cl::Optional, cl::cat(AggregatorCategory)); |
| 245 | |
| 246 | cl::opt<bool> SplitEH("split-eh" , cl::desc("split C++ exception handling code" ), |
| 247 | cl::Hidden, cl::cat(BoltOptCategory)); |
| 248 | |
| 249 | cl::opt<bool> |
| 250 | StrictMode("strict" , |
| 251 | cl::desc("trust the input to be from a well-formed source" ), |
| 252 | |
| 253 | cl::cat(BoltCategory)); |
| 254 | |
| 255 | cl::opt<bool> TimeOpts("time-opts" , |
| 256 | cl::desc("print time spent in each optimization" ), |
| 257 | cl::cat(BoltOptCategory)); |
| 258 | |
| 259 | cl::opt<bool> TimeRewrite("time-rewrite" , |
| 260 | cl::desc("print time spent in rewriting passes" ), |
| 261 | cl::Hidden, cl::cat(BoltCategory)); |
| 262 | |
| 263 | cl::opt<bool> UseOldText( |
| 264 | "use-old-text" , |
| 265 | cl::desc("re-use space in old .text if possible (relocation mode)" ), |
| 266 | cl::cat(BoltCategory)); |
| 267 | |
| 268 | cl::opt<bool> UpdateDebugSections( |
| 269 | "update-debug-sections" , |
| 270 | cl::desc("update DWARF debug sections of the executable" ), |
| 271 | cl::cat(BoltCategory)); |
| 272 | |
| 273 | cl::opt<unsigned> |
| 274 | Verbosity("v" , cl::desc("set verbosity level for diagnostic output" ), |
| 275 | cl::init(Val: 0), cl::ZeroOrMore, cl::cat(BoltCategory), |
| 276 | cl::sub(cl::SubCommand::getAll())); |
| 277 | |
| 278 | bool processAllFunctions() { |
| 279 | if (opts::AggregateOnly) |
| 280 | return false; |
| 281 | |
| 282 | if (UseOldText || StrictMode) |
| 283 | return true; |
| 284 | |
| 285 | return false; |
| 286 | } |
| 287 | |
| 288 | } // namespace opts |
| 289 | |