diff --git a/flang/examples/CMakeLists.txt b/flang/examples/CMakeLists.txt --- a/flang/examples/CMakeLists.txt +++ b/flang/examples/CMakeLists.txt @@ -11,4 +11,4 @@ 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 --- 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 rename from flang/examples/HelloWorld/CMakeLists.txt rename to flang/examples/PrintFlangFunctionNames/CMakeLists.txt --- 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 --- /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 --- a/flang/include/flang/Frontend/FrontendActions.h +++ b/flang/include/flang/Frontend/FrontendActions.h @@ -30,10 +30,6 @@ // Custom Consumer Actions //===----------------------------------------------------------------------===// -class PluginParseTreeAction : public FrontendAction { - void ExecuteAction() override; -}; - class InputOutputTestAction : public FrontendAction { void ExecuteAction() override; }; @@ -131,6 +127,10 @@ 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 --- a/flang/test/CMakeLists.txt +++ b/flang/test/CMakeLists.txt @@ -56,7 +56,9 @@ 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 --- 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 --- /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 --- /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 --- /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 --- /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