[llvm] [PassBuilder] Treat pipeline aliases as normal passes (PR #146038)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 27 01:16:28 PDT 2025
https://github.com/nikic created https://github.com/llvm/llvm-project/pull/146038
Pipelines like `-passes="default<O3>"` are currently parsed in a special way. Switch them to work like normal, parameterized module passes.
>From 660cfbe0e17ca3135502bde2ce1bb93284ddbb21 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Fri, 27 Jun 2025 10:09:13 +0200
Subject: [PATCH] [PassBuilder] Treat pipeline aliases as normal passes
Pipelines like `-passes="default<O3>"` are currently parsed in a
special way. Switch them to work like normal, parameterized
module passes.
---
llvm/lib/Passes/PassBuilder.cpp | 73 ++++++------------------
llvm/lib/Passes/PassRegistry.def | 37 ++++++++++++
llvm/test/Other/pipeline-alias-errors.ll | 5 ++
3 files changed, 60 insertions(+), 55 deletions(-)
create mode 100644 llvm/test/Other/pipeline-alias-errors.ll
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 4603eaff8ade9..f95c32d859d14 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -374,9 +374,6 @@
using namespace llvm;
-static const Regex DefaultAliasRegex(
- "^(default|thinlto-pre-link|thinlto|lto-pre-link|lto)<(O[0123sz])>$");
-
cl::opt<bool> llvm::PrintPipelinePasses(
"print-pipeline-passes",
cl::desc("Print a '-passes' compatible string describing the pipeline "
@@ -618,6 +615,15 @@ static std::optional<OptimizationLevel> parseOptLevel(StringRef S) {
.Default(std::nullopt);
}
+static Expected<OptimizationLevel> parseOptLevelParam(StringRef S) {
+ std::optional<OptimizationLevel> OptLevel = parseOptLevel(S);
+ if (OptLevel)
+ return *OptLevel;
+ return make_error<StringError>(
+ formatv("invalid optimization level '{}'", S).str(),
+ inconvertibleErrorCode());
+}
+
Expected<bool> PassBuilder::parseSinglePassOption(StringRef Params,
StringRef OptionName,
StringRef PassName) {
@@ -1507,13 +1513,6 @@ Expected<bool> parseVirtRegRewriterPassOptions(StringRef Params) {
} // namespace
-/// Tests whether a pass name starts with a valid prefix for a default pipeline
-/// alias.
-static bool startsWithDefaultPipelineAliasPrefix(StringRef Name) {
- return Name.starts_with("default") || Name.starts_with("thinlto") ||
- Name.starts_with("lto");
-}
-
/// Tests whether registered callbacks will accept a given pass name.
///
/// When parsing a pipeline text, the type of the outermost pipeline may be
@@ -1535,10 +1534,6 @@ static bool callbacksAcceptPassName(StringRef Name, CallbacksT &Callbacks) {
template <typename CallbacksT>
static bool isModulePassName(StringRef Name, CallbacksT &Callbacks) {
- // Manually handle aliases for pre-configured pipeline fragments.
- if (startsWithDefaultPipelineAliasPrefix(Name))
- return DefaultAliasRegex.match(Name);
-
StringRef NameNoBracket = Name.take_until([](char C) { return C == '<'; });
// Explicitly handle pass manager names.
@@ -1737,6 +1732,15 @@ PassBuilder::parsePipelineText(StringRef Text) {
return {std::move(ResultPipeline)};
}
+static void setupOptionsForPipelineAlias(PipelineTuningOptions &PTO,
+ OptimizationLevel L) {
+ // This is consistent with old pass manager invoked via opt, but
+ // inconsistent with clang. Clang doesn't enable loop vectorization
+ // but does enable slp vectorization at Oz.
+ PTO.LoopVectorization = L.getSpeedupLevel() > 1 && L != OptimizationLevel::Oz;
+ PTO.SLPVectorization = L.getSpeedupLevel() > 1 && L != OptimizationLevel::Oz;
+}
+
Error PassBuilder::parseModulePass(ModulePassManager &MPM,
const PipelineElement &E) {
auto &Name = E.Name;
@@ -1789,47 +1793,6 @@ Error PassBuilder::parseModulePass(ModulePassManager &MPM,
;
}
- // Manually handle aliases for pre-configured pipeline fragments.
- if (startsWithDefaultPipelineAliasPrefix(Name)) {
- SmallVector<StringRef, 3> Matches;
- if (!DefaultAliasRegex.match(Name, &Matches))
- return make_error<StringError>(
- formatv("unknown default pipeline alias '{}'", Name).str(),
- inconvertibleErrorCode());
-
- assert(Matches.size() == 3 && "Must capture two matched strings!");
-
- OptimizationLevel L = *parseOptLevel(Matches[2]);
-
- // This is consistent with old pass manager invoked via opt, but
- // inconsistent with clang. Clang doesn't enable loop vectorization
- // but does enable slp vectorization at Oz.
- PTO.LoopVectorization =
- L.getSpeedupLevel() > 1 && L != OptimizationLevel::Oz;
- PTO.SLPVectorization =
- L.getSpeedupLevel() > 1 && L != OptimizationLevel::Oz;
-
- if (Matches[1] == "default") {
- MPM.addPass(buildPerModuleDefaultPipeline(L));
- } else if (Matches[1] == "thinlto-pre-link") {
- MPM.addPass(buildThinLTOPreLinkDefaultPipeline(L));
- } else if (Matches[1] == "thinlto") {
- MPM.addPass(buildThinLTODefaultPipeline(L, nullptr));
- } else if (Matches[1] == "lto-pre-link") {
- if (PTO.UnifiedLTO)
- // When UnifiedLTO is enabled, use the ThinLTO pre-link pipeline. This
- // avoids compile-time performance regressions and keeps the pre-link
- // LTO pipeline "unified" for both LTO modes.
- MPM.addPass(buildThinLTOPreLinkDefaultPipeline(L));
- else
- MPM.addPass(buildLTOPreLinkDefaultPipeline(L));
- } else {
- assert(Matches[1] == "lto" && "Not one of the matched options!");
- MPM.addPass(buildLTODefaultPipeline(L, nullptr));
- }
- return Error::success();
- }
-
// Finally expand the basic registered passes from the .inc file.
#define MODULE_PASS(NAME, CREATE_PASS) \
if (Name == NAME) { \
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index ec14c6a9211d9..c681b6069b5f8 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -240,6 +240,43 @@ MODULE_PASS_WITH_PARAMS(
},
parseStructuralHashPrinterPassOptions, "detailed;call-target-ignored")
+MODULE_PASS_WITH_PARAMS(
+ "default", "", [&](OptimizationLevel L) {
+ setupOptionsForPipelineAlias(PTO, L);
+ return buildPerModuleDefaultPipeline(L);
+ },
+ parseOptLevelParam, "O0;O1;O2;O3;Os;Oz")
+MODULE_PASS_WITH_PARAMS(
+ "thinlto-pre-link", "", [&](OptimizationLevel L) {
+ setupOptionsForPipelineAlias(PTO, L);
+ return buildThinLTOPreLinkDefaultPipeline(L);
+ },
+ parseOptLevelParam, "O0;O1;O2;O3;Os;Oz")
+MODULE_PASS_WITH_PARAMS(
+ "thinlto", "", [&](OptimizationLevel L) {
+ setupOptionsForPipelineAlias(PTO, L);
+ return buildThinLTODefaultPipeline(L, nullptr);
+ },
+ parseOptLevelParam, "O0;O1;O2;O3;Os;Oz")
+MODULE_PASS_WITH_PARAMS(
+ "lto-pre-link", "", [&](OptimizationLevel L) {
+ setupOptionsForPipelineAlias(PTO, L);
+ if (PTO.UnifiedLTO)
+ // When UnifiedLTO is enabled, use the ThinLTO pre-link pipeline. This
+ // avoids compile-time performance regressions and keeps the pre-link
+ // LTO pipeline "unified" for both LTO modes.
+ return buildThinLTOPreLinkDefaultPipeline(L);
+ else
+ return buildLTOPreLinkDefaultPipeline(L);
+ },
+ parseOptLevelParam, "O0;O1;O2;O3;Os;Oz")
+MODULE_PASS_WITH_PARAMS(
+ "lto", "", [&](OptimizationLevel L) {
+ setupOptionsForPipelineAlias(PTO, L);
+ return buildLTODefaultPipeline(L, nullptr);
+ },
+ parseOptLevelParam, "O0;O1;O2;O3;Os;Oz")
+
#undef MODULE_PASS_WITH_PARAMS
#ifndef CGSCC_ANALYSIS
diff --git a/llvm/test/Other/pipeline-alias-errors.ll b/llvm/test/Other/pipeline-alias-errors.ll
new file mode 100644
index 0000000000000..294b595ca8ba0
--- /dev/null
+++ b/llvm/test/Other/pipeline-alias-errors.ll
@@ -0,0 +1,5 @@
+; RUN: not opt -passes="default" < %s 2>&1 | FileCheck %s --check-prefix=MISSING-OPT-LEVEL
+; RUN: not opt -passes="default<foo>" < %s 2>&1 | FileCheck %s --check-prefix=INVALID-OPT-LEVEL
+
+; MISSING-OPT-LEVEL: invalid optimization level ''
+; INVALID-OPT-LEVEL: invalid optimization level 'foo'
More information about the llvm-commits
mailing list