Author: Fangrui Song Date: 2026-10-08T23:14:26-07:00 New Revision: 16dae0f25275c01fd11c787797c2a546f51f208d
URL: https://github.com/llvm/llvm-project/commit/16dae0f25275c01fd11c787797c2a546f51f208d DIFF: https://github.com/llvm/llvm-project/commit/16dae0f25275c01fd11c787797c2a546f51f208d.diff LOG: [Passes] Port -print-pipeline-passes to PassesOptions (#230220) -print-pipeline-passes takes an optional value (=text or =tree). Add FlagOrEnumField, an EnumField whose bare option assigns a given enumerator and never consumes the next argument, as cl::ValueOptional does. BoolField and OptionalBoolField use the same BareValue. clang, flang, LTO, llc, and opt read the format through PassBuilder::getPrintPipelinePasses() instead of the exported cl::opt. Aided by Opus 5.5 Added: Modified: clang/lib/CodeGen/BackendUtil.cpp flang/lib/Frontend/FrontendActions.cpp llvm/docs/CommandLine.md llvm/include/llvm/Option/LibraryOptions.td llvm/include/llvm/Passes/PassBuilder.h llvm/lib/LTO/LTOBackend.cpp llvm/lib/Option/LibraryOptions.cpp llvm/lib/Passes/PassBuilder.cpp llvm/lib/Passes/PassesOptions.h llvm/lib/Passes/PassesOptions.td llvm/tools/llc/lib/NewPMDriver.cpp llvm/tools/opt/NewPMDriver.cpp llvm/unittests/Option/LibraryOptionsTest.cpp llvm/unittests/Option/LibraryOpts.td llvm/utils/TableGen/Basic/OptionParserEmitter.cpp Removed: ################################################################################ diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 531e53b3329254..1803c7943b326c 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 (PB.getPrintPipelinePasses()) { MPM.printPipeline(outs(), [&PIC](StringRef ClassName) { auto PassName = PIC.getPassNameForClassName(ClassName); return PassName.empty() ? ClassName : PassName; diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp index 06d67e9417bc44..b8b8b426dfe86d 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 (pb.getPrintPipelinePasses()) { mpm.printPipeline(llvm::outs(), [&pic](llvm::StringRef className) { auto passName = pic.getPassNameForClassName(className); return passName.empty() ? className : passName; diff --git a/llvm/docs/CommandLine.md b/llvm/docs/CommandLine.md index bdb25833caf735..5e3941881f9935 100644 --- a/llvm/docs/CommandLine.md +++ b/llvm/docs/CommandLine.md @@ -1588,6 +1588,7 @@ The `BoolField` is set by `-enable-foo` or `-enable-foo=true|false|1|0`. A `ValueField`, of an integer type, `float`, `double`, or `StringRef`, is set by `-foo-threshold=8` or `-foo-threshold 8`. An `OptionalBoolField` is a `BoolOrDefault` that stays `Default` unless the option is given, replacing `cl::boolOrDefault`; read it with `valueOr(X, Default)`. An `EnumField` maps each of its comma-separated values to an enumerator, replacing `cl::values`: `defm : EnumField<"foo-mode", "FooMode", "FooMode::Fast", "fast,safe", ["FooMode::Fast", "FooMode::Safe"], "The mode">;` accepts `-foo-mode=fast` and `-foo-mode=safe`. +A `FlagOrEnumField` takes one more argument, the enumerator the bare option selects, replacing `cl::ValueOptional`: with `"FooMode::Fast"`, `-foo-mode` sets `FooMode::Fast`, and `-foo-mode safe` does not consume `safe`. A `DefaultOnOffField` is a `BoolOrDefault` set by `=Default`, `=Enable`, or `=Disable`. Both accept `--` for `-`. Only `-help-hidden` lists the options, like `cl::Hidden`. diff --git a/llvm/include/llvm/Option/LibraryOptions.td b/llvm/include/llvm/Option/LibraryOptions.td index 6a38d87fc13f0c..54e0b540152a7c 100644 --- a/llvm/include/llvm/Option/LibraryOptions.td +++ b/llvm/include/llvm/Option/LibraryOptions.td @@ -29,6 +29,15 @@ class Field<string name, string type, code default> { code FieldDefault = default; } +// The value a FlagOrEq row assigns when given without one. +class BareValue<code value> { code BareValue = value; } + +// The comma-separated <values> of an enum row, which map in order to the C++ +// <enumerators>. +class EnumValues<string values, list<code> enumerators> + : Values<values>, NormalizedValues<enumerators>, + MetaVarName<"<"#!subst(",", "|", values)#">">; + // 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>, @@ -49,8 +58,16 @@ multiclass OptionalBoolField<string name, string help> { multiclass EnumField<string name, string type, code default, string values, list<code> enumerators, string help> { def NAME : SeparateOrEq<["-", "--"], name>, Field<NAME, type, default>, - Values<values>, NormalizedValues<enumerators>, - MetaVarName<"<"#!subst(",", "|", values)#">">, HelpText<help>; + EnumValues<values, enumerators>, HelpText<help>; +} + +// 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> { + def NAME : FlagOrEq<["-", "--"], name>, Field<NAME, type, default>, + EnumValues<values, enumerators>, BareValue<bare>, HelpText<help>; } // A BoolOrDefault member, Default unless -<name>=Enable or -<name>=Disable is diff --git a/llvm/include/llvm/Passes/PassBuilder.h b/llvm/include/llvm/Passes/PassBuilder.h index 245725f13ed8a4..502dfd93842bea 100644 --- a/llvm/include/llvm/Passes/PassBuilder.h +++ b/llvm/include/llvm/Passes/PassBuilder.h @@ -106,6 +106,11 @@ class PipelineTuningOptions { bool DevirtualizeSpeculatively; }; +enum class PrintPipelinePassesFormat { + Text, + Tree, +}; + /// This class provides access to building LLVM's passes. /// /// Its members provide the baseline state available to passes during their @@ -417,6 +422,11 @@ class PassBuilder { /// Print pass names. LLVM_ABI void printPassNames(raw_ostream &OS); + /// The format -print-pipeline-passes requests, or std::nullopt if it is not + /// given. + LLVM_ABI std::optional<PrintPipelinePassesFormat> + getPrintPipelinePasses() const; + /// Register a callback for a default optimizer pipeline extension /// point /// @@ -1030,23 +1040,6 @@ class NoOpLoopAnalysis : public AnalysisInfoMixin<NoOpLoopAnalysis> { } }; -enum class PrintPipelinePassesFormat { - Text, - 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; - LLVM_ABI void printFormattedPipelinePasses( raw_ostream &OS, StringRef Pipeline, PrintPipelinePassesFormat Format = PrintPipelinePassesFormat::Text); diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp index 1136d50e056559..13b5e6e93335dc 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 (PB.getPrintPipelinePasses()) { std::string PipelineStr; raw_string_ostream OS(PipelineStr); MPM.printPipeline(OS, [&PIC](StringRef ClassName) { diff --git a/llvm/lib/Option/LibraryOptions.cpp b/llvm/lib/Option/LibraryOptions.cpp index 3f54c850ad726a..6bba23a66262f0 100644 --- a/llvm/lib/Option/LibraryOptions.cpp +++ b/llvm/lib/Option/LibraryOptions.cpp @@ -24,11 +24,12 @@ void LibraryOptionsParser::forEachOption( unsigned Kind = T.getOptionKind(ID); if (Kind != Option::FlagOrEqClass && Kind != Option::SeparateOrEqClass) continue; + StringRef V = T.getOptionMetaVar(ID); std::string MetaVar; - if (Kind == Option::SeparateOrEqClass) { - StringRef V = T.getOptionMetaVar(ID); + if (Kind == Option::SeparateOrEqClass) MetaVar = ("=" + (V.empty() ? StringRef("<value>") : V)).str(); - } + else if (!V.empty()) + MetaVar = ("[=" + V + "]").str(); Fn(T.getOptionName(ID), MetaVar, T.getOptionHelpText(ID)); } } diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 4f973196eb951e..96d18057a594d4 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,9 @@ 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> +PassBuilder::getPrintPipelinePasses() const { + return Opts.print_pipeline_passes; } 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..b303cb8b73bae0 100644 --- a/llvm/lib/Passes/PassesOptions.td +++ b/llvm/lib/Passes/PassesOptions.td @@ -10,6 +10,13 @@ include "llvm/Option/LibraryOptions.td" def PassesOptions : OptionsStruct; +// PassBuilder.cpp +defm : FlagOrEnumField<"print-pipeline-passes", + "std::optional<PrintPipelinePassesFormat>", "std::nullopt", "text,tree", + ["PrintPipelinePassesFormat::Text", "PrintPipelinePassesFormat::Tree"], + "PrintPipelinePassesFormat::Text", + "Print string describing the pipeline (best-effort only): =text for a '-passes' compatible string, =tree for 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/tools/llc/lib/NewPMDriver.cpp b/llvm/tools/llc/lib/NewPMDriver.cpp index 3eac9bd15355e0..5b9bcef40224dc 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 = + PB.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..2dd062f8d2c9c0 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 = + PB.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) { diff --git a/llvm/unittests/Option/LibraryOptionsTest.cpp b/llvm/unittests/Option/LibraryOptionsTest.cpp index 073a1c96adc7f4..46d638fa583092 100644 --- a/llvm/unittests/Option/LibraryOptionsTest.cpp +++ b/llvm/unittests/Option/LibraryOptionsTest.cpp @@ -36,6 +36,7 @@ TEST(LibraryOptionsTest, Apply) { EXPECT_EQ(O.limit, std::nullopt); EXPECT_EQ(O.mode, test::Mode::A); EXPECT_EQ(O.override, BoolOrDefault::Default); + EXPECT_EQ(O.print, std::nullopt); EXPECT_EQ(O.ratio, 0.5); EXPECT_EQ(O.tristate, BoolOrDefault::Default); EXPECT_EQ(O.Path, "p"); @@ -71,16 +72,22 @@ TEST(LibraryOptionsTest, Apply) { EXPECT_EQ(O.tristate, BoolOrDefault::True); EXPECT_THAT(Apply({"-lib-tristate=Default"}), testing::Each(true)); EXPECT_EQ(O.tristate, BoolOrDefault::Default); + EXPECT_THAT(Apply({"-lib-print=a"}), testing::Each(true)); + EXPECT_EQ(O.print, test::Mode::A); + EXPECT_THAT(Apply({"-lib-print"}), testing::Each(true)); + EXPECT_EQ(O.print, test::Mode::B); // A rejected value leaves the member unchanged. - EXPECT_THAT(Apply({"-lib-enable=2", "-lib-count=-1", "-lib-limit=x", - "-lib-mode=c", "-lib-override=y", "-lib-ratio=y"}), - testing::Each(false)); + EXPECT_THAT( + Apply({"-lib-enable=2", "-lib-count=-1", "-lib-limit=x", "-lib-mode=c", + "-lib-override=y", "-lib-print=c", "-lib-ratio=y"}), + testing::Each(false)); EXPECT_TRUE(O.enable); EXPECT_EQ(O.count, 7u); EXPECT_EQ(O.limit, 0u); EXPECT_EQ(O.mode, test::Mode::B); EXPECT_EQ(O.override, BoolOrDefault::False); + EXPECT_EQ(O.print, test::Mode::B); EXPECT_EQ(O.ratio, 0.25); } @@ -102,13 +109,14 @@ TEST(LibraryOptionsTest, Parser) { P.forEachOption([&](StringRef Spelling, StringRef MetaVar, StringRef Help) { Rows.push_back((Spelling + "|" + MetaVar + "|" + Help).str()); }); - EXPECT_THAT(Rows, - testing::ElementsAre( - "lib-count|=<value>|An unsigned", "lib-enable||A bool", - "lib-limit|=<value>|An optional", "lib-mode|=<a|b>|An enum", - "lib-override||An optional bool", - "lib-path|=<value>|A string", "lib-ratio|=<value>|A double", - "lib-tristate|=<Default|Enable|Disable>|A tri-state")); + EXPECT_THAT( + Rows, + testing::ElementsAre( + "lib-count|=<value>|An unsigned", "lib-enable||A bool", + "lib-limit|=<value>|An optional", "lib-mode|=<a|b>|An enum", + "lib-override||An optional bool", "lib-path|=<value>|A string", + "lib-print|[=<a|b>]|A flag or enum", "lib-ratio|=<value>|A double", + "lib-tristate|=<Default|Enable|Disable>|A tri-state")); auto Parse = [&](std::initializer_list<const char *> Argv) { unsigned Consumed = 0; @@ -124,6 +132,7 @@ TEST(LibraryOptionsTest, Parser) { EXPECT_EQ(Parse({"-lib-other"}), "1 unknown argument '-lib-other'"); EXPECT_EQ(Parse({"-lib-counts=1"}), "1 unknown argument '-lib-counts=1'"); EXPECT_EQ(Parse({"-lib-enable", "0"}), "1 "); + EXPECT_EQ(Parse({"-lib-print", "a"}), "1 "); EXPECT_EQ(Parse({"-lib-count", "x"}), "2 invalid value 'x' in '-lib-count=x'"); EXPECT_EQ(Parse({"-lib-enable=x"}), "1 invalid value 'x' in '-lib-enable=x'"); diff --git a/llvm/unittests/Option/LibraryOpts.td b/llvm/unittests/Option/LibraryOpts.td index 50d282ebccb50c..5fabf7718cfc54 100644 --- a/llvm/unittests/Option/LibraryOpts.td +++ b/llvm/unittests/Option/LibraryOpts.td @@ -17,6 +17,9 @@ defm : ValueField<"lib-limit", "std::optional<unsigned>", "std::nullopt", defm : EnumField<"lib-mode", "test::Mode", "test::Mode::A", "a,b", ["test::Mode::A", "test::Mode::B"], "An enum">; defm : OptionalBoolField<"lib-override", "An optional bool">; +defm : FlagOrEnumField<"lib-print", "std::optional<test::Mode>", "std::nullopt", + "a,b", ["test::Mode::A", "test::Mode::B"], "test::Mode::B", + "A flag or enum">; defm : ValueField<"lib-ratio", "double", "0.5", "A double">; defm : DefaultOnOffField<"lib-tristate", "A tri-state">; defm Path : ValueField<"lib-path", "StringRef", "\"p\"", "A string">; diff --git a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp index 33b4be58580fb2..23b73ee3d9f2f0 100644 --- a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp +++ b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp @@ -283,8 +283,9 @@ static void emitOptionsStruct(const Record &Struct, PrintFatalError(R->getLoc(), "a member is set by a FlagOrEq or SeparateOrEq"); StringRef Type = R->getValueAsString("FieldType"); - if (Kind == "FlagOrEq" && Type != "bool" && Type != "llvm::BoolOrDefault") - PrintFatalError(R->getLoc(), "a FlagOrEq sets a bool member"); + 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,6 +345,9 @@ static void emitOptionsStruct(const Record &Struct, for (const Record *R : Fields) { OS << " case OPT_" << getStructOptionID(*R) << ":\n"; std::string Member = getMemberName(*R, Prefix); + if (R->getValue("BareValue")) + 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, ','); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
