https://github.com/MaskRay updated 
https://github.com/llvm/llvm-project/pull/230220

>From 818b8d737747e2a28fb0dc5b219a3245255fb571 Mon Sep 17 00:00:00 2001
From: Fangrui Song <[email protected]>
Date: Thu, 8 Oct 2026 00:55:49 -0700
Subject: [PATCH 1/3] [Passes] Declare command line options in TableGen

Move the LLVMPasses cl::opts into a private PassesOptions struct
(lib/Passes/PassesOptions.td). PassBuilder holds
`const PassesOptions &Opts`, forward-declared in PassBuilder.h.

-force-new-pm-codegen and -verify-analysis-invalidation become
BoolOrDefault members; the latter defaults to on with EXPENSIVE_CHECKS.
DotCfgChangeReporter keeps the expanded -dot-cfg-dir in a new OutputDir
member instead of writing it back to the option.

-print-{before,after}-pass-number (cl::list) and -print-pipeline-passes
(custom parser) stay cl::opt.

Aided by Opus 5.5
---
 llvm/include/llvm/Passes/PassBuilder.h        |   2 +
 .../llvm/Passes/StandardInstrumentations.h    |   6 +-
 llvm/lib/Passes/CMakeLists.txt                |   8 +
 llvm/lib/Passes/PassBuilder.cpp               |   4 +-
 llvm/lib/Passes/PassBuilderPipelines.cpp      | 299 ++++--------------
 llvm/lib/Passes/PassesOptions.cpp             |  15 +
 llvm/lib/Passes/PassesOptions.h               |  18 ++
 llvm/lib/Passes/PassesOptions.td              | 150 +++++++++
 llvm/lib/Passes/RunCodeGen.cpp                |  12 +-
 llvm/lib/Passes/StandardInstrumentations.cpp  | 210 +++++-------
 .../gn/secondary/llvm/lib/Passes/BUILD.gn     |  10 +
 .../llvm-project-overlay/llvm/BUILD.bazel     |  12 +
 12 files changed, 364 insertions(+), 382 deletions(-)
 create mode 100644 llvm/lib/Passes/PassesOptions.cpp
 create mode 100644 llvm/lib/Passes/PassesOptions.h
 create mode 100644 llvm/lib/Passes/PassesOptions.td

diff --git a/llvm/include/llvm/Passes/PassBuilder.h 
b/llvm/include/llvm/Passes/PassBuilder.h
index 879e78fae4bf9b..245725f13ed8a4 100644
--- a/llvm/include/llvm/Passes/PassBuilder.h
+++ b/llvm/include/llvm/Passes/PassBuilder.h
@@ -36,6 +36,7 @@ class StringRef;
 class AAManager;
 class TargetMachine;
 class ModuleSummaryIndex;
+struct PassesOptions;
 
 /// Tunable parameters for passes in the default pipelines.
 class PipelineTuningOptions {
@@ -112,6 +113,7 @@ class PipelineTuningOptions {
 /// of the built-in passes, and those may reference these members during
 /// construction.
 class PassBuilder {
+  const PassesOptions &Opts;
   TargetMachine *TM;
   PipelineTuningOptions PTO;
   std::optional<PGOOptions> PGOOpt;
diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h 
b/llvm/include/llvm/Passes/StandardInstrumentations.h
index 7c47a93f4e24d8..a25ef0ea4eec35 100644
--- a/llvm/include/llvm/Passes/StandardInstrumentations.h
+++ b/llvm/include/llvm/Passes/StandardInstrumentations.h
@@ -617,8 +617,8 @@ class LLVM_ABI DotCfgChangeReporter : public 
ChangeReporter<IRDataT<DCData>> {
 
   // Generate the pdf file into \p Dir / \p PDFFileName using \p DotFile as
   // input and return the html <a> tag with \Text as the content.
-  static std::string genHTML(StringRef Text, StringRef DotFile,
-                             StringRef PDFFileName);
+  std::string genHTML(StringRef Text, StringRef DotFile,
+                      StringRef PDFFileName) const;
 
   void handleFunctionCompare(StringRef Name, StringRef Prefix, StringRef 
PassID,
                              StringRef Divider, bool InModule, unsigned Minor,
@@ -626,6 +626,8 @@ class LLVM_ABI DotCfgChangeReporter : public 
ChangeReporter<IRDataT<DCData>> {
                              const FuncDataT<DCData> &After);
 
   unsigned N = 0;
+  // -dot-cfg-dir with ~ expanded, made absolute.
+  std::string OutputDir;
   std::unique_ptr<raw_fd_ostream> HTML;
 };
 
diff --git a/llvm/lib/Passes/CMakeLists.txt b/llvm/lib/Passes/CMakeLists.txt
index 6a966d582b63b1..e4abb96cfd23b1 100644
--- a/llvm/lib/Passes/CMakeLists.txt
+++ b/llvm/lib/Passes/CMakeLists.txt
@@ -1,8 +1,14 @@
+set(LLVM_TABLEGEN_PROJECT LLVM_HEADERS)
+set(LLVM_TARGET_DEFINITIONS PassesOptions.td)
+tablegen(LLVM PassesOptions.inc -gen-opt-parser-defs)
+add_public_tablegen_target(PassesOptionsTableGen)
+
 add_llvm_component_library(LLVMPasses
   CodeGenPassBuilder.cpp
   PassBuilder.cpp
   PassBuilderBindings.cpp
   PassBuilderPipelines.cpp
+  PassesOptions.cpp
   RunCodeGen.cpp
   StandardInstrumentations.cpp
   TriggerCrashPasses.cpp
@@ -12,6 +18,7 @@ add_llvm_component_library(LLVMPasses
   ${LLVM_MAIN_INCLUDE_DIR}/llvm/Passes
 
   DEPENDS
+  PassesOptionsTableGen
   intrinsics_gen
 
   LINK_COMPONENTS
@@ -28,6 +35,7 @@ add_llvm_component_library(LLVMPasses
   InstCombine
   IRPrinter
   ObjCARC
+  Option
   Scalar
   Support
   Target
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 30a6f75a1b86d5..4f973196eb951e 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -15,6 +15,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "llvm/Passes/PassBuilder.h"
+#include "PassesOptions.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/ADT/StringTable.h"
 #include "llvm/Analysis/AliasAnalysisEvaluator.h"
@@ -593,7 +594,8 @@ PassBuilder::PassBuilder(TargetMachine *TM, 
PipelineTuningOptions PTO,
                          std::optional<PGOOptions> PGOOpt,
                          PassInstrumentationCallbacks *PIC,
                          IntrusiveRefCntPtr<vfs::FileSystem> FS)
-    : TM(TM), PTO(PTO), PGOOpt(PGOOpt), PIC(PIC), FS(std::move(FS)) {
+    : Opts(PassesOptions::Global), TM(TM), PTO(PTO), PGOOpt(PGOOpt), PIC(PIC),
+      FS(std::move(FS)) {
   if (TM)
     TM->registerPassBuilderCallbacks(*this);
   if (PIC) {
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp 
b/llvm/lib/Passes/PassBuilderPipelines.cpp
index bd37fffc8e9914..c2ce33e8a4ad5a 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -14,6 +14,7 @@
 ///
 
//===----------------------------------------------------------------------===//
 
+#include "PassesOptions.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/AliasAnalysis.h"
 #include "llvm/Analysis/BasicAliasAnalysis.h"
@@ -160,171 +161,6 @@
 using namespace llvm;
 
 namespace llvm {
-
-static cl::opt<InliningAdvisorMode> UseInlineAdvisor(
-    "enable-ml-inliner", cl::init(InliningAdvisorMode::Default), cl::Hidden,
-    cl::desc("Enable ML policy for inliner. Currently trained for -Oz only"),
-    cl::values(clEnumValN(InliningAdvisorMode::Default, "default",
-                          "Heuristics-based inliner version"),
-               clEnumValN(InliningAdvisorMode::Development, "development",
-                          "Use development mode (runtime-loadable model)"),
-               clEnumValN(InliningAdvisorMode::Release, "release",
-                          "Use release mode (AOT-compiled model)")));
-
-/// Flag to enable inline deferral during PGO.
-static cl::opt<bool>
-    EnablePGOInlineDeferral("enable-npm-pgo-inline-deferral", cl::init(true),
-                            cl::Hidden,
-                            cl::desc("Enable inline deferral during PGO"));
-
-static cl::opt<bool> EnableModuleInliner("enable-module-inliner",
-                                         cl::init(false), cl::Hidden,
-                                         cl::desc("Enable module inliner"));
-
-static cl::opt<bool> PerformMandatoryInliningsFirst(
-    "mandatory-inlining-first", cl::init(false), cl::Hidden,
-    cl::desc("Perform mandatory inlinings module-wide, before performing "
-             "inlining"));
-
-static cl::opt<bool> EnableEagerlyInvalidateAnalyses(
-    "eagerly-invalidate-analyses", cl::init(true), cl::Hidden,
-    cl::desc("Eagerly invalidate more analyses in default pipelines"));
-
-static cl::opt<bool> EnableMergeFunctions(
-    "enable-merge-functions", cl::init(false), cl::Hidden,
-    cl::desc("Enable function merging as part of the optimization pipeline"));
-
-static cl::opt<bool> EnablePostPGOLoopRotation(
-    "enable-post-pgo-loop-rotation", cl::init(true), cl::Hidden,
-    cl::desc("Run the loop rotation transformation after PGO 
instrumentation"));
-
-static cl::opt<bool>
-    TriggerCrash("opt-pipeline-trigger-crash", cl::init(false), cl::Hidden,
-                 cl::desc("Trigger crash in optimization pipeline"));
-
-static cl::opt<bool> EnableGlobalAnalyses(
-    "enable-global-analyses", cl::init(true), cl::Hidden,
-    cl::desc("Enable inter-procedural analyses"));
-
-static cl::opt<bool> RunPartialInlining("enable-partial-inlining",
-                                        cl::init(false), cl::Hidden,
-                                        cl::desc("Run Partial inlining pass"));
-
-static cl::opt<bool> ExtraVectorizerPasses(
-    "extra-vectorizer-passes", cl::init(false), cl::Hidden,
-    cl::desc("Run cleanup optimization passes after vectorization"));
-
-static cl::opt<bool> RunNewGVN("enable-newgvn", cl::init(false), cl::Hidden,
-                               cl::desc("Run the NewGVN pass"));
-
-static cl::opt<bool>
-    EnableLoopInterchange("enable-loopinterchange", cl::init(true), cl::Hidden,
-                          cl::desc("Enable the LoopInterchange Pass"));
-
-static cl::opt<bool> EnableUnrollAndJam("enable-unroll-and-jam",
-                                        cl::init(false), cl::Hidden,
-                                        cl::desc("Enable Unroll And Jam 
Pass"));
-
-static cl::opt<bool> EnableLoopFlatten("enable-loop-flatten", cl::init(false),
-                                       cl::Hidden,
-                                       cl::desc("Enable the LoopFlatten 
Pass"));
-
-static cl::opt<bool>
-    EnableInstrumentor("enable-instrumentor", cl::init(false), cl::Hidden,
-                       cl::desc("Enable the Instrumentor Pass"));
-
-static cl::opt<bool>
-    EnableDFAJumpThreading("enable-dfa-jump-thread",
-                           cl::desc("Enable DFA jump threading"),
-                           cl::init(true), cl::Hidden);
-
-static cl::opt<bool>
-    EnableHotColdSplit("hot-cold-split",
-                       cl::desc("Enable hot-cold splitting pass"));
-
-static cl::opt<bool>
-    DisablePreInliner("disable-preinline", cl::init(false), cl::Hidden,
-                      cl::desc("Disable pre-instrumentation inliner"));
-
-static cl::opt<int> PreInlineThreshold(
-    "preinline-threshold", cl::Hidden, cl::init(75),
-    cl::desc("Control the amount of inlining in pre-instrumentation inliner "
-             "(default = 75)"));
-
-static cl::opt<bool>
-    EnableGVNHoist("enable-gvn-hoist",
-                   cl::desc("Enable the GVN hoisting pass (default = off)"));
-
-static cl::opt<bool>
-    EnableGVNSink("enable-gvn-sink",
-                  cl::desc("Enable the GVN sinking pass (default = off)"));
-
-static cl::opt<bool> EnableJumpTableToSwitch(
-    "enable-jump-table-to-switch", cl::init(true),
-    cl::desc("Enable JumpTableToSwitch pass (default = true)"));
-
-// This option is used in simplifying testing SampleFDO optimizations for
-// profile loading.
-static cl::opt<bool>
-    EnableCHR("enable-chr", cl::init(true), cl::Hidden,
-              cl::desc("Enable control height reduction optimization (CHR)"));
-
-static cl::opt<bool> FlattenedProfileUsed(
-    "flattened-profile-used", cl::init(false), cl::Hidden,
-    cl::desc("Indicate the sample profile being used is flattened, i.e., "
-             "no inline hierarchy exists in the profile"));
-
-static cl::opt<bool>
-    EnableMatrix("enable-matrix", cl::init(false), cl::Hidden,
-                 cl::desc("Enable lowering of the matrix intrinsics"));
-
-static cl::opt<bool> EnableMergeICmps(
-    "enable-mergeicmps", cl::init(true), cl::Hidden,
-    cl::desc("Enable MergeICmps pass in the optimization pipeline"));
-
-static cl::opt<bool> EnableConstraintElimination(
-    "enable-constraint-elimination", cl::init(true), cl::Hidden,
-    cl::desc(
-        "Enable pass to eliminate conditions based on linear constraints"));
-
-static cl::opt<AttributorRunOption> AttributorRun(
-    "attributor-enable", cl::Hidden, cl::init(AttributorRunOption::NONE),
-    cl::desc("Enable the attributor inter-procedural deduction pass"),
-    cl::values(clEnumValN(AttributorRunOption::FULL, "full",
-                          "enable all full attributor runs"),
-               clEnumValN(AttributorRunOption::LIGHT, "light",
-                          "enable all attributor-light runs"),
-               clEnumValN(AttributorRunOption::MODULE, "module",
-                          "enable module-wide attributor runs"),
-               clEnumValN(AttributorRunOption::MODULE_LIGHT, "module-light",
-                          "enable module-wide attributor-light runs"),
-               clEnumValN(AttributorRunOption::CGSCC, "cgscc",
-                          "enable call graph SCC attributor runs"),
-               clEnumValN(AttributorRunOption::CGSCC_LIGHT, "cgscc-light",
-                          "enable call graph SCC attributor-light runs"),
-               clEnumValN(AttributorRunOption::NONE, "none",
-                          "disable attributor runs")));
-
-static cl::opt<bool> EnableSampledInstr(
-    "enable-sampled-instrumentation", cl::init(false), cl::Hidden,
-    cl::desc("Enable profile instrumentation sampling (default = off)"));
-static cl::opt<bool> UseLoopVersioningLICM(
-    "enable-loop-versioning-licm", cl::init(false), cl::Hidden,
-    cl::desc("Enable the experimental Loop Versioning LICM pass"));
-
-static cl::opt<std::string> InstrumentColdFuncOnlyPath(
-    "instrument-cold-function-only-path", cl::init(""),
-    cl::desc("File path for cold function only instrumentation(requires use "
-             "with --pgo-instrument-cold-function-only)"),
-    cl::Hidden);
-
-// TODO: There is a similar flag in WPD pass, we should consolidate them by
-// parsing the option only once in PassBuilder and share it across both places.
-static cl::opt<bool> EnableDevirtualizeSpeculatively(
-    "enable-devirtualize-speculatively",
-    cl::desc("Enable speculative devirtualization optimization"),
-    cl::init(false));
-
 extern cl::opt<std::string> UseCtxProfile;
 extern cl::opt<bool> PGOInstrumentColdFunctionOnly;
 
@@ -332,21 +168,22 @@ extern cl::opt<bool> EnableMemProfContextDisambiguation;
 } // namespace llvm
 
 PipelineTuningOptions::PipelineTuningOptions() {
+  const PassesOptions &Opts = PassesOptions::Global;
   LoopInterleaving = true;
   LoopVectorization = true;
   SLPVectorization = false;
   LoopUnrolling = true;
-  LoopInterchange = EnableLoopInterchange;
+  LoopInterchange = Opts.enable_loopinterchange;
   LoopFusion = false;
   ForgetAllSCEVInLoopUnroll = ForgetSCEVInLoopUnroll;
   LicmMssaOptCap = SetLicmMssaOptCap;
   LicmMssaNoAccForPromotionCap = SetLicmMssaNoAccForPromotionCap;
   CallGraphProfile = true;
   UnifiedLTO = false;
-  MergeFunctions = EnableMergeFunctions;
+  MergeFunctions = Opts.enable_merge_functions;
   InlinerThreshold = -1;
-  EagerlyInvalidateAnalyses = EnableEagerlyInvalidateAnalyses;
-  DevirtualizeSpeculatively = EnableDevirtualizeSpeculatively;
+  EagerlyInvalidateAnalyses = Opts.eagerly_invalidate_analyses;
+  DevirtualizeSpeculatively = Opts.enable_devirtualize_speculatively;
 }
 
 namespace llvm {
@@ -558,7 +395,7 @@ 
PassBuilder::buildO1FunctionSimplificationPipeline(OptimizationLevel Level,
   LPM1.addPass(LICMPass(PTO.LicmMssaOptCap, PTO.LicmMssaNoAccForPromotionCap,
                         /*AllowSpeculation=*/true));
   LPM1.addPass(SimpleLoopUnswitchPass());
-  if (EnableLoopFlatten)
+  if (Opts.enable_loop_flatten)
     LPM1.addPass(LoopFlattenPass());
 
   LPM2.addPass(LoopIdiomRecognizePass());
@@ -653,11 +490,11 @@ 
PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
     FPM.addPass(AssumeSimplifyPass());
 
   // Hoisting of scalars and load expressions.
-  if (EnableGVNHoist)
+  if (Opts.enable_gvn_hoist)
     FPM.addPass(GVNHoistPass());
 
   // Global value numbering based sinking.
-  if (EnableGVNSink) {
+  if (Opts.enable_gvn_sink) {
     FPM.addPass(GVNSinkPass());
     FPM.addPass(
         SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true)));
@@ -671,7 +508,7 @@ 
PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
   FPM.addPass(CorrelatedValuePropagationPass());
 
   // Jump table to switch conversion.
-  if (EnableJumpTableToSwitch)
+  if (Opts.enable_jump_table_to_switch)
     FPM.addPass(JumpTableToSwitchPass(/*InLTO=*/isLTOPostLink(Phase)));
 
   FPM.addPass(
@@ -697,7 +534,7 @@ 
PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
   // minimal multiplication trees.
   FPM.addPass(ReassociatePass());
 
-  if (EnableConstraintElimination)
+  if (Opts.enable_constraint_elimination)
     FPM.addPass(ConstraintEliminationPass());
 
   // Add the primary loop simplification pipeline.
@@ -733,7 +570,7 @@ 
PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
                         /*AllowSpeculation=*/true));
   LPM1.addPass(
       SimpleLoopUnswitchPass(/* NonTrivial */ Level == OptimizationLevel::O3));
-  if (EnableLoopFlatten)
+  if (Opts.enable_loop_flatten)
     LPM1.addPass(LoopFlattenPass());
 
   LPM2.addPass(LoopIdiomRecognizePass());
@@ -783,7 +620,7 @@ 
PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
 
   // Eliminate redundancies.
   FPM.addPass(MergedLoadStoreMotionPass());
-  if (RunNewGVN)
+  if (Opts.enable_newgvn)
     FPM.addPass(NewGVNPass());
   else
     FPM.addPass(GVNPass());
@@ -805,7 +642,7 @@ 
PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
 
   // Re-consider control flow based optimizations after redundancy elimination,
   // redo DCE, etc.
-  if (EnableDFAJumpThreading)
+  if (Opts.enable_dfa_jump_thread)
     FPM.addPass(DFAJumpThreadingPass());
 
   FPM.addPass(JumpThreadingPass());
@@ -852,18 +689,18 @@ void PassBuilder::addPreInlinerPasses(ModulePassManager 
&MPM,
                                       OptimizationLevel Level,
                                       ThinOrFullLTOPhase LTOPhase) {
   assert(Level != OptimizationLevel::O0 && "Not expecting O0 here!");
-  if (DisablePreInliner)
+  if (Opts.disable_preinline)
     return;
   InlineParams IP;
 
-  IP.DefaultThreshold = PreInlineThreshold;
+  IP.DefaultThreshold = Opts.preinline_threshold;
 
   // FIXME: The hint threshold has the same value used by the regular inliner
   // when not optimzing for size. This should probably be lowered after
   // performance testing.
   // FIXME: this comment is cargo culted from the old pass manager, revisit).
   IP.HintThreshold = 325;
-  IP.OptSizeHintThreshold = PreInlineThreshold;
+  IP.OptSizeHintThreshold = Opts.preinline_threshold;
   ModuleInlinerWrapperPass MIWP(
       IP, /* MandatoryFirst */ true,
       InlineContext{LTOPhase, InlinePass::EarlyInliner});
@@ -890,7 +727,7 @@ void PassBuilder::addPreInlinerPasses(ModulePassManager 
&MPM,
 
 void PassBuilder::addPostPGOLoopRotation(ModulePassManager &MPM,
                                          OptimizationLevel Level) {
-  if (EnablePostPGOLoopRotation) {
+  if (Opts.enable_post_pgo_loop_rotation) {
     // Disable header duplication in loop rotation at -Oz.
     MPM.addPass(createModuleToFunctionPassAdaptor(
         createFunctionToLoopPassAdaptor(LoopRotatePass(),
@@ -928,7 +765,7 @@ void PassBuilder::addPGOInstrPasses(ModulePassManager &MPM,
   // Do counter promotion at Level greater than O0.
   Options.DoCounterPromotion = true;
   Options.UseBFIInPromotion = IsCS;
-  if (EnableSampledInstr) {
+  if (Opts.enable_sampled_instrumentation) {
     Options.Sampling = true;
     // With sampling, there is little beneifit to enable counter promotion.
     // But note that sampling does work with counter promotion.
@@ -990,15 +827,15 @@ PassBuilder::buildInlinerPipeline(OptimizationLevel Level,
     IP.HotCallSiteThreshold = 0;
 
   if (PGOOpt)
-    IP.EnableDeferral = EnablePGOInlineDeferral;
+    IP.EnableDeferral = Opts.enable_npm_pgo_inline_deferral;
 
-  ModuleInlinerWrapperPass MIWP(IP, PerformMandatoryInliningsFirst,
+  ModuleInlinerWrapperPass MIWP(IP, Opts.mandatory_inlining_first,
                                 InlineContext{Phase, InlinePass::CGSCCInliner},
-                                UseInlineAdvisor, MaxDevirtIterations);
+                                Opts.enable_ml_inliner, MaxDevirtIterations);
 
   // Require the GlobalsAA analysis for the module so we can query it within
   // the CGSCC pipeline.
-  if (EnableGlobalAnalyses) {
+  if (Opts.enable_global_analyses) {
     MIWP.addModulePass(RequireAnalysisPass<GlobalsAA, Module>());
     // Invalidate AAManager so it can be recreated and pick up the newly
     // available GlobalsAA.
@@ -1021,9 +858,9 @@ PassBuilder::buildInlinerPipeline(OptimizationLevel Level,
   // valuable as the inliner doesn't currently care whether it is inlining an
   // invoke or a call.
 
-  if (AttributorRun & AttributorRunOption::CGSCC)
+  if (Opts.attributor_enable & AttributorRunOption::CGSCC)
     MainCGPipeline.addPass(AttributorCGSCCPass());
-  else if (AttributorRun & AttributorRunOption::CGSCC_LIGHT)
+  else if (Opts.attributor_enable & AttributorRunOption::CGSCC_LIGHT)
     MainCGPipeline.addPass(AttributorLightCGSCCPass());
 
   // Deduce function attributes. We do another run of this after the function
@@ -1089,7 +926,7 @@ PassBuilder::buildModuleInlinerPipeline(OptimizationLevel 
Level,
     IP.HotCallSiteThreshold = 0;
 
   if (PGOOpt)
-    IP.EnableDeferral = EnablePGOInlineDeferral;
+    IP.EnableDeferral = Opts.enable_npm_pgo_inline_deferral;
 
   // The inline deferral logic is used to avoid losing some
   // inlining chance in future. It is helpful in SCC inliner, in which
@@ -1099,7 +936,7 @@ PassBuilder::buildModuleInlinerPipeline(OptimizationLevel 
Level,
   // inline deferral logic in module inliner.
   IP.EnableDeferral = false;
 
-  MPM.addPass(ModuleInlinerPass(IP, UseInlineAdvisor, Phase));
+  MPM.addPass(ModuleInlinerPass(IP, Opts.enable_ml_inliner, Phase));
   if (!UseCtxProfile.empty() && Phase == ThinOrFullLTOPhase::ThinLTOPostLink) {
     MPM.addPass(GlobalOptPass());
     MPM.addPass(GlobalDCEPass());
@@ -1142,8 +979,8 @@ 
PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
   // In ThinLTO mode, when flattened profile is used, all the available
   // profile information will be annotated in PreLink phase so there is
   // no need to load the profile again in PostLink.
-  bool LoadSampleProfile =
-      HasSampleProfile && !(FlattenedProfileUsed && isThinLTOPostLink(Phase));
+  bool LoadSampleProfile = HasSampleProfile && !(Opts.flattened_profile_used &&
+                                                 isThinLTOPostLink(Phase));
 
   // During the ThinLTO backend phase we perform early indirect call promotion
   // here, before globalopt. Otherwise imported available_externally functions
@@ -1209,9 +1046,9 @@ 
PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
   // (quick!) no-op if there are no OpenMP runtime calls present in the module.
   MPM.addPass(OpenMPOptPass(Phase));
 
-  if (AttributorRun & AttributorRunOption::MODULE)
+  if (Opts.attributor_enable & AttributorRunOption::MODULE)
     MPM.addPass(AttributorPass());
-  else if (AttributorRun & AttributorRunOption::MODULE_LIGHT)
+  else if (Opts.attributor_enable & AttributorRunOption::MODULE_LIGHT)
     MPM.addPass(AttributorLightPass());
 
   // Lower type metadata and the type.test intrinsic in the ThinLTO
@@ -1266,13 +1103,13 @@ 
PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
          "supported.");
   const bool IsCtxProfUse = !UseCtxProfile.empty() && isThinLTOPreLink(Phase);
 
-  assert(
-      (InstrumentColdFuncOnlyPath.empty() || PGOInstrumentColdFunctionOnly) &&
-      "--instrument-cold-function-only-path is provided but "
-      "--pgo-instrument-cold-function-only is not enabled");
-  const bool IsColdFuncOnlyInstrGen = PGOInstrumentColdFunctionOnly &&
-                                      IsPGOPreLink &&
-                                      !InstrumentColdFuncOnlyPath.empty();
+  assert((Opts.instrument_cold_function_only_path.empty() ||
+          PGOInstrumentColdFunctionOnly) &&
+         "--instrument-cold-function-only-path is provided but "
+         "--pgo-instrument-cold-function-only is not enabled");
+  const bool IsColdFuncOnlyInstrGen =
+      PGOInstrumentColdFunctionOnly && IsPGOPreLink &&
+      !Opts.instrument_cold_function_only_path.empty();
 
   if (IsPGOInstrGen || IsPGOInstrUse || IsMemprofUse || IsCtxProfGen ||
       IsCtxProfUse || IsColdFuncOnlyInstrGen)
@@ -1307,7 +1144,7 @@ 
PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
   } else if (IsColdFuncOnlyInstrGen) {
     addPGOInstrPasses(MPM, Level, /* RunProfileGen */ true, /* IsCS */ false,
                       /* AtomicCounterUpdate */ false,
-                      InstrumentColdFuncOnlyPath,
+                      Opts.instrument_cold_function_only_path.str(),
                       /* ProfileRemappingFile */ "");
   }
 
@@ -1315,8 +1152,8 @@ 
PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
     MPM.addPass(PGOIndirectCallPromotion(false, false));
 
   if (IsPGOPreLink && PGOOpt->CSAction == PGOOptions::CSIRInstr)
-    MPM.addPass(PGOInstrumentationGenCreateVar(PGOOpt->CSProfileGenFile,
-                                               EnableSampledInstr));
+    MPM.addPass(PGOInstrumentationGenCreateVar(
+        PGOOpt->CSProfileGenFile, Opts.enable_sampled_instrumentation));
 
   if (IsMemprofUse)
     MPM.addPass(MemProfUsePass(PGOOpt->MemoryProfile, FS));
@@ -1327,7 +1164,7 @@ 
PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
 
   MPM.addPass(AlwaysInlinerPass(/*InsertLifetimeIntrinsics=*/true));
 
-  if (EnableModuleInliner)
+  if (Opts.enable_module_inliner)
     MPM.addPass(buildModuleInlinerPipeline(Level, Phase));
   else
     MPM.addPass(buildInlinerPipeline(Level, Phase));
@@ -1371,7 +1208,7 @@ void PassBuilder::addVectorPasses(OptimizationLevel Level,
     // combiner for cleanup here so that the unrolling and LICM can be 
pipelined
     // across the loop nests.
     // We do UnrollAndJam in a separate LPM to ensure it happens before unroll
-    if (EnableUnrollAndJam && PTO.LoopUnrolling)
+    if (Opts.enable_unroll_and_jam && PTO.LoopUnrolling)
       FPM.addPass(createFunctionToLoopPassAdaptor(
           LoopUnrollAndJamPass(static_cast<int>(Level))));
     FPM.addPass(LoopUnrollPass(LoopUnrollOptions(
@@ -1404,7 +1241,7 @@ void PassBuilder::addVectorPasses(OptimizationLevel Level,
   // Cleanup after the loop optimization passes.
   FPM.addPass(InstCombinePass());
 
-  if (Level > OptimizationLevel::O1 && ExtraVectorizerPasses) {
+  if (Level > OptimizationLevel::O1 && Opts.extra_vectorizer_passes) {
     ExtraFunctionPassManager<ShouldRunExtraVectorPasses> ExtraPasses;
     // At higher optimization levels, try to clean up any runtime overlap and
     // alignment checks inserted by the vectorizer. We want to track correlated
@@ -1455,7 +1292,7 @@ void PassBuilder::addVectorPasses(OptimizationLevel Level,
   // Optimize parallel scalar instruction chains into SIMD instructions.
   if (PTO.SLPVectorization) {
     FPM.addPass(SLPVectorizerPass());
-    if (Level >= OptimizationLevel::O2 && ExtraVectorizerPasses) {
+    if (Level >= OptimizationLevel::O2 && Opts.extra_vectorizer_passes) {
       FPM.addPass(EarlyCSEPass());
     }
   }
@@ -1471,7 +1308,7 @@ void PassBuilder::addVectorPasses(OptimizationLevel Level,
     // combiner for cleanup here so that the unrolling and LICM can be 
pipelined
     // across the loop nests.
     // We do UnrollAndJam in a separate LPM to ensure it happens before unroll
-    if (EnableUnrollAndJam && PTO.LoopUnrolling) {
+    if (Opts.enable_unroll_and_jam && PTO.LoopUnrolling) {
       FPM.addPass(createFunctionToLoopPassAdaptor(
           LoopUnrollAndJamPass(static_cast<int>(Level))));
     }
@@ -1523,7 +1360,7 @@ 
PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
 
   // Run partial inlining pass to partially inline functions that have
   // large bodies.
-  if (RunPartialInlining)
+  if (Opts.enable_partial_inlining)
     MPM.addPass(PartialInlinerPass());
 
   // Remove avail extern fns and globals definitions since we aren't compiling
@@ -1565,7 +1402,7 @@ 
PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   // information for all local globals here, the late loop passes and notably
   // the vectorizer will be able to use them to help recognize vectorizable
   // memory operations.
-  if (EnableGlobalAnalyses)
+  if (Opts.enable_global_analyses)
     MPM.addPass(RecomputeGlobalsAAPass());
 
   invokeOptimizerEarlyEPCallbacks(MPM, Level, LTOPhase);
@@ -1583,7 +1420,7 @@ 
PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   // early versioning may prevent further inlining due to increase of code
   // size. Other optimizations which runs later might get benefit of no-alias
   // assumption in clone loop.
-  if (UseLoopVersioningLICM) {
+  if (Opts.enable_loop_versioning_licm) {
     OptimizePM.addPass(
         createFunctionToLoopPassAdaptor(LoopVersioningLICMPass()));
     // LoopVersioningLICM pass might increase new LICM opportunities.
@@ -1598,14 +1435,14 @@ 
PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   if (!isLTOPreLink(LTOPhase))
     OptimizePM.addPass(LowerConstantIntrinsicsPass());
 
-  if (EnableMatrix) {
+  if (Opts.enable_matrix) {
     OptimizePM.addPass(LowerMatrixIntrinsicsPass());
     OptimizePM.addPass(EarlyCSEPass());
   }
 
   // CHR pass should only be applied with the profile information.
   // The check is to check the profile summary information in CHR.
-  if (EnableCHR && Level == OptimizationLevel::O3)
+  if (Opts.enable_chr && Level == OptimizationLevel::O3)
     OptimizePM.addPass(ControlHeightReductionPass());
 
   // FIXME: We need to run some loop optimizations to re-rotate loops after
@@ -1670,7 +1507,7 @@ 
PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
 
   // Merge adjacent icmps into memcmp, then expand memcmp to loads/compares.
   // TODO: move this furter up so that it can be optimized by GVN, etc.
-  if (EnableMergeICmps)
+  if (Opts.enable_mergeicmps)
     OptimizePM.addPass(MergeICmpsPass());
   OptimizePM.addPass(ExpandMemCmpPass());
 
@@ -1699,13 +1536,13 @@ 
PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   invokeOptimizerLastEPCallbacks(MPM, Level, LTOPhase);
 
   // Run the Instrumentor pass late.
-  if (EnableInstrumentor)
+  if (Opts.enable_instrumentor)
     MPM.addPass(InstrumentorPass(FS));
 
   // Split out cold code. Splitting is done late to avoid hiding context from
   // other optimizations and inadvertently regressing performance. The tradeoff
   // is that this has a higher code size cost than splitting early.
-  if (EnableHotColdSplit && !isLTOPreLink(LTOPhase))
+  if (Opts.hot_cold_split && !isLTOPreLink(LTOPhase))
     MPM.addPass(HotColdSplittingPass());
 
   // Now we need to do some global optimization transforms.
@@ -1745,9 +1582,9 @@ 
PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
     // Also, we can't run devirtualization before inlining because the
     // devirtualization depends on the passes optimizing/eliminating vtable GVs
     // and those passes are only effective after inlining.
-    if (EnableModuleInliner) {
+    if (Opts.enable_module_inliner) {
       MPM.addPass(ModuleInlinerPass(::getInlineParamsFromOptLevel(Level),
-                                    UseInlineAdvisor,
+                                    Opts.enable_ml_inliner,
                                     ThinOrFullLTOPhase::None));
     } else {
       MPM.addPass(ModuleInlinerWrapperPass(
@@ -1787,7 +1624,7 @@ 
PassBuilder::buildPerModuleDefaultPipeline(OptimizationLevel Level,
   // Force any function attributes we want the rest of the pipeline to observe.
   MPM.addPass(ForceFunctionAttrsPass());
 
-  if (TriggerCrash)
+  if (Opts.opt_pipeline_trigger_crash)
     MPM.addPass(createModuleToFunctionPassAdaptor(TriggerCrashFunctionPass()));
 
   if (PGOOpt && PGOOpt->DebugInfoForProfiling)
@@ -1918,7 +1755,7 @@ 
PassBuilder::buildThinLTOPreLinkDefaultPipeline(OptimizationLevel Level) {
   // phase that will run after the thin link, running this here ends up with
   // less information than will be available later and it may grow functions in
   // ways that aren't beneficial.
-  if (RunPartialInlining)
+  if (Opts.enable_partial_inlining)
     MPM.addPass(PartialInlinerPass());
 
   if (PGOOpt && PGOOpt->PseudoProbeForProfiling &&
@@ -2207,9 +2044,9 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel 
Level,
   // valuable as the inliner doesn't currently care whether it is inlining an
   // invoke or a call.
   // Run the inliner now.
-  if (EnableModuleInliner) {
+  if (Opts.enable_module_inliner) {
     MPM.addPass(ModuleInlinerPass(::getInlineParamsFromOptLevel(Level),
-                                  UseInlineAdvisor,
+                                  Opts.enable_ml_inliner,
                                   ThinOrFullLTOPhase::FullLTOPostLink));
   } else {
     MPM.addPass(ModuleInlinerWrapperPass(
@@ -2250,7 +2087,7 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel 
Level,
   FPM.addPass(InstCombinePass());
   invokePeepholeEPCallbacks(FPM, Level);
 
-  if (EnableConstraintElimination)
+  if (Opts.enable_constraint_elimination)
     FPM.addPass(ConstraintEliminationPass());
 
   FPM.addPass(JumpThreadingPass());
@@ -2285,7 +2122,7 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel 
Level,
 
   // Require the GlobalsAA analysis for the module so we can query it within
   // MainFPM.
-  if (EnableGlobalAnalyses) {
+  if (Opts.enable_global_analyses) {
     MPM.addPass(RequireAnalysisPass<GlobalsAA, Module>());
     // Invalidate AAManager so it can be recreated and pick up the newly
     // available GlobalsAA.
@@ -2299,7 +2136,7 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel 
Level,
                /*AllowSpeculation=*/true),
       /*USeMemorySSA=*/true));
 
-  if (RunNewGVN)
+  if (Opts.enable_newgvn)
     MainFPM.addPass(NewGVNPass());
   else
     MainFPM.addPass(GVNPass());
@@ -2317,7 +2154,7 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel 
Level,
   invokeVectorizerStartEPCallbacks(MainFPM, Level);
 
   LoopPassManager LPM;
-  if (EnableLoopFlatten && Level >= OptimizationLevel::O2)
+  if (Opts.enable_loop_flatten && Level >= OptimizationLevel::O2)
     LPM.addPass(LoopFlattenPass());
   LPM.addPass(IndVarSimplifyPass());
   LPM.addPass(LoopDeletionPass());
@@ -2357,7 +2194,7 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel 
Level,
   MPM.addPass(DropTypeTestsPass());
 
   // Enable splitting late in the FullLTO post-link pipeline.
-  if (EnableHotColdSplit)
+  if (Opts.hot_cold_split)
     MPM.addPass(HotColdSplittingPass());
 
   // Add late LTO optimization passes.
@@ -2471,7 +2308,7 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel 
Level,
   if (PTO.MergeFunctions)
     MPM.addPass(MergeFunctionsPass());
 
-  if (EnableMatrix)
+  if (Opts.enable_matrix)
     MPM.addPass(
         createModuleToFunctionPassAdaptor(LowerMatrixIntrinsicsPass(true)));
 
@@ -2529,7 +2366,7 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel 
Level,
 
   invokeOptimizerLastEPCallbacks(MPM, Level, Phase);
 
-  if (EnableInstrumentor)
+  if (Opts.enable_instrumentor)
     MPM.addPass(InstrumentorPass(FS));
 
   // Attach !implicit.ref metadata from all functions to copyright strings.
@@ -2570,7 +2407,7 @@ AAManager PassBuilder::buildDefaultAAPipeline() {
   // Because the `AAManager` is a function analysis and `GlobalsAA` is a module
   // analysis, all that the `AAManager` can do is query for any *cached*
   // results from `GlobalsAA` through a readonly proxy.
-  if (EnableGlobalAnalyses)
+  if (Opts.enable_global_analyses)
     AA.registerModuleAnalysis<GlobalsAA>();
 
   // Add target-specific alias analyses.
diff --git a/llvm/lib/Passes/PassesOptions.cpp 
b/llvm/lib/Passes/PassesOptions.cpp
new file mode 100644
index 00000000000000..2afa77a597708c
--- /dev/null
+++ b/llvm/lib/Passes/PassesOptions.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "PassesOptions.h"
+#include "llvm/Option/LibraryOptions.h"
+
+#define OPTIONS_STRUCT_DEFS
+#include "PassesOptions.inc"
+
+static llvm::opt::RegisterLibraryOptions<llvm::PassesOptions> Registration;
diff --git a/llvm/lib/Passes/PassesOptions.h b/llvm/lib/Passes/PassesOptions.h
new file mode 100644
index 00000000000000..5fa83efdd9f0fe
--- /dev/null
+++ b/llvm/lib/Passes/PassesOptions.h
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_PASSES_PASSESOPTIONS_H
+#define LLVM_LIB_PASSES_PASSESOPTIONS_H
+
+#include "llvm/Analysis/InlineAdvisor.h"
+#include "llvm/Transforms/IPO/Attributor.h"
+
+#define OPTIONS_STRUCT_DECL
+#include "PassesOptions.inc"
+
+#endif // LLVM_LIB_PASSES_PASSESOPTIONS_H
diff --git a/llvm/lib/Passes/PassesOptions.td b/llvm/lib/Passes/PassesOptions.td
new file mode 100644
index 00000000000000..add8c5c04165d8
--- /dev/null
+++ b/llvm/lib/Passes/PassesOptions.td
@@ -0,0 +1,150 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+include "llvm/Option/LibraryOptions.td"
+
+def PassesOptions : OptionsStruct;
+
+// PassBuilderPipelines.cpp
+defm : EnumField<"attributor-enable", "AttributorRunOption", 
"AttributorRunOption::NONE",
+  "full,light,module,module-light,cgscc,cgscc-light,none",
+  ["AttributorRunOption::FULL", "AttributorRunOption::LIGHT",
+   "AttributorRunOption::MODULE", "AttributorRunOption::MODULE_LIGHT",
+   "AttributorRunOption::CGSCC", "AttributorRunOption::CGSCC_LIGHT",
+   "AttributorRunOption::NONE"],
+  "Enable the attributor inter-procedural deduction pass">;
+defm : BoolField<"disable-preinline", "false",
+  "Disable pre-instrumentation inliner">;
+defm : BoolField<"eagerly-invalidate-analyses", "true",
+  "Eagerly invalidate more analyses in default pipelines">;
+// This option is used in simplifying testing SampleFDO optimizations for
+// profile loading.
+defm : BoolField<"enable-chr", "true",
+  "Enable control height reduction optimization (CHR)">;
+defm : BoolField<"enable-constraint-elimination", "true",
+  "Enable pass to eliminate conditions based on linear constraints">;
+// TODO: There is a similar flag in WPD pass, we should consolidate them by
+// parsing the option only once in PassBuilder and share it across both places.
+defm : BoolField<"enable-devirtualize-speculatively", "false",
+  "Enable speculative devirtualization optimization">;
+defm : BoolField<"enable-dfa-jump-thread", "true",
+  "Enable DFA jump threading">;
+defm : BoolField<"enable-global-analyses", "true",
+  "Enable inter-procedural analyses">;
+defm : BoolField<"enable-gvn-hoist", "false",
+  "Enable the GVN hoisting pass (default = off)">;
+defm : BoolField<"enable-gvn-sink", "false",
+  "Enable the GVN sinking pass (default = off)">;
+defm : BoolField<"enable-instrumentor", "false",
+  "Enable the Instrumentor Pass">;
+defm : BoolField<"enable-jump-table-to-switch", "true",
+  "Enable JumpTableToSwitch pass (default = true)">;
+defm : BoolField<"enable-loop-flatten", "false",
+  "Enable the LoopFlatten Pass">;
+defm : BoolField<"enable-loop-versioning-licm", "false",
+  "Enable the experimental Loop Versioning LICM pass">;
+defm : BoolField<"enable-loopinterchange", "true",
+  "Enable the LoopInterchange Pass">;
+defm : BoolField<"enable-matrix", "false",
+  "Enable lowering of the matrix intrinsics">;
+defm : BoolField<"enable-merge-functions", "false",
+  "Enable function merging as part of the optimization pipeline">;
+defm : BoolField<"enable-mergeicmps", "true",
+  "Enable MergeICmps pass in the optimization pipeline">;
+defm : EnumField<"enable-ml-inliner", "InliningAdvisorMode", 
"InliningAdvisorMode::Default",
+  "default,development,release",
+  ["InliningAdvisorMode::Default", "InliningAdvisorMode::Development",
+   "InliningAdvisorMode::Release"],
+  "Enable ML policy for inliner. Currently trained for -Oz only">;
+defm : BoolField<"enable-module-inliner", "false",
+  "Enable module inliner">;
+defm : BoolField<"enable-newgvn", "false",
+  "Run the NewGVN pass">;
+defm : BoolField<"enable-npm-pgo-inline-deferral", "true",
+  "Enable inline deferral during PGO">;
+defm : BoolField<"enable-partial-inlining", "false",
+  "Run Partial inlining pass">;
+defm : BoolField<"enable-post-pgo-loop-rotation", "true",
+  "Run the loop rotation transformation after PGO instrumentation">;
+defm : BoolField<"enable-sampled-instrumentation", "false",
+  "Enable profile instrumentation sampling (default = off)">;
+defm : BoolField<"enable-unroll-and-jam", "false",
+  "Enable Unroll And Jam Pass">;
+defm : BoolField<"extra-vectorizer-passes", "false",
+  "Run cleanup optimization passes after vectorization">;
+defm : BoolField<"flattened-profile-used", "false",
+  "Indicate the sample profile being used is flattened, i.e., no inline 
hierarchy exists in the profile">;
+defm : BoolField<"hot-cold-split", "false",
+  "Enable hot-cold splitting pass">;
+defm : ValueField<"instrument-cold-function-only-path", "StringRef", "",
+  "File path for cold function only instrumentation(requires use with 
--pgo-instrument-cold-function-only)">;
+defm : BoolField<"mandatory-inlining-first", "false",
+  "Perform mandatory inlinings module-wide, before performing inlining">;
+defm : BoolField<"opt-pipeline-trigger-crash", "false",
+  "Trigger crash in optimization pipeline">;
+defm : ValueField<"preinline-threshold", "int", "75",
+  "Control the amount of inlining in pre-instrumentation inliner (default = 
75)">;
+
+// RunCodeGen.cpp
+defm : OptionalBoolField<"force-new-pm-codegen",
+  "Whether to force the NewPM on/off. Not setting the option will default to 
what the target prefers">;
+
+// StandardInstrumentations.cpp
+// An option that determines the colour used for elements that are only in the
+// after part. Must be a colour named in appendix J of
+// https://graphviz.org/pdf/dotguide.pdf
+defm : ValueField<"dot-cfg-after-color", "StringRef", "\"forestgreen\"",
+  "Color for dot-cfg after elements">;
+// An option that determines the colour used for elements that are only in the
+// before part. Must be a colour named in appendix J of
+// https://graphviz.org/pdf/dotguide.pdf
+defm : ValueField<"dot-cfg-before-color", "StringRef", "\"red\"",
+  "Color for dot-cfg before elements">;
+// An option that determines the colour used for elements that are in both the
+// before and after parts. Must be a colour named in appendix J of
+// https://graphviz.org/pdf/dotguide.pdf
+defm : ValueField<"dot-cfg-common-color", "StringRef", "\"black\"",
+  "Color for dot-cfg common elements">;
+// An option that determines where the generated website file (named
+// passes.html) and the associated pdf files (named diff_*.pdf) are saved.
+defm : ValueField<"dot-cfg-dir", "StringRef", "\"./\"",
+  "Generate dot files into specified directory for changed IRs">;
+defm : BoolField<"dropped-variable-stats", "false",
+  "Dump dropped debug variables stats">;
+// An option for specifying an executable that will be called with the IR
+// everytime it changes in the opt pipeline. It will also be called on the
+// initial IR as it enters the pipeline. The executable will be passed the name
+// of a temporary file containing the IR and the PassID. This may be used, for
+// example, to call llc on the IR and run a test to determine which pass makes 
a
+// change that changes the functioning of the IR. The usual modifier options
+// work as expected.
+defm : ValueField<"exec-on-ir-change", "StringRef", "",
+  "exe called with module IR after each pass that changes it">;
+let MetaVarName = "<filename>" in
+defm : ValueField<"ir-dump-directory", "StringRef", "",
+  "If specified, IR printed using the -print-[before|after]{-all} options will 
be dumped into files in this directory rather than written to stderr">;
+defm : ValueField<"opt-bisect-print-ir-path", "StringRef", "",
+  "Print IR to path when opt-bisect-limit is reached">;
+// An option that supports the -print-changed option. See the description for
+// -print-changed for an explanation of the use of this option. Note that this
+// option has no effect without -print-changed.
+defm : BoolField<"print-before-changed", "false",
+  "Print before passes that change them">;
+// An option for specifying the dot used by
+// print-changed=[dot-cfg | dot-cfg-quiet]
+defm : ValueField<"print-changed-dot-path", "StringRef", "\"dot\"",
+  "system dot used by change reporters">;
+// Options to print the IR that was being processed when a pass crashes.
+defm : BoolField<"print-on-crash", "false",
+  "Print the last form of the IR before crash (use -print-on-crash-path to 
dump to a file)">;
+defm : ValueField<"print-on-crash-path", "StringRef", "",
+  "Print the last form of the IR before crash to a file">;
+defm : BoolField<"print-pass-numbers", "false",
+  "Print pass names and their ordinals">;
+defm : OptionalBoolField<"verify-analysis-invalidation",
+  "Verify that analyses a pass reports as preserved are unchanged (default: on 
with EXPENSIVE_CHECKS)">;
diff --git a/llvm/lib/Passes/RunCodeGen.cpp b/llvm/lib/Passes/RunCodeGen.cpp
index 0ba67d01c3a9b1..e52491dd187a07 100644
--- a/llvm/lib/Passes/RunCodeGen.cpp
+++ b/llvm/lib/Passes/RunCodeGen.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "llvm/Passes/RunCodeGen.h"
+#include "PassesOptions.h"
 #include "llvm/Analysis/CGSCCPassManager.h"
 #include "llvm/Analysis/LoopAnalysisManager.h"
 #include "llvm/Analysis/RuntimeLibcallInfo.h"
@@ -25,12 +26,6 @@
 
 using namespace llvm;
 
-static cl::opt<cl::boolOrDefault>
-    ForceNewPM("force-new-pm-codegen",
-               cl::desc("Whether to force the NewPM on/off. Not setting the "
-                        "option will default to what the target prefers."),
-               cl::init(cl::boolOrDefault::BOU_UNSET));
-
 static Error
 runCodeGenPipelineLegacy(TargetMachine &TM, Module &M, raw_pwrite_stream &OS,
                          std::unique_ptr<ToolOutputFile> &DwoOS,
@@ -100,9 +95,8 @@ Error llvm::runCodeGenPipeline(TargetMachine &TM, Module &M,
                                CodeGenFileType CGFT, bool PrintPipelinePasses,
                                bool DisableVerify, bool 
DisableSimplifyLibCalls,
                                IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
-  if (ForceNewPM == cl::boolOrDefault::BOU_TRUE ||
-      (TM.shouldDefaultToNewPM() &&
-       ForceNewPM != cl::boolOrDefault::BOU_FALSE)) {
+  if (valueOr(PassesOptions::Global.force_new_pm_codegen,
+              TM.shouldDefaultToNewPM())) {
     return runCodeGenPipelineNewPM(TM, M, OS, DwoOS, CGFT, DisableVerify, VFS);
   }
 
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp 
b/llvm/lib/Passes/StandardInstrumentations.cpp
index 2dbbf23c9eeab6..750d9152484099 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -13,6 +13,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "llvm/Passes/StandardInstrumentations.h"
+#include "PassesOptions.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/StringRef.h"
@@ -49,77 +50,6 @@
 
 using namespace llvm;
 
-static cl::opt<bool> VerifyAnalysisInvalidation("verify-analysis-invalidation",
-                                                cl::Hidden,
-#ifdef EXPENSIVE_CHECKS
-                                                cl::init(true)
-#else
-                                                cl::init(false)
-#endif
-);
-
-// An option that supports the -print-changed option.  See
-// the description for -print-changed for an explanation of the use
-// of this option.  Note that this option has no effect without -print-changed.
-static cl::opt<bool>
-    PrintChangedBefore("print-before-changed",
-                       cl::desc("Print before passes that change them"),
-                       cl::init(false), cl::Hidden);
-
-// An option for specifying the dot used by
-// print-changed=[dot-cfg | dot-cfg-quiet]
-static cl::opt<std::string>
-    DotBinary("print-changed-dot-path", cl::Hidden, cl::init("dot"),
-              cl::desc("system dot used by change reporters"));
-
-// An option that determines the colour used for elements that are only
-// in the before part.  Must be a colour named in appendix J of
-// https://graphviz.org/pdf/dotguide.pdf
-static cl::opt<std::string>
-    BeforeColour("dot-cfg-before-color",
-                 cl::desc("Color for dot-cfg before elements"), cl::Hidden,
-                 cl::init("red"));
-// An option that determines the colour used for elements that are only
-// in the after part.  Must be a colour named in appendix J of
-// https://graphviz.org/pdf/dotguide.pdf
-static cl::opt<std::string>
-    AfterColour("dot-cfg-after-color",
-                cl::desc("Color for dot-cfg after elements"), cl::Hidden,
-                cl::init("forestgreen"));
-// An option that determines the colour used for elements that are in both
-// the before and after parts.  Must be a colour named in appendix J of
-// https://graphviz.org/pdf/dotguide.pdf
-static cl::opt<std::string>
-    CommonColour("dot-cfg-common-color",
-                 cl::desc("Color for dot-cfg common elements"), cl::Hidden,
-                 cl::init("black"));
-
-// An option that determines where the generated website file (named
-// passes.html) and the associated pdf files (named diff_*.pdf) are saved.
-static cl::opt<std::string> DotCfgDir(
-    "dot-cfg-dir",
-    cl::desc("Generate dot files into specified directory for changed IRs"),
-    cl::Hidden, cl::init("./"));
-
-// Options to print the IR that was being processed when a pass crashes.
-static cl::opt<std::string> PrintOnCrashPath(
-    "print-on-crash-path",
-    cl::desc("Print the last form of the IR before crash to a file"),
-    cl::Hidden);
-
-static cl::opt<bool> PrintOnCrash(
-    "print-on-crash",
-    cl::desc("Print the last form of the IR before crash (use 
-print-on-crash-path to dump to a file)"),
-    cl::Hidden);
-
-static cl::opt<std::string> OptBisectPrintIRPath(
-    "opt-bisect-print-ir-path",
-    cl::desc("Print IR to path when opt-bisect-limit is reached"), cl::Hidden);
-
-static cl::opt<bool> PrintPassNumbers(
-    "print-pass-numbers", cl::init(false), cl::Hidden,
-    cl::desc("Print pass names and their ordinals"));
-
 static cl::list<unsigned> PrintBeforePassNumber(
     "print-before-pass-number", cl::CommaSeparated, cl::Hidden,
     cl::desc("Print IR before the passes with specified numbers as "
@@ -130,35 +60,11 @@ static cl::list<unsigned> PrintAfterPassNumber(
     cl::desc("Print IR after the passes with specified numbers as "
              "reported by print-pass-numbers"));
 
-static cl::opt<std::string> IRDumpDirectory(
-    "ir-dump-directory",
-    cl::desc("If specified, IR printed using the "
-             "-print-[before|after]{-all} options will be dumped into "
-             "files in this directory rather than written to stderr"),
-    cl::Hidden, cl::value_desc("filename"));
-
-static cl::opt<bool>
-    DroppedVarStats("dropped-variable-stats", cl::Hidden,
-                    cl::desc("Dump dropped debug variables stats"),
-                    cl::init(false));
-
 static bool shouldGenerateData(const Function &F);
 static bool shouldGenerateData(const MachineFunction &MF);
 
 namespace {
 
-// An option for specifying an executable that will be called with the IR
-// everytime it changes in the opt pipeline.  It will also be called on
-// the initial IR as it enters the pipeline.  The executable will be passed
-// the name of a temporary file containing the IR and the PassID.  This may
-// be used, for example, to call llc on the IR and run a test to determine
-// which pass makes a change that changes the functioning of the IR.
-// The usual modifier options work as expected.
-static cl::opt<std::string>
-    TestChanged("exec-on-ir-change", cl::Hidden, cl::init(""),
-                cl::desc("exe called with module IR after each pass that "
-                         "changes it"));
-
 bool loopContainsPrintSourceLoc(const Loop &L) {
   const Function *F = L.getHeader()->getParent();
   bool SourceLocFilterEmpty = isSourceLocFilterEmpty();
@@ -547,7 +453,7 @@ void IRChangedPrinter::handleAfter(StringRef PassID, 
std::string &Name,
                                    const std::string &Before,
                                    const std::string &After, IRUnitRef) {
   // Report the IR before the changes when requested.
-  if (PrintChangedBefore)
+  if (PassesOptions::Global.print_before_changed)
     Out << "*** IR Dump Before " << PassID << " on " << Name << " ***\n"
         << Before;
 
@@ -564,11 +470,12 @@ void IRChangedPrinter::handleAfter(StringRef PassID, 
std::string &Name,
 IRChangedTester::~IRChangedTester() = default;
 
 void IRChangedTester::registerCallbacks(PassInstrumentationCallbacks &PIC) {
-  if (TestChanged != "")
+  if (PassesOptions::Global.exec_on_ir_change != "")
     TextChangeReporter<std::string>::registerRequiredCallbacks(PIC);
 }
 
 void IRChangedTester::handleIR(const std::string &S, StringRef PassID) {
+  const PassesOptions &Opts = PassesOptions::Global;
   // Store the body into a temporary file
   static SmallVector<int> FD{-1};
   SmallVector<StringRef> SR{S};
@@ -577,13 +484,14 @@ void IRChangedTester::handleIR(const std::string &S, 
StringRef PassID) {
     dbgs() << "Unable to create temporary file.";
     return;
   }
-  static ErrorOr<std::string> Exe = sys::findProgramByName(TestChanged);
+  static ErrorOr<std::string> Exe =
+      sys::findProgramByName(Opts.exec_on_ir_change);
   if (!Exe) {
     dbgs() << "Unable to find test-changed executable.";
     return;
   }
 
-  StringRef Args[] = {TestChanged, FileName[0], PassID};
+  StringRef Args[] = {Opts.exec_on_ir_change, FileName[0], PassID};
   int Result = sys::ExecuteAndWait(*Exe, Args);
   if (Result < 0) {
     dbgs() << "Error executing test-changed executable.";
@@ -839,7 +747,8 @@ StringRef 
PrintIRInstrumentation::getFileSuffix(IRDumpFileSuffixType Type) {
 std::string PrintIRInstrumentation::fetchDumpFilename(
     StringRef PassName, StringRef IRFileDisplayName, unsigned PassNumber,
     IRDumpFileSuffixType SuffixType) {
-  assert(!IRDumpDirectory.empty() &&
+  const PassesOptions &Opts = PassesOptions::Global;
+  assert(!Opts.ir_dump_directory.empty() &&
          "The flag -ir-dump-directory must be passed to dump IR to files");
 
   SmallString<64> Filename;
@@ -850,7 +759,7 @@ std::string PrintIRInstrumentation::fetchDumpFilename(
   FilenameStream << getFileSuffix(SuffixType);
 
   SmallString<128> ResultPath;
-  sys::path::append(ResultPath, IRDumpDirectory, Filename);
+  sys::path::append(ResultPath, Opts.ir_dump_directory, Filename);
   return std::string(ResultPath);
 }
 
@@ -923,7 +832,7 @@ void PrintIRInstrumentation::printBeforePass(StringRef 
PassID, IRUnitRef IR) {
     unwrapAndPrint(Stream, IR);
   };
 
-  if (!IRDumpDirectory.empty()) {
+  if (!PassesOptions::Global.ir_dump_directory.empty()) {
     std::string DumpIRFilename =
         fetchDumpFilename(PassID, getIRFileDisplayName(IR), CurrentPassNumber,
                           IRDumpFileSuffixType::Before);
@@ -958,7 +867,7 @@ void PrintIRInstrumentation::printAfterPass(StringRef 
PassID, IRUnitRef IR) {
     unwrapAndPrint(Stream, IR);
   };
 
-  if (!IRDumpDirectory.empty()) {
+  if (!PassesOptions::Global.ir_dump_directory.empty()) {
     std::string DumpIRFilename =
         fetchDumpFilename(PassID, getIRFileDisplayName(IR), CurrentPassNumber,
                           IRDumpFileSuffixType::After);
@@ -996,7 +905,7 @@ void 
PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID) {
     printIR(Stream, M);
   };
 
-  if (!IRDumpDirectory.empty()) {
+  if (!PassesOptions::Global.ir_dump_directory.empty()) {
     std::string DumpIRFilename =
         fetchDumpFilename(PassID, IRFileDisplayName, PassNumber,
                           IRDumpFileSuffixType::Invalidated);
@@ -1036,7 +945,7 @@ bool 
PrintIRInstrumentation::shouldPrintAfterCurrentPassNumber() {
 }
 
 bool PrintIRInstrumentation::shouldPrintPassNumbers() {
-  return PrintPassNumbers;
+  return PassesOptions::Global.print_pass_numbers;
 }
 
 bool PrintIRInstrumentation::shouldPrintBeforeSomePassNumber() {
@@ -1097,19 +1006,21 @@ bool OptNoneInstrumentation::shouldRun(StringRef 
PassID, IRUnitRef IR) {
 }
 
 bool OptPassGateInstrumentation::shouldRun(StringRef PassName, IRUnitRef IR) {
+  const PassesOptions &Opts = PassesOptions::Global;
   if (isIgnored(PassName))
     return true;
 
   bool ShouldRun = Context.getOptPassGate().shouldRunPass(
       PassName, getIRName(IR, IRContext));
-  if (!ShouldRun && !this->HasWrittenIR && !OptBisectPrintIRPath.empty()) {
+  if (!ShouldRun && !this->HasWrittenIR &&
+      !Opts.opt_bisect_print_ir_path.empty()) {
     // FIXME: print IR if limit is higher than number of opt-bisect
     // invocations
     this->HasWrittenIR = true;
     const Module *M = unwrapModule(IR, /*Force=*/true);
     assert((M && &M->getContext() == &Context) && "Missing/Mismatching 
Module");
     std::error_code EC;
-    raw_fd_ostream OS(OptBisectPrintIRPath, EC);
+    raw_fd_ostream OS(Opts.opt_bisect_print_ir_path, EC);
     if (EC)
       report_fatal_error(errorCodeToError(EC));
     M->print(OS, nullptr);
@@ -1403,7 +1314,13 @@ static SmallVector<Function *, 1> GetFunctions(IRUnitRef 
IR) {
 
 void PreservedCFGCheckerInstrumentation::registerCallbacks(
     PassInstrumentationCallbacks &PIC, ModuleAnalysisManager &MAM) {
-  if (!VerifyAnalysisInvalidation)
+#ifdef EXPENSIVE_CHECKS
+  constexpr bool VerifyByDefault = true;
+#else
+  constexpr bool VerifyByDefault = false;
+#endif
+  if (!valueOr(PassesOptions::Global.verify_analysis_invalidation,
+               VerifyByDefault))
     return;
 
   bool Registered = false;
@@ -1864,12 +1781,13 @@ class DotCfgDiffNode {
   void setCommon(const BlockDataT<DCData> &Other) {
     assert(!Data[1] && "Expected only one block datum");
     Data[1] = &Other;
-    Colour = CommonColour;
+    Colour = PassesOptions::Global.dot_cfg_common_color;
   }
   // Add an edge to \p E of colour {\p Value, \p Colour}.
   void addEdge(unsigned E, StringRef Value, StringRef Colour) {
     // This is a new edge or it is an edge being made common.
-    assert((EdgesMap.count(E) == 0 || Colour == CommonColour) &&
+    assert((EdgesMap.count(E) == 0 ||
+            Colour == PassesOptions::Global.dot_cfg_common_color) &&
            "Unexpected edge count and color.");
     EdgesMap[E] = {Value.str(), Colour};
   }
@@ -1955,7 +1873,8 @@ class DotCfgDiff {
 };
 
 std::string DotCfgDiffNode::getBodyContent() const {
-  if (Colour == CommonColour) {
+  const PassesOptions &Opts = PassesOptions::Global;
+  if (Colour == Opts.dot_cfg_common_color) {
     assert(Data[1] && "Expected Data[1] to be set.");
 
     StringRef SR[2];
@@ -1967,12 +1886,15 @@ std::string DotCfgDiffNode::getBodyContent() const {
       SR[I] = SR[I].drop_until([](char C) { return C == '\n'; }).drop_front();
     }
 
-    SmallString<80> OldLineFormat = formatv(
-        "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", BeforeColour);
-    SmallString<80> NewLineFormat = formatv(
-        "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", AfterColour);
-    SmallString<80> UnchangedLineFormat = formatv(
-        "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", CommonColour);
+    SmallString<80> OldLineFormat =
+        formatv("<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>",
+                Opts.dot_cfg_before_color);
+    SmallString<80> NewLineFormat =
+        formatv("<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>",
+                Opts.dot_cfg_after_color);
+    SmallString<80> UnchangedLineFormat =
+        formatv("<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>",
+                Opts.dot_cfg_common_color);
     std::string Diff = Data[0]->getLabel().str();
     Diff += ":\n<BR align=\"left\"/>" +
             doSystemDiff(makeHTMLReady(SR[0]), makeHTMLReady(SR[1]),
@@ -2029,13 +1951,14 @@ std::string DotCfgDiff::colourize(std::string S, 
StringRef Colour) const {
 DotCfgDiff::DotCfgDiff(StringRef Title, const FuncDataT<DCData> &Before,
                        const FuncDataT<DCData> &After)
     : GraphName(Title.str()) {
+  const PassesOptions &Opts = PassesOptions::Global;
   StringMap<StringRef> EdgesMap;
 
   // Handle each basic block in the before IR.
   for (auto &B : Before.getData()) {
     StringRef Label = B.getKey();
     const BlockDataT<DCData> &BD = B.getValue();
-    createNode(Label, BD, BeforeColour);
+    createNode(Label, BD, Opts.dot_cfg_before_color);
 
     // Create transitions with names made up of the from block label, the value
     // on which the transition is made and the to block label.
@@ -2044,7 +1967,7 @@ DotCfgDiff::DotCfgDiff(StringRef Title, const 
FuncDataT<DCData> &Before,
          Sink != E; ++Sink) {
       std::string Key = (Label + " " + Sink->getKey().str()).str() + " " +
                         BD.getData().getSuccessorLabel(Sink->getKey()).str();
-      EdgesMap.insert({Key, BeforeColour});
+      EdgesMap.insert({Key, Opts.dot_cfg_before_color});
     }
   }
 
@@ -2055,7 +1978,7 @@ DotCfgDiff::DotCfgDiff(StringRef Title, const 
FuncDataT<DCData> &Before,
     auto It = NodePosition.find(Label);
     if (It == NodePosition.end())
       // This only exists in the after IR.  Create the node.
-      createNode(Label, BD, AfterColour);
+      createNode(Label, BD, Opts.dot_cfg_after_color);
     else
       Nodes[It->second].setCommon(BD);
     // Add in the edges between the nodes (as common or only in after).
@@ -2064,9 +1987,9 @@ DotCfgDiff::DotCfgDiff(StringRef Title, const 
FuncDataT<DCData> &Before,
          Sink != E; ++Sink) {
       std::string Key = (Label + " " + Sink->getKey().str()).str() + " " +
                         BD.getData().getSuccessorLabel(Sink->getKey()).str();
-      auto [It, Inserted] = EdgesMap.try_emplace(Key, AfterColour);
+      auto [It, Inserted] = EdgesMap.try_emplace(Key, 
Opts.dot_cfg_after_color);
       if (!Inserted)
-        It->second = CommonColour;
+        It->second = Opts.dot_cfg_common_color;
     }
   }
 
@@ -2094,7 +2017,7 @@ DotCfgDiff::DotCfgDiff(StringRef Title, const 
FuncDataT<DCData> &Before,
     else {
       StringRef V = It->getValue();
       std::string NV = colourize(V.str() + " " + Value.str(), Colour);
-      Colour = CommonColour;
+      Colour = Opts.dot_cfg_common_color;
       It->getValue() = NV;
     }
     SourceNode.addEdge(SinkNode, Value, Colour);
@@ -2311,14 +2234,17 @@ void DotCfgChangeReporter::handleFunctionCompare(
 }
 
 std::string DotCfgChangeReporter::genHTML(StringRef Text, StringRef DotFile,
-                                          StringRef PDFFileName) {
-  SmallString<20> PDFFile = formatv("{0}/{1}", DotCfgDir, PDFFileName);
+                                          StringRef PDFFileName) const {
+  const PassesOptions &Opts = PassesOptions::Global;
+  SmallString<20> PDFFile = formatv("{0}/{1}", OutputDir, PDFFileName);
   // Create the PDF file.
-  static ErrorOr<std::string> DotExe = sys::findProgramByName(DotBinary);
+  static ErrorOr<std::string> DotExe =
+      sys::findProgramByName(Opts.print_changed_dot_path);
   if (!DotExe)
     return "Unable to find dot executable.";
 
-  StringRef Args[] = {DotBinary, "-Tpdf", "-o", PDFFile, DotFile};
+  StringRef Args[] = {Opts.print_changed_dot_path, "-Tpdf", "-o", PDFFile,
+                      DotFile};
   int Result = sys::ExecuteAndWait(*DotExe, Args, std::nullopt);
   if (Result < 0)
     return "Error executing system dot.";
@@ -2412,7 +2338,7 @@ void DotCfgChangeReporter::handleIgnored(StringRef 
PassID, std::string &Name) {
 
 bool DotCfgChangeReporter::initializeHTML() {
   std::error_code EC;
-  HTML = std::make_unique<raw_fd_ostream>(DotCfgDir + "/passes.html", EC);
+  HTML = std::make_unique<raw_fd_ostream>(OutputDir + "/passes.html", EC);
   if (EC) {
     HTML = nullptr;
     return false;
@@ -2475,11 +2401,11 @@ void DotCfgChangeReporter::registerCallbacks(
     PassInstrumentationCallbacks &PIC) {
   if (PrintChanged == ChangePrinter::DotCfgVerbose ||
        PrintChanged == ChangePrinter::DotCfgQuiet) {
-    SmallString<128> OutputDir;
-    sys::fs::expand_tilde(DotCfgDir, OutputDir);
-    sys::fs::make_absolute(OutputDir);
-    assert(!OutputDir.empty() && "expected output dir to be non-empty");
-    DotCfgDir = OutputDir.c_str();
+    SmallString<128> Dir;
+    sys::fs::expand_tilde(PassesOptions::Global.dot_cfg_dir, Dir);
+    sys::fs::make_absolute(Dir);
+    assert(!Dir.empty() && "expected output dir to be non-empty");
+    OutputDir = Dir.str();
     if (initializeHTML()) {
       ChangeReporter<IRDataT<DCData>>::registerRequiredCallbacks(PIC);
       return;
@@ -2499,16 +2425,18 @@ StandardInstrumentations::StandardInstrumentations(
                        PrintChanged == ChangePrinter::ColourDiffVerbose ||
                            PrintChanged == ChangePrinter::ColourDiffQuiet),
       WebsiteChangeReporter(PrintChanged == ChangePrinter::DotCfgVerbose),
-      Verify(DebugLogging), DroppedStatsIR(DroppedVarStats),
+      Verify(DebugLogging),
+      DroppedStatsIR(PassesOptions::Global.dropped_variable_stats),
       VerifyEach(VerifyEach) {}
 
 PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter =
     nullptr;
 
 void PrintCrashIRInstrumentation::reportCrashIR() {
-  if (!PrintOnCrashPath.empty()) {
+  const PassesOptions &Opts = PassesOptions::Global;
+  if (!Opts.print_on_crash_path.empty()) {
     std::error_code EC;
-    raw_fd_ostream Out(PrintOnCrashPath, EC);
+    raw_fd_ostream Out(Opts.print_on_crash_path, EC);
     if (EC)
       report_fatal_error(errorCodeToError(EC));
     Out << SavedIR;
@@ -2523,7 +2451,8 @@ void PrintCrashIRInstrumentation::SignalHandler(void *) {
   if (!CrashReporter)
     return;
 
-  assert((PrintOnCrash || !PrintOnCrashPath.empty()) &&
+  assert((PassesOptions::Global.print_on_crash ||
+          !PassesOptions::Global.print_on_crash_path.empty()) &&
          "Did not expect to get here without option set.");
   CrashReporter->reportCrashIR();
 }
@@ -2532,14 +2461,17 @@ 
PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() {
   if (!CrashReporter)
     return;
 
-  assert((PrintOnCrash || !PrintOnCrashPath.empty()) &&
+  assert((PassesOptions::Global.print_on_crash ||
+          !PassesOptions::Global.print_on_crash_path.empty()) &&
          "Did not expect to get here without option set.");
   CrashReporter = nullptr;
 }
 
 void PrintCrashIRInstrumentation::registerCallbacks(
     PassInstrumentationCallbacks &PIC) {
-  if ((!PrintOnCrash && PrintOnCrashPath.empty()) || CrashReporter)
+  const PassesOptions &Opts = PassesOptions::Global;
+  if ((!Opts.print_on_crash && Opts.print_on_crash_path.empty()) ||
+      CrashReporter)
     return;
 
   sys::AddSignalHandler(SignalHandler, nullptr);
diff --git a/llvm/utils/gn/secondary/llvm/lib/Passes/BUILD.gn 
b/llvm/utils/gn/secondary/llvm/lib/Passes/BUILD.gn
index 7f60735acfd687..c99b3c50256e21 100644
--- a/llvm/utils/gn/secondary/llvm/lib/Passes/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/lib/Passes/BUILD.gn
@@ -1,12 +1,21 @@
+import("//llvm/utils/TableGen/tablegen.gni")
+
+tablegen("PassesOptions") {
+  visibility = [ ":Passes" ]
+  args = [ "-gen-opt-parser-defs" ]
+}
+
 static_library("Passes") {
   output_name = "LLVMPasses"
   deps = [
+    ":PassesOptions",
     "//llvm/lib/Analysis",
     "//llvm/lib/CodeGen",
     "//llvm/lib/CodeGen/AsmPrinter",
     "//llvm/lib/CodeGen/GlobalISel",
     "//llvm/lib/IR",
     "//llvm/lib/IRPrinter",
+    "//llvm/lib/Option",
     "//llvm/lib/Support",
     "//llvm/lib/Target",
     "//llvm/lib/Transforms/AggressiveInstCombine",
@@ -26,6 +35,7 @@ static_library("Passes") {
     "PassBuilder.cpp",
     "PassBuilderBindings.cpp",
     "PassBuilderPipelines.cpp",
+    "PassesOptions.cpp",
     "RunCodeGen.cpp",
     "StandardInstrumentations.cpp",
     "TriggerCrashPasses.cpp",
diff --git a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel 
b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
index 5e803e902478f7..5491022ea7ab82 100644
--- a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
@@ -4391,10 +4391,20 @@ cc_library(
     textual_hdrs = ["lib/Passes/PassRegistry.def"],
 )
 
+gentbl_cc_library(
+    name = "PassesOptionsTableGen",
+    strip_include_prefix = "lib/Passes",
+    tbl_outs = {"lib/Passes/PassesOptions.inc": ["-gen-opt-parser-defs"]},
+    tblgen = ":llvm-min-tblgen",
+    td_file = "lib/Passes/PassesOptions.td",
+    deps = [":OptParserTdFiles"],
+)
+
 cc_library(
     name = "Passes",
     srcs = glob([
         "lib/Passes/*.cpp",
+        "lib/Passes/*.h",
     ]),
     hdrs = glob([
         "include/llvm/Passes/*.h",
@@ -4416,6 +4426,8 @@ cc_library(
         ":Instrumentation",
         ":MC",
         ":ObjCARC",
+        ":Option",
+        ":PassesOptionsTableGen",
         ":Plugins",
         ":Scalar",
         ":Support",

>From 0b9f6e0619f3485b92614ab572987fb88ea97bfd Mon Sep 17 00:00:00 2001
From: Fangrui Song <[email protected]>
Date: Thu, 8 Oct 2026 10:17:31 -0700
Subject: [PATCH 2/3] [Passes] Port -print-pipeline-passes to PassesOptions

-print-pipeline-passes takes an optional value (=text or =tree), which
LibraryOptions does not support. Split it into a bool
-print-pipeline-passes and -print-pipeline-passes-format=text|tree.

clang, flang, LTO, llc, and opt read the format through
getPrintPipelinePasses() instead of the exported cl::opt.

Aided by Opus 5.5
---
 clang/lib/CodeGen/BackendUtil.cpp            |  4 +--
 flang/lib/Frontend/FrontendActions.cpp       |  2 +-
 llvm/include/llvm/Passes/PassBuilder.h       | 14 ++-------
 llvm/lib/LTO/LTOBackend.cpp                  |  2 +-
 llvm/lib/Passes/PassBuilder.cpp              | 32 +++-----------------
 llvm/lib/Passes/PassesOptions.h              |  1 +
 llvm/lib/Passes/PassesOptions.td             |  8 +++++
 llvm/test/CodeGen/AMDGPU/llc-pipeline-npm.ll |  6 ++--
 llvm/test/CodeGen/Lanai/llc-pipeline-npm.ll  |  2 +-
 llvm/test/CodeGen/NVPTX/llc-pipeline-npm.ll  |  4 +--
 llvm/test/CodeGen/RISCV/O0-newpm-pipeline.ll |  4 +--
 llvm/test/CodeGen/RISCV/O3-newpm-pipeline.ll |  4 +--
 llvm/test/CodeGen/X86/llc-pipeline-npm.ll    |  8 ++---
 llvm/test/tools/opt/print-pipeline-passes.ll |  2 +-
 llvm/tools/llc/lib/NewPMDriver.cpp           |  5 +--
 llvm/tools/opt/NewPMDriver.cpp               |  5 +--
 16 files changed, 42 insertions(+), 61 deletions(-)

diff --git a/clang/lib/CodeGen/BackendUtil.cpp 
b/clang/lib/CodeGen/BackendUtil.cpp
index c754c75aa59de5..2cdc655f99f066 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1185,7 +1185,7 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
   // This should be done for both clang and flang simultaneously.
   // Print a textual, '-passes=' compatible, representation of pipeline if
   // requested.
-  if (PrintPipelinePasses) {
+  if (getPrintPipelinePasses()) {
     MPM.printPipeline(outs(), [&PIC](StringRef ClassName) {
       auto PassName = PIC.getPassNameForClassName(ClassName);
       return PassName.empty() ? ClassName : PassName;
@@ -1234,7 +1234,7 @@ void EmitAssemblyHelper::RunCodegenPipeline(
 
   TimeCodegenPasses([&]() {
     Error CodeGenError = runCodeGenPipeline(
-        *TM, *TheModule, *OS, DwoOS, CGFT, PrintPipelinePasses.has_value(),
+        *TM, *TheModule, *OS, DwoOS, CGFT, 
getPrintPipelinePasses().has_value(),
         !CodeGenOpts.VerifyModule, /*DisableSimplifyLibCalls=*/false,
         CI.getVirtualFileSystemPtr());
     if (CodeGenError)
diff --git a/flang/lib/Frontend/FrontendActions.cpp 
b/flang/lib/Frontend/FrontendActions.cpp
index 06d67e9417bc44..92fb562a9b6a0c 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -1108,7 +1108,7 @@ void 
CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
   // Print a textual, '-passes=' compatible, representation of pipeline if
   // requested. In this case, don't run the passes. This mimics the behavior of
   // clang.
-  if (llvm::PrintPipelinePasses) {
+  if (llvm::getPrintPipelinePasses()) {
     mpm.printPipeline(llvm::outs(), [&pic](llvm::StringRef className) {
       auto passName = pic.getPassNameForClassName(className);
       return passName.empty() ? className : passName;
diff --git a/llvm/include/llvm/Passes/PassBuilder.h 
b/llvm/include/llvm/Passes/PassBuilder.h
index 245725f13ed8a4..e77a05012a4d3f 100644
--- a/llvm/include/llvm/Passes/PassBuilder.h
+++ b/llvm/include/llvm/Passes/PassBuilder.h
@@ -1035,17 +1035,9 @@ enum class PrintPipelinePassesFormat {
   Tree,
 };
 
-struct PrintPipelinePassesFormatParser
-    : public cl::parser<std::optional<PrintPipelinePassesFormat>> {
-  using cl::parser<std::optional<PrintPipelinePassesFormat>>::parser;
-  LLVM_ABI bool parse(cl::Option &O, StringRef ArgName, StringRef ArgValue,
-                      std::optional<PrintPipelinePassesFormat> &Val);
-};
-
-/// Common option used by multiple tools to print pipeline passes
-LLVM_ABI extern cl::opt<std::optional<PrintPipelinePassesFormat>, false,
-                        PrintPipelinePassesFormatParser>
-    PrintPipelinePasses;
+/// The format -print-pipeline-passes requests, or std::nullopt if it is not
+/// given.
+LLVM_ABI std::optional<PrintPipelinePassesFormat> getPrintPipelinePasses();
 
 LLVM_ABI void printFormattedPipelinePasses(
     raw_ostream &OS, StringRef Pipeline,
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index 1136d50e056559..454095ea5ac836 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -378,7 +378,7 @@ static void runNewPMPasses(const Config &Conf, Module &Mod, 
TargetMachine *TM,
   if (!Conf.DisableVerify)
     MPM.addPass(VerifierPass());
 
-  if (PrintPipelinePasses) {
+  if (getPrintPipelinePasses()) {
     std::string PipelineStr;
     raw_string_ostream OS(PipelineStr);
     MPM.printPipeline(OS, [&PIC](StringRef ClassName) {
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 4f973196eb951e..1d507a12069dbc 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -219,7 +219,6 @@
 #include "llvm/Passes/OptimizationLevel.h"
 #include "llvm/Passes/TriggerCrashPasses.h"
 #include "llvm/Support/CodeGen.h"
-#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -425,32 +424,11 @@
 
 using namespace llvm;
 
-cl::opt<std::optional<PrintPipelinePassesFormat>, false,
-        PrintPipelinePassesFormatParser>
-    llvm::PrintPipelinePasses(
-        "print-pipeline-passes", cl::ValueOptional,
-        cl::desc(
-            "Print string describing the pipeline (best-effort only).\n"
-            "  - =text\tPrint a '-passes' compatible string describing the "
-            "pipeline.\n"
-            "  - =tree\tPrint a tree-like structure describing the 
pipeline."));
-
-bool PrintPipelinePassesFormatParser::parse(
-    cl::Option &O, StringRef ArgName, StringRef Arg,
-    std::optional<PrintPipelinePassesFormat> &Val) {
-  std::optional<PrintPipelinePassesFormat> Format =
-      StringSwitch<std::optional<PrintPipelinePassesFormat>>(Arg)
-          .Case("text", PrintPipelinePassesFormat::Text)
-          .Case("", PrintPipelinePassesFormat::Text)
-          .Case("tree", PrintPipelinePassesFormat::Tree)
-          .Default(std::nullopt);
-
-  if (!Format)
-    return O.error(formatv(
-        "'{0}' value invalid for print-pipeline-passes argument!", Arg));
-
-  Val = Format;
-  return false;
+std::optional<PrintPipelinePassesFormat> llvm::getPrintPipelinePasses() {
+  const PassesOptions &Opts = PassesOptions::Global;
+  if (!Opts.print_pipeline_passes)
+    return std::nullopt;
+  return Opts.print_pipeline_passes_format;
 }
 
 void llvm::printFormattedPipelinePasses(raw_ostream &OS, StringRef Pipeline,
diff --git a/llvm/lib/Passes/PassesOptions.h b/llvm/lib/Passes/PassesOptions.h
index 5fa83efdd9f0fe..5e434e5d25d17c 100644
--- a/llvm/lib/Passes/PassesOptions.h
+++ b/llvm/lib/Passes/PassesOptions.h
@@ -10,6 +10,7 @@
 #define LLVM_LIB_PASSES_PASSESOPTIONS_H
 
 #include "llvm/Analysis/InlineAdvisor.h"
+#include "llvm/Passes/PassBuilder.h"
 #include "llvm/Transforms/IPO/Attributor.h"
 
 #define OPTIONS_STRUCT_DECL
diff --git a/llvm/lib/Passes/PassesOptions.td b/llvm/lib/Passes/PassesOptions.td
index add8c5c04165d8..4a98d394d292d0 100644
--- a/llvm/lib/Passes/PassesOptions.td
+++ b/llvm/lib/Passes/PassesOptions.td
@@ -10,6 +10,14 @@ include "llvm/Option/LibraryOptions.td"
 
 def PassesOptions : OptionsStruct;
 
+// PassBuilder.cpp
+defm : BoolField<"print-pipeline-passes", "false",
+  "Print string describing the pipeline (best-effort only)">;
+defm : EnumField<"print-pipeline-passes-format", "PrintPipelinePassesFormat",
+  "PrintPipelinePassesFormat::Text", "text,tree",
+  ["PrintPipelinePassesFormat::Text", "PrintPipelinePassesFormat::Tree"],
+  "The -print-pipeline-passes format: a '-passes' compatible string, or a 
tree-like structure">;
+
 // PassBuilderPipelines.cpp
 defm : EnumField<"attributor-enable", "AttributorRunOption", 
"AttributorRunOption::NONE",
   "full,light,module,module-light,cgscc,cgscc-light,none",
diff --git a/llvm/test/CodeGen/AMDGPU/llc-pipeline-npm.ll 
b/llvm/test/CodeGen/AMDGPU/llc-pipeline-npm.ll
index 073d46d24ea8af..0e3f9eb7baadd5 100644
--- a/llvm/test/CodeGen/AMDGPU/llc-pipeline-npm.ll
+++ b/llvm/test/CodeGen/AMDGPU/llc-pipeline-npm.ll
@@ -1,10 +1,10 @@
-; RUN: llc -enable-new-pm -mtriple=amdgpu7.00--amdhsa -O0 
-print-pipeline-passes=tree < %s 2>&1 \
+; RUN: llc -enable-new-pm -mtriple=amdgpu7.00--amdhsa -O0 
-print-pipeline-passes -print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | FileCheck -check-prefix=GCN-O0 %s
 
-; RUN: llc -enable-new-pm -mtriple=amdgpu7.00--amdhsa 
-print-pipeline-passes=tree < %s 2>&1 \
+; RUN: llc -enable-new-pm -mtriple=amdgpu7.00--amdhsa -print-pipeline-passes 
-print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | FileCheck -check-prefix=GCN-O2 %s
 
-; RUN: llc -O3 -enable-new-pm -mtriple=amdgpu7.00--amdhsa 
-print-pipeline-passes=tree < %s 2>&1 \
+; RUN: llc -O3 -enable-new-pm -mtriple=amdgpu7.00--amdhsa 
-print-pipeline-passes -print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | FileCheck -check-prefix=GCN-O3 %s
 
 ; GCN-O0: require<MachineModuleAnalysis>
diff --git a/llvm/test/CodeGen/Lanai/llc-pipeline-npm.ll 
b/llvm/test/CodeGen/Lanai/llc-pipeline-npm.ll
index e0a80177683a43..33ec73ef7dd9f8 100644
--- a/llvm/test/CodeGen/Lanai/llc-pipeline-npm.ll
+++ b/llvm/test/CodeGen/Lanai/llc-pipeline-npm.ll
@@ -1,4 +1,4 @@
-; RUN: llc -enable-new-pm -mtriple=lanai -print-pipeline-passes=tree < %s 2>&1 
\
+; RUN: llc -enable-new-pm -mtriple=lanai -print-pipeline-passes 
-print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | FileCheck %s
 
 ; CHECK: require<MachineModuleAnalysis>
diff --git a/llvm/test/CodeGen/NVPTX/llc-pipeline-npm.ll 
b/llvm/test/CodeGen/NVPTX/llc-pipeline-npm.ll
index c8fc7952678fbe..5aae85ddb0c27c 100644
--- a/llvm/test/CodeGen/NVPTX/llc-pipeline-npm.ll
+++ b/llvm/test/CodeGen/NVPTX/llc-pipeline-npm.ll
@@ -1,7 +1,7 @@
 ; RUN: llc -enable-new-pm -mtriple=nvptx64-nvidia-cuda -mcpu=sm_90 -O0 \
-; RUN:     -print-pipeline-passes=tree < %s 2>&1 | FileCheck %s 
--check-prefix=O0
+; RUN:     -print-pipeline-passes -print-pipeline-passes-format=tree < %s 2>&1 
| FileCheck %s --check-prefix=O0
 ; RUN: llc -enable-new-pm -mtriple=nvptx64-nvidia-cuda -mcpu=sm_90 -O2 \
-; RUN:     -print-pipeline-passes=tree < %s 2>&1 | FileCheck %s 
--check-prefix=O2
+; RUN:     -print-pipeline-passes -print-pipeline-passes-format=tree < %s 2>&1 
| FileCheck %s --check-prefix=O2
 
 ; O0: require<MachineModuleAnalysis>
 ; O0-NEXT: require<profile-summary>
diff --git a/llvm/test/CodeGen/RISCV/O0-newpm-pipeline.ll 
b/llvm/test/CodeGen/RISCV/O0-newpm-pipeline.ll
index 713a977b3024f2..92bb328e2f1812 100644
--- a/llvm/test/CodeGen/RISCV/O0-newpm-pipeline.ll
+++ b/llvm/test/CodeGen/RISCV/O0-newpm-pipeline.ll
@@ -1,7 +1,7 @@
-; RUN: llc -enable-new-pm -mtriple=riscv32 -O0 -print-pipeline-passes=tree < 
%s 2>&1 \
+; RUN: llc -enable-new-pm -mtriple=riscv32 -O0 -print-pipeline-passes 
-print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | grep -v verify \
 ; RUN:   | FileCheck %s
-; RUN: llc -enable-new-pm -mtriple=riscv64 -O0 -print-pipeline-passes=tree < 
%s 2>&1 \
+; RUN: llc -enable-new-pm -mtriple=riscv64 -O0 -print-pipeline-passes 
-print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | grep -v verify \
 ; RUN:   | FileCheck %s
 
diff --git a/llvm/test/CodeGen/RISCV/O3-newpm-pipeline.ll 
b/llvm/test/CodeGen/RISCV/O3-newpm-pipeline.ll
index df952e3077de4c..8f9a57b14ec3d9 100644
--- a/llvm/test/CodeGen/RISCV/O3-newpm-pipeline.ll
+++ b/llvm/test/CodeGen/RISCV/O3-newpm-pipeline.ll
@@ -1,7 +1,7 @@
-; RUN: llc -enable-new-pm -mtriple=riscv32 -O3 -print-pipeline-passes=tree < 
%s 2>&1 \
+; RUN: llc -enable-new-pm -mtriple=riscv32 -O3 -print-pipeline-passes 
-print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | grep -v verify \
 ; RUN:   | FileCheck %s
-; RUN: llc -enable-new-pm -mtriple=riscv64 -O3 -print-pipeline-passes=tree < 
%s 2>&1 \
+; RUN: llc -enable-new-pm -mtriple=riscv64 -O3 -print-pipeline-passes 
-print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | grep -v verify \
 ; RUN:   | FileCheck %s --check-prefixes=CHECK,RV64
 
diff --git a/llvm/test/CodeGen/X86/llc-pipeline-npm.ll 
b/llvm/test/CodeGen/X86/llc-pipeline-npm.ll
index e5c2010840565e..64b17d8c53046d 100644
--- a/llvm/test/CodeGen/X86/llc-pipeline-npm.ll
+++ b/llvm/test/CodeGen/X86/llc-pipeline-npm.ll
@@ -1,13 +1,13 @@
-; RUN: llc -enable-new-pm -mtriple=x86_64 -O0 -print-pipeline-passes=tree < %s 
2>&1 \
+; RUN: llc -enable-new-pm -mtriple=x86_64 -O0 -print-pipeline-passes 
-print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | FileCheck -check-prefix=O0 %s
 
-; RUN: llc -enable-new-pm -mtriple=x86_64 -O2 -print-pipeline-passes=tree < %s 
2>&1 \
+; RUN: llc -enable-new-pm -mtriple=x86_64 -O2 -print-pipeline-passes 
-print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | FileCheck -check-prefix=O2 %s
 
-; RUN: llc -enable-new-pm -mtriple=x86_64-windows -O0 
-print-pipeline-passes=tree < %s 2>&1 \
+; RUN: llc -enable-new-pm -mtriple=x86_64-windows -O0 -print-pipeline-passes 
-print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | FileCheck -check-prefix=O0-WINDOWS %s
 
-; RUN: llc -enable-new-pm -mtriple=x86_64-windows -O3 
-print-pipeline-passes=tree < %s 2>&1 \
+; RUN: llc -enable-new-pm -mtriple=x86_64-windows -O3 -print-pipeline-passes 
-print-pipeline-passes-format=tree < %s 2>&1 \
 ; RUN:   | FileCheck -check-prefix=O3-WINDOWS %s
 
 ; O0: require<MachineModuleAnalysis>
diff --git a/llvm/test/tools/opt/print-pipeline-passes.ll 
b/llvm/test/tools/opt/print-pipeline-passes.ll
index e63ccf53f439ce..1b511a8240a71f 100644
--- a/llvm/test/tools/opt/print-pipeline-passes.ll
+++ b/llvm/test/tools/opt/print-pipeline-passes.ll
@@ -2,7 +2,7 @@
 ; RUN: FileCheck %s --check-prefix=TEXT
 
 ; RUN: opt -p 'no-op-module,function(no-op-function)' %s --filetype=null \
-; RUN: -S --print-pipeline-passes=tree | \
+; RUN: -S --print-pipeline-passes --print-pipeline-passes-format=tree | \
 ; RUN: FileCheck %s --strict-whitespace --match-full-lines --check-prefix=TREE
 
 ; TEXT: no-op-module,verify,print
diff --git a/llvm/tools/llc/lib/NewPMDriver.cpp 
b/llvm/tools/llc/lib/NewPMDriver.cpp
index 3eac9bd15355e0..46f08ce194ab7d 100644
--- a/llvm/tools/llc/lib/NewPMDriver.cpp
+++ b/llvm/tools/llc/lib/NewPMDriver.cpp
@@ -182,14 +182,15 @@ int llvm::compileModuleWithNewPM(
   }
 
   // If user only wants to print the pipeline, print it before parsing the MIR.
-  if (PrintPipelinePasses) {
+  if (std::optional<PrintPipelinePassesFormat> Format =
+          getPrintPipelinePasses()) {
     std::string PipelineStr;
     raw_string_ostream OS(PipelineStr);
     MPM.printPipeline(OS, [&PIC](StringRef ClassName) {
       auto PassName = PIC.getPassNameForClassName(ClassName);
       return PassName.empty() ? ClassName : PassName;
     });
-    printFormattedPipelinePasses(outs(), PipelineStr, *PrintPipelinePasses);
+    printFormattedPipelinePasses(outs(), PipelineStr, *Format);
     outs() << '\n';
     return 0;
   }
diff --git a/llvm/tools/opt/NewPMDriver.cpp b/llvm/tools/opt/NewPMDriver.cpp
index e19dc61e772a31..5312da538a209c 100644
--- a/llvm/tools/opt/NewPMDriver.cpp
+++ b/llvm/tools/opt/NewPMDriver.cpp
@@ -571,14 +571,15 @@ bool llvm::runPassPipeline(
 
   // Print a textual, '-passes=' compatible, representation of pipeline if
   // requested.
-  if (PrintPipelinePasses) {
+  if (std::optional<PrintPipelinePassesFormat> Format =
+          getPrintPipelinePasses()) {
     std::string Pipeline;
     raw_string_ostream SOS(Pipeline);
     MPM.printPipeline(SOS, [&PIC](StringRef ClassName) {
       auto PassName = PIC.getPassNameForClassName(ClassName);
       return PassName.empty() ? ClassName : PassName;
     });
-    printFormattedPipelinePasses(outs(), Pipeline, *PrintPipelinePasses);
+    printFormattedPipelinePasses(outs(), Pipeline, *Format);
     outs() << "\n";
 
     if (!DisablePipelineVerification) {

>From 1f62e6fa7dd8c0eda21a2e58baf7d4355e82b60f Mon Sep 17 00:00:00 2001
From: Fangrui Song <[email protected]>
Date: Thu, 8 Oct 2026 20:49:45 -0700
Subject: [PATCH 3/3] Keep generated .inc files smaller

---
 llvm/include/llvm/Option/LibraryOptions.td     |  4 ++--
 .../TableGen/Basic/OptionParserEmitter.cpp     | 18 ++++++++++++------
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/Option/LibraryOptions.td 
b/llvm/include/llvm/Option/LibraryOptions.td
index 5bad10abcadce5..7bd708a7d8f3bb 100644
--- a/llvm/include/llvm/Option/LibraryOptions.td
+++ b/llvm/include/llvm/Option/LibraryOptions.td
@@ -41,7 +41,7 @@ class EnumValues<string values, list<code> enumerators>
 // A bool member set by -<name> or -<name>=<true|false|1|0>.
 multiclass BoolField<string name, code default, string help> {
   def NAME : FlagOrEq<["-", "--"], name>, Field<NAME, "bool", default>,
-    BareValue<"true">, HelpText<help>;
+    HelpText<help>;
 }
 
 // A BoolOrDefault member, Default unless -<name> or -<name>=<true|false|1|0>
@@ -49,7 +49,7 @@ multiclass BoolField<string name, code default, string help> {
 multiclass OptionalBoolField<string name, string help> {
   def NAME : FlagOrEq<["-", "--"], name>,
     Field<NAME, "llvm::BoolOrDefault", "llvm::BoolOrDefault::Default">,
-    BareValue<"llvm::BoolOrDefault::True">, HelpText<help>;
+    HelpText<help>;
 }
 
 // A member of the enumeration <type> set by -<name>=<value> or -<name> 
<value>,
diff --git a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp 
b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
index 4c390b472ed1ae..23b73ee3d9f2f0 100644
--- a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
@@ -282,8 +282,10 @@ static void emitOptionsStruct(const Record &Struct,
     if (Kind != "FlagOrEq" && Kind != "SeparateOrEq")
       PrintFatalError(R->getLoc(),
                       "a member is set by a FlagOrEq or SeparateOrEq");
-    if (Kind == "FlagOrEq" && !R->getValue("BareValue"))
-      PrintFatalError(R->getLoc(), "a FlagOrEq needs a BareValue");
+    StringRef Type = R->getValueAsString("FieldType");
+    if (Kind == "FlagOrEq" && Type != "bool" && Type != "llvm::BoolOrDefault" 
&&
+        !R->getValue("BareValue"))
+      PrintFatalError(R->getLoc(), "a FlagOrEq sets a bool or has a 
BareValue");
     Fields.push_back(R);
   }
   // Members in declaration order.
@@ -344,9 +346,8 @@ static void emitOptionsStruct(const Record &Struct,
     OS << "  case OPT_" << getStructOptionID(*R) << ":\n";
     std::string Member = getMemberName(*R, Prefix);
     if (R->getValue("BareValue"))
-      OS << "    if (!A.getNumValues()) {\n      " << Member << " = "
-         << R->getValueAsString("BareValue")
-         << ";\n      return true;\n    }\n";
+      OS << "    if (!A.getNumValues()) { " << Member << " = "
+         << R->getValueAsString("BareValue") << "; return true; }\n";
     if (!isa<UnsetInit>(R->getValueInit("NormalizedValues"))) {
       SmallVector<StringRef> Values;
       R->getValueAsString("Values").split(Values, ',');
@@ -362,7 +363,12 @@ static void emitOptionsStruct(const Record &Struct,
       OS << "      return false;\n    }\n";
       continue;
     }
-    OS << "    return llvm::opt::parseArgValue(A.getValue(), " << Member
+    // A FlagOrEq without a value means =true.
+    StringRef Value =
+        R->getValueAsDef("Kind")->getValueAsString("Name") == "FlagOrEq"
+            ? "A.getNumValues() ? A.getValue() : \"true\""
+            : "A.getValue()";
+    OS << "    return llvm::opt::parseArgValue(" << Value << ", " << Member
        << ");\n";
   }
   OS << "  }\n  llvm_unreachable(\"option without a member\");\n}\n";

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to