diff --git a/flang/docs/FlangDriver.md b/flang/docs/FlangDriver.md --- a/flang/docs/FlangDriver.md +++ b/flang/docs/FlangDriver.md @@ -272,3 +272,119 @@ test as only available on Unix-like systems (i.e. systems that contain a Unix shell). In practice this means that the corresponding test is skipped on Windows. + +# Frontend Driver Plugins +Plugins are an extension to the Frontend Driver that make it possible to run +extra user defined actions, in the form of a `FrontendAction`, during a +compilation, after semantic checks. Similarly to Clang, Flang leverages +`LoadLibraryPermanently` from LLVM's `llvm::sys::DynamicLibrary` to load dynamic +objects that implement Plugins. The process for using Plugins includes: +* Creating a Plugin Shared Object library (i.e. implementation) +* Loading and running a Plugin Shared Object (i.e. usage) + +Plugins are currently only available, and have been tested, on Linux. + +## Creating the Plugin +There are three parts required for Plugins to work: +1. A class that wraps everything together and represents the Frontend Action + corresponding to your Plugin, which inherits from `PluginParseTreeAction` +1. An `ExecuteAction` function that implements what the Plugin actually does +1. A line to register the Plugin + +There is an example Plugin located in `flang/example/PrintFlangFunctionNames` +that demonstrates these points by using the Parse Tree to print out function +names. + +### A `PluginParseTreeAction` Subclass +The main class of the Plugin will need to inherit from `PluginParseTreeAction` +(defined in `flang/include/flang/FrontendActions.h`), in order to have access to +the Parse Tree post semantic checks, and also so that it can be registerd, e.g. +```cpp +class PrintFunctionNamesAction : public PluginParseTreeAction +``` + +### Implementation of `ExecuteAction` +Like a normal `FrontendAction`, your Plugin class will need to implement the +`ExecuteAction` method. This method will contain what the Plugin actually does, +and will be the function that runs when the Plugin is used, for example: +```cpp +void ExecuteAction() override { + auto &parseTree{instance().parsing().parseTree()}; + ParseTreeVisitor visitor; + Fortran::parser::Walk(parseTree, visitor); +} +``` +In the example Plugin, the `ExecuteAction` method first gets a reference to the +Parse Tree, `instance().parsing().parseTree()`, then declares a `visitor` +struct, before passing both of these to the `Fortran::parser::Walk` function. + +A `visitor` struct should have functions `Pre` and `Post` defined, which take +a type of Parse Tree node as an argument. When the `Walk` function is +traversing the Parse Tree, these functions will be run before/after a node of +that type is visited. Template functions for `Pre`/`Post` are defined so that +when a node is visited that you have not defined a function for, it will still +be able to continue. `Pre` returns a `bool` indicating whether to visit that +node or not, for example: +```cpp +struct ParseTreeVisitor { + template bool Pre(const A&) { return true; } + template void Post(const A &) {} + void Post(const Fortran::parser::FunctionStmt &f) { + llvm::outs() << std::get(f.t).ToString() << "\n" ; + } +} +``` +The different types of nodes and also what each node structure contain are in +`flang/include/flang/Parser/parse-tree.h`. In the example, there is a `Post` +function, with a line that gets the `Name` element from a tuple `t` in the +`FunctionStmt` struct and prints it. This function will be run after every +`FunctionStmt` node is visited in the Parse Tree. + +### Plugin Registration +A Plugin registry is used to store names and descriptions of a collection of +Plugins. The Flang Plugin registry, defined in +`flang/include/flang/Frontend/FrontendPluginRegistry.h`, is an alias of +`llvm::Registry` of type `PluginParseTreeAction`. + +So, the Plugin will need to be registered, which will add the Plugin to the +registry and allow it to be used. The format is as follows, with `print-fns` +being the Plugin name that is used later to call the Plugin and +`Print Function names` being the description: +```cpp +static FrontendPluginRegistry::Add X( + "print-fns", "Print Function names"); +``` + +## Loading and Running a Plugin +In order to use Plugins, there are 2 command line options for the Frontend Driver: +* `-load ` for loading the dynamic shared object of the Plugin +* `-plugin ` for calling the registered Plugin + +Invocation of the example plugin is done through: +```bash +flang-new -fc1 -load flangPrintFunctionNames.so -plugin print-fns file.f90 +``` + +Both these options are parsed in `flang/lib/Frontend/CompilerInvocation.cpp` and +fulfil their actions in +`flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp` + +### The `-load` option +This loads the Plugin shared object library, with the path given to the option, +using `LoadLibraryPermantly` from LLVM's `llvm::sys::DynamicLibrary`, which +itself uses `dlopen`. During this stage, the Plugin is registered with the +registration line from the Plugin, storing the name and description. + +### The `-plugin` option +This sets `frontend::ActionKind programAction` in `FrontendOptions` to +`PluginAction`, through which it searches the Plugin registry for the Plugin +name that was given to it. If found, it returns the instantiated Plugin, +otherwise it reports an error diagnostic and returns `nullptr`. + +## Plugin CMake Flag +There is a CMake flag `FLANG_BUILD_EXAMPLES`, which when enabled, builds the +Plugins that are in the `flang/example` directory and added as a `sub_directory` +to the `flang/examples/CMakeLists.txt`. Additionally, enabling the flag also +allows the example Plugin tests to run. Appending the Plugin to +`FLANG_TEST_DEPENDS` in `flang/test/CMakeLists.txt` means that tests can be +added for new Plugins.