[flang][driver] Add print function name Plugin example

Replacing Hello World example Plugin with one that counts and prints the names of
functions and subroutines.
This involves changing the `PluginParseTreeAction` Plugin base class to
inherit from `PrescanAndSemaAction` class to get access to the Parse Tree
so that the Plugin can walk it.
Additionally, there are tests of this new Plugin to check it prints the correct
things in different circumstances.

Depends on: D106137

Reviewed By: awarzynski

Differential Revision: https://reviews.llvm.org/D107089
This commit is contained in:
Stuart Ellis 2021-08-19 08:07:45 +00:00 committed by Andrzej Warzynski
parent 8e8b70aa84
commit 520e5db26a
11 changed files with 185 additions and 44 deletions

View File

@ -11,4 +11,4 @@ target_link_libraries(external-hello-world
FortranRuntime
)
add_subdirectory(HelloWorld)
add_subdirectory(PrintFlangFunctionNames)

View File

@ -1,25 +0,0 @@
//===-- 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");

View File

@ -1,7 +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
flangPrintFunctionNames
MODULE
HelloWorldPlugin.cpp
PrintFlangFunctionNames.cpp
)

View File

@ -0,0 +1,81 @@
//===-- PrintFlangFunctionNames.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
//
//===----------------------------------------------------------------------===//
//
// Small example Flang plugin to count/print Functions & Subroutines names.
// It walks the Parse Tree using a Visitor struct that has Post functions for
// FunctionStmt and SubroutineStmt to access the names of functions &
// subroutines. It also has Pre functions for FunctionSubprogram and
// SubroutineSubprogram so a Bool can be set to show that it is the definition
// of a function/subroutine, and not print those that are in an Interface.
// This plugin does not recognise Statement Functions or Module Procedures,
// which could be dealt with through StmtFunctionStmt and MpSubprogramStmt nodes
// respectively.
//
//===----------------------------------------------------------------------===//
#include "flang/Frontend/CompilerInstance.h"
#include "flang/Frontend/FrontendActions.h"
#include "flang/Frontend/FrontendPluginRegistry.h"
#include "flang/Parser/dump-parse-tree.h"
#include "flang/Parser/parsing.h"
using namespace Fortran::frontend;
class PrintFunctionNamesAction : public PluginParseTreeAction {
// Visitor struct that defines Pre/Post functions for different types of nodes
struct ParseTreeVisitor {
template <typename A> bool Pre(const A &) { return true; }
template <typename A> void Post(const A &) {}
bool Pre(const Fortran::parser::FunctionSubprogram &) {
isInSubprogram_ = true;
return true;
}
void Post(const Fortran::parser::FunctionStmt &f) {
if (isInSubprogram_) {
llvm::outs() << "Function:\t"
<< std::get<Fortran::parser::Name>(f.t).ToString() << "\n";
fcounter++;
isInSubprogram_ = false;
}
}
bool Pre(const Fortran::parser::SubroutineSubprogram &) {
isInSubprogram_ = true;
return true;
}
void Post(const Fortran::parser::SubroutineStmt &s) {
if (isInSubprogram_) {
llvm::outs() << "Subroutine:\t"
<< std::get<Fortran::parser::Name>(s.t).ToString() << "\n";
scounter++;
isInSubprogram_ = false;
}
}
int fcounter{0};
int scounter{0};
private:
bool isInSubprogram_{false};
};
void ExecuteAction() override {
auto &parseTree{instance().parsing().parseTree()};
ParseTreeVisitor visitor;
Fortran::parser::Walk(parseTree, visitor);
llvm::outs() << "\n==== Functions: " << visitor.fcounter << " ====\n";
llvm::outs() << "==== Subroutines: " << visitor.scounter << " ====\n";
}
};
static FrontendPluginRegistry::Add<PrintFunctionNamesAction> X(
"print-fns", "Print Function names");

View File

@ -30,10 +30,6 @@ struct MeasurementVisitor {
// Custom Consumer Actions
//===----------------------------------------------------------------------===//
class PluginParseTreeAction : public FrontendAction {
void ExecuteAction() override;
};
class InputOutputTestAction : public FrontendAction {
void ExecuteAction() override;
};
@ -131,6 +127,10 @@ class ParseSyntaxOnlyAction : public PrescanAndSemaAction {
void ExecuteAction() override;
};
class PluginParseTreeAction : public PrescanAndSemaAction {
void ExecuteAction() override;
};
} // namespace Fortran::frontend
#endif // LLVM_FLANG_FRONTEND_FRONTENDACTIONS_H

View File

@ -56,7 +56,9 @@ if (FLANG_INCLUDE_TESTS)
endif()
if (FLANG_BUILD_EXAMPLES)
list(APPEND FLANG_TEST_DEPENDS flangHelloWorldPlugin)
list(APPEND FLANG_TEST_DEPENDS
flangPrintFunctionNames
)
endif ()
add_custom_target(flang-test-depends DEPENDS ${FLANG_TEST_DEPENDS})

View File

@ -1,11 +0,0 @@
! 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: 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'

View File

@ -0,0 +1,7 @@
! Check the correct error diagnostic is reported when a plugin name isn't found
! REQUIRES: plugins, shell
! RUN: not %flang_fc1 -plugin -wrong-name %s 2>&1 | FileCheck %s --check-prefix=ERROR
! ERROR: error: unable to find plugin '-wrong-name'

View File

@ -0,0 +1,21 @@
! Check the Flang Print Function Names example plugin doesn't count/print function/subroutine calls (should only count definitions)
! This requires that the examples are built (FLANG_BUILD_EXAMPLES=ON) to access flangPrintFunctionNames.so
! REQUIRES: plugins, examples, shell
! RUN: %flang_fc1 -load %llvmshlibdir/flangPrintFunctionNames%pluginext -plugin print-fns %s 2>&1 | FileCheck %s
!-----------------------------
! EXPECTED OUTPUT: Counts == 0
!-----------------------------
! CHECK: ==== Functions: 0 ====
! CHECK-NEXT: ==== Subroutines: 0 ====
!-----------------------------
! INPUT
!-----------------------------
program main
call subroutine1
fn1 = function1()
fn2 = function2()
end program main

View File

@ -0,0 +1,40 @@
! Check the Flang Print Function Names example plugin prints and counts function/subroutine definitions
! This includes internal and external Function/Subroutines, but not Statement Functions
! This requires that the examples are built (FLANG_BUILD_EXAMPLES=ON) to access flangPrintFunctionNames.so
! REQUIRES: plugins, examples, shell
! RUN: %flang_fc1 -load %llvmshlibdir/flangPrintFunctionNames%pluginext -plugin print-fns %s 2>&1 | FileCheck %s
!-------------------------------------------------
! EXPECTED OUTPUT: Names printed and counts != 0
!-------------------------------------------------
! CHECK: Function: external_func1
! CHECK-NEXT: Function: external_func2
! CHECK-NEXT: Subroutine: external_subr
! CHECK-NEXT: Function: internal_func
! CHECK-NEXT: Subroutine: internal_subr
! CHECK-EMPTY:
! CHECK-NEXT: ==== Functions: 3 ====
! CHECK-NEXT: ==== Subroutines: 2 ====
!--------------------------
! INPUT
!--------------------------
function external_func1()
end function
function external_func2()
end function
subroutine external_subr
end subroutine
program main
contains
function internal_func()
end function
subroutine internal_subr
end subroutine
end program main

View File

@ -0,0 +1,26 @@
! Check the Flang Print Function Names example plugin doesn't count/print Functions/Subroutines in interfaces
! (It should only count definitions, which will appear elsewhere for interfaced functions/subroutines)
! This requires that the examples are built (FLANG_BUILD_EXAMPLES=ON) to access flangPrintFunctionNames.so
! REQUIRES: plugins, examples, shell
! RUN: %flang_fc1 -load %llvmshlibdir/flangPrintFunctionNames%pluginext -plugin print-fns %s 2>&1 | FileCheck %s
!-----------------------------
! EXPECTED OUTPUT: Counts == 0
!-----------------------------
! CHECK: ==== Functions: 0 ====
! CHECK-NEXT: ==== Subroutines: 0 ====
!--------------------------
! INPUT
!--------------------------
program main
interface
function interface_func()
end function
subroutine interface_subr()
end subroutine
end interface
end program main