[llvm] [CGData] Declare command line options in TableGen, one struct per library (PR #226087)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 24 23:03:19 PDT 2026
================
@@ -1680,3 +1680,36 @@ TODO: complete this section
:::{todo}
TODO: fill in this section
:::
+
+## Declaring a Library's Options in TableGen
+
+A library can declare its options in a `.td` file instead of as `cl::opt`
+globals. `llvm-tblgen -gen-opt-parser-defs` generates a struct with a member
+per option, the table that parses them, and the hooks through which
+`cl::ParseCommandLineOptions` parses them and `-help-hidden` lists them.
+
+```text
+include "llvm/Option/OptParser.td"
+
+def FooOptions : OptionsStruct;
+// The spellings of FooMode, a C++ enumeration declared elsewhere.
+def FooMode : OptionEnum<"FooMode", [EnumMember<"Fast", "fast">,
+ EnumMember<"Small", "small">]>;
+
+defm Enable : BoolField<"foo-enable", "1", "Enable foo">;
+defm Threshold : ValueField<"foo-threshold", "unsigned", "8", "The threshold">;
+defm Mode : EnumField<"foo-mode", FooMode, "FooMode::Fast", "Foo's mode">;
+```
+
+The `defm` name is the member name. A `BoolField` is set by `-x`, `-no-x`, or
+`-x=true|false|1|0`; a `ValueField` of an integer type, `double`,
+or `std::string` by `-x=value` or `-x value`. Both accept `--` for `-`.
+
+The header declares the struct after including what the member defaults need,
+and one source file defines it and registers it with `cl::`.
+
+The library then lists `XXOptionsTableGen` under `DEPENDS` and `Option`
+under `LINK_COMPONENTS`. Code reads `XXOptions::Global.CodeGenDataGenerate`,
+the instance the command line sets. Keep the header in `lib/`, as private as the
+`static cl::opt` it replaces; another library that needs a value calls a
----------------
MaskRay wrote:
```
┌──────────────────────────────────────────┬───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Pattern │ Knobs │ Examples │ Does "promote to a public struct" work? │
├──────────────────────────────────────────┼───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ A. Configuration read while Y builds a │ │ EnableMemProfContextDisambiguation, UseCtxProfile, PGOInstrumentColdFunctionOnly, MaxDevirtIterations (Passes); │ │
│ pipeline or drives X, through an │ 11 │ ForceImportAll, SupportsHotColdNew, CodeGenDataThinLTOTwoRounds (LTO); ProfileCorrelate (clang) │ Yes. It becomes a field of PTO, lto::Config, clang's CodeGenOptions, or a pass's options struct, filled from the context's XOptions the way PTO does. │
│ existing carrier │ │ │ │
├──────────────────────────────────────────┼───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ B. Read deep inside another library's │ │ ViewBlockFreqFuncName in CodeGen; ProfileSummaryCutoffHot and the working-set thresholds in ProfileSummaryInfo; │ No. No object flows to those sites. A struct would have to live in the context: either a public generated OptionsStruct (generated .inc under include/, plus │
│ pass or analysis at run time │ 21 │ RequireAndPreserveDomTree (14 uses, AMDGPU and Scalar); EnableFSDiscriminator and ProfcheckDisableMetadataFixes read by │ DEPENDS in every consumer), or a hand-written struct kept in sync with the private one, which duplicates each option's name, type and default. A context │
│ │ │ inline header code in 3 and 9 libraries │ accessor fits: bool ir::enableFSDiscriminator(const LLVMContext &). │
├──────────────────────────────────────────┼───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │ │ SampleProfile sets EnableExtTspBlockPlacement, SampleProfileUseProfi and UseIterativeBFIInference from the profile; │ No. A snapshot can't carry writes back. These are cross-library mutations of global state, which break per-context options anyway. They need redesign: pass │
│ C. Another library writes the option │ 9 │ DirectX sets MC's EmbedDebug/StripDebug; LTOBackend sets NoPGOWarnMismatch from its Config; llvm-profgen overwrites IPO's │ parameters (DirectX's writer options, profgen's CSPreInliner limits), or a per-module or per-function fact instead of a global knob (profile-derived flags). │
│ │ │ inline thresholds │ │
├──────────────────────────────────────────┼───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ D. A tool reads a library's knob for its │ 5 │ llvm-profgen reads ProfileInlineLimitMin and SortProfiledSCC; llvm-ir2vec reads IR2VecEmbeddingKind │ Usually not needed. Pass the value as a parameter, or use an accessor. │
│ own logic │ │ │ │
└──────────────────────────────────────────┴───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
```
https://github.com/llvm/llvm-project/pull/226087
More information about the llvm-commits
mailing list