[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