Test entry-points for pass-plugins with Bye in clang, flang and lld
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.
@llvm/pr-subscribers-lld-elf
Author: Stefan Gränitz (weliveindetail)
Changes
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.
Full diff: https://github.com/llvm/llvm-project/pull/172463.diff
10 Files Affected:
- (modified) clang/test/CMakeLists.txt (+6)
- (added) clang/test/CodeGen/pass-plugins-entrypoints.c (+66)
- (added) clang/test/Interpreter/pass-plugins.cpp (+9)
- (modified) clang/test/lit.cfg.py (+2)
- (modified) clang/test/lit.site.cfg.py.in (+1)
- (added) flang/test/Integration/pass-plugins-entrypoints.f90 (+64)
- (modified) flang/test/lit.cfg.py (+3-2)
- (modified) flang/test/lit.site.cfg.py.in (+1-1)
- (modified) lld/test/ELF/lto/ltopasses-extension.ll (+36-1)
- (modified) llvm/examples/Bye/Bye.cpp (+101-15)
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
@llvm/pr-subscribers-lld
Author: Stefan Gränitz (weliveindetail)
Changes
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.
Full diff: https://github.com/llvm/llvm-project/pull/172463.diff
10 Files Affected:
- (modified) clang/test/CMakeLists.txt (+6)
- (added) clang/test/CodeGen/pass-plugins-entrypoints.c (+66)
- (added) clang/test/Interpreter/pass-plugins.cpp (+9)
- (modified) clang/test/lit.cfg.py (+2)
- (modified) clang/test/lit.site.cfg.py.in (+1)
- (added) flang/test/Integration/pass-plugins-entrypoints.f90 (+64)
- (modified) flang/test/lit.cfg.py (+3-2)
- (modified) flang/test/lit.site.cfg.py.in (+1-1)
- (modified) lld/test/ELF/lto/ltopasses-extension.ll (+36-1)
- (modified) llvm/examples/Bye/Bye.cpp (+101-15)
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
By the way, an alternative testing method of which callbacks are invoked by which pipeline can be seen in llvm/test/Other/new-pm-O0-ep-callbacks.ll. That does not rely on plugins at all.
:penguin: Linux x64 Test Results
- 194434 tests passed
- 6299 tests skipped
:white_check_mark: The build succeeded and all tests passed.
I'd rather not have these tests duplicated across different tools in different projects. Changing pass builder callbacks should not require changes to flang tests.
I think tests that the plugin mechanism is working makes sense for all tools. A lot can wrong in each tool, from not parsing the command line arguments to forgetting to call the plugin handling mechnism. In short: it should be tested, at least a smoke test.
Shouldn't testing these via opt be sufficient? [...] As to which callbacks should get invoked, yeah, that's somewhat messy.
It would be great to have transparency, what entry-points are available across tools with their individual configurations. It's anything than obvious which of the LTO pipeline builder is used in which configuration of a tool. Tests are a good way to document that behavior and make sure it's up-to-date.
It looks like some of the callbacks are also missing here, e.g. the loop ones?
Not all callbacks are registered (yet). The ones that are, also get tested. Happy to add others on request.
Would it be better to have the
PrintStagepass be part of its own pass plugin?
No strong opinion here. Happy to go for what seems best for everyone. The reason for extending Bye is that it is already used for testing across projects.
I think tests that the plugin mechanism is working makes sense for all tools.
I agree, but I also agree with nikic that it should only be a smoke test per tool. The extension points could be just tested with opt.
That said, could we wait with Clang plugin changes until #171868 is merged? For Clang, this PR is currently just duplicating efforts.
I think tests that the plugin mechanism is working makes sense for all tools.
I agree, but I also agree with nikic that it should only be a smoke test per tool. The extension points could be just tested with opt.
That said, could we wait with Clang plugin changes until #171868 is merged? For Clang, this PR is currently just duplicating efforts.
Right. To be clear, I do think we should have basic tests for the plugin mechanism in all tools, along the lines of what https://github.com/llvm/llvm-project/pull/171868 does. I'm only objecting to testing of pass pipeline hooks being spread across lots of different projects.
Feedback so far is, that these tests aren't necessary. I think they are.
There is value in documenting plugin entry-points for tools in their individual configurations. It makes sense to do this in tests in a readable way. We must fix them if the behavior changes and we can add entry-points when we introduce new ones. It would be easy to check which version of which tools supported which set of entry-points.
Yes, there are tests for the individual pipelines in LLVM. These are tests for core features, they are hard to read and they give no overview. I can imagine that this is all obvious for full-time LLVM experts, but it's not clear for plugin developers who use LLVM as one tool out of many. I think we should provide better transparency and this patch could help.
Let me leave the PR here for documentation for now. And maybe as a reference for people who start working with pass plugins in the future.
FYI @jeanPerier @eugeneepshteyn @vzakhari