https://github.com/weliveindetail created 
https://github.com/llvm/llvm-project/pull/172463

LLVM pass-plugins are loaded into various tools. Plugins can register passes at 
different stages of the pipeline, called entry-points. Depending on the 
pipeline's configuration, different entry-points will be exercised. AFAIK there 
is no documentation on which entry-points are supposed to be exercised under 
which conditions. This patch adds regression tests for the status quo in the 
major consumers of pass-plugins.

From 49666ffaa61612ba3f8e22d4b9a4c8abc4b95a17 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <[email protected]>
Date: Mon, 15 Dec 2025 12:34:48 +0100
Subject: [PATCH] Test entry-points for pass-plugins with Bye in clang, flang
 and lld

---
 clang/test/CMakeLists.txt                     |   6 +
 clang/test/CodeGen/pass-plugins-entrypoints.c |  66 ++++++++++
 clang/test/Interpreter/pass-plugins.cpp       |   9 ++
 clang/test/lit.cfg.py                         |   2 +
 clang/test/lit.site.cfg.py.in                 |   1 +
 .../Integration/pass-plugins-entrypoints.f90  |  64 ++++++++++
 flang/test/lit.cfg.py                         |   5 +-
 flang/test/lit.site.cfg.py.in                 |   2 +-
 lld/test/ELF/lto/ltopasses-extension.ll       |  37 +++++-
 llvm/examples/Bye/Bye.cpp                     | 116 +++++++++++++++---
 10 files changed, 289 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/CodeGen/pass-plugins-entrypoints.c
 create mode 100644 clang/test/Interpreter/pass-plugins.cpp
 create mode 100644 flang/test/Integration/pass-plugins-entrypoints.f90

diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt
index bcb6bd68fafc2..05b0ee42da42b 100644
--- a/clang/test/CMakeLists.txt
+++ b/clang/test/CMakeLists.txt
@@ -134,6 +134,12 @@ if(CLANG_BUILD_EXAMPLES AND CLANG_PLUGIN_SUPPORT)
     )
 endif ()
 
+if(LLVM_BUILD_EXAMPLES AND NOT WIN32)
+  list(APPEND CLANG_TEST_DEPS
+    Bye
+    )
+endif()
+
 if(LLVM_INCLUDE_SPIRV_TOOLS_TESTS)
   list(APPEND CLANG_TEST_DEPS
     spirv-dis
diff --git a/clang/test/CodeGen/pass-plugins-entrypoints.c 
b/clang/test/CodeGen/pass-plugins-entrypoints.c
new file mode 100644
index 0000000000000..90182961b8057
--- /dev/null
+++ b/clang/test/CodeGen/pass-plugins-entrypoints.c
@@ -0,0 +1,66 @@
+// REQUIRES: plugins, llvm-examples
+
+// Entry-points in default and -O0 pipeline
+//
+// RUN: %clang -fpass-plugin=%llvmshlibdir/Bye%pluginext \
+// RUN:        -Xclang -load -Xclang %llvmshlibdir/Bye%pluginext \
+// RUN:        -mllvm -print-ep-callbacks -o /dev/null -S -emit-llvm %s | 
FileCheck --check-prefix=EP %s
+//
+// RUN: %clang -fpass-plugin=%llvmshlibdir/Bye%pluginext -flto=full -O0 \
+// RUN:        -Xclang -load -Xclang %llvmshlibdir/Bye%pluginext \
+// RUN:        -mllvm -print-ep-callbacks -o /dev/null -S -emit-llvm %s | 
FileCheck --check-prefix=EP %s
+//
+// RUN: %clang -fpass-plugin=%llvmshlibdir/Bye%pluginext -flto=thin -O0 \
+// RUN:        -Xclang -load -Xclang %llvmshlibdir/Bye%pluginext \
+// RUN:        -mllvm -print-ep-callbacks -o /dev/null -S -emit-llvm %s | 
FileCheck --check-prefix=EP %s
+//
+// EP:     PipelineStart
+// EP:     PipelineEarlySimplification
+// EP-NOT: Peephole
+// EP-NOT: ScalarOptimizerLate
+// EP-NOT: Peephole
+// EP:     OptimizerEarly
+// EP-NOT: Vectorizer
+// EP:     OptimizerLast
+
+// Entry-points in optimizer pipeline
+//
+// RUN: %clang -fpass-plugin=%llvmshlibdir/Bye%pluginext -O2 \
+// RUN:        -Xclang -load -Xclang %llvmshlibdir/Bye%pluginext \
+// RUN:        -mllvm -print-ep-callbacks -o /dev/null -S -emit-llvm %s | 
FileCheck --check-prefix=EP-OPT %s
+//
+// RUN: %clang -fpass-plugin=%llvmshlibdir/Bye%pluginext -O2 -flto=full \
+// RUN:        -Xclang -load -Xclang %llvmshlibdir/Bye%pluginext \
+// RUN:        -mllvm -print-ep-callbacks -o /dev/null -S -emit-llvm %s | 
FileCheck --check-prefix=EP-OPT %s
+//
+// RUN: %clang -fpass-plugin=%llvmshlibdir/Bye%pluginext -O2 -ffat-lto-objects 
\
+// RUN:        -Xclang -load -Xclang %llvmshlibdir/Bye%pluginext \
+// RUN:        -mllvm -print-ep-callbacks -o /dev/null -S -emit-llvm %s | 
FileCheck --check-prefix=EP-OPT %s
+//
+// EP-OPT: PipelineStart
+// EP-OPT: PipelineEarlySimplification
+// EP-OPT: Peephole
+// EP-OPT: ScalarOptimizerLate
+// EP-OPT: Peephole
+// EP-OPT: OptimizerEarly
+// EP-OPT: VectorizerStart
+// EP-OPT: VectorizerEnd
+// EP-OPT: OptimizerLast
+
+// FIXME: Thin-LTO does not invoke vectorizer callbacks
+//
+// RUN: %clang -fpass-plugin=%llvmshlibdir/Bye%pluginext -O2 -flto=thin \
+// RUN:        -Xclang -load -Xclang %llvmshlibdir/Bye%pluginext \
+// RUN:        -mllvm -print-ep-callbacks -o /dev/null -S -emit-llvm %s | 
FileCheck --check-prefix=EP-LTO-THIN %s
+//
+// EP-LTO-THIN:     PipelineStart
+// EP-LTO-THIN:     PipelineEarlySimplification
+// EP-LTO-THIN:     Peephole
+// EP-LTO-THIN:     ScalarOptimizerLate
+// EP-LTO-THIN:     OptimizerEarly
+// EP-LTO-THIN-NOT: Vectorizer
+// EP-LTO-THIN:     OptimizerLast
+
+int f(int x) {
+  return x;
+}
diff --git a/clang/test/Interpreter/pass-plugins.cpp 
b/clang/test/Interpreter/pass-plugins.cpp
new file mode 100644
index 0000000000000..4d445807c3d70
--- /dev/null
+++ b/clang/test/Interpreter/pass-plugins.cpp
@@ -0,0 +1,9 @@
+// RUN: cat %s | clang-repl -Xcc 
-fpass-plugin=%plugindir/pypass-plugin%pluginext \
+// RUN:                     -Xcc -load=%plugindir/pypass-plugin%pluginext \
+// RUN:                     -Xcc -Xclang -Xcc -mllvm -Xcc -wave-goodbye | 
FileCheck %s
+// REQUIRES: plugins, llvm-examples
+
+int i = 10;
+%quit
+
+// CHECK: Bye
diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py
index 52b275c095475..a622f5335354a 100644
--- a/clang/test/lit.cfg.py
+++ b/clang/test/lit.cfg.py
@@ -126,6 +126,8 @@
 
 if config.clang_examples:
     config.available_features.add("examples")
+if config.llvm_examples:
+    config.available_features.add("llvm-examples")
 
 
 def have_host_out_of_process_jit_feature_support():
diff --git a/clang/test/lit.site.cfg.py.in b/clang/test/lit.site.cfg.py.in
index f50953a93a412..8e0ecdbe07805 100644
--- a/clang/test/lit.site.cfg.py.in
+++ b/clang/test/lit.site.cfg.py.in
@@ -28,6 +28,7 @@ config.clang_staticanalyzer_z3 = @LLVM_WITH_Z3@
 config.clang_staticanalyzer_z3_mock = @TEST_WITH_Z3_MOCK@
 config.clang_enable_cir = @CLANG_ENABLE_CIR@
 config.clang_examples = @CLANG_BUILD_EXAMPLES@
+config.llvm_examples = @LLVM_BUILD_EXAMPLES@
 config.enable_shared = @ENABLE_SHARED@
 config.enable_backtrace = @ENABLE_BACKTRACES@
 config.enable_threads = @LLVM_ENABLE_THREADS@
diff --git a/flang/test/Integration/pass-plugins-entrypoints.f90 
b/flang/test/Integration/pass-plugins-entrypoints.f90
new file mode 100644
index 0000000000000..6bbb3421f2dc4
--- /dev/null
+++ b/flang/test/Integration/pass-plugins-entrypoints.f90
@@ -0,0 +1,64 @@
+! REQUIRES: plugins, examples
+
+! Entry-points in default and -O0 pipeline
+!
+! RUN: %flang -fpass-plugin=%llvmshlibdir/Bye%pluginext \
+! RUN:        -Xflang -load -Xflang %llvmshlibdir/Bye%pluginext \
+! RUN:        -mllvm -print-ep-callbacks -o /dev/null -S %s | FileCheck 
--check-prefix=EP %s
+!
+! RUN: %flang -fpass-plugin=%llvmshlibdir/Bye%pluginext -flto=full -O0 \
+! RUN:        -Xflang -load -Xflang %llvmshlibdir/Bye%pluginext \
+! RUN:        -mllvm -print-ep-callbacks -o /dev/null -S %s | FileCheck 
--check-prefix=EP %s
+!
+! RUN: %flang -fpass-plugin=%llvmshlibdir/Bye%pluginext -flto=thin -O0 \
+! RUN:        -Xflang -load -Xflang %llvmshlibdir/Bye%pluginext \
+! RUN:        -mllvm -print-ep-callbacks -o /dev/null -S %s | FileCheck 
--check-prefix=EP %s
+!
+! EP:     PipelineStart
+! EP:     PipelineEarlySimplification
+! EP-NOT: Peephole
+! EP:     ScalarOptimizerLate
+! EP-NOT: Peephole
+! EP:     OptimizerEarly
+! EP:     VectorizerStart
+! EP:     VectorizerEnd
+! EP:     OptimizerLast
+
+! Entry-points in optimizer pipeline
+!
+! RUN: %flang -fpass-plugin=%llvmshlibdir/Bye%pluginext -O2 \
+! RUN:        -Xflang -load -Xflang %llvmshlibdir/Bye%pluginext \
+! RUN:        -mllvm -print-ep-callbacks -o /dev/null -S %s | FileCheck 
--check-prefix=EP-OPT %s
+!
+! RUN: %flang -fpass-plugin=%llvmshlibdir/Bye%pluginext -O2 -flto=full \
+! RUN:        -Xflang -load -Xflang %llvmshlibdir/Bye%pluginext \
+! RUN:        -mllvm -print-ep-callbacks -o /dev/null -S %s | FileCheck 
--check-prefix=EP-OPT %s
+!
+! EP-OPT: PipelineStart
+! EP-OPT: PipelineEarlySimplification
+! EP-OPT: Peephole
+! EP-OPT: ScalarOptimizerLate
+! EP-OPT: Peephole
+! EP-OPT: OptimizerEarly
+! EP-OPT: VectorizerStart
+! EP-OPT: VectorizerEnd
+! EP-OPT: OptimizerLast
+
+! FIXME: Thin-LTO does not invoke vectorizer callbacks
+!
+! RUN: %flang -fpass-plugin=%llvmshlibdir/Bye%pluginext -O2 -flto=thin \
+! RUN:        -Xflang -load -Xflang %llvmshlibdir/Bye%pluginext \
+! RUN:        -mllvm -print-ep-callbacks -o /dev/null -S %s | FileCheck 
--check-prefix=EP-LTO-THIN %s
+!
+! EP-LTO-THIN:     PipelineStart
+! EP-LTO-THIN:     PipelineEarlySimplification
+! EP-LTO-THIN:     Peephole
+! EP-LTO-THIN:     ScalarOptimizerLate
+! EP-LTO-THIN:     OptimizerEarly
+! EP-LTO-THIN-NOT: Vectorizer
+! EP-LTO-THIN:     OptimizerLast
+
+INTEGER FUNCTION f(x)
+  INTEGER, INTENT(IN) :: x
+  f = x
+END FUNCTION f
diff --git a/flang/test/lit.cfg.py b/flang/test/lit.cfg.py
index 4221354df34a2..1d8a1704e6838 100644
--- a/flang/test/lit.cfg.py
+++ b/flang/test/lit.cfg.py
@@ -90,8 +90,9 @@
 # directories.
 config.excludes = ["Inputs", "CMakeLists.txt", "README.txt", "LICENSE.txt"]
 
-# If the flang examples are built, add examples to the config
-if config.flang_examples:
+# Some tests depend on examples, mark them as available in the config. This
+# setting includes examples from both, flang and llvm.
+if config.build_examples:
     config.available_features.add("examples")
 
 # Plugins (loadable modules)
diff --git a/flang/test/lit.site.cfg.py.in b/flang/test/lit.site.cfg.py.in
index cc1f4fa6cc9c5..1422cc36232cb 100644
--- a/flang/test/lit.site.cfg.py.in
+++ b/flang/test/lit.site.cfg.py.in
@@ -17,7 +17,7 @@ config.flang_intrinsic_modules_dir = 
"@FLANG_INTRINSIC_MODULES_DIR@"
 config.flang_headers_dir = "@HEADER_BINARY_DIR@"
 config.flang_llvm_tools_dir = "@CMAKE_BINARY_DIR@/bin"
 config.flang_test_triple = "@FLANG_TEST_TARGET_TRIPLE@"
-config.flang_examples = @LLVM_BUILD_EXAMPLES@
+config.build_examples = @LLVM_BUILD_EXAMPLES@
 config.python_executable = "@PYTHON_EXECUTABLE@"
 config.flang_standalone_build = @FLANG_STANDALONE_BUILD@
 config.has_plugins = @LLVM_ENABLE_PLUGINS@
diff --git a/lld/test/ELF/lto/ltopasses-extension.ll 
b/lld/test/ELF/lto/ltopasses-extension.ll
index db8edbb8e087d..ce92176d07249 100644
--- a/lld/test/ELF/lto/ltopasses-extension.ll
+++ b/lld/test/ELF/lto/ltopasses-extension.ll
@@ -1,9 +1,44 @@
 ; REQUIRES: x86, plugins, examples
 ; UNSUPPORTED: target={{.*windows.*}}
-; RUN: opt -module-summary %s -o %t.o
+; RUN: opt %s -o %t.o
+; RUN: opt -module-summary %s -o %t_thin.o
+
 ; RUN: ld.lld -%loadnewpmbye --lto-newpm-passes="goodbye" -mllvm=%loadbye 
-mllvm=-wave-goodbye %t.o -o /dev/null 2>&1 | FileCheck %s
 ; CHECK: Bye
 
+; Entry-points in pipeline for regular/monolithic LTO
+;
+; RUN: ld.lld -%loadnewpmbye -mllvm=%loadbye -mllvm=-print-ep-callbacks %t.o \
+; RUN:         -shared -o /dev/null | FileCheck --check-prefix=REGULAR %s
+;
+; REGULAR-NOT: PipelineStart
+; REGULAR-NOT: PipelineEarlySimplification
+; REGULAR-NOT: Peephole
+; REGULAR-NOT: ScalarOptimizerLate
+; REGULAR-NOT: Vectorizer
+; REGULAR-NOT: Optimizer
+;
+; REGULAR: FullLinkTimeOptimizationEarly
+; REGULAR: FullLinkTimeOptimizationLast
+
+; Entry-points in Thin-LTO pipeline
+;
+; RUN: ld.lld -%loadnewpmbye -mllvm=%loadbye -mllvm=-print-ep-callbacks 
%t_thin.o \
+; RUN:         -shared -o /dev/null | FileCheck --check-prefix=THIN %s
+;
+; THIN-NOT: FullLinkTimeOptimizationEarly
+; THIN-NOT: FullLinkTimeOptimizationLast
+; THIN-NOT: PipelineStart
+;
+; THIN: PipelineEarlySimplification
+; THIN: Peephole
+; THIN: ScalarOptimizerLate
+; THIN: Peephole
+; THIN: OptimizerEarly
+; THIN: VectorizerStart
+; THIN: VectorizerEnd
+; THIN: OptimizerLast
+
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 target triple = "x86_64-unknown-linux-gnu"
 @junk = global i32 0
diff --git a/llvm/examples/Bye/Bye.cpp b/llvm/examples/Bye/Bye.cpp
index d88bf9e490e9c..5108cf4b3d0c0 100644
--- a/llvm/examples/Bye/Bye.cpp
+++ b/llvm/examples/Bye/Bye.cpp
@@ -11,6 +11,10 @@ using namespace llvm;
 static cl::opt<bool> Wave("wave-goodbye", cl::init(false),
                           cl::desc("wave good bye"));
 
+static cl::opt<bool> PrintEntryPointCallbacks(
+    "print-ep-callbacks", cl::init(false),
+    cl::desc("Print names of all entry-points upon callback"));
+
 namespace {
 
 bool runBye(Function &F) {
@@ -35,6 +39,102 @@ struct Bye : PassInfoMixin<Bye> {
   }
 };
 
+struct PrintStage : PassInfoMixin<Bye> {
+  PrintStage(std::string Name) : Name(std::move(Name)) {}
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
+    outs() << Name << "\n";
+    return PreservedAnalyses::none();
+  }
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &) {
+    outs() << Name << "\n";
+    return PreservedAnalyses::none();
+  }
+
+private:
+  std::string Name;
+};
+
+void registerPassBuilderCallbacks(PassBuilder &PB) {
+  PB.registerVectorizerStartEPCallback(
+      [](llvm::FunctionPassManager &PM, OptimizationLevel Level) {
+        PM.addPass(Bye());
+      });
+  PB.registerPipelineParsingCallback(
+      [](StringRef Name, llvm::FunctionPassManager &PM,
+         ArrayRef<llvm::PassBuilder::PipelineElement>) {
+        if (Name == "goodbye") {
+          PM.addPass(Bye());
+          return true;
+        }
+        return false;
+      });
+
+  if (PrintEntryPointCallbacks) {
+    PB.registerPipelineStartEPCallback(
+        [](ModulePassManager &MPM, OptimizationLevel Opt) {
+          MPM.addPass(PrintStage("PipelineStart"));
+          return true;
+        });
+
+    PB.registerPipelineEarlySimplificationEPCallback(
+        [](ModulePassManager &MPM, OptimizationLevel Opt,
+           ThinOrFullLTOPhase Phase) {
+          MPM.addPass(PrintStage("PipelineEarlySimplification"));
+          return true;
+        });
+
+    PB.registerOptimizerEarlyEPCallback([](ModulePassManager &MPM,
+                                           OptimizationLevel Opt,
+                                           ThinOrFullLTOPhase Phase) {
+      MPM.addPass(PrintStage("OptimizerEarly"));
+      return true;
+    });
+
+    PB.registerOptimizerLastEPCallback([](ModulePassManager &MPM,
+                                          OptimizationLevel Opt,
+                                          ThinOrFullLTOPhase Phase) {
+      MPM.addPass(PrintStage("OptimizerLast"));
+      return true;
+    });
+
+    PB.registerPeepholeEPCallback(
+        [](FunctionPassManager &FPM, OptimizationLevel Opt) {
+          FPM.addPass(PrintStage("Peephole"));
+          return true;
+        });
+
+    PB.registerScalarOptimizerLateEPCallback(
+        [](FunctionPassManager &FPM, OptimizationLevel Opt) {
+          FPM.addPass(PrintStage("ScalarOptimizerLate"));
+          return true;
+        });
+
+    PB.registerVectorizerStartEPCallback(
+        [](FunctionPassManager &FPM, OptimizationLevel Opt) {
+          FPM.addPass(PrintStage("VectorizerStart"));
+          return true;
+        });
+
+    PB.registerVectorizerEndEPCallback(
+        [](FunctionPassManager &FPM, OptimizationLevel Opt) {
+          FPM.addPass(PrintStage("VectorizerEnd"));
+          return true;
+        });
+
+    PB.registerFullLinkTimeOptimizationEarlyEPCallback(
+        [](ModulePassManager &MPM, OptimizationLevel Opt) {
+          MPM.addPass(PrintStage("FullLinkTimeOptimizationEarly"));
+          return true;
+        });
+
+    PB.registerFullLinkTimeOptimizationLastEPCallback(
+        [](ModulePassManager &MPM, OptimizationLevel Opt) {
+          MPM.addPass(PrintStage("FullLinkTimeOptimizationLast"));
+          return true;
+        });
+  }
+}
+
 } // namespace
 
 char LegacyBye::ID = 0;
@@ -46,21 +146,7 @@ static RegisterPass<LegacyBye> X("goodbye", "Good Bye World 
Pass",
 /* New PM Registration */
 llvm::PassPluginLibraryInfo getByePluginInfo() {
   return {LLVM_PLUGIN_API_VERSION, "Bye", LLVM_VERSION_STRING,
-          [](PassBuilder &PB) {
-            PB.registerVectorizerStartEPCallback(
-                [](llvm::FunctionPassManager &PM, OptimizationLevel Level) {
-                  PM.addPass(Bye());
-                });
-            PB.registerPipelineParsingCallback(
-                [](StringRef Name, llvm::FunctionPassManager &PM,
-                   ArrayRef<llvm::PassBuilder::PipelineElement>) {
-                  if (Name == "goodbye") {
-                    PM.addPass(Bye());
-                    return true;
-                  }
-                  return false;
-                });
-          }};
+          registerPassBuilderCallbacks};
 }
 
 #ifndef LLVM_BYE_LINK_INTO_TOOLS

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to