CarolineConcatto created this revision.
Herald added subscribers: cfe-commits, dang, mgorny.
Herald added a reviewer: DavidTruby.
Herald added a reviewer: sscalpone.
Herald added a project: clang.
CarolineConcatto requested review of this revision.

This patch runs Preprocess and PrintProcess actions for Fortran
files.

It makes use of Fortran::parser to run preprocessing for Fortran file and
print them. The two functions are:
`Fortran::parser::Prescan`  and
`Fortran::parser::DumpCookedChars`

This patch assumes that all files need to run preprocessing before running
other action. The PrintPreprocessedInput only prints the files. This is
compatible with the through away driver.

It does not map the driver options(clang::driver::options) to
Fortran::parser::options to be used by Fortran::parser::Prescan.
However, it sets Frotran::parser::options to:
`searchDirectories ` to the current directory
`encoding` to UTF_8
`isFixedForm`
These default settings are compatible with the trough away driver.
Further work needs to be done in CompilerInvocation to read and map
clang::driver::options to Fortran::parser::options.

Depends on D87989 <https://reviews.llvm.org/D87989>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D88381

Files:
  clang/include/clang/Driver/Options.td
  flang/include/flang/Frontend/CompilerInstance.h
  flang/include/flang/Frontend/CompilerInvocation.h
  flang/include/flang/Frontend/FrontendActions.h
  flang/include/flang/Frontend/FrontendOptions.h
  flang/lib/Frontend/CMakeLists.txt
  flang/lib/Frontend/CompilerInstance.cpp
  flang/lib/Frontend/CompilerInvocation.cpp
  flang/lib/Frontend/FrontendAction.cpp
  flang/lib/Frontend/FrontendActions.cpp
  flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
  flang/test/Flang-Driver/driver-help-hidden.f90
  flang/test/Flang-Driver/driver-help.f90
  flang/test/Frontend/Inputs/hello-world.c
  flang/test/Frontend/print-preprocess-C-file.f90
  flang/test/Frontend/print-preprocessed-file.f90
  flang/unittests/Frontend/CMakeLists.txt
  flang/unittests/Frontend/CompilerInstanceTest.cpp
  flang/unittests/Frontend/PrintPreprocessedTest.cpp

Index: flang/unittests/Frontend/PrintPreprocessedTest.cpp
===================================================================
--- /dev/null
+++ flang/unittests/Frontend/PrintPreprocessedTest.cpp
@@ -0,0 +1,69 @@
+//===- unittests/Frontend/PrintPreprocessedTest.cpp --- FrontendAction tests
+//--===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+#include "flang/Frontend/CompilerInstance.h"
+#include "flang/Frontend/FrontendOptions.h"
+#include "flang/FrontendTool/Utils.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <filesystem>
+
+using namespace Fortran::frontend;
+
+namespace {
+
+TEST(PrintPreprocessedTests, TestPreprocessedOutputStreamOwned) {
+
+  // 1. Prepare the input source code
+  // Flang function 'SourceFile *Prescan' needs a physical file
+  // and the full path to work with
+  std::string inputFilename = "preprocessing-file-test.f";
+  std::error_code ec;
+  std::unique_ptr<llvm::raw_fd_ostream> os{
+      new llvm::raw_fd_ostream(inputFilename, ec, llvm::sys::fs::OF_None)};
+  if (ec)
+    llvm::errs() << "Fail to create the file need by the test";
+  *(os) << "! preprocessing-file-test.F:\n #ifdef NEW \n  Program A \n#else "
+           "\nProgram B \n #endif";
+  os.reset();
+  std::string getFileFullPath = std::filesystem::current_path().c_str();
+  getFileFullPath = getFileFullPath + "/" + inputFilename;
+
+  // 2. Set-up the output stream. Initialize buffer to be used to receive output
+  // file content
+  llvm::SmallVector<char, 256> outputFileBuffer;
+  std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
+      new llvm::raw_svector_ostream(outputFileBuffer));
+
+  // 3. Prepare the compiler invocation with the preprocessing action
+  auto invocation = std::make_shared<CompilerInvocation>();
+  invocation->GetFrontendOpts().programAction_ = PrintPreprocessedInput;
+
+  // 4. Configure CompilerInstance and set the input file
+  CompilerInstance compInst;
+  compInst.CreateDiagnostics();
+  compInst.SetOutputStream(std::move(outputFileStream));
+  compInst.SetInvocation(std::move(invocation));
+  compInst.GetFrontendOpts().inputs_.push_back(
+      FrontendInputFile(getFileFullPath, Language::Fortran90));
+
+  // 5. Run the earlier defined FrontendAction
+  bool success = ExecuteCompilerInvocation(&compInst);
+
+  EXPECT_TRUE(success);
+  EXPECT_TRUE(!outputFileBuffer.empty());
+  EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data()).startswith("program b"));
+
+  // 6. Delete output files in flight and the test file
+  llvm::sys::fs::remove(inputFilename);
+  compInst.ClearOutputFiles(/*EraseFiles=*/true);
+}
+} // namespace
Index: flang/unittests/Frontend/CompilerInstanceTest.cpp
===================================================================
--- flang/unittests/Frontend/CompilerInstanceTest.cpp
+++ flang/unittests/Frontend/CompilerInstanceTest.cpp
@@ -13,6 +13,8 @@
 #include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 
+#include <filesystem>
+
 using namespace llvm;
 using namespace Fortran::frontend;
 #include <filesystem>
Index: flang/unittests/Frontend/CMakeLists.txt
===================================================================
--- flang/unittests/Frontend/CMakeLists.txt
+++ flang/unittests/Frontend/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_flang_unittest(FlangFrontendTests
   CompilerInstanceTest.cpp
   InputOutputTest.cpp
+  PrintPreprocessedTest.cpp
 )
 
 target_link_libraries(FlangFrontendTests
Index: flang/test/Frontend/print-preprocessed-file.f90
===================================================================
--- /dev/null
+++ flang/test/Frontend/print-preprocessed-file.f90
@@ -0,0 +1,37 @@
+! Test printpreprocessed action
+
+!--------------------------
+! FLANG DRIVER (flang-new)
+!--------------------------
+! RUN: %flang-new -E %s  2>&1 | FileCheck %s
+! RUN: %flang-new -E  -o - %s 2>&1 | FileCheck %s
+! RUN: %flang-new -E -o %t  %s 2>&1 && FileCheck %s --input-file=%t
+
+!-----------------------------------------
+!   FRONTEND FLANG DRIVER (flang-new -fc1)
+!-----------------------------------------
+! RUN: %flang-new -fc1 -E %s  2>&1 | FileCheck %s
+! RUN: %flang-new -fc1 -E  -o - %s 2>&1 | FileCheck %s
+! RUN: %flang-new -fc1 -E -o %t  %s 2>&1 && FileCheck %s --input-file=%t
+
+
+!-----------------------
+! EXPECTED OUTPUT
+!-----------------------
+! flang-new -E  %s
+! CHECK:program a
+! CHECK-NOT:program b
+! CHECK-NEXT:x = 1
+! CHECK-NEXT:write(*,*) x
+! CHECK-NEXT:end
+
+! Preprocessed-file.F:
+#define NEW
+#ifdef NEW
+  program A
+#else
+  program B
+#endif
+    x = 1
+    write(*,*) x
+  end
\ No newline at end of file
Index: flang/test/Frontend/print-preprocess-C-file.f90
===================================================================
--- /dev/null
+++ flang/test/Frontend/print-preprocess-C-file.f90
@@ -0,0 +1,16 @@
+! Test preprocessing for C files using Flang driver
+
+! REQUIRES: new-flang-driver
+
+!--------------------------
+! FLANG DRIVER (flang-new)
+!--------------------------
+! TEST 1: Print to stdout (implicit)
+! RUN: not %flang-new -E %S/Inputs/hello-world.c  2>&1 | FileCheck %s
+! RUN: not %flang-new -E %S/Inputs/hello-world.c -o - 2>&1 | FileCheck %s
+! RUN: not %flang-new -E %S/Inputs/hello-world.c -o test.F90 2>&1 | FileCheck %s 
+
+!-----------------------
+! EXPECTED OUTPUT
+!-----------------------
+! CHECK: error: unknown integrated tool '-cc1'. Valid tools include '-fc1'.
\ No newline at end of file
Index: flang/test/Frontend/Inputs/hello-world.c
===================================================================
--- /dev/null
+++ flang/test/Frontend/Inputs/hello-world.c
@@ -0,0 +1,5 @@
+a #include<stdio.h> int main() {
+  // printf() displays the string inside quotation
+  printf("Hello, World!");
+  return 0;
+}
\ No newline at end of file
Index: flang/test/Flang-Driver/driver-help.f90
===================================================================
--- flang/test/Flang-Driver/driver-help.f90
+++ flang/test/Flang-Driver/driver-help.f90
@@ -19,6 +19,7 @@
 ! CHECK-FLANG: USAGE: flang-new
 ! CHECK-FLANG-EMPTY:
 ! CHECK-FLANG-NEXT:OPTIONS:
+! CHECK-FLANG-NEXT: -E        Only run the preprocessor
 ! CHECK-FLANG-NEXT: -help     Display available options
 ! CHECK-FLANG-NEXT: --version Print version information
 
@@ -30,6 +31,7 @@
 ! CHECK-FLANG-FC1: USAGE: flang-new
 ! CHECK-FLANG-FC1-EMPTY:
 ! CHECK-FLANG-FC1-NEXT:OPTIONS:
+! CHECK-FLANG-FC1-NEXT: -E        Only run the preprocessor
 ! CHECK-FLANG-FC1-NEXT: -help     Display available options
 ! CHECK-FLANG-FC1-NEXT: -o <file> Write output to <file>
 ! CHECK-FLANG-FC1-NEXT: --version Print version information
Index: flang/test/Flang-Driver/driver-help-hidden.f90
===================================================================
--- flang/test/Flang-Driver/driver-help-hidden.f90
+++ flang/test/Flang-Driver/driver-help-hidden.f90
@@ -19,6 +19,7 @@
 ! CHECK:USAGE: flang-new
 ! CHECK-EMPTY:
 ! CHECK-NEXT:OPTIONS:
+! CHECK-NEXT: -E        Only run the preprocessor
 ! CHECK-NEXT: -help     Display available options
 
 ! CHECK-NEXT: -test-io  Use for Flang development and testing only. Only read and write files.
Index: flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
===================================================================
--- flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -29,6 +29,9 @@
   case InputOutputTest:
     return std::make_unique<InputOutputTestAction>();
     break;
+  case PrintPreprocessedInput:
+    return std::make_unique<PrintPreprocessedAction>();
+    break;
   default:
     break;
     // TODO:
Index: flang/lib/Frontend/FrontendActions.cpp
===================================================================
--- flang/lib/Frontend/FrontendActions.cpp
+++ flang/lib/Frontend/FrontendActions.cpp
@@ -5,12 +5,12 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+
 #include "flang/Frontend/FrontendActions.h"
-#include "flang/Common/Fortran-features.h"
-#include "flang/Common/default-kinds.h"
 #include "flang/Frontend/CompilerInstance.h"
+#include "flang/Parser/parsing.h"
+#include "flang/Parser/provenance.h"
 #include "flang/Parser/source.h"
-#include "clang/Serialization/PCHContainerOperations.h"
 
 using namespace Fortran::frontend;
 
@@ -43,3 +43,29 @@
     ci.WriteOutputStream(fileContent.data());
   }
 }
+
+void PrintPreprocessedAction::ExecuteAction() {
+
+  std::string buf;
+  llvm::raw_string_ostream out{buf};
+
+  // Fortran::parser writes on out the parseState content
+  CompilerInstance &ci = GetCompilerInstance();
+  ci.GetParsing().DumpCookedChars(out);
+
+  // Writes the preprocessed content into the output
+  if (!ci.IsOutputStreamNull()) {
+    // Send the output to the pre-defined output buffer.
+    ci.WriteOutputStream(out.str());
+  } else {
+    std::unique_ptr<llvm::raw_pwrite_stream> os;
+    os = ci.CreateDefaultOutputFile(
+        /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName());
+    if (!os) {
+      llvm::errs() << "Unable to create the output file\n";
+      return;
+    }
+
+    (*os) << out.str();
+  }
+}
Index: flang/lib/Frontend/FrontendAction.cpp
===================================================================
--- flang/lib/Frontend/FrontendAction.cpp
+++ flang/lib/Frontend/FrontendAction.cpp
@@ -46,6 +46,19 @@
 }
 
 llvm::Error FrontendAction::Execute() {
+
+  // Get file's name from FrontendInputFile current
+  std::string path{GetCurrentFileOrBufferName()};
+
+  // Option is used inside Parser::Prescan
+  // Frontend driver use Fortran::parser::Option to have the 2 drivers working
+  Fortran::parser::Options options =
+      GetCompilerInstance().GetInvocation().GetFortranOpts();
+
+  // Read files, scan and run preprocessor
+  // Needed by all next fases of the frontend
+  GetCompilerInstance().GetParsing().Prescan(path, options);
+
   ExecuteAction();
   return llvm::Error::success();
 }
@@ -59,4 +72,4 @@
 
   SetCompilerInstance(nullptr);
   SetCurrentInput(FrontendInputFile());
-}
\ No newline at end of file
+}
Index: flang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- flang/lib/Frontend/CompilerInvocation.cpp
+++ flang/lib/Frontend/CompilerInvocation.cpp
@@ -47,8 +47,12 @@
     case clang::driver::options::OPT_TEST_IO:
       opts.programAction_ = InputOutputTest;
       break;
+    case clang::driver::options::OPT_E:
+      opts.programAction_ = PrintPreprocessedInput;
+      ;
+      break;
+
       // TODO:
-      // case clang::driver::options::OPT_E:
       // case clang::driver::options::OPT_emit_obj:
       // case calng::driver::options::OPT_emit_llvm:
       // case clang::driver::options::OPT_emit_llvm_only:
@@ -103,6 +107,7 @@
 
     opts.inputs_.emplace_back(std::move(inputs[i]), ik);
   }
+
   return dashX;
 }
 
Index: flang/lib/Frontend/CompilerInstance.cpp
===================================================================
--- flang/lib/Frontend/CompilerInstance.cpp
+++ flang/lib/Frontend/CompilerInstance.cpp
@@ -8,6 +8,7 @@
 
 #include "flang/Frontend/CompilerInstance.h"
 #include "flang/Frontend/CompilerInvocation.h"
+#include "flang/Parser/parsing.h"
 #include "flang/Parser/provenance.h"
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "llvm/Support/Errc.h"
@@ -20,7 +21,9 @@
 
 CompilerInstance::CompilerInstance()
     : invocation_(new CompilerInvocation()),
-      allSources_(new Fortran::parser::AllSources()) {}
+      allSources_(new Fortran::parser::AllSources()),
+      allCookedSources_(new Fortran::parser::AllCookedSources(*allSources_)),
+      parsing_(new Fortran::parser::Parsing(*allCookedSources_)) {}
 
 CompilerInstance::~CompilerInstance() {
   assert(outputFiles_.empty() && "Still output files in flight?");
@@ -113,6 +116,15 @@
 
 bool CompilerInstance::ExecuteAction(FrontendAction &act) {
 
+  // TODO:Remove once ExecuteCompilerInvocation maps driver::option to
+  // Fortran::parser::option
+  // Set defaults for Parser, as it use as flang options
+  // Default consistent with the temporary driver in f18/f18.cpp
+  std::vector<std::string> searchDirectories{"."s};
+  GetInvocation().GetFortranOpts().searchDirectories = searchDirectories;
+  GetInvocation().GetFortranOpts().isFixedForm = false;
+  GetAllSources().set_encoding(Fortran::parser::Encoding::UTF_8);
+
   // Connect Input to a CompileInstance
   for (const FrontendInputFile &fif : GetFrontendOpts().inputs_) {
     if (act.BeginSourceFile(*this, fif)) {
Index: flang/lib/Frontend/CMakeLists.txt
===================================================================
--- flang/lib/Frontend/CMakeLists.txt
+++ flang/lib/Frontend/CMakeLists.txt
@@ -9,7 +9,9 @@
   clangBasic
 
   LINK_LIBS
+  FortranCommon
   FortranParser
+  FortranSemantics
   clangBasic
   clangDriver
   # TODO: Added to re-use clang's TextDiagnosticBuffer & TextDiagnosticPrinter.
Index: flang/include/flang/Frontend/FrontendOptions.h
===================================================================
--- flang/include/flang/Frontend/FrontendOptions.h
+++ flang/include/flang/Frontend/FrontendOptions.h
@@ -23,6 +23,8 @@
   /// -test-io mode
   InputOutputTest,
 
+  /// -E mode.
+  PrintPreprocessedInput,
   /// TODO: ADD flags as the Actions are implemented
   // RunPreprocessor, ParserSyntaxOnly, EmitLLVM, EmitLLVMOnly,
   // EmitCodeGenOnly, EmitAssembly, (...)
@@ -32,6 +34,8 @@
   switch (ak) {
   case InputOutputTest:
     return "InputOutputTest";
+  case PrintPreprocessedInput:
+    return "PrintPreprocessedInput";
   default:
     return "<unknown ActionKind>";
     // TODO:
Index: flang/include/flang/Frontend/FrontendActions.h
===================================================================
--- flang/include/flang/Frontend/FrontendActions.h
+++ flang/include/flang/Frontend/FrontendActions.h
@@ -17,6 +17,10 @@
   void ExecuteAction() override;
 };
 
+class PrintPreprocessedAction : public FrontendAction {
+  void ExecuteAction() override;
+};
+
 } // namespace Fortran::frontend
 
 #endif // LLVM_FLANG_FRONTEND_FRONTENDACTIONS_H
\ No newline at end of file
Index: flang/include/flang/Frontend/CompilerInvocation.h
===================================================================
--- flang/include/flang/Frontend/CompilerInvocation.h
+++ flang/include/flang/Frontend/CompilerInvocation.h
@@ -9,6 +9,7 @@
 #define LLVM_FLANG_FRONTEND_COMPILERINVOCATION_H
 
 #include "flang/Frontend/FrontendOptions.h"
+#include "flang/Parser/parsing.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticOptions.h"
 
@@ -34,12 +35,20 @@
   /// Options controlling the frontend itself.
   FrontendOptions frontendOpts_;
 
+  // Maps clang::driver options to frontend options use by
+  // Fortran::parser::Prescan
+  // TODO: map the option needed by the frontend
+  Fortran::parser::Options options_;
+
 public:
   CompilerInvocation() = default;
 
   FrontendOptions &GetFrontendOpts() { return frontendOpts_; }
   const FrontendOptions &GetFrontendOpts() const { return frontendOpts_; }
 
+  Fortran::parser::Options &GetFortranOpts() { return options_; }
+  const Fortran::parser::Options &GetFortranOpts() const { return options_; }
+
   /// Create a compiler invocation from a list of input options.
   /// \returns true on success.
   /// \returns false if an error was encountered while parsing the arguments
Index: flang/include/flang/Frontend/CompilerInstance.h
===================================================================
--- flang/include/flang/Frontend/CompilerInstance.h
+++ flang/include/flang/Frontend/CompilerInstance.h
@@ -10,12 +10,10 @@
 
 #include "flang/Frontend/CompilerInvocation.h"
 #include "flang/Frontend/FrontendAction.h"
+#include "flang/Parser/parsing.h"
 #include "flang/Parser/provenance.h"
 #include "llvm/Support/raw_ostream.h"
 
-#include <cassert>
-#include <memory>
-
 namespace Fortran::frontend {
 
 class CompilerInstance {
@@ -26,6 +24,10 @@
   /// Flang file  manager.
   std::shared_ptr<Fortran::parser::AllSources> allSources_;
 
+  std::shared_ptr<Fortran::parser::AllCookedSources> allCookedSources_;
+
+  std::shared_ptr<Fortran::parser::Parsing> parsing_;
+
   /// The diagnostics engine instance.
   llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_;
 
@@ -76,6 +78,13 @@
 
   bool HasAllSources() const { return allSources_ != nullptr; }
 
+  /// }
+  /// @name Parser Operations
+  /// {
+
+  /// Return parsing to be used by Actions.
+  Fortran::parser::Parsing &GetParsing() const { return *parsing_; }
+
   /// }
   /// @name High-Level Operations
   /// {
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -387,7 +387,7 @@
 def D : JoinedOrSeparate<["-"], "D">, Group<Preprocessor_Group>,
     Flags<[CC1Option]>, MetaVarName<"<macro>=<value>">,
     HelpText<"Define <macro> to <value> (or 1 if <value> omitted)">;
-def E : Flag<["-"], "E">, Flags<[DriverOption,CC1Option]>, Group<Action_Group>,
+def E : Flag<["-"], "E">, Flags<[DriverOption,CC1Option, FlangOption, FC1Option]>, Group<Action_Group>,
     HelpText<"Only run the preprocessor">;
 def F : JoinedOrSeparate<["-"], "F">, Flags<[RenderJoined,CC1Option]>,
     HelpText<"Add directory to framework include search path">;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to