[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