From 520e5db26a4a9fcb418d9ef2da813155038caade Mon Sep 17 00:00:00 2001 From: Stuart Ellis Date: Thu, 19 Aug 2021 08:07:45 +0000 Subject: [PATCH] [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 --- flang/examples/CMakeLists.txt | 2 +- .../examples/HelloWorld/HelloWorldPlugin.cpp | 25 ------ .../CMakeLists.txt | 4 +- .../PrintFlangFunctionNames.cpp | 81 +++++++++++++++++++ .../include/flang/Frontend/FrontendActions.h | 8 +- flang/test/CMakeLists.txt | 4 +- flang/test/Driver/plugin-example.f90 | 11 --- flang/test/Driver/plugin-invalid-name.f90 | 7 ++ flang/test/Examples/print-fns-calls.f90 | 21 +++++ flang/test/Examples/print-fns-definitions.f90 | 40 +++++++++ flang/test/Examples/print-fns-interfaces.f90 | 26 ++++++ 11 files changed, 185 insertions(+), 44 deletions(-) delete mode 100644 flang/examples/HelloWorld/HelloWorldPlugin.cpp rename flang/examples/{HelloWorld => PrintFlangFunctionNames}/CMakeLists.txt (72%) create mode 100644 flang/examples/PrintFlangFunctionNames/PrintFlangFunctionNames.cpp delete mode 100644 flang/test/Driver/plugin-example.f90 create mode 100644 flang/test/Driver/plugin-invalid-name.f90 create mode 100644 flang/test/Examples/print-fns-calls.f90 create mode 100644 flang/test/Examples/print-fns-definitions.f90 create mode 100644 flang/test/Examples/print-fns-interfaces.f90 diff --git a/flang/examples/CMakeLists.txt b/flang/examples/CMakeLists.txt index c4ef3bf20d4b..41a29fbda1f4 100644 --- a/flang/examples/CMakeLists.txt +++ b/flang/examples/CMakeLists.txt @@ -11,4 +11,4 @@ target_link_libraries(external-hello-world FortranRuntime ) -add_subdirectory(HelloWorld) +add_subdirectory(PrintFlangFunctionNames) diff --git a/flang/examples/HelloWorld/HelloWorldPlugin.cpp b/flang/examples/HelloWorld/HelloWorldPlugin.cpp deleted file mode 100644 index 11100384aed9..000000000000 --- a/flang/examples/HelloWorld/HelloWorldPlugin.cpp +++ /dev/null @@ -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 X( - "-hello-world", "Hello World Plugin example"); diff --git a/flang/examples/HelloWorld/CMakeLists.txt b/flang/examples/PrintFlangFunctionNames/CMakeLists.txt similarity index 72% rename from flang/examples/HelloWorld/CMakeLists.txt rename to flang/examples/PrintFlangFunctionNames/CMakeLists.txt index 8552284c8052..6b107b4e1ea5 100644 --- a/flang/examples/HelloWorld/CMakeLists.txt +++ b/flang/examples/PrintFlangFunctionNames/CMakeLists.txt @@ -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 ) diff --git a/flang/examples/PrintFlangFunctionNames/PrintFlangFunctionNames.cpp b/flang/examples/PrintFlangFunctionNames/PrintFlangFunctionNames.cpp new file mode 100644 index 000000000000..0afbf9f35e53 --- /dev/null +++ b/flang/examples/PrintFlangFunctionNames/PrintFlangFunctionNames.cpp @@ -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 bool Pre(const A &) { return true; } + template 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(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(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 X( + "print-fns", "Print Function names"); diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h index 43fd1f0f6596..ec9d9f79c694 100644 --- a/flang/include/flang/Frontend/FrontendActions.h +++ b/flang/include/flang/Frontend/FrontendActions.h @@ -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 diff --git a/flang/test/CMakeLists.txt b/flang/test/CMakeLists.txt index d7a7dccdc172..1d0bef1cbe66 100644 --- a/flang/test/CMakeLists.txt +++ b/flang/test/CMakeLists.txt @@ -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}) diff --git a/flang/test/Driver/plugin-example.f90 b/flang/test/Driver/plugin-example.f90 deleted file mode 100644 index 73a48e34e72c..000000000000 --- a/flang/test/Driver/plugin-example.f90 +++ /dev/null @@ -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' diff --git a/flang/test/Driver/plugin-invalid-name.f90 b/flang/test/Driver/plugin-invalid-name.f90 new file mode 100644 index 000000000000..55fc423b5e8f --- /dev/null +++ b/flang/test/Driver/plugin-invalid-name.f90 @@ -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' diff --git a/flang/test/Examples/print-fns-calls.f90 b/flang/test/Examples/print-fns-calls.f90 new file mode 100644 index 000000000000..4702e1b56a57 --- /dev/null +++ b/flang/test/Examples/print-fns-calls.f90 @@ -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 diff --git a/flang/test/Examples/print-fns-definitions.f90 b/flang/test/Examples/print-fns-definitions.f90 new file mode 100644 index 000000000000..fc8fcb29f97b --- /dev/null +++ b/flang/test/Examples/print-fns-definitions.f90 @@ -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 diff --git a/flang/test/Examples/print-fns-interfaces.f90 b/flang/test/Examples/print-fns-interfaces.f90 new file mode 100644 index 000000000000..39a2bf3e465f --- /dev/null +++ b/flang/test/Examples/print-fns-interfaces.f90 @@ -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