[flang-commits] [flang] 16dae0f - [Passes] Port -print-pipeline-passes to PassesOptions (#230220)
via flang-commits
flang-commits at lists.llvm.org
Thu Oct 8 23:14:40 PDT 2026
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, ',');
More information about the flang-commits
mailing list