[flang-commits] [flang] 277069e - [Flang] Adjust pass plugin support to match Clang (#174006)
via flang-commits
flang-commits at lists.llvm.org
Wed Jan 7 01:59:21 PST 2026
Author: Alexis Engelke
Date: 2026-01-07T09:59:17Z
New Revision: 277069e8c184be85854f4065df45af4778e2f9a0
URL: https://github.com/llvm/llvm-project/commit/277069e8c184be85854f4065df45af4778e2f9a0
DIFF: https://github.com/llvm/llvm-project/commit/277069e8c184be85854f4065df45af4778e2f9a0.diff
LOG: [Flang] Adjust pass plugin support to match Clang (#174006)
- Pass plugins can use LLVM options, matching #173287.
- Pass plugins can run a hook before codegen, matching #171872.
- Pass plugins are now tested whenever they can be built, matching
#171998.
Plugins currently don't work on AIX, tracked in #172203.
Added:
flang/test/Driver/pass-plugin-codegen.f90
Modified:
flang/include/flang/Frontend/CompilerInstance.h
flang/lib/Frontend/FrontendActions.cpp
flang/lib/FrontendTool/CMakeLists.txt
flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
flang/test/CMakeLists.txt
flang/test/Driver/pass-plugin.f90
flang/test/lit.site.cfg.py.in
Removed:
################################################################################
diff --git a/flang/include/flang/Frontend/CompilerInstance.h b/flang/include/flang/Frontend/CompilerInstance.h
index 4234e13597cd7..c0acd57cdc9ee 100644
--- a/flang/include/flang/Frontend/CompilerInstance.h
+++ b/flang/include/flang/Frontend/CompilerInstance.h
@@ -20,6 +20,7 @@
#include "flang/Semantics/runtime-type-info.h"
#include "flang/Semantics/semantics.h"
#include "flang/Support/StringOstream.h"
+#include "llvm/Plugins/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
@@ -60,6 +61,9 @@ class CompilerInstance {
std::unique_ptr<llvm::TargetMachine> targetMachine;
+ /// LLVM pass plugins.
+ std::vector<std::unique_ptr<llvm::PassPlugin>> passPlugins;
+
/// The stream for diagnostics from Semantics
llvm::raw_ostream *semaOutputStream = &llvm::errs();
@@ -261,7 +265,19 @@ class CompilerInstance {
createDefaultOutputFile(bool binary = true, llvm::StringRef baseInput = "",
llvm::StringRef extension = "");
- /// {
+ /// }
+ /// @name LLVM Pass Plugins
+ /// @{
+
+ void addPassPlugin(std::unique_ptr<llvm::PassPlugin> plugin) {
+ passPlugins.emplace_back(std::move(plugin));
+ }
+
+ llvm::ArrayRef<std::unique_ptr<llvm::PassPlugin>> getPassPlugins() const {
+ return passPlugins;
+ }
+
+ /// }
/// @name Target Machine
/// {
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index 5c0311ccab8fd..b3731ee2526ce 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -47,6 +47,7 @@
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
+#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LLVMRemarkStreamer.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Verifier.h"
@@ -882,15 +883,22 @@ getOutputStream(CompilerInstance &ci, llvm::StringRef inFile,
/// \param [in] llvmModule LLVM module to lower to assembly/machine-code
/// \param [in] codeGenOpts options configuring codegen pipeline
/// \param [out] os Output stream to emit the generated code to
-static void generateMachineCodeOrAssemblyImpl(clang::DiagnosticsEngine &diags,
- llvm::TargetMachine &tm,
- BackendActionTy act,
- llvm::Module &llvmModule,
- const CodeGenOptions &codeGenOpts,
- llvm::raw_pwrite_stream &os) {
+static void generateMachineCodeOrAssemblyImpl(
+ CompilerInstance &ci, clang::DiagnosticsEngine &diags,
+ llvm::TargetMachine &tm, BackendActionTy act, llvm::Module &llvmModule,
+ const CodeGenOptions &codeGenOpts, llvm::raw_pwrite_stream &os) {
assert(((act == BackendActionTy::Backend_EmitObj) ||
(act == BackendActionTy::Backend_EmitAssembly)) &&
"Unsupported action");
+ llvm::CodeGenFileType cgft = (act == BackendActionTy::Backend_EmitAssembly)
+ ? llvm::CodeGenFileType::AssemblyFile
+ : llvm::CodeGenFileType::ObjectFile;
+
+ // Invoke pre-codegen callback from plugin, which might want to take over the
+ // entire code generation itself.
+ for (const std::unique_ptr<llvm::PassPlugin> &plugin : ci.getPassPlugins())
+ if (plugin->invokePreCodeGenCallback(llvmModule, tm, cgft, os))
+ return;
// Set-up the pass manager, i.e create an LLVM code-gen pass pipeline.
// Currently only the legacy pass manager is supported.
@@ -907,9 +915,6 @@ static void generateMachineCodeOrAssemblyImpl(clang::DiagnosticsEngine &diags,
triple, tm.Options.ExceptionModel, tm.Options.FloatABIType,
tm.Options.EABIVersion, tm.Options.MCOptions.ABIName, tm.Options.VecLib));
- llvm::CodeGenFileType cgft = (act == BackendActionTy::Backend_EmitAssembly)
- ? llvm::CodeGenFileType::AssemblyFile
- : llvm::CodeGenFileType::ObjectFile;
std::unique_ptr<llvm::ToolOutputFile> dwoOS;
if (!codeGenOpts.SplitDwarfOutput.empty()) {
std::error_code ec;
@@ -943,7 +948,6 @@ static void generateMachineCodeOrAssemblyImpl(clang::DiagnosticsEngine &diags,
void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
CompilerInstance &ci = getInstance();
const CodeGenOptions &opts = ci.getInvocation().getCodeGenOpts();
- clang::DiagnosticsEngine &diags = ci.getDiagnostics();
llvm::OptimizationLevel level = mapToLevel(opts);
llvm::TargetMachine *targetMachine = &ci.getTargetMachine();
@@ -992,16 +996,9 @@ void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
llvm::PassBuilder pb(targetMachine, pto, pgoOpt, &pic);
- // Attempt to load pass plugins and register their callbacks with PB.
- for (auto &pluginFile : opts.LLVMPassPlugins) {
- auto passPlugin = llvm::PassPlugin::Load(pluginFile);
- if (passPlugin) {
- passPlugin->registerPassBuilderCallbacks(pb);
- } else {
- diags.Report(clang::diag::err_fe_unable_to_load_plugin)
- << pluginFile << passPlugin.takeError();
- }
- }
+ // Register plugin callbacks with PB.
+ for (const std::unique_ptr<llvm::PassPlugin> &plugin : ci.getPassPlugins())
+ plugin->registerPassBuilderCallbacks(pb);
// Register static plugin extensions.
#define HANDLE_EXTENSION(Ext) \
get##Ext##PluginInfo().RegisterPassBuilderCallbacks(pb);
@@ -1185,6 +1182,29 @@ class BackendRemarkConsumer : public llvm::DiagnosticHandler {
clang::diag::remark_fe_backend_optimization_remark_analysis);
}
+ void pluginDiagnosticHandler(const llvm::DiagnosticInfo &di) {
+ unsigned diagID;
+ switch (di.getSeverity()) {
+ case llvm::DS_Error:
+ diagID = clang::diag::err_fe_backend_plugin;
+ break;
+ case llvm::DS_Warning:
+ diagID = clang::diag::warn_fe_backend_plugin;
+ break;
+ case llvm::DS_Remark:
+ diagID = clang::diag::remark_fe_backend_plugin;
+ break;
+ case llvm::DS_Note:
+ diagID = clang::diag::note_fe_backend_plugin;
+ break;
+ }
+ std::string msg;
+ llvm::raw_string_ostream os(msg);
+ llvm::DiagnosticPrinterRawOStream diagPrinter(os);
+ di.print(diagPrinter);
+ diags.Report(diagID) << msg;
+ }
+
bool handleDiagnostics(const llvm::DiagnosticInfo &di) override {
switch (di.getKind()) {
case llvm::DK_OptimizationRemark:
@@ -1210,6 +1230,7 @@ class BackendRemarkConsumer : public llvm::DiagnosticHandler {
llvm::cast<llvm::MachineOptimizationRemarkAnalysis>(di));
break;
default:
+ pluginDiagnosticHandler(di);
break;
}
return true;
@@ -1438,7 +1459,7 @@ void CodeGenAction::executeAction() {
if (action == BackendActionTy::Backend_EmitAssembly ||
action == BackendActionTy::Backend_EmitObj) {
generateMachineCodeOrAssemblyImpl(
- diags, targetMachine, action, *llvmModule, codeGenOpts,
+ ci, diags, targetMachine, action, *llvmModule, codeGenOpts,
ci.isOutputStreamNull() ? *os : ci.getOutputStream());
if (timingMgr.isEnabled())
llvm::reportAndResetTimings(&ci.getTimingStreamCodeGen());
diff --git a/flang/lib/FrontendTool/CMakeLists.txt b/flang/lib/FrontendTool/CMakeLists.txt
index b69436c36d438..666fab1a6e2c0 100644
--- a/flang/lib/FrontendTool/CMakeLists.txt
+++ b/flang/lib/FrontendTool/CMakeLists.txt
@@ -11,6 +11,7 @@ add_flang_library(flangFrontendTool
LINK_COMPONENTS
Option
+ Plugins
Support
MLIR_LIBS
diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 7586be59ba01b..429a98416daf1 100644
--- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -26,6 +26,7 @@
#include "clang/Options/Options.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Option/Option.h"
+#include "llvm/Plugins/PassPlugin.h"
#include "llvm/Support/BuryPointer.h"
#include "llvm/Support/CommandLine.h"
@@ -177,6 +178,20 @@ bool executeCompilerInvocation(CompilerInstance *flang) {
}
}
+ // Load and store LLVM pass plugins.
+ for (const std::string &path :
+ flang->getInvocation().getCodeGenOpts().LLVMPassPlugins) {
+ if (llvm::Expected<llvm::PassPlugin> passPlugin =
+ llvm::PassPlugin::Load(path)) {
+ flang->addPassPlugin(std::make_unique<llvm::PassPlugin>(*passPlugin));
+ } else {
+ unsigned diagID = flang->getDiagnostics().getCustomDiagID(
+ clang::DiagnosticsEngine::Error, "unable to load plugin '%0': '%1'");
+ flang->getDiagnostics().Report(diagID)
+ << path << toString(passPlugin.takeError());
+ }
+ }
+
// Honor -mllvm. This should happen AFTER plugins have been loaded!
if (!flang->getFrontendOpts().llvmArgs.empty()) {
unsigned numArgs = flang->getFrontendOpts().llvmArgs.size();
diff --git a/flang/test/CMakeLists.txt b/flang/test/CMakeLists.txt
index 8c8e92faa787a..d3da00c755cf8 100644
--- a/flang/test/CMakeLists.txt
+++ b/flang/test/CMakeLists.txt
@@ -4,9 +4,9 @@ add_subdirectory(lib)
llvm_canonicalize_cmake_booleans(
FLANG_STANDALONE_BUILD
- LLVM_BUILD_EXAMPLES
LLVM_BYE_LINK_INTO_TOOLS
LLVM_ENABLE_PLUGINS
+ LLVM_INCLUDE_EXAMPLES
)
set(FLANG_TOOLS_DIR ${FLANG_BINARY_DIR}/bin)
@@ -83,7 +83,7 @@ if (NOT FLANG_STANDALONE_BUILD)
)
endif ()
-if (LLVM_BUILD_EXAMPLES AND LLVM_ENABLE_PLUGINS AND NOT (WIN32 OR CYGWIN) AND NOT FLANG_STANDALONE_BUILD)
+if (LLVM_INCLUDE_EXAMPLES AND LLVM_ENABLE_PLUGINS AND NOT (WIN32 OR CYGWIN) AND NOT FLANG_STANDALONE_BUILD)
list(APPEND FLANG_TEST_DEPENDS Bye)
endif()
@@ -93,7 +93,7 @@ if (FLANG_INCLUDE_TESTS)
endif()
endif()
-if (LLVM_BUILD_EXAMPLES)
+if (LLVM_INCLUDE_EXAMPLES)
list(APPEND FLANG_TEST_DEPENDS
flangPrintFunctionNames
flangOmpReport
diff --git a/flang/test/Driver/pass-plugin-codegen.f90 b/flang/test/Driver/pass-plugin-codegen.f90
new file mode 100644
index 0000000000000..c7dec9d6806c8
--- /dev/null
+++ b/flang/test/Driver/pass-plugin-codegen.f90
@@ -0,0 +1,38 @@
+! This test checks that the pre-codegen hook of LLVM pass plugins is executed
+! before the code generation pipeline. The hook can also replace the output
+! with its own.
+
+! UNSUPPORTED: system-windows
+
+! REQUIRES: plugins, examples
+! Plugins are currently broken on AIX, at least in the CI.
+! XFAIL: system-aix
+
+! Without -last-words the pass does nothing, flang emits assembly.
+! RUN: %flang_fc1 -S %s -o - %loadbye \
+! RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-INACTIVE
+
+! With -last-words the pass intercepts the code generation and prints
+! "CodeGen Bye" instead.
+! RUN: %flang_fc1 -S %s -o - %loadbye -mllvm -last-words \
+! RUN: | FileCheck %s --check-prefix=CHECK-ACTIVE
+
+! When emitting LLVM IR, no back-end is executed and therefore no
+! pre-codegen hook of LLVM pass plugins are executed.
+! RUN: %flang_fc1 -emit-llvm %s -o - %loadbye -mllvm -last-words \
+! RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-LLVM
+
+! Bye fails and reports an error when an object file should be emitted.
+! (Note that this is specific for Bye; other plugins can support this.)
+! RUN: not %flang_fc1 -emit-obj %s -o - %loadbye -mllvm -last-words \
+! RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-ERR
+
+! CHECK-INACTIVE-NOT: Bye
+! CHECK-INACTIVE: empty_:
+! CHECK-ACTIVE: CodeGen Bye
+! CHECK-LLVM-NOT: Bye
+! CHECK-LLVM: define{{.*}} void @empty_
+! CHECK-ERR: error: last words unsupported for binary output
+
+subroutine empty
+end subroutine empty
diff --git a/flang/test/Driver/pass-plugin.f90 b/flang/test/Driver/pass-plugin.f90
index 53c16cacdd247..5669025c2c787 100644
--- a/flang/test/Driver/pass-plugin.f90
+++ b/flang/test/Driver/pass-plugin.f90
@@ -1,17 +1,20 @@
-! Verify that the static and dynamically loaded pass plugins work as expected.
+! This tests that the plugin is correctly added to and executed as part of the
+! optimization pipeline.
! UNSUPPORTED: system-windows
! REQUIRES: plugins, examples
+! Plugins are currently broken on AIX, at least in the CI.
+! XFAIL: system-aix
-! RUN: %flang -S %s %loadbye -Xflang -fdebug-pass-manager -o /dev/null \
+! RUN: %flang -S %s %loadbye -mllvm -wave-goodbye -o /dev/null \
! RUN: 2>&1 | FileCheck %s
-! RUN: %flang_fc1 -S %s %loadbye -fdebug-pass-manager -o /dev/null \
+! RUN: %flang_fc1 -S %s %loadbye -mllvm -wave-goodbye -o /dev/null \
! RUN: 2>&1 | FileCheck %s
-! CHECK: Running pass: {{.*}}Bye on empty_
+! CHECK: Bye: empty_
subroutine empty
end subroutine empty
diff --git a/flang/test/lit.site.cfg.py.in b/flang/test/lit.site.cfg.py.in
index cc1f4fa6cc9c5..2b66dd64b8c13 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.flang_examples = @LLVM_INCLUDE_EXAMPLES@
config.python_executable = "@PYTHON_EXECUTABLE@"
config.flang_standalone_build = @FLANG_STANDALONE_BUILD@
config.has_plugins = @LLVM_ENABLE_PLUGINS@
More information about the flang-commits
mailing list