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