[llvm] [PassBuilder] Add callback invoking to PassBuilder string API (PR #157153)
Gabriel Baraldi via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 16 07:17:03 PDT 2025
https://github.com/gbaraldi updated https://github.com/llvm/llvm-project/pull/157153
>From cebd0a9a5341e51ffa7774e1960a8de385011a27 Mon Sep 17 00:00:00 2001
From: gbaraldi <baraldigabriel at gmail.com>
Date: Fri, 5 Sep 2025 14:55:28 -0300
Subject: [PATCH 1/2] Initial stage of callback invoking with PassBuilder
---
.../llvm/Passes/TargetPassRegistry.inc | 36 +++++++++++++++++++
llvm/lib/Passes/PassRegistry.def | 28 ++++++++++++++-
2 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/llvm/include/llvm/Passes/TargetPassRegistry.inc b/llvm/include/llvm/Passes/TargetPassRegistry.inc
index 068b27794191c..2d76db006e4a1 100644
--- a/llvm/include/llvm/Passes/TargetPassRegistry.inc
+++ b/llvm/include/llvm/Passes/TargetPassRegistry.inc
@@ -133,6 +133,42 @@ PB.registerPipelineParsingCallback([=](StringRef Name, LoopPassManager &PM,
return false;
});
+PB.registerPipelineParsingCallback([=](StringRef Name, ModulePassManager &PM,
+ ArrayRef<PassBuilder::PipelineElement>) {
+#define MODULE_CALLBACK(NAME, INVOKE) if (Name == NAME) { PB.INVOKE(PM, OptimizationLevel::O2); return true; }
+#include GET_PASS_REGISTRY
+#undef MODULE_CALLBACK
+#define MODULE_LTO_CALLBACK(NAME, INVOKE) if (Name == NAME) { PB.INVOKE(PM, OptimizationLevel::O2, ThinOrFullLTOPhase::None); return true; }
+#include GET_PASS_REGISTRY
+#undef MODULE_LTO_CALLBACK
+ return false;
+});
+
+PB.registerPipelineParsingCallback([=](StringRef Name, FunctionPassManager &PM,
+ ArrayRef<PassBuilder::PipelineElement>) {
+#define FUNCTION_CALLBACK(NAME, INVOKE) if (Name == NAME) { PB.INVOKE(PM, OptimizationLevel::O2); return true; }
+#include GET_PASS_REGISTRY
+#undef FUNCTION_CALLBACK
+ return false;
+});
+
+
+PB.registerPipelineParsingCallback([=](StringRef Name, CGSCCPassManager &CGPM,
+ ArrayRef<PassBuilder::PipelineElement>) {
+#define CGSCC_CALLBACK(NAME, INVOKE) if (Name == NAME) { PB.INVOKE(CGPM, OptimizationLevel::O2); return true; }
+#include GET_PASS_REGISTRY
+#undef CGSCC_CALLBACK
+ return false;
+});
+PB.registerPipelineParsingCallback([=](StringRef Name, LoopPassManager &PM,
+ ArrayRef<PassBuilder::PipelineElement>) {
+#define LOOP_CALLBACK(NAME, INVOKE) if (Name == NAME) { PB.INVOKE(PM, OptimizationLevel::O2); return true; }
+#include GET_PASS_REGISTRY
+#undef LOOP_CALLBACK
+ return false;
+});
+
+
PB.registerPipelineParsingCallback([=](StringRef Name,
MachineFunctionPassManager &PM,
ArrayRef<PassBuilder::PipelineElement>) {
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 299aaa801439b..dfa41d618643e 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -725,7 +725,6 @@ FUNCTION_PASS_WITH_PARAMS(
return ExpandFpPass(TM, OL);
},
parseExpandFpOptions, "opt-level=N")
-
#undef FUNCTION_PASS_WITH_PARAMS
#ifndef LOOPNEST_PASS
@@ -804,3 +803,30 @@ LOOP_PASS_WITH_PARAMS(
},
parseLoopUnswitchOptions, "nontrivial;no-nontrivial;trivial;no-trivial")
#undef LOOP_PASS_WITH_PARAMS
+
+#ifdef MODULE_CALLBACK
+MODULE_CALLBACK("PipelineStartCallbacks", invokePipelineStartEPCallbacks)
+#endif
+
+// There are some full lto specific ones that are ignored here for now
+#ifdef MODULE_LTO_CALLBACK
+MODULE_LTO_CALLBACK("PipelineEarlySimplificationCallbacks", invokePipelineEarlySimplificationEPCallbacks)
+MODULE_LTO_CALLBACK("OptimizerEarlyCallbacks", invokeOptimizerEarlyEPCallbacks)
+MODULE_LTO_CALLBACK("OptimizerLastCallbacks", invokeOptimizerLastEPCallbacks)
+#endif
+
+#ifdef FUNCTION_CALLBACK
+FUNCTION_CALLBACK("PeepholeCallbacks", invokePeepholeEPCallbacks)
+FUNCTION_CALLBACK("ScalarOptimizerLateCallbacks", invokeScalarOptimizerLateEPCallbacks)
+FUNCTION_CALLBACK("VectorizerStartCallbacks", invokeVectorizerStartEPCallbacks)
+#endif
+
+#ifdef LOOP_CALLBACK
+LOOP_CALLBACK("LateLoopOptimizationsCallbacks", invokeLateLoopOptimizationsEPCallbacks)
+LOOP_CALLBACK("LoopOptimizerEndCallbacks", invokeLoopOptimizerEndEPCallbacks)
+#endif
+
+#ifdef CGSCC_CALLBACK
+CGSCC_CALLBACK("CGSCCOptimizerLateCallbacks", invokeCGSCCOptimizerLateEPCallbacks)
+#endif
+
>From 52540e73ab626318aebbeee74b0b3f6d52cbdc0e Mon Sep 17 00:00:00 2001
From: Gabriel Baraldi <baraldigabriel at gmail.com>
Date: Tue, 16 Sep 2025 14:16:43 +0000
Subject: [PATCH 2/2] Cleanup code, address suggestions and add test
---
.../llvm/Passes/TargetPassRegistry.inc | 39 +-------
llvm/lib/Passes/PassBuilder.cpp | 96 +++++++++++++++++++
.../Other/pipeline-callbacks-string-api.ll | 27 ++++++
3 files changed, 125 insertions(+), 37 deletions(-)
create mode 100644 llvm/test/Other/pipeline-callbacks-string-api.ll
diff --git a/llvm/include/llvm/Passes/TargetPassRegistry.inc b/llvm/include/llvm/Passes/TargetPassRegistry.inc
index 2d76db006e4a1..48948528a95d6 100644
--- a/llvm/include/llvm/Passes/TargetPassRegistry.inc
+++ b/llvm/include/llvm/Passes/TargetPassRegistry.inc
@@ -92,7 +92,7 @@ if (PIC) {
return true; \
}
-PB.registerPipelineParsingCallback([=](StringRef Name, ModulePassManager &PM,
+PB.registerPipelineParsingCallback([&](StringRef Name, ModulePassManager &PM,
ArrayRef<PassBuilder::PipelineElement>) {
#define MODULE_PASS(NAME, CREATE_PASS) ADD_PASS(NAME, CREATE_PASS)
#include GET_PASS_REGISTRY
@@ -109,7 +109,7 @@ PB.registerPipelineParsingCallback([=](StringRef Name, ModulePassManager &PM,
return false;
});
-PB.registerPipelineParsingCallback([=](StringRef Name, FunctionPassManager &PM,
+PB.registerPipelineParsingCallback([&](StringRef Name, FunctionPassManager &PM,
ArrayRef<PassBuilder::PipelineElement>) {
#define FUNCTION_PASS(NAME, CREATE_PASS) ADD_PASS(NAME, CREATE_PASS)
#include GET_PASS_REGISTRY
@@ -133,41 +133,6 @@ PB.registerPipelineParsingCallback([=](StringRef Name, LoopPassManager &PM,
return false;
});
-PB.registerPipelineParsingCallback([=](StringRef Name, ModulePassManager &PM,
- ArrayRef<PassBuilder::PipelineElement>) {
-#define MODULE_CALLBACK(NAME, INVOKE) if (Name == NAME) { PB.INVOKE(PM, OptimizationLevel::O2); return true; }
-#include GET_PASS_REGISTRY
-#undef MODULE_CALLBACK
-#define MODULE_LTO_CALLBACK(NAME, INVOKE) if (Name == NAME) { PB.INVOKE(PM, OptimizationLevel::O2, ThinOrFullLTOPhase::None); return true; }
-#include GET_PASS_REGISTRY
-#undef MODULE_LTO_CALLBACK
- return false;
-});
-
-PB.registerPipelineParsingCallback([=](StringRef Name, FunctionPassManager &PM,
- ArrayRef<PassBuilder::PipelineElement>) {
-#define FUNCTION_CALLBACK(NAME, INVOKE) if (Name == NAME) { PB.INVOKE(PM, OptimizationLevel::O2); return true; }
-#include GET_PASS_REGISTRY
-#undef FUNCTION_CALLBACK
- return false;
-});
-
-
-PB.registerPipelineParsingCallback([=](StringRef Name, CGSCCPassManager &CGPM,
- ArrayRef<PassBuilder::PipelineElement>) {
-#define CGSCC_CALLBACK(NAME, INVOKE) if (Name == NAME) { PB.INVOKE(CGPM, OptimizationLevel::O2); return true; }
-#include GET_PASS_REGISTRY
-#undef CGSCC_CALLBACK
- return false;
-});
-PB.registerPipelineParsingCallback([=](StringRef Name, LoopPassManager &PM,
- ArrayRef<PassBuilder::PipelineElement>) {
-#define LOOP_CALLBACK(NAME, INVOKE) if (Name == NAME) { PB.INVOKE(PM, OptimizationLevel::O2); return true; }
-#include GET_PASS_REGISTRY
-#undef LOOP_CALLBACK
- return false;
-});
-
PB.registerPipelineParsingCallback([=](StringRef Name,
MachineFunctionPassManager &PM,
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 587f0ece0859b..cda64690ffc5d 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -528,6 +528,102 @@ PassBuilder::PassBuilder(TargetMachine *TM, PipelineTuningOptions PTO,
#include "llvm/Passes/MachinePassRegistry.def"
});
}
+ auto parseLevelParam = [](StringRef P) -> Expected<OptimizationLevel> {
+ if (P == "O0") return OptimizationLevel::O0;
+ if (P == "O1") return OptimizationLevel::O1;
+ if (P == "O2") return OptimizationLevel::O2;
+ if (P == "O3") return OptimizationLevel::O3;
+ if (P == "Os") return OptimizationLevel::Os;
+ if (P == "Oz") return OptimizationLevel::Oz;
+ return make_error<StringError>(
+ formatv("invalid optimization level '{}'", P).str(),
+ inconvertibleErrorCode());
+ };
+
+ // Module-level callbacks without LTO phase
+ this->registerPipelineParsingCallback(
+ [this, parseLevelParam](StringRef Name, ModulePassManager &PM,
+ ArrayRef<PassBuilder::PipelineElement>) {
+#define MODULE_CALLBACK(NAME, INVOKE) \
+ if (PassBuilder::checkParametrizedPassName(Name, NAME)) { \
+ auto L = PassBuilder::parsePassParameters(parseLevelParam, Name, NAME); \
+ if (!L) \
+ return (errs() << NAME ": " << toString(L.takeError()) << '\n', false); \
+ this->INVOKE(PM, L.get()); \
+ return true; \
+ }
+#include "PassRegistry.def"
+#undef MODULE_CALLBACK
+ return false;
+ });
+
+ // Module-level callbacks with LTO phase (use Phase::None for string API)
+ this->registerPipelineParsingCallback(
+ [this, parseLevelParam](StringRef Name, ModulePassManager &PM,
+ ArrayRef<PassBuilder::PipelineElement>) {
+#define MODULE_LTO_CALLBACK(NAME, INVOKE) \
+ if (PassBuilder::checkParametrizedPassName(Name, NAME)) { \
+ auto L = PassBuilder::parsePassParameters(parseLevelParam, Name, NAME); \
+ if (!L) \
+ return (errs() << NAME ": " << toString(L.takeError()) << '\n', false); \
+ this->INVOKE(PM, L.get(), ThinOrFullLTOPhase::None); \
+ return true; \
+ }
+#include "PassRegistry.def"
+#undef MODULE_LTO_CALLBACK
+ return false;
+ });
+
+ // Function-level callbacks
+ this->registerPipelineParsingCallback(
+ [this, parseLevelParam](StringRef Name, FunctionPassManager &PM,
+ ArrayRef<PassBuilder::PipelineElement>) {
+#define FUNCTION_CALLBACK(NAME, INVOKE) \
+ if (PassBuilder::checkParametrizedPassName(Name, NAME)) { \
+ auto L = PassBuilder::parsePassParameters(parseLevelParam, Name, NAME); \
+ if (!L) \
+ return (errs() << NAME ": " << toString(L.takeError()) << '\n', false); \
+ this->INVOKE(PM, L.get()); \
+ return true; \
+ }
+#include "PassRegistry.def"
+#undef FUNCTION_CALLBACK
+ return false;
+ });
+
+ // CGSCC-level callbacks
+ this->registerPipelineParsingCallback(
+ [this, parseLevelParam](StringRef Name, CGSCCPassManager &PM,
+ ArrayRef<PassBuilder::PipelineElement>) {
+#define CGSCC_CALLBACK(NAME, INVOKE) \
+ if (PassBuilder::checkParametrizedPassName(Name, NAME)) { \
+ auto L = PassBuilder::parsePassParameters(parseLevelParam, Name, NAME); \
+ if (!L) \
+ return (errs() << NAME ": " << toString(L.takeError()) << '\n', false); \
+ this->INVOKE(PM, L.get()); \
+ return true; \
+ }
+#include "PassRegistry.def"
+#undef CGSCC_CALLBACK
+ return false;
+ });
+
+ // Loop-level callbacks
+ this->registerPipelineParsingCallback(
+ [this, parseLevelParam](StringRef Name, LoopPassManager &PM,
+ ArrayRef<PassBuilder::PipelineElement>) {
+#define LOOP_CALLBACK(NAME, INVOKE) \
+ if (PassBuilder::checkParametrizedPassName(Name, NAME)) { \
+ auto L = PassBuilder::parsePassParameters(parseLevelParam, Name, NAME); \
+ if (!L) \
+ return (errs() << NAME ": " << toString(L.takeError()) << '\n', false); \
+ this->INVOKE(PM, L.get()); \
+ return true; \
+ }
+#include "PassRegistry.def"
+#undef LOOP_CALLBACK
+ return false;
+ });
}
void PassBuilder::registerModuleAnalyses(ModuleAnalysisManager &MAM) {
diff --git a/llvm/test/Other/pipeline-callbacks-string-api.ll b/llvm/test/Other/pipeline-callbacks-string-api.ll
new file mode 100644
index 0000000000000..609e1d755f332
--- /dev/null
+++ b/llvm/test/Other/pipeline-callbacks-string-api.ll
@@ -0,0 +1,27 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-glob -p=VECSTART,VECEND,MODSTART,LTOEARLY,INVALID
+; RUN: opt -disable-output -print-pipeline-passes \
+; RUN: -passes-ep-vectorizer-start='no-op-function' \
+; RUN: -passes='function(VectorizerStartCallbacks<O3>)' < %s 2>&1 | FileCheck %s --check-prefix=VECSTART
+; RUN: opt -disable-output -print-pipeline-passes \
+; RUN: -passes-ep-peephole='no-op-function' \
+; RUN: -passes='PeepholeCallbacks<Os>' < %s 2>&1 | FileCheck %s --check-prefix=PEEP
+; RUN: opt -disable-output -print-pipeline-passes \
+; RUN: -passes-ep-pipeline-start='no-op-module' \
+; RUN: -passes='PipelineStartCallbacks<O1>' < %s 2>&1 | FileCheck %s --check-prefix=MODSTART
+; RUN: opt -disable-output -print-pipeline-passes \
+; RUN: -passes-ep-pipeline-early-simplification='no-op-module' \
+; RUN: -passes='PipelineEarlySimplificationCallbacks<O2>' < %s 2>&1 | FileCheck %s --check-prefix=LTOEARLY
+; RUN: not opt -disable-output -passes='VectorizerStartCallbacks<foo>' < %s 2>&1 | FileCheck %s --check-prefix=INVALID
+
+; VECSTART: no-op-function
+; PEEP: no-op-function
+; MODSTART: no-op-module
+; LTOEARLY: no-op-module
+; INVALID: invalid optimization level 'foo'
+
+define void @f() {
+entry:
+ ret void
+}
+
+
More information about the llvm-commits
mailing list