[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
This commit is contained in:
Andrzej Warzynski 2022-03-04 13:05:21 +00:00
parent e69c21f75b
commit 2186a4aea0
6 changed files with 57 additions and 32 deletions

View File

@ -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`.
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`.
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:
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 <typename A> bool Pre(const A&) { return true; }

View File

@ -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 - <source_file.f90>
// ./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<LogRecord> {
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<llvm::raw_pwrite_stream> OS{ci.CreateDefaultOutputFile(
/*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(),
/*Extension=*/".yaml")};
std::unique_ptr<llvm::raw_pwrite_stream> OS{
createOutputFile(/*extension=*/"yaml")};
llvm::yaml::Output yout(*OS);
yout << visitor.constructClauses;
}
};

View File

@ -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.

View File

@ -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";

View File

@ -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 <memory>
@ -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<llvm::raw_pwrite_stream> createOutputFile(
llvm::StringRef extension);
};
//===----------------------------------------------------------------------===//

View File

@ -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<llvm::raw_pwrite_stream>
PluginParseTreeAction::createOutputFile(llvm::StringRef extension = "") {
std::unique_ptr<llvm::raw_pwrite_stream> OS{
instance().CreateDefaultOutputFile(
/*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName(),
extension)};
return OS;
}