[flang-commits] [flang] f52fc59 - [flang][driver] Add support for Frontend Plugins
Andrzej Warzynski via flang-commits
flang-commits at lists.llvm.org
Thu Aug 12 03:56:16 PDT 2021
Author: Stuart Ellis
Date: 2021-08-12T11:42:16+01:00
New Revision: f52fc591fa34a8c85577108358b3b36c42b6d364
URL: https://github.com/llvm/llvm-project/commit/f52fc591fa34a8c85577108358b3b36c42b6d364
DIFF: https://github.com/llvm/llvm-project/commit/f52fc591fa34a8c85577108358b3b36c42b6d364.diff
LOG: [flang][driver] Add support for Frontend Plugins
Introducing a plugin API and a simple HelloWorld Plugin example.
This patch adds the `-load` and `-plugin` flags to frontend driver and
the code around using custom frontend actions from within a plugin
shared library object.
It also adds to the Driver-help test to check the help option with the
updated driver flags.
Additionally, the patch creates a plugin-example test to check the
HelloWorld plugin example runs correctly. As part of this, a new CMake
flag (`FLANG_BUILD_EXAMPLES`) is added to allow the example to be built
and for the test to run.
This Plugin API has only been tested on Linux.
Reviewed By: awarzynski
Differential Revision: https://reviews.llvm.org/D106137
Added:
flang/examples/HelloWorld/CMakeLists.txt
flang/examples/HelloWorld/HelloWorldPlugin.cpp
flang/include/flang/Frontend/FrontendPluginRegistry.h
flang/test/Driver/plugin-example.f90
Modified:
clang/include/clang/Driver/Options.td
flang/CMakeLists.txt
flang/examples/CMakeLists.txt
flang/include/flang/Frontend/FrontendActions.h
flang/include/flang/Frontend/FrontendOptions.h
flang/lib/Frontend/CompilerInvocation.cpp
flang/lib/Frontend/FrontendAction.cpp
flang/lib/Frontend/FrontendActions.cpp
flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
flang/test/CMakeLists.txt
flang/test/Driver/driver-help.f90
flang/test/lit.cfg.py
flang/test/lit.site.cfg.py.in
flang/tools/flang-driver/CMakeLists.txt
Removed:
################################################################################
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 7525795bf9d3c..a91114f76ff07 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -5266,10 +5266,6 @@ def enable_noundef_analysis : Flag<["-"], "enable-noundef-analysis">, Group<f_Gr
def discard_value_names : Flag<["-"], "discard-value-names">,
HelpText<"Discard value names in LLVM IR">,
MarshallingInfoFlag<CodeGenOpts<"DiscardValueNames">>;
-def load : Separate<["-"], "load">, MetaVarName<"<dsopath>">,
- HelpText<"Load the named plugin (dynamic shared object)">;
-def plugin : Separate<["-"], "plugin">, MetaVarName<"<name>">,
- HelpText<"Use the named plugin action instead of the default action (use \"help\" to list available options)">;
def plugin_arg : JoinedAndSeparate<["-"], "plugin-arg-">,
MetaVarName<"<name> <arg>">,
HelpText<"Pass <arg> to plugin <name>">;
@@ -5836,6 +5832,12 @@ def init_only : Flag<["-"], "init-only">,
HelpText<"Only execute frontend initialization">;
} // let Group = Action_Group
+
+def load : Separate<["-"], "load">, MetaVarName<"<dsopath>">,
+ HelpText<"Load the named plugin (dynamic shared object)">;
+def plugin : Separate<["-"], "plugin">, MetaVarName<"<name>">,
+ HelpText<"Use the named plugin action instead of the default action (use \"help\" to list available options)">;
+
} // let Flags = [CC1Option, FC1Option, NoDriverOption]
//===----------------------------------------------------------------------===//
diff --git a/flang/CMakeLists.txt b/flang/CMakeLists.txt
index fc85f44513163..cf6055075c383 100644
--- a/flang/CMakeLists.txt
+++ b/flang/CMakeLists.txt
@@ -390,6 +390,8 @@ if (FLANG_BUILD_TOOLS)
add_subdirectory(tools)
endif()
add_subdirectory(runtime)
+
+option(FLANG_BUILD_EXAMPLES "Build Flang example programs by default." OFF)
add_subdirectory(examples)
if (FLANG_INCLUDE_TESTS)
diff --git a/flang/examples/CMakeLists.txt b/flang/examples/CMakeLists.txt
index 3ca9feddf33e9..c4ef3bf20d4b0 100644
--- a/flang/examples/CMakeLists.txt
+++ b/flang/examples/CMakeLists.txt
@@ -1,3 +1,7 @@
+if(NOT FLANG_BUILD_EXAMPLES)
+ set(EXCLUDE_FROM_ALL ON)
+endif()
+
# This test is not run by default as it requires input.
add_executable(external-hello-world
external-hello.cpp
@@ -6,3 +10,5 @@ add_executable(external-hello-world
target_link_libraries(external-hello-world
FortranRuntime
)
+
+add_subdirectory(HelloWorld)
diff --git a/flang/examples/HelloWorld/CMakeLists.txt b/flang/examples/HelloWorld/CMakeLists.txt
new file mode 100644
index 0000000000000..8552284c80529
--- /dev/null
+++ b/flang/examples/HelloWorld/CMakeLists.txt
@@ -0,0 +1,7 @@
+# TODO: Note that this is currently only available on Linux.
+# On Windows, we would also have to specify e.g. `PLUGIN_TOOL`.
+add_llvm_library(
+ flangHelloWorldPlugin
+ MODULE
+ HelloWorldPlugin.cpp
+)
diff --git a/flang/examples/HelloWorld/HelloWorldPlugin.cpp b/flang/examples/HelloWorld/HelloWorldPlugin.cpp
new file mode 100644
index 0000000000000..11100384aed9f
--- /dev/null
+++ b/flang/examples/HelloWorld/HelloWorldPlugin.cpp
@@ -0,0 +1,25 @@
+//===-- HelloWorldPlugin.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Basic example Flang plugin which simply prints a Hello World statement
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Frontend/FrontendActions.h"
+#include "flang/Frontend/FrontendPluginRegistry.h"
+
+using namespace Fortran::frontend;
+
+class HelloWorldFlangPlugin : public PluginParseTreeAction {
+ void ExecuteAction() override {
+ llvm::outs() << "Hello World from your new Flang plugin\n";
+ }
+};
+
+static FrontendPluginRegistry::Add<HelloWorldFlangPlugin> X(
+ "-hello-world", "Hello World Plugin example");
diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h
index 72eb44223fe49..d30ae1dbed0ff 100644
--- a/flang/include/flang/Frontend/FrontendActions.h
+++ b/flang/include/flang/Frontend/FrontendActions.h
@@ -30,6 +30,10 @@ struct MeasurementVisitor {
// Custom Consumer Actions
//===----------------------------------------------------------------------===//
+class PluginParseTreeAction : public FrontendAction {
+ void ExecuteAction() override;
+};
+
class InputOutputTestAction : public FrontendAction {
void ExecuteAction() override;
};
diff --git a/flang/include/flang/Frontend/FrontendOptions.h b/flang/include/flang/Frontend/FrontendOptions.h
index 6dc397104efa1..26a5728bc3ea3 100644
--- a/flang/include/flang/Frontend/FrontendOptions.h
+++ b/flang/include/flang/Frontend/FrontendOptions.h
@@ -77,7 +77,10 @@ enum ActionKind {
GetSymbolsSources,
/// Only execute frontend initialization
- InitOnly
+ InitOnly,
+
+ /// Run a plugin action
+ PluginAction
/// TODO: RunPreprocessor, EmitLLVM, EmitLLVMOnly,
/// EmitCodeGenOnly, EmitAssembly, (...)
@@ -248,6 +251,12 @@ struct FrontendOptions {
// Source file encoding
Fortran::parser::Encoding encoding{Fortran::parser::Encoding::UTF_8};
+ /// The list of plugins to load.
+ std::vector<std::string> plugins;
+
+ /// The name of the action to run when using a plugin action.
+ std::string ActionName;
+
// Return the appropriate input kind for a file extension. For example,
/// "*.f" would return Language::Fortran.
///
diff --git a/flang/include/flang/Frontend/FrontendPluginRegistry.h b/flang/include/flang/Frontend/FrontendPluginRegistry.h
new file mode 100644
index 0000000000000..555a06ff65df3
--- /dev/null
+++ b/flang/include/flang/Frontend/FrontendPluginRegistry.h
@@ -0,0 +1,26 @@
+//===- FrontendPluginRegistry.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Pluggable Frontend Action Interface
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FLANG_FRONTEND_FRONTENDPLUGINREGISTRY_H
+#define FLANG_FRONTEND_FRONTENDPLUGINREGISTRY_H
+
+#include "flang/Frontend/FrontendActions.h"
+#include "llvm/Support/Registry.h"
+
+namespace Fortran::frontend {
+
+/// The frontend plugin registry.
+using FrontendPluginRegistry = llvm::Registry<PluginParseTreeAction>;
+
+} // namespace Fortran::frontend
+
+#endif // FLANG_FRONTEND_FRONTENDPLUGINREGISTRY_H
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index baa0f32c55c87..c16c9690f0599 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -199,6 +199,18 @@ static bool ParseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
}
}
+ // Parsing -load <dsopath> option and storing shared object path
+ if (llvm::opt::Arg *a = args.getLastArg(clang::driver::options::OPT_load)) {
+ opts.plugins.push_back(a->getValue());
+ }
+
+ // Parsing -plugin <name> option and storing plugin name and setting action
+ if (const llvm::opt::Arg *a =
+ args.getLastArg(clang::driver::options::OPT_plugin)) {
+ opts.programAction = PluginAction;
+ opts.ActionName = a->getValue();
+ }
+
opts.outputFile = args.getLastArgValue(clang::driver::options::OPT_o);
opts.showHelp = args.hasArg(clang::driver::options::OPT_help);
opts.showVersion = args.hasArg(clang::driver::options::OPT_version);
diff --git a/flang/lib/Frontend/FrontendAction.cpp b/flang/lib/Frontend/FrontendAction.cpp
index 24efcc7cdc7f3..77700d2abec78 100644
--- a/flang/lib/Frontend/FrontendAction.cpp
+++ b/flang/lib/Frontend/FrontendAction.cpp
@@ -10,6 +10,7 @@
#include "flang/Frontend/CompilerInstance.h"
#include "flang/Frontend/FrontendActions.h"
#include "flang/Frontend/FrontendOptions.h"
+#include "flang/Frontend/FrontendPluginRegistry.h"
#include "flang/FrontendTool/Utils.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "llvm/Support/Errc.h"
@@ -17,6 +18,8 @@
using namespace Fortran::frontend;
+LLVM_INSTANTIATE_REGISTRY(FrontendPluginRegistry)
+
void FrontendAction::set_currentInput(const FrontendInputFile ¤tInput) {
this->currentInput_ = currentInput;
}
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index d8bdb46c6958a..c12cafc02dbf3 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -479,3 +479,5 @@ void InitOnlyAction::ExecuteAction() {
"Use `-init-only` for testing purposes only");
ci.diagnostics().Report(DiagID);
}
+
+void PluginParseTreeAction::ExecuteAction() {}
diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index a17b6b5079e91..677f8cd75c0ff 100644
--- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -13,6 +13,7 @@
#include "flang/Frontend/CompilerInstance.h"
#include "flang/Frontend/FrontendActions.h"
+#include "flang/Frontend/FrontendPluginRegistry.h"
#include "clang/Driver/Options.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Option/Option.h"
@@ -62,6 +63,19 @@ static std::unique_ptr<FrontendAction> CreateFrontendBaseAction(
return std::make_unique<GetSymbolsSourcesAction>();
case InitOnly:
return std::make_unique<InitOnlyAction>();
+ case PluginAction: {
+ for (const FrontendPluginRegistry::entry &plugin :
+ FrontendPluginRegistry::entries()) {
+ if (plugin.getName() == ci.frontendOpts().ActionName) {
+ std::unique_ptr<PluginParseTreeAction> p(plugin.instantiate());
+ return std::move(p);
+ }
+ }
+ unsigned diagID = ci.diagnostics().getCustomDiagID(
+ clang::DiagnosticsEngine::Error, "unable to find plugin '%0'");
+ ci.diagnostics().Report(diagID) << ci.frontendOpts().ActionName;
+ return nullptr;
+ }
default:
break;
// TODO:
@@ -82,6 +96,7 @@ std::unique_ptr<FrontendAction> CreateFrontendAction(CompilerInstance &ci) {
return act;
}
+
bool ExecuteCompilerInvocation(CompilerInstance *flang) {
// Honor -help.
if (flang->frontendOpts().showHelp) {
@@ -99,6 +114,22 @@ bool ExecuteCompilerInvocation(CompilerInstance *flang) {
return true;
}
+ // Load any requested plugins.
+ for (const std::string &Path : flang->frontendOpts().plugins) {
+ std::string Error;
+ if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(
+ Path.c_str(), &Error)) {
+ unsigned diagID = flang->diagnostics().getCustomDiagID(
+ clang::DiagnosticsEngine::Error, "unable to load plugin '%0': '%1'");
+ flang->diagnostics().Report(diagID) << Path << Error;
+ }
+ }
+
+ // If there were errors in processing arguments, don't do anything else.
+ if (flang->diagnostics().hasErrorOccurred()) {
+ return false;
+ }
+
// Create and execute the frontend action.
std::unique_ptr<FrontendAction> act(CreateFrontendAction(*flang));
if (!act)
diff --git a/flang/test/CMakeLists.txt b/flang/test/CMakeLists.txt
index e2e95596d30b8..8cd1e0ef93d14 100644
--- a/flang/test/CMakeLists.txt
+++ b/flang/test/CMakeLists.txt
@@ -2,7 +2,9 @@
# for use by Lit, and delegates to LLVM's lit test handlers.
llvm_canonicalize_cmake_booleans(
+ FLANG_BUILD_EXAMPLES
FLANG_STANDALONE_BUILD
+ LLVM_ENABLE_PLUGINS
)
set(FLANG_TOOLS_DIR ${FLANG_BINARY_DIR}/bin)
@@ -12,6 +14,8 @@ configure_lit_site_cfg(
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py
MAIN_CONFIG
${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py
+ PATHS
+ "SHLIBDIR"
)
configure_lit_site_cfg(
@@ -41,6 +45,10 @@ if (FLANG_INCLUDE_TESTS)
endif()
endif()
+if (FLANG_BUILD_EXAMPLES)
+ list(APPEND FLANG_TEST_DEPENDS flangHelloWorldPlugin)
+endif ()
+
add_custom_target(flang-test-depends DEPENDS ${FLANG_TEST_DEPENDS})
add_lit_testsuite(check-flang "Running the Flang regression tests"
diff --git a/flang/test/Driver/driver-help.f90 b/flang/test/Driver/driver-help.f90
index 288f03dc4dea7..bc2dc7e3fc991 100644
--- a/flang/test/Driver/driver-help.f90
+++ b/flang/test/Driver/driver-help.f90
@@ -123,11 +123,13 @@
! HELP-FC1-NEXT: -help Display available options
! HELP-FC1-NEXT: -init-only Only execute frontend initialization
! HELP-FC1-NEXT: -I <dir> Add directory to the end of the list of include search paths
+! HELP-FC1-NEXT: -load <dsopath> Load the named plugin (dynamic shared object)
! HELP-FC1-NEXT: -module-dir <dir> Put MODULE files in <dir>
! HELP-FC1-NEXT: -module-suffix <suffix> Use <suffix> as the suffix for module files (the default value is `.mod`)
! HELP-FC1-NEXT: -nocpp Disable predefined and command line preprocessor macros
! HELP-FC1-NEXT: -o <file> Write output to <file>
! HELP-FC1-NEXT: -pedantic Warn on language extensions
+! HELP-FC1-NEXT: -plugin <name> Use the named plugin action instead of the default action (use "help" to list available options)
! HELP-FC1-NEXT: -P Disable linemarker output in -E mode
! HELP-FC1-NEXT: -std=<value> Language standard to compile for
! HELP-FC1-NEXT: -test-io Run the InputOuputTest action. Use for development and testing only.
diff --git a/flang/test/Driver/plugin-example.f90 b/flang/test/Driver/plugin-example.f90
new file mode 100644
index 0000000000000..eb009dea1f13f
--- /dev/null
+++ b/flang/test/Driver/plugin-example.f90
@@ -0,0 +1,11 @@
+! Check that loading and running the Hello World plugin example results in the correct print statement
+! Also check that when a plugin name isn't found, the error diagnostic is correct
+! This requires that the examples are built (FLANG_BUILD_EXAMPLES=ON)
+
+! REQUIRES: new-flang-driver, plugins, examples, shell
+
+! RUN: %flang_fc1 -load %llvmshlibdir/flangHelloWorldPlugin%pluginext -plugin -hello-world %s 2>&1 | FileCheck %s
+! CHECK: Hello World from your new Flang plugin
+
+! RUN: not %flang_fc1 -load %llvmshlibdir/flangHelloWorldPlugin%pluginext -plugin -wrong-name %s 2>&1 | FileCheck %s --check-prefix=ERROR
+! ERROR: error: unable to find plugin '-wrong-name'
diff --git a/flang/test/lit.cfg.py b/flang/test/lit.cfg.py
index 854b4e723884a..4c69b688d0426 100644
--- a/flang/test/lit.cfg.py
+++ b/flang/test/lit.cfg.py
@@ -30,6 +30,8 @@
'.CUF', '.f18', '.F18', '.fir', '.f03', '.F03', '.f08', '.F08']
config.substitutions.append(('%PATH%', config.environment['PATH']))
+config.substitutions.append(('%llvmshlibdir', config.llvm_shlib_dir))
+config.substitutions.append(('%pluginext', config.llvm_plugin_ext))
llvm_config.use_default_substitutions()
@@ -42,6 +44,14 @@
# config.
config.available_features.add('new-flang-driver')
+# If the flang examples are built, add examples to the config
+if config.flang_examples:
+ config.available_features.add('examples')
+
+# Plugins (loadable modules)
+if config.has_plugins:
+ config.available_features.add('plugins')
+
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)
diff --git a/flang/test/lit.site.cfg.py.in b/flang/test/lit.site.cfg.py.in
index 746866bd722a6..5314be8502f6b 100644
--- a/flang/test/lit.site.cfg.py.in
+++ b/flang/test/lit.site.cfg.py.in
@@ -3,6 +3,8 @@
import sys
config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
+config.llvm_shlib_dir = path(r"@SHLIBDIR@")
+config.llvm_plugin_ext = "@LLVM_PLUGIN_EXT@"
config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
config.flang_obj_root = "@FLANG_BINARY_DIR@"
config.flang_src_dir = "@FLANG_SOURCE_DIR@"
@@ -10,8 +12,10 @@ config.flang_tools_dir = "@FLANG_TOOLS_DIR@"
config.flang_intrinsic_modules_dir = "@FLANG_INTRINSIC_MODULES_DIR@"
config.flang_llvm_tools_dir = "@CMAKE_BINARY_DIR@/bin"
config.flang_lib_dir = "@CMAKE_BINARY_DIR@/lib"
+config.flang_examples = @FLANG_BUILD_EXAMPLES@
config.python_executable = "@PYTHON_EXECUTABLE@"
config.flang_standalone_build = @FLANG_STANDALONE_BUILD@
+config.has_plugins = @LLVM_ENABLE_PLUGINS@
config.cc = "@CMAKE_C_COMPILER@"
# Support substitution of the tools_dir with user parameters. This is
@@ -19,6 +23,7 @@ config.cc = "@CMAKE_C_COMPILER@"
try:
config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params
config.flang_tools_dir = config.flang_tools_dir % lit_config.params
+ config.llvm_shlib_dir = config.llvm_shlib_dir % lit_config.params
except KeyError:
e = sys.exc_info()[1]
key, = e.args
diff --git a/flang/tools/flang-driver/CMakeLists.txt b/flang/tools/flang-driver/CMakeLists.txt
index 4c7ad220d223a..d747fb19dfc61 100644
--- a/flang/tools/flang-driver/CMakeLists.txt
+++ b/flang/tools/flang-driver/CMakeLists.txt
@@ -27,4 +27,11 @@ clang_target_link_libraries(flang-new
clangBasic
)
+option(FLANG_PLUGIN_SUPPORT "Build Flang with plugin support." ON)
+
+# Enable support for plugins, which need access to symbols from flang-new
+if(FLANG_PLUGIN_SUPPORT)
+ export_executable_symbols_for_plugins(flang-new)
+endif()
+
install(TARGETS flang-new DESTINATION bin)
More information about the flang-commits
mailing list