From 2186a4aea0e3e50e51b121d304889349c4e033ef Mon Sep 17 00:00:00 2001 From: Andrzej Warzynski Date: Fri, 4 Mar 2022 13:05:21 +0000 Subject: [PATCH] [flang] Make the plugin API independent of the driver internals This patch adds a few new member methods in the `PluginParseTreeAction` frontend action base class. With these new methods, the plugin API becomes independent of the driver internals. In particular, plugin writers no longer require the `CompilerInstance.h` header file to access various driver data structures (instead, they can use newly added hooks). This change is desirable as `CompilerInstance.h` includes various headers from Clang (both explicitly and implicitly). Some of these header files are generated at build time (through TableGen) and including them creates a dependency on some of Clang's build targets. However, plugins in Flang should not depend on Clang build targets. Note that plugins might still work fine most of the time, even without this change and without adding Clang build targets as dependency in plugin's CMake definition. Indeed, these Clang build targets are often generated early in the build process. However, that's not guaranteed and we did notice that on occasions plugins would fail to build. Differential Revision: https://reviews.llvm.org/D120999 --- flang/docs/FlangDriver.md | 35 ++++++++++++---------- flang/examples/FlangOmpReport/FlangOmpReport.cpp | 22 ++++++-------- flang/examples/FlangOmpReport/yaml_summarizer.py | 2 +- .../PrintFlangFunctionNames.cpp | 5 +--- flang/include/flang/Frontend/FrontendActions.h | 13 ++++++++ flang/lib/Frontend/FrontendActions.cpp | 14 +++++++++ 6 files changed, 58 insertions(+), 33 deletions(-) diff --git a/flang/docs/FlangDriver.md b/flang/docs/FlangDriver.md index cf363d1..ec75162 100644 --- a/flang/docs/FlangDriver.md +++ b/flang/docs/FlangDriver.md @@ -395,25 +395,30 @@ to run, so in order for your plugin to do something, you will need to implement the `ExecuteAction` method in your plugin class. This method will contain the implementation of what the plugin actually does, for example: ```cpp +// Forward declaration +struct ParseTreeVisitor; + void ExecuteAction() override { - auto &parseTree{instance().parsing().parseTree()}; ParseTreeVisitor visitor; - Fortran::parser::Walk(parseTree, visitor); + Fortran::parser::Walk(getParsing().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 -that will traverse the parse tree. Implementation and details of the `Walk` -function can be found in `flang/include/flang/Parser/parse-tree-visitor.h`. - -A `visitor` struct should define different `Pre` and `Post` functions that take -the type of a specific `ParseTree` 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's children or not. For example: +In the example plugin, the `ExecuteAction` method first creates an instance of +`visitor` struct, before passing it together with the parse tree to the +`Fortran::parser::Walk` function that will traverse the parse tree. The parse +tree will normally be generated by the frontend driver and can be retrieved in +your plugin through the `getParsing()` member method. Implementation and +details of the `Walk` function can be found in +`flang/include/flang/Parser/parse-tree-visitor.h`. + +You will have to define your own `visitor` struct. It should define different +`Pre` and `Post` functions that take the type of a specific `ParseTree` 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's children or not. For +example: ```cpp struct ParseTreeVisitor { template bool Pre(const A&) { return true; } diff --git a/flang/examples/FlangOmpReport/FlangOmpReport.cpp b/flang/examples/FlangOmpReport/FlangOmpReport.cpp index 0fa7582..229d6d1 100644 --- a/flang/examples/FlangOmpReport/FlangOmpReport.cpp +++ b/flang/examples/FlangOmpReport/FlangOmpReport.cpp @@ -5,19 +5,17 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// This plugin parses a Fortran source file and generates a YAML -// report with all the OpenMP constructs and clauses and which -// line they're located on. +// This plugin parses a Fortran source file and generates a YAML report with +// all the OpenMP constructs and clauses and which line they're located on. // // The plugin may be invoked as: -// ./bin/flang-new -fc1 -load lib/flangOmpReport.so -plugin -// flang-omp-report -fopenmp -o - +// ./bin/flang-new -fc1 -load lib/flangOmpReport.so -plugin flang-omp-report +// -fopenmp // //===----------------------------------------------------------------------===// #include "FlangOmpReportVisitor.h" -#include "flang/Frontend/CompilerInstance.h" #include "flang/Frontend/FrontendActions.h" #include "flang/Frontend/FrontendPluginRegistry.h" #include "flang/Parser/dump-parse-tree.h" @@ -53,20 +51,18 @@ template <> struct MappingTraits { class FlangOmpReport : public PluginParseTreeAction { void ExecuteAction() override { // Prepare the parse tree and the visitor - CompilerInstance &ci = this->instance(); - Parsing &parsing = ci.parsing(); - const Program &parseTree = *parsing.parseTree(); + Parsing &parsing = getParsing(); OpenMPCounterVisitor visitor; visitor.parsing = &parsing; // Walk the parse tree - Walk(parseTree, visitor); + Walk(parsing.parseTree(), visitor); // Dump the output - std::unique_ptr OS{ci.CreateDefaultOutputFile( - /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), - /*Extension=*/".yaml")}; + std::unique_ptr OS{ + createOutputFile(/*extension=*/"yaml")}; llvm::yaml::Output yout(*OS); + yout << visitor.constructClauses; } }; diff --git a/flang/examples/FlangOmpReport/yaml_summarizer.py b/flang/examples/FlangOmpReport/yaml_summarizer.py index 4c9981d..9d8df28 100644 --- a/flang/examples/FlangOmpReport/yaml_summarizer.py +++ b/flang/examples/FlangOmpReport/yaml_summarizer.py @@ -1,6 +1,6 @@ """YAML Summariser -The flang plugin ``flang-omp-report`` takes one fortran +The flang plugin ``flang-omp-report`` takes one Fortran file in and returns a YAML report file of the input file. This becomes an issue when you want to analyse an entire project into one final report. diff --git a/flang/examples/PrintFlangFunctionNames/PrintFlangFunctionNames.cpp b/flang/examples/PrintFlangFunctionNames/PrintFlangFunctionNames.cpp index 0afbf9f..90590be 100644 --- a/flang/examples/PrintFlangFunctionNames/PrintFlangFunctionNames.cpp +++ b/flang/examples/PrintFlangFunctionNames/PrintFlangFunctionNames.cpp @@ -18,7 +18,6 @@ // //===----------------------------------------------------------------------===// -#include "flang/Frontend/CompilerInstance.h" #include "flang/Frontend/FrontendActions.h" #include "flang/Frontend/FrontendPluginRegistry.h" #include "flang/Parser/dump-parse-tree.h" @@ -67,10 +66,8 @@ class PrintFunctionNamesAction : public PluginParseTreeAction { }; void ExecuteAction() override { - auto &parseTree{instance().parsing().parseTree()}; - ParseTreeVisitor visitor; - Fortran::parser::Walk(parseTree, visitor); + Fortran::parser::Walk(getParsing().parseTree(), visitor); llvm::outs() << "\n==== Functions: " << visitor.fcounter << " ====\n"; llvm::outs() << "==== Subroutines: " << visitor.scounter << " ====\n"; diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h index 48836c6..4831b1e 100644 --- a/flang/include/flang/Frontend/FrontendActions.h +++ b/flang/include/flang/Frontend/FrontendActions.h @@ -10,9 +10,11 @@ #define LLVM_FLANG_FRONTEND_FRONTENDACTIONS_H #include "flang/Frontend/FrontendAction.h" +#include "flang/Parser/parsing.h" #include "flang/Semantics/semantics.h" #include "mlir/IR/BuiltinOps.h" +#include "llvm/ADT/StringRef.h" #include "llvm/IR/Module.h" #include @@ -131,6 +133,17 @@ class ParseSyntaxOnlyAction : public PrescanAndSemaAction { class PluginParseTreeAction : public PrescanAndSemaAction { void ExecuteAction() override = 0; + +public: + Fortran::parser::Parsing &getParsing(); + /// Creates an output file. This is just a wrapper for calling + /// CreateDefaultOutputFile from CompilerInstance. Use it to make sure that + /// your plugin respects driver's `-o` flag. + /// \param extension The extension to use for the output file (ignored when + /// the user decides to print to stdout via `-o -`) + /// \return Null on error, ostream for the output file otherwise + std::unique_ptr createOutputFile( + llvm::StringRef extension); }; //===----------------------------------------------------------------------===// diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp index 83e795c..fd93bca 100644 --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -648,3 +648,17 @@ void DebugDumpPFTAction::ExecuteAction() { clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL."); ci.diagnostics().Report(DiagID); } + +Fortran::parser::Parsing &PluginParseTreeAction::getParsing() { + return instance().parsing(); +} + +std::unique_ptr +PluginParseTreeAction::createOutputFile(llvm::StringRef extension = "") { + + std::unique_ptr OS{ + instance().CreateDefaultOutputFile( + /*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName(), + extension)}; + return OS; +} -- 2.7.4