[flang-commits] [flang] [flang] Move parser invocations into ParserActions (PR #130309)

Kajetan Puchalski via flang-commits flang-commits at lists.llvm.org
Fri Mar 7 09:27:49 PST 2025


https://github.com/mrkajetanp created https://github.com/llvm/llvm-project/pull/130309

FrontendActions.cpp is currently one of the biggest compilation units in all of flang. Measuring its compilation gives the following metrics:

User time (seconds): 139.21
System time (seconds): 4.65
Maximum resident set size (kbytes): 5891440 (5.61 GB)

This commit separates out explicit invocations of the parser into a separate compilation unit - ParserActions.cpp - through helper functions in order to decrease the maximum compilation time and memory usage of a single unit.
After the split, the measurements of FrontendActions.cpp are as follows:

User time (seconds): 70.08
System time (seconds): 3.16
Maximum resident set size (kbytes): 3961492 (3.7 GB)

While the ones for the newly created ParserActions.cpp as follows:

User time (seconds): 104.33
System time (seconds): 3.37
Maximum resident set size (kbytes): 4185600 (3.99 GB)

>From 1dab2b97db1e8e1bdc3832633d83252b73faf3e8 Mon Sep 17 00:00:00 2001
From: Kajetan Puchalski <kajetan.puchalski at arm.com>
Date: Tue, 4 Mar 2025 18:07:35 +0000
Subject: [PATCH] [flang] Move parser invocations into ParserActions

FrontendActions.cpp is currently one of the biggest compilation units in
all of flang. Measuring its compilation gives the following metrics:

User time (seconds): 139.21
System time (seconds): 4.65
Maximum resident set size (kbytes): 5891440 (5.61 GB)

This commit separates out explicit invocations of the parser into a
separate compilation unit - ParserActions.cpp - through helper functions
in order to decrease the maximum compilation time and memory usage of a
single unit.
After the split, the measurements of FrontendActions.cpp are as follows:

User time (seconds): 70.08
System time (seconds): 3.16
Maximum resident set size (kbytes): 3961492 (3.7 GB)

While the ones for the newly created ParserActions.cpp as follows:

User time (seconds): 104.33
System time (seconds): 3.37
Maximum resident set size (kbytes): 4185600 (3.99 GB)

Signed-off-by: Kajetan Puchalski <kajetan.puchalski at arm.com>
---
 .../include/flang/Frontend/CompilerInstance.h |   3 +-
 .../flang/Frontend/CompilerInvocation.h       |   4 +-
 .../include/flang/Frontend/FrontendActions.h  |  19 +--
 flang/include/flang/Frontend/ParserActions.h  |  65 ++++++++
 flang/include/flang/Parser/options.h          |  43 +++++
 flang/include/flang/Parser/parsing.h          |  25 +--
 flang/lib/Frontend/CMakeLists.txt             |   1 +
 flang/lib/Frontend/FrontendAction.cpp         |   1 +
 flang/lib/Frontend/FrontendActions.cpp        | 147 ++---------------
 flang/lib/Frontend/ParserActions.cpp          | 152 ++++++++++++++++++
 10 files changed, 283 insertions(+), 177 deletions(-)
 create mode 100644 flang/include/flang/Frontend/ParserActions.h
 create mode 100644 flang/include/flang/Parser/options.h
 create mode 100644 flang/lib/Frontend/ParserActions.cpp

diff --git a/flang/include/flang/Frontend/CompilerInstance.h b/flang/include/flang/Frontend/CompilerInstance.h
index 509c9f4b9e91a..d34c128d01794 100644
--- a/flang/include/flang/Frontend/CompilerInstance.h
+++ b/flang/include/flang/Frontend/CompilerInstance.h
@@ -16,8 +16,7 @@
 #include "flang/Frontend/CompilerInvocation.h"
 #include "flang/Frontend/FrontendAction.h"
 #include "flang/Frontend/PreprocessorOptions.h"
-#include "flang/Parser/parsing.h"
-#include "flang/Parser/provenance.h"
+#include "flang/Frontend/ParserActions.h"
 #include "flang/Semantics/runtime-type-info.h"
 #include "flang/Semantics/semantics.h"
 #include "flang/Support/StringOstream.h"
diff --git a/flang/include/flang/Frontend/CompilerInvocation.h b/flang/include/flang/Frontend/CompilerInvocation.h
index 9e6724be33033..d6ee1511cdb4b 100644
--- a/flang/include/flang/Frontend/CompilerInvocation.h
+++ b/flang/include/flang/Frontend/CompilerInvocation.h
@@ -18,7 +18,7 @@
 #include "flang/Frontend/PreprocessorOptions.h"
 #include "flang/Frontend/TargetOptions.h"
 #include "flang/Lower/LoweringOptions.h"
-#include "flang/Parser/parsing.h"
+#include "flang/Parser/options.h"
 #include "flang/Semantics/semantics.h"
 #include "flang/Support/LangOptions.h"
 #include "mlir/Support/Timing.h"
@@ -29,7 +29,7 @@
 
 namespace llvm {
 class TargetMachine;
-}
+} // namespace llvm
 
 namespace Fortran::frontend {
 
diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h
index 4e3d3cb2657db..f9a45bd6c0a56 100644
--- a/flang/include/flang/Frontend/FrontendActions.h
+++ b/flang/include/flang/Frontend/FrontendActions.h
@@ -13,10 +13,8 @@
 #ifndef FORTRAN_FRONTEND_FRONTENDACTIONS_H
 #define FORTRAN_FRONTEND_FRONTENDACTIONS_H
 
-#include "flang/Frontend/CodeGenOptions.h"
 #include "flang/Frontend/FrontendAction.h"
-#include "flang/Parser/parsing.h"
-#include "flang/Semantics/semantics.h"
+#include "flang/Frontend/ParserActions.h"
 
 #include "mlir/IR/BuiltinOps.h"
 #include "mlir/IR/OwningOpRef.h"
@@ -26,21 +24,6 @@
 
 namespace Fortran::frontend {
 
-// TODO: This is a copy from f18.cpp. It doesn't really belong here and should
-// be moved to a more suitable place in future.
-struct MeasurementVisitor {
-  template <typename A>
-  bool Pre(const A &) {
-    return true;
-  }
-  template <typename A>
-  void Post(const A &) {
-    ++objects;
-    bytes += sizeof(A);
-  }
-  size_t objects{0}, bytes{0};
-};
-
 //===----------------------------------------------------------------------===//
 // Custom Consumer Actions
 //===----------------------------------------------------------------------===//
diff --git a/flang/include/flang/Frontend/ParserActions.h b/flang/include/flang/Frontend/ParserActions.h
new file mode 100644
index 0000000000000..9c76f2bc9b1ed
--- /dev/null
+++ b/flang/include/flang/Frontend/ParserActions.h
@@ -0,0 +1,65 @@
+//===- ParserActions.h -------------------------------------------*- C++-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_PARSER_ACTIONS_H_
+#define FORTRAN_PARSER_ACTIONS_H_
+
+#include <string>
+
+namespace llvm {
+    class raw_string_ostream;
+    class raw_ostream;
+    class StringRef;
+} // namespace llvm
+
+namespace Fortran::lower {
+    class LoweringBridge;
+} // namespace Fortran::lower
+
+namespace Fortran::parser {
+    class Parsing;
+    class AllCookedSources;
+} // namespace Fortran::parser
+
+namespace lower::pft {
+    class Program;
+} // namespace lower::pft
+
+//=== Frontend Parser helpers ===
+
+namespace Fortran::frontend {
+class CompilerInstance;
+
+Fortran::parser::AllCookedSources& getAllCooked(CompilerInstance &ci);
+
+void parseAndLowerTree(CompilerInstance &ci, Fortran::lower::LoweringBridge &lb);
+
+void dumpTree(CompilerInstance &ci);
+
+void dumpProvenance(CompilerInstance &ci);
+
+void dumpPreFIRTree(CompilerInstance &ci);
+
+void formatOrDumpPrescanner(std::string &buf, llvm::raw_string_ostream &outForPP, CompilerInstance &ci);
+
+void debugMeasureParseTree(CompilerInstance &ci, llvm::StringRef filename);
+
+void debugUnparseNoSema(CompilerInstance &ci, llvm::raw_ostream &out);
+
+void debugUnparseWithSymbols(CompilerInstance &ci);
+
+void debugUnparseWithModules(CompilerInstance &ci);
+
+void debugDumpParsingLog(CompilerInstance &ci);
+} // namespace Fortran::frontend
+
+#endif // FORTRAN_PARSER_ACTIONS_H_
diff --git a/flang/include/flang/Parser/options.h b/flang/include/flang/Parser/options.h
new file mode 100644
index 0000000000000..a9f93221ffdf1
--- /dev/null
+++ b/flang/include/flang/Parser/options.h
@@ -0,0 +1,43 @@
+//===-- include/flang/Parser/options.h --------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_PARSER_OPTIONS_H_
+#define FORTRAN_PARSER_OPTIONS_H_
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "characters.h"
+#include "flang/Support/Fortran-features.h"
+
+namespace Fortran::parser {
+
+struct Options {
+  Options() {}
+
+  using Predefinition = std::pair<std::string, std::optional<std::string>>;
+
+  bool isFixedForm{false};
+  int fixedFormColumns{72};
+  common::LanguageFeatureControl features;
+  std::vector<std::string> searchDirectories;
+  std::vector<std::string> intrinsicModuleDirectories;
+  std::vector<Predefinition> predefinitions;
+  bool instrumentedParse{false};
+  bool isModuleFile{false};
+  bool needProvenanceRangeToCharBlockMappings{false};
+  Fortran::parser::Encoding encoding{Fortran::parser::Encoding::UTF_8};
+  bool prescanAndReformat{false}; // -E
+  bool expandIncludeLinesInPreprocessedOutput{true};
+  bool showColors{false};
+};
+
+} // namespace Fortran::parser
+
+#endif // FORTRAN_PARSER_OPTIONS_H_
diff --git a/flang/include/flang/Parser/parsing.h b/flang/include/flang/Parser/parsing.h
index 116b6bd6f191f..3b4f5f5145a58 100644
--- a/flang/include/flang/Parser/parsing.h
+++ b/flang/include/flang/Parser/parsing.h
@@ -9,41 +9,18 @@
 #ifndef FORTRAN_PARSER_PARSING_H_
 #define FORTRAN_PARSER_PARSING_H_
 
-#include "characters.h"
 #include "instrumented-parser.h"
 #include "message.h"
 #include "parse-tree.h"
 #include "provenance.h"
+#include "options.h"
 #include "flang/Parser/preprocessor.h"
-#include "flang/Support/Fortran-features.h"
 #include "llvm/Support/raw_ostream.h"
 #include <optional>
 #include <string>
-#include <utility>
-#include <vector>
 
 namespace Fortran::parser {
 
-struct Options {
-  Options() {}
-
-  using Predefinition = std::pair<std::string, std::optional<std::string>>;
-
-  bool isFixedForm{false};
-  int fixedFormColumns{72};
-  common::LanguageFeatureControl features;
-  std::vector<std::string> searchDirectories;
-  std::vector<std::string> intrinsicModuleDirectories;
-  std::vector<Predefinition> predefinitions;
-  bool instrumentedParse{false};
-  bool isModuleFile{false};
-  bool needProvenanceRangeToCharBlockMappings{false};
-  Fortran::parser::Encoding encoding{Fortran::parser::Encoding::UTF_8};
-  bool prescanAndReformat{false}; // -E
-  bool expandIncludeLinesInPreprocessedOutput{true};
-  bool showColors{false};
-};
-
 class Parsing {
 public:
   explicit Parsing(AllCookedSources &);
diff --git a/flang/lib/Frontend/CMakeLists.txt b/flang/lib/Frontend/CMakeLists.txt
index 80d63fca6fb76..c80373799b015 100644
--- a/flang/lib/Frontend/CMakeLists.txt
+++ b/flang/lib/Frontend/CMakeLists.txt
@@ -5,6 +5,7 @@ add_flang_library(flangFrontend
   CompilerInstance.cpp
   CompilerInvocation.cpp
   CodeGenOptions.cpp
+  ParserActions.cpp
   FrontendAction.cpp
   FrontendActions.cpp
   FrontendOptions.cpp
diff --git a/flang/lib/Frontend/FrontendAction.cpp b/flang/lib/Frontend/FrontendAction.cpp
index 9a555bc7cd23b..5813f776efad4 100644
--- a/flang/lib/Frontend/FrontendAction.cpp
+++ b/flang/lib/Frontend/FrontendAction.cpp
@@ -17,6 +17,7 @@
 #include "flang/Frontend/FrontendPluginRegistry.h"
 #include "clang/Basic/DiagnosticFrontend.h"
 #include "llvm/Support/Errc.h"
+#include "flang/Parser/parsing.h"
 #include "llvm/Support/VirtualFileSystem.h"
 
 using namespace Fortran::frontend;
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index 76d329d043731..69d7cf6cc9e7a 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -14,9 +14,8 @@
 #include "flang/Frontend/CompilerInstance.h"
 #include "flang/Frontend/CompilerInvocation.h"
 #include "flang/Frontend/FrontendOptions.h"
-#include "flang/Frontend/PreprocessorOptions.h"
+#include "flang/Frontend/ParserActions.h"
 #include "flang/Lower/Bridge.h"
-#include "flang/Lower/PFTBuilder.h"
 #include "flang/Lower/Support/Verifier.h"
 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
@@ -25,13 +24,7 @@
 #include "flang/Optimizer/Support/InitFIR.h"
 #include "flang/Optimizer/Support/Utils.h"
 #include "flang/Optimizer/Transforms/Passes.h"
-#include "flang/Parser/dump-parse-tree.h"
-#include "flang/Parser/parsing.h"
-#include "flang/Parser/provenance.h"
-#include "flang/Parser/source.h"
-#include "flang/Parser/unparse.h"
 #include "flang/Semantics/runtime-type-info.h"
-#include "flang/Semantics/semantics.h"
 #include "flang/Semantics/unparse-with-symbols.h"
 #include "flang/Support/default-kinds.h"
 #include "flang/Tools/CrossToolHelpers.h"
@@ -318,7 +311,7 @@ bool CodeGenAction::beginSourceFileAction() {
       *mlirCtx, ci.getSemanticsContext(), defKinds,
       ci.getSemanticsContext().intrinsics(),
       ci.getSemanticsContext().targetCharacteristics(),
-      ci.getParsing().allCooked(), ci.getInvocation().getTargetOpts().triple,
+      Fortran::frontend::getAllCooked(ci), ci.getInvocation().getTargetOpts().triple,
       kindMap, ci.getInvocation().getLoweringOpts(),
       ci.getInvocation().getFrontendOpts().envDefaults,
       ci.getInvocation().getFrontendOpts().features, targetMachine,
@@ -333,8 +326,7 @@ bool CodeGenAction::beginSourceFileAction() {
   }
 
   // Create a parse tree and lower it to FIR
-  Fortran::parser::Program &parseTree{*ci.getParsing().parseTree()};
-  lb.lower(parseTree, ci.getSemanticsContext());
+  Fortran::frontend::parseAndLowerTree(ci, lb);
 
   // Fetch module from lb, so we can set
   mlirModule = lb.getModuleAndRelease();
@@ -431,19 +423,8 @@ void PrintPreprocessedAction::executeAction() {
   std::string buf;
   llvm::raw_string_ostream outForPP{buf};
 
-  // Format or dump the prescanner's output
   CompilerInstance &ci = this->getInstance();
-  if (ci.getInvocation().getPreprocessorOpts().showMacros) {
-    ci.getParsing().EmitPreprocessorMacros(outForPP);
-  } else if (ci.getInvocation().getPreprocessorOpts().noReformat) {
-    ci.getParsing().DumpCookedChars(outForPP);
-  } else {
-    ci.getParsing().EmitPreprocessedSource(
-        outForPP, !ci.getInvocation().getPreprocessorOpts().noLineDirectives);
-  }
-
-  // Print getDiagnostics from the prescanner
-  ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources());
+  Fortran::frontend::formatOrDumpPrescanner(buf, outForPP, ci);
 
   // If a pre-defined output stream exists, dump the preprocessed content there
   if (!ci.isOutputStreamNull()) {
@@ -463,60 +444,31 @@ void PrintPreprocessedAction::executeAction() {
 }
 
 void DebugDumpProvenanceAction::executeAction() {
-  this->getInstance().getParsing().DumpProvenance(llvm::outs());
+  Fortran::frontend::dumpProvenance(this->getInstance());
 }
 
 void ParseSyntaxOnlyAction::executeAction() {}
 
 void DebugUnparseNoSemaAction::executeAction() {
-  auto &invoc = this->getInstance().getInvocation();
-  auto &parseTree{getInstance().getParsing().parseTree()};
-
-  // TODO: Options should come from CompilerInvocation
-  Unparse(llvm::outs(), *parseTree,
-          /*encoding=*/Fortran::parser::Encoding::UTF_8,
-          /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
-          /*preStatement=*/nullptr,
-          invoc.getUseAnalyzedObjectsForUnparse() ? &invoc.getAsFortran()
-                                                  : nullptr);
+  Fortran::frontend::debugUnparseNoSema(this->getInstance(), llvm::outs());
 }
 
 void DebugUnparseAction::executeAction() {
-  auto &invoc = this->getInstance().getInvocation();
-  auto &parseTree{getInstance().getParsing().parseTree()};
-
   CompilerInstance &ci = this->getInstance();
   auto os{ci.createDefaultOutputFile(
       /*Binary=*/false, /*InFile=*/getCurrentFileOrBufferName())};
 
-  // TODO: Options should come from CompilerInvocation
-  Unparse(*os, *parseTree,
-          /*encoding=*/Fortran::parser::Encoding::UTF_8,
-          /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
-          /*preStatement=*/nullptr,
-          invoc.getUseAnalyzedObjectsForUnparse() ? &invoc.getAsFortran()
-                                                  : nullptr);
-
-  // Report fatal semantic errors
+  Fortran::frontend::debugUnparseNoSema(ci, *os);
   reportFatalSemanticErrors();
 }
 
 void DebugUnparseWithSymbolsAction::executeAction() {
-  auto &parseTree{*getInstance().getParsing().parseTree()};
-
-  Fortran::semantics::UnparseWithSymbols(
-      llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
-
-  // Report fatal semantic errors
+  Fortran::frontend::debugUnparseWithSymbols(this->getInstance());
   reportFatalSemanticErrors();
 }
 
 void DebugUnparseWithModulesAction::executeAction() {
-  auto &parseTree{*getInstance().getParsing().parseTree()};
-  CompilerInstance &ci{getInstance()};
-  Fortran::semantics::UnparseWithModules(
-      llvm::outs(), ci.getSemantics().context(), parseTree,
-      /*encoding=*/Fortran::parser::Encoding::UTF_8);
+  Fortran::frontend::debugUnparseWithModules(this->getInstance());
   reportFatalSemanticErrors();
 }
 
@@ -540,12 +492,7 @@ void DebugDumpAllAction::executeAction() {
   CompilerInstance &ci = this->getInstance();
 
   // Dump parse tree
-  auto &parseTree{getInstance().getParsing().parseTree()};
-  llvm::outs() << "========================";
-  llvm::outs() << " Flang: parse tree dump ";
-  llvm::outs() << "========================\n";
-  Fortran::parser::DumpTree(llvm::outs(), parseTree,
-                            &ci.getInvocation().getAsFortran());
+  Fortran::frontend::dumpTree(ci);
 
   if (!ci.getRtTyTables().schemata) {
     unsigned diagID = ci.getDiagnostics().getCustomDiagID(
@@ -564,21 +511,11 @@ void DebugDumpAllAction::executeAction() {
 }
 
 void DebugDumpParseTreeNoSemaAction::executeAction() {
-  auto &parseTree{getInstance().getParsing().parseTree()};
-
-  // Dump parse tree
-  Fortran::parser::DumpTree(
-      llvm::outs(), parseTree,
-      &this->getInstance().getInvocation().getAsFortran());
+  Fortran::frontend::dumpTree(this->getInstance());
 }
 
 void DebugDumpParseTreeAction::executeAction() {
-  auto &parseTree{getInstance().getParsing().parseTree()};
-
-  // Dump parse tree
-  Fortran::parser::DumpTree(
-      llvm::outs(), parseTree,
-      &this->getInstance().getInvocation().getAsFortran());
+  Fortran::frontend::dumpTree(this->getInstance());
 
   // Report fatal semantic errors
   reportFatalSemanticErrors();
@@ -586,62 +523,20 @@ void DebugDumpParseTreeAction::executeAction() {
 
 void DebugMeasureParseTreeAction::executeAction() {
   CompilerInstance &ci = this->getInstance();
-
-  // Parse. In case of failure, report and return.
-  ci.getParsing().Parse(llvm::outs());
-
-  if ((ci.getParsing().parseTree().has_value() &&
-       !ci.getParsing().consumedWholeFile()) ||
-      (!ci.getParsing().messages().empty() &&
-       (ci.getInvocation().getWarnAsErr() ||
-        ci.getParsing().messages().AnyFatalError()))) {
-    unsigned diagID = ci.getDiagnostics().getCustomDiagID(
-        clang::DiagnosticsEngine::Error, "Could not parse %0");
-    ci.getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
-
-    ci.getParsing().messages().Emit(llvm::errs(),
-                                    this->getInstance().getAllCookedSources());
-    return;
-  }
-
-  // Report the getDiagnostics from parsing
-  ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources());
-
-  auto &parseTree{*ci.getParsing().parseTree()};
-
-  // Measure the parse tree
-  MeasurementVisitor visitor;
-  Fortran::parser::Walk(parseTree, visitor);
-  llvm::outs() << "Parse tree comprises " << visitor.objects
-               << " objects and occupies " << visitor.bytes
-               << " total bytes.\n";
+  Fortran::frontend::debugMeasureParseTree(ci, getCurrentFileOrBufferName());
 }
 
 void DebugPreFIRTreeAction::executeAction() {
-  CompilerInstance &ci = this->getInstance();
   // Report and exit if fatal semantic errors are present
   if (reportFatalSemanticErrors()) {
     return;
   }
 
-  auto &parseTree{*ci.getParsing().parseTree()};
-
-  // Dump pre-FIR tree
-  if (auto ast{
-          Fortran::lower::createPFT(parseTree, ci.getSemanticsContext())}) {
-    Fortran::lower::dumpPFT(llvm::outs(), *ast);
-  } else {
-    unsigned diagID = ci.getDiagnostics().getCustomDiagID(
-        clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
-    ci.getDiagnostics().Report(diagID);
-  }
+  Fortran::frontend::dumpPreFIRTree(this->getInstance());
 }
 
 void DebugDumpParsingLogAction::executeAction() {
-  CompilerInstance &ci = this->getInstance();
-
-  ci.getParsing().Parse(llvm::errs());
-  ci.getParsing().DumpParsingLog(llvm::outs());
+  Fortran::frontend::debugDumpParsingLog(this->getInstance());
 }
 
 void GetDefinitionAction::executeAction() {
@@ -1473,17 +1368,7 @@ void InitOnlyAction::executeAction() {
 void PluginParseTreeAction::executeAction() {}
 
 void DebugDumpPFTAction::executeAction() {
-  CompilerInstance &ci = this->getInstance();
-
-  if (auto ast = Fortran::lower::createPFT(*ci.getParsing().parseTree(),
-                                           ci.getSemantics().context())) {
-    Fortran::lower::dumpPFT(llvm::outs(), *ast);
-    return;
-  }
-
-  unsigned diagID = ci.getDiagnostics().getCustomDiagID(
-      clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
-  ci.getDiagnostics().Report(diagID);
+  Fortran::frontend::dumpPreFIRTree(this->getInstance());
 }
 
 Fortran::parser::Parsing &PluginParseTreeAction::getParsing() {
diff --git a/flang/lib/Frontend/ParserActions.cpp b/flang/lib/Frontend/ParserActions.cpp
new file mode 100644
index 0000000000000..48cacaa5bed71
--- /dev/null
+++ b/flang/lib/Frontend/ParserActions.cpp
@@ -0,0 +1,152 @@
+//===--- ParserActions.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Frontend/ParserActions.h"
+#include "flang/Frontend/CompilerInstance.h"
+#include "flang/Parser/dump-parse-tree.h"
+#include "flang/Parser/parsing.h"
+#include "flang/Parser/unparse.h"
+#include "flang/Parser/source.h"
+#include "flang/Parser/provenance.h"
+#include "flang/Semantics/unparse-with-symbols.h"
+#include "flang/Lower/Bridge.h"
+#include "flang/Lower/PFTBuilder.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace Fortran::frontend {
+
+Fortran::parser::AllCookedSources&
+getAllCooked(Fortran::frontend::CompilerInstance &ci) {
+  return ci.getParsing().allCooked();
+}
+
+void parseAndLowerTree(Fortran::frontend::CompilerInstance &ci,
+                       Fortran::lower::LoweringBridge &lb) {
+  Fortran::parser::Program &parseTree{*ci.getParsing().parseTree()};
+  lb.lower(parseTree, ci.getSemanticsContext());
+}
+
+void dumpTree(Fortran::frontend::CompilerInstance &ci) {
+  auto &parseTree{ci.getParsing().parseTree()};
+  llvm::outs() << "========================";
+  llvm::outs() << " Flang: parse tree dump ";
+  llvm::outs() << "========================\n";
+  Fortran::parser::DumpTree(llvm::outs(), parseTree,
+                            &ci.getInvocation().getAsFortran());
+}
+
+void dumpProvenance(CompilerInstance &ci) {
+  ci.getParsing().DumpProvenance(llvm::outs());
+}
+
+void dumpPreFIRTree(CompilerInstance &ci) {
+  auto &parseTree{*ci.getParsing().parseTree()};
+
+  if (auto ast{
+          Fortran::lower::createPFT(parseTree, ci.getSemanticsContext())}) {
+    Fortran::lower::dumpPFT(llvm::outs(), *ast);
+  } else {
+    unsigned diagID = ci.getDiagnostics().getCustomDiagID(
+        clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
+    ci.getDiagnostics().Report(diagID);
+  }
+}
+
+void formatOrDumpPrescanner(std::string &buf,
+                            llvm::raw_string_ostream &outForPP,
+                            CompilerInstance &ci) {
+  if (ci.getInvocation().getPreprocessorOpts().showMacros) {
+    ci.getParsing().EmitPreprocessorMacros(outForPP);
+  } else if (ci.getInvocation().getPreprocessorOpts().noReformat) {
+    ci.getParsing().DumpCookedChars(outForPP);
+  } else {
+    ci.getParsing().EmitPreprocessedSource(
+        outForPP, !ci.getInvocation().getPreprocessorOpts().noLineDirectives);
+  }
+
+  // Print getDiagnostics from the prescanner
+  ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources());
+}
+
+struct MeasurementVisitor {
+  template <typename A>
+  bool Pre(const A &) {
+    return true;
+  }
+  template <typename A>
+  void Post(const A &) {
+    ++objects;
+    bytes += sizeof(A);
+  }
+  size_t objects{0}, bytes{0};
+};
+
+void debugMeasureParseTree(CompilerInstance &ci, llvm::StringRef filename) {
+  // Parse. In case of failure, report and return.
+  ci.getParsing().Parse(llvm::outs());
+
+  if ((ci.getParsing().parseTree().has_value() &&
+       !ci.getParsing().consumedWholeFile()) ||
+      (!ci.getParsing().messages().empty() &&
+       (ci.getInvocation().getWarnAsErr() ||
+        ci.getParsing().messages().AnyFatalError()))) {
+    unsigned diagID = ci.getDiagnostics().getCustomDiagID(
+        clang::DiagnosticsEngine::Error, "Could not parse %0");
+    ci.getDiagnostics().Report(diagID) << filename;
+
+    ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources());
+    return;
+  }
+
+  // Report the getDiagnostics from parsing
+  ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources());
+
+  auto &parseTree{ci.getParsing().parseTree()};
+  MeasurementVisitor visitor;
+  Fortran::parser::Walk(parseTree, visitor);
+  llvm::outs() << "Parse tree comprises " << visitor.objects
+               << " objects and occupies " << visitor.bytes
+               << " total bytes.\n";
+}
+
+void debugUnparseNoSema(CompilerInstance &ci, llvm::raw_ostream &out) {
+  auto &invoc = ci.getInvocation();
+  auto &parseTree{ci.getParsing().parseTree()};
+
+  // TODO: Options should come from CompilerInvocation
+  Unparse(out, *parseTree,
+          /*encoding=*/Fortran::parser::Encoding::UTF_8,
+          /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
+          /*preStatement=*/nullptr,
+          invoc.getUseAnalyzedObjectsForUnparse() ? &invoc.getAsFortran()
+                                                  : nullptr);
+}
+
+void debugUnparseWithSymbols(CompilerInstance &ci) {
+  auto &parseTree{*ci.getParsing().parseTree()};
+
+  Fortran::semantics::UnparseWithSymbols(
+      llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
+}
+
+void debugUnparseWithModules(CompilerInstance &ci) {
+  auto &parseTree{*ci.getParsing().parseTree()};
+  Fortran::semantics::UnparseWithModules(
+      llvm::outs(), ci.getSemantics().context(), parseTree,
+      /*encoding=*/Fortran::parser::Encoding::UTF_8);
+}
+
+void debugDumpParsingLog(CompilerInstance &ci) {
+  ci.getParsing().Parse(llvm::errs());
+  ci.getParsing().DumpParsingLog(llvm::outs());
+}
+} // namespace Fortran::frontend



More information about the flang-commits mailing list