| //===- bolt/Utils/CommandLineOpts.cpp - BOLT CLI options ------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // BOLT CLI options | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "bolt/Utils/CommandLineOpts.h" | |
| #include "VCSVersion.inc" | |
| #include "llvm/Support/Regex.h" | |
| using namespace llvm; | |
| namespace llvm { | |
| namespace bolt { | |
| const char *BoltRevision = | |
| #ifdef BOLT_REVISION | |
| BOLT_REVISION; | |
| #else | |
| "<unknown>"; | |
| #endif | |
| } | |
| } | |
| namespace opts { | |
| HeatmapModeKind HeatmapMode = HM_None; | |
| bool BinaryAnalysisMode = false; | |
| cl::OptionCategory BoltCategory("BOLT generic options"); | |
| cl::OptionCategory BoltDiffCategory("BOLTDIFF generic options"); | |
| cl::OptionCategory BoltOptCategory("BOLT optimization options"); | |
| cl::OptionCategory BoltRelocCategory("BOLT options in relocation mode"); | |
| cl::OptionCategory BoltOutputCategory("Output options"); | |
| cl::OptionCategory AggregatorCategory("Data aggregation options"); | |
| cl::OptionCategory BoltInstrCategory("BOLT instrumentation options"); | |
| cl::OptionCategory HeatmapCategory("Heatmap options"); | |
| cl::OptionCategory BinaryAnalysisCategory("BinaryAnalysis options"); | |
| cl::opt<unsigned> AlignText("align-text", | |
| cl::desc("alignment of .text section"), cl::Hidden, | |
| cl::cat(BoltCategory)); | |
| cl::opt<unsigned> AlignFunctions( | |
| "align-functions", | |
| cl::desc("align functions at a given value (relocation mode)"), | |
| cl::init(64), cl::cat(BoltOptCategory)); | |
| cl::opt<bool> AlignBlocks("align-blocks", cl::desc("align basic blocks"), | |
| cl::cat(BoltOptCategory)); | |
| cl::opt<unsigned> AlignBlocksMinSize( | |
| "align-blocks-min-size", | |
| cl::desc("minimal size of the basic block that should be aligned"), | |
| cl::init(0), cl::Hidden, cl::cat(BoltOptCategory)); | |
| cl::opt<unsigned> AlignBlocksThreshold( | |
| "align-blocks-threshold", | |
| cl::desc( | |
| "align only blocks with frequency larger than containing function " | |
| "execution frequency specified in percent. E.g. 1000 means aligning " | |
| "blocks that are 10 times more frequently executed than the " | |
| "containing function."), | |
| cl::init(800), cl::Hidden, cl::cat(BoltOptCategory)); | |
| cl::opt<unsigned> AlignFunctionsMaxBytes( | |
| "align-functions-max-bytes", | |
| cl::desc("maximum number of bytes to use to align functions"), cl::init(32), | |
| cl::cat(BoltOptCategory)); | |
| cl::opt<unsigned> | |
| BlockAlignment("block-alignment", | |
| cl::desc("boundary to use for alignment of basic blocks"), | |
| cl::init(16), cl::cat(BoltOptCategory)); | |
| cl::opt<bool> | |
| PreserveBlocksAlignment("preserve-blocks-alignment", | |
| cl::desc("try to preserve basic block alignment"), | |
| cl::cat(BoltOptCategory)); | |
| cl::opt<bool> | |
| UseCompactAligner("use-compact-aligner", | |
| cl::desc("Use compact approach for aligning functions"), | |
| cl::init(true), cl::cat(BoltOptCategory)); | |
| cl::opt<bool> X86AlignBranchBoundaryHotOnly( | |
| "x86-align-branch-boundary-hot-only", | |
| cl::desc("only apply branch boundary alignment in hot code"), | |
| cl::init(true), cl::cat(BoltOptCategory)); | |
| cl::opt<bool> | |
| AggregateOnly("aggregate-only", | |
| cl::desc("exit after writing aggregated data file"), | |
| cl::Hidden, | |
| cl::cat(AggregatorCategory)); | |
| cl::opt<unsigned> | |
| BucketsPerLine("line-size", | |
| cl::desc("number of entries per line (default 256)"), | |
| cl::init(256), cl::cat(HeatmapCategory)); | |
| cl::opt<bool> | |
| CompactCodeModel("compact-code-model", | |
| cl::desc("generate code for binaries <128MB on AArch64"), | |
| cl::init(false), cl::cat(BoltCategory)); | |
| cl::opt<bool> | |
| DiffOnly("diff-only", | |
| cl::desc("stop processing once we have enough to compare two binaries"), | |
| cl::Hidden, | |
| cl::cat(BoltDiffCategory)); | |
| cl::opt<bool> | |
| EnableBAT("enable-bat", | |
| cl::desc("write BOLT Address Translation tables"), | |
| cl::init(false), | |
| cl::cat(BoltCategory)); | |
| cl::opt<bool> EqualizeBBCounts( | |
| "equalize-bb-counts", | |
| cl::desc("use same count for BBs that should have equivalent count (used " | |
| "in non-LBR and shrink wrapping)"), | |
| cl::init(false), cl::Hidden, cl::cat(BoltOptCategory)); | |
| llvm::cl::opt<bool> ForcePatch( | |
| "force-patch", | |
| llvm::cl::desc("force patching of original entry points to ensure " | |
| "execution follows only the new/optimized code."), | |
| llvm::cl::Hidden, llvm::cl::cat(BoltCategory)); | |
| cl::opt<bool> RemoveSymtab("remove-symtab", cl::desc("Remove .symtab section"), | |
| cl::cat(BoltCategory)); | |
| cl::opt<unsigned> | |
| ExecutionCountThreshold("execution-count-threshold", | |
| cl::desc("perform profiling accuracy-sensitive optimizations only if " | |
| "function execution count >= the threshold (default: 0)"), | |
| cl::init(0), | |
| cl::Hidden, | |
| cl::cat(BoltOptCategory)); | |
| cl::opt<SplitFunctionsStrategy> SplitStrategy( | |
| "split-strategy", cl::init(SplitFunctionsStrategy::Profile2), | |
| cl::values(clEnumValN(SplitFunctionsStrategy::Profile2, "profile2", | |
| "split each function into a hot and cold fragment " | |
| "using profiling information")), | |
| cl::values(clEnumValN(SplitFunctionsStrategy::CDSplit, "cdsplit", | |
| "split each function into a hot, warm, and cold " | |
| "fragment using profiling information")), | |
| cl::values(clEnumValN( | |
| SplitFunctionsStrategy::Random2, "random2", | |
| "split each function into a hot and cold fragment at a randomly chosen " | |
| "split point (ignoring any available profiling information)")), | |
| cl::values(clEnumValN( | |
| SplitFunctionsStrategy::RandomN, "randomN", | |
| "split each function into N fragments at a randomly chosen split " | |
| "points (ignoring any available profiling information)")), | |
| cl::values(clEnumValN( | |
| SplitFunctionsStrategy::All, "all", | |
| "split all basic blocks of each function into fragments such that each " | |
| "fragment contains exactly a single basic block")), | |
| cl::desc("strategy used to partition blocks into fragments"), | |
| cl::cat(BoltOptCategory)); | |
| bool HeatmapBlockSpecParser::parse(cl::Option &O, StringRef ArgName, | |
| StringRef Arg, HeatmapBlockSizes &Val) { | |
| // Parses a human-readable suffix into a shift amount or nullopt on error. | |
| auto parseSuffix = [](StringRef Suffix) -> std::optional<unsigned> { | |
| if (Suffix.empty()) | |
| return 0; | |
| if (!Regex{"^[kKmMgG]i?[bB]?$"}.match(Suffix)) | |
| return std::nullopt; | |
| // clang-format off | |
| switch (Suffix.front()) { | |
| case 'k': case 'K': return 10; | |
| case 'm': case 'M': return 20; | |
| case 'g': case 'G': return 30; | |
| } | |
| // clang-format on | |
| llvm_unreachable("Unexpected suffix"); | |
| }; | |
| SmallVector<StringRef> Sizes; | |
| Arg.split(Sizes, ','); | |
| unsigned PreviousSize = 0; | |
| for (StringRef Size : Sizes) { | |
| StringRef OrigSize = Size; | |
| HeatmapBlockSize &Block = Val.emplace_back(); | |
| Block.Spec = OrigSize.str(); | |
| unsigned &SizeVal = Block.Value; | |
| if (Size.consumeInteger(10, SizeVal)) { | |
| O.error("'" + OrigSize + "' value can't be parsed as an integer"); | |
| return true; | |
| } | |
| if (std::optional<unsigned> ShiftAmt = parseSuffix(Size)) { | |
| SizeVal <<= *ShiftAmt; | |
| } else { | |
| O.error("'" + Size + "' value can't be parsed as a suffix"); | |
| return true; | |
| } | |
| if (SizeVal <= PreviousSize || (PreviousSize && SizeVal % PreviousSize)) { | |
| O.error("'" + OrigSize + "' must be a multiple of previous value"); | |
| return true; | |
| } | |
| PreviousSize = SizeVal; | |
| } | |
| return false; | |
| } | |
| cl::opt<opts::HeatmapBlockSizes, false, opts::HeatmapBlockSpecParser> | |
| HeatmapBlock( | |
| "block-size", cl::value_desc("initial_size{,zoom-out_size,...}"), | |
| cl::desc("heatmap bucket size, optionally followed by zoom-out sizes " | |
| "for coarse-grained heatmaps (default 64, 4K, 16K, 64K, 2M)."), | |
| // Cache line, then the page sizes x86-64 and AArch64 actually use | |
| // (4K, and 16K/64K on AArch64), then the PMD hugepage above a 4K base | |
| // page. | |
| cl::init(HeatmapBlockSizes{/*Initial*/ {64, "64"}, | |
| /*Zoom-out*/ {4096, "4K"}, | |
| {16384, "16K"}, | |
| {65536, "64K"}, | |
| {2097152, "2M"}}), | |
| cl::cat(HeatmapCategory)); | |
| cl::opt<int> HeatmapCdfPct( | |
| "heatmap-cdf-pct", cl::init(990000), | |
| cl::desc("Sample CDF cutoff, in millionths, at which to report the working " | |
| "set."), | |
| cl::value_desc("n"), cl::cat(HeatmapCategory)); | |
| cl::opt<unsigned long long> HeatmapMaxAddress( | |
| "max-address", cl::init(0xffffffff), | |
| cl::desc("maximum address considered valid for heatmap (default 4GB)"), | |
| cl::cat(HeatmapCategory)); | |
| cl::opt<unsigned long long> HeatmapMinAddress( | |
| "min-address", cl::init(0x0), | |
| cl::desc("minimum address considered valid for heatmap (default 0)"), | |
| cl::cat(HeatmapCategory)); | |
| cl::opt<bool> HeatmapPrintMappings( | |
| "print-mappings", cl::init(false), | |
| cl::desc("print mappings in the legend, between characters/blocks and text " | |
| "sections (default false)"), | |
| cl::cat(HeatmapCategory)); | |
| cl::opt<std::string> HeatmapOutput("heatmap", | |
| cl::desc("print heatmap to a given file"), | |
| cl::cat(HeatmapCategory)); | |
| cl::opt<bool> HotData("hot-data", | |
| cl::desc("hot data symbols support (relocation mode)"), | |
| cl::cat(BoltCategory)); | |
| cl::opt<bool> HotFunctionsAtEnd( | |
| "hot-functions-at-end", | |
| cl::desc( | |
| "if reorder-functions is used, order functions putting hottest last"), | |
| cl::cat(BoltCategory)); | |
| cl::opt<bool> HotText( | |
| "hot-text", | |
| cl::desc( | |
| "Generate hot text symbols. Apply this option to a precompiled binary " | |
| "that manually calls into hugify, such that at runtime hugify call " | |
| "will put hot code into 2M pages. This requires relocation."), | |
| cl::cat(BoltCategory)); | |
| cl::opt<bool> Hugify( | |
| "hugify", | |
| cl::desc("Automatically put hot code on 2MB page(s) (hugify) at runtime. " | |
| "No manual call to hugify is needed in the binary (which is what " | |
| "--hot-text relies on)."), | |
| cl::cat(BoltOptCategory)); | |
| cl::opt<bool> | |
| Instrument("instrument", | |
| cl::desc("instrument code to generate accurate profile data"), | |
| cl::cat(BoltOptCategory)); | |
| cl::opt<bool> LargeCodeModel( | |
| "large-code-model", | |
| cl::desc("use large code model for exception handling encodings. " | |
| "Auto-detected by the presence of .ltext sections otherwise."), | |
| cl::cat(BoltCategory)); | |
| cl::opt<bool> Lite("lite", cl::desc("skip processing of cold functions"), | |
| cl::cat(BoltCategory)); | |
| cl::opt<std::string> | |
| OutputFilename("o", | |
| cl::desc("<output file>"), | |
| cl::cat(BoltOutputCategory)); | |
| cl::list<std::string> PerfData("perfdata", cl::CommaSeparated, | |
| cl::desc("<data file>"), | |
| cl::cat(AggregatorCategory), | |
| cl::sub(cl::SubCommand::getAll())); | |
| static cl::alias PerfDataA("p", cl::CommaSeparated, | |
| cl::desc("alias for -perfdata"), | |
| cl::aliasopt(PerfData), cl::cat(AggregatorCategory)); | |
| cl::opt<bool> PrintCacheMetrics( | |
| "print-cache-metrics", | |
| cl::desc("calculate and print various metrics for instruction cache"), | |
| cl::cat(BoltOptCategory)); | |
| cl::list<std::string> PrintOnly("print-only", cl::CommaSeparated, | |
| cl::desc("list of functions to print"), | |
| cl::value_desc("func1,func2,func3,..."), | |
| cl::Hidden, cl::cat(BoltCategory)); | |
| cl::opt<std::string> | |
| PrintOnlyFile("print-only-file", | |
| cl::desc("file with list of functions to print"), cl::Hidden, | |
| cl::cat(BoltCategory)); | |
| cl::opt<bool> PrintSections("print-sections", | |
| cl::desc("print all registered sections"), | |
| cl::Hidden, cl::cat(BoltCategory)); | |
| cl::opt<ProfileFormatKind> ProfileFormat( | |
| "profile-format", | |
| cl::desc( | |
| "format to dump profile output in aggregation mode, default is fdata"), | |
| cl::init(PF_Fdata), | |
| cl::values(clEnumValN(PF_Fdata, "fdata", "offset-based plaintext format"), | |
| clEnumValN(PF_YAML, "yaml", "dense YAML representation"), | |
| clEnumValN(PF_PreAgg, "preagg", "pre-aggregated profile format"), | |
| clEnumValN(PF_PerfScript, "perfscript", | |
| "perfscript profile format")), | |
| cl::Hidden, cl::cat(BoltCategory)); | |
| cl::list<std::string> ReorderData( | |
| "reorder-data", cl::CommaSeparated, cl::desc("list of sections to reorder"), | |
| cl::value_desc("section1,section2,section3,..."), cl::cat(BoltOptCategory)); | |
| cl::opt<std::string> SaveProfile("w", | |
| cl::desc("save recorded profile to a file"), | |
| cl::cat(BoltOutputCategory)); | |
| cl::opt<bool> ShowDensity("show-density", | |
| cl::desc("show profile density details"), | |
| cl::cat(AggregatorCategory)); | |
| cl::opt<bool> SplitEH("split-eh", cl::desc("split C++ exception handling code"), | |
| cl::Hidden, cl::cat(BoltOptCategory)); | |
| cl::opt<bool> | |
| StrictMode("strict", | |
| cl::desc("trust the input to be from a well-formed source"), | |
| cl::cat(BoltCategory)); | |
| cl::opt<bool> TimeOpts("time-opts", | |
| cl::desc("print time spent in each optimization"), | |
| cl::cat(BoltOptCategory)); | |
| cl::opt<bool> TimeRewrite("time-rewrite", | |
| cl::desc("print time spent in rewriting passes"), | |
| cl::Hidden, cl::cat(BoltCategory)); | |
| cl::opt<bool> UseOldText( | |
| "use-old-text", | |
| cl::desc("reuse space in old .text if possible (relocation mode)"), | |
| cl::cat(BoltCategory)); | |
| cl::opt<bool> UpdateDebugSections( | |
| "update-debug-sections", | |
| cl::desc("update DWARF debug sections of the executable"), | |
| cl::cat(BoltCategory)); | |
| cl::opt<unsigned> | |
| Verbosity("v", cl::desc("set verbosity level for diagnostic output"), | |
| cl::init(0), cl::cat(BoltCategory), | |
| cl::sub(cl::SubCommand::getAll())); | |
| cl::opt<bool> FixBranchesWithLiveness( | |
| "fix-branches-with-liveness", | |
| cl::desc("use liveness analysis during branch fixup " | |
| "(needed for branch inversion on AArch64)"), | |
| cl::init(false), cl::cat(BoltCategory)); | |
| bool processAllFunctions() { | |
| if (opts::AggregateOnly) | |
| return false; | |
| if (UseOldText || StrictMode) | |
| return true; | |
| return false; | |
| } | |
| } // namespace opts |