forked from OSchip/llvm-project
[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
This commit is contained in:
parent
8f359a80e4
commit
f52fc591fa
|
@ -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]
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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");
|
|
@ -30,6 +30,10 @@ struct MeasurementVisitor {
|
|||
// Custom Consumer Actions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
class PluginParseTreeAction : public FrontendAction {
|
||||
void ExecuteAction() override;
|
||||
};
|
||||
|
||||
class InputOutputTestAction : public FrontendAction {
|
||||
void ExecuteAction() override;
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -479,3 +479,5 @@ void InitOnlyAction::ExecuteAction() {
|
|||
"Use `-init-only` for testing purposes only");
|
||||
ci.diagnostics().Report(DiagID);
|
||||
}
|
||||
|
||||
void PluginParseTreeAction::ExecuteAction() {}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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'
|
|
@ -30,6 +30,8 @@ config.suffixes = ['.c', '.cpp', '.f', '.F', '.ff', '.FOR', '.for', '.f77', '.f9
|
|||
'.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.excludes = ['Inputs', 'CMakeLists.txt', 'README.txt', 'LICENSE.txt']
|
|||
# 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__)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue