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,82 @@ +//===-- 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 which walks the Parse Tree using a visitor struct +// that has Post functions for FunctionStmt and SubroutineStmt to count and +// print out the names of the functions and subroutines. It also has Pre +// functions for FunctionSubprogram and SubroutineSubprogram so a Bool can be +// set to show that it is the definition of the procedure, and not print the +// procedures that are in an Interface +// +//===----------------------------------------------------------------------===// + +#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; + } + } + + void Post(const Fortran::parser::MpSubprogramStmt &m) { + llvm::outs() << "ModuleProcedure: " << (m.v).ToString() << "\n"; + } + + 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; }; @@ -140,6 +136,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-print-fns.f90 b/flang/test/Driver/plugin-print-fns.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/plugin-print-fns.f90 @@ -0,0 +1,111 @@ +! Check that the Flang Print Function Names example plugin correctly prints & counts function/subroutine names +! It checks that it prints external & internal procedures, but not procedure calls, +! and that the procedures in interfaces are only printed once +! 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 + +!---------------------------------------------- +! Check Error Diagnostic for wrong plugin name +!---------------------------------------------- +! RUN: not %flang_fc1 -load %llvmshlibdir/flangPrintFunctionNames%pluginext -plugin -wrong-name %s 2>&1 | FileCheck %s --check-prefix=ERROR +! ERROR: error: unable to find plugin '-wrong-name' + +!---------------------------------------------- +! Check printing of procedures +!---------------------------------------------- +! RUN: %flang_fc1 -load %llvmshlibdir/flangPrintFunctionNames%pluginext -plugin print-fns %s 2>&1 | FileCheck %s + +!------------------------------------------------- +! EXPECTED OUTPUT: Print names & num of each type +!------------------------------------------------- +! 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-NEXT: Function: interface_func +! CHECK-NEXT: Subroutine: interface_subr +! CHECK-NEXT: ModuleProcedure: sub +! CHECK-EMPTY: +! CHECK-NEXT: ==== Functions: 4 ==== +! CHECK-NEXT: ==== Subroutines: 3 ==== + +!-------------------------- +! INPUT +!-------------------------- + +function external_func1() + integer :: external_func1 + external_func1 = 1 +end function external_func1 + +function external_func2() + integer :: external_func2 + external_func2 = 2 +end function external_func2 + +subroutine external_subr + print *, "External Subroutine" +end subroutine external_subr + +program main + implicit none + + interface + function interface_func() + integer :: interface_func + end function interface_func + + subroutine interface_subr(out) + integer, intent(out) :: out + end subroutine interface_subr + end interface + + integer :: external_func1 + integer :: external_func2 + + integer :: subr_result + + call external_subr + call interface_subr(subr_result) + call internal_subr + + print *, external_func1(), external_func2(), interface_func(), internal_func() + +contains + function internal_func() + integer :: internal_func + internal_func = 4 + end function + + subroutine internal_subr + print *, "Internal Subroutine" + end subroutine internal_subr +end program main + +function interface_func() + integer :: interface_func + interface_func = 3 +end function interface_func + +subroutine interface_subr(out) + integer, intent(out) :: out + out = 6 +end subroutine interface_subr + +module m + interface + module subroutine sub(arg) + integer :: arg + end subroutine + end interface +end module + +submodule (m) n +contains + module procedure sub + arg = 1 + end procedure +end submodule