[flang][driver] Add print function name Plugin example
authorStuart Ellis <stuart.ellis@arm.com>
Thu, 19 Aug 2021 08:07:45 +0000 (08:07 +0000)
committerAndrzej Warzynski <andrzej.warzynski@arm.com>
Thu, 19 Aug 2021 08:25:34 +0000 (08:25 +0000)
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
flang/examples/HelloWorld/HelloWorldPlugin.cpp [deleted file]
flang/examples/PrintFlangFunctionNames/CMakeLists.txt [moved from flang/examples/HelloWorld/CMakeLists.txt with 72% similarity]
flang/examples/PrintFlangFunctionNames/PrintFlangFunctionNames.cpp [new file with mode: 0644]
flang/include/flang/Frontend/FrontendActions.h
flang/test/CMakeLists.txt
flang/test/Driver/plugin-example.f90 [deleted file]
flang/test/Driver/plugin-invalid-name.f90 [new file with mode: 0644]
flang/test/Examples/print-fns-calls.f90 [new file with mode: 0644]
flang/test/Examples/print-fns-definitions.f90 [new file with mode: 0644]
flang/test/Examples/print-fns-interfaces.f90 [new file with mode: 0644]

index c4ef3bf..41a29fb 100644 (file)
@@ -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 (file)
index 1110038..0000000
+++ /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<HelloWorldFlangPlugin> X(
-    "-hello-world", "Hello World Plugin example");
@@ -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 (file)
index 0000000..0afbf9f
--- /dev/null
@@ -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");
index 43fd1f0..ec9d9f7 100644 (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
index d7a7dcc..1d0bef1 100644 (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})
diff --git a/flang/test/Driver/plugin-example.f90 b/flang/test/Driver/plugin-example.f90
deleted file mode 100644 (file)
index 73a48e3..0000000
+++ /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 (file)
index 0000000..55fc423
--- /dev/null
@@ -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 (file)
index 0000000..4702e1b
--- /dev/null
@@ -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 (file)
index 0000000..fc8fcb2
--- /dev/null
@@ -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 (file)
index 0000000..39a2bf3
--- /dev/null
@@ -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