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/4] [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/4] [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/4] 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"; >From f32c4ed097512dff41ea947ddbd0950db9fa7045 Mon Sep 17 00:00:00 2001 From: Fangrui Song <[email protected]> Date: Thu, 8 Oct 2026 23:13:29 -0700 Subject: [PATCH 4/4] improve FlagOrEnumField comment --- llvm/include/llvm/Option/LibraryOptions.td | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/Option/LibraryOptions.td b/llvm/include/llvm/Option/LibraryOptions.td index 7bd708a7d8f3bb..54e0b540152a7c 100644 --- a/llvm/include/llvm/Option/LibraryOptions.td +++ b/llvm/include/llvm/Option/LibraryOptions.td @@ -61,8 +61,8 @@ multiclass EnumField<string name, string type, code default, string values, EnumValues<values, enumerators>, HelpText<help>; } -// An EnumField also set to <bare> by -<name> without a value. It never consumes -// the next argument. +// An EnumField that -<name> alone sets to <bare>; <default> applies when the +// option is absent. -<name> doesn't consume the next argument. multiclass FlagOrEnumField<string name, string type, code default, string values, list<code> enumerators, code bare, string help> { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
