[flang][driver] Add options for unparsing

This patch adds the following compiler frontend driver options:
  * -fdebug-unparse (f18 spelling: -funparse)
  * -fdebug-unparse-with-symbols (f18 spelling: -funparse-with-symbols)
The new driver will only accept the new spelling. `f18` will accept both
the original and the new spelling.

A new base class for frontend actions is added: `PrescanAndSemaAction`.
This is added to reduce code duplication that otherwise these new
options would lead to. Implementation from
  * `ParseSyntaxOnlyAction::ExecutionAction`
is moved to:
  * `PrescanAndSemaAction::BeginSourceFileAction`
This implementation is now shared between:
  * PrescanAndSemaAction
  * ParseSyntaxOnlyAction
  * DebugUnparseAction
  * DebugUnparseWithSymbolsAction

All tests that don't require other yet unimplemented options are
updated. This way `flang-new -fc1` is used instead of `f18` when
`FLANG_BUILD_NEW_DRIVER` is set to `On`. In order to facilitate this,
`%flang_fc1` is added in the LIT configuration (lit.cfg.py).

`asFortran` from f18.cpp is duplicated as `getBasicAsFortran` in
FrontendOptions.cpp. At this stage it's hard to find a good place to
share this method. I suggest that we revisit this once a switch from
`f18` to `flang-new` is complete.

Differential Revision: https://reviews.llvm.org/D96483
This commit is contained in:
Andrzej Warzynski 2021-02-04 11:14:57 +00:00
parent 00fe10c6a6
commit 96d229c9ab
37 changed files with 206 additions and 61 deletions

View File

@ -4244,6 +4244,18 @@ def fopenacc : Flag<["-"], "fopenacc">, Group<f_Group>,
}
//===----------------------------------------------------------------------===//
// FC1 Options
//===----------------------------------------------------------------------===//
let Flags = [FC1Option, FlangOnlyOption] in {
def fdebug_unparse : Flag<["-"], "fdebug-unparse">, Group<Action_Group>,
HelpText<"Unparse and stop.">;
def fdebug_unparse_with_symbols : Flag<["-"], "fdebug-unparse-with-symbols">, Group<Action_Group>,
HelpText<"Unparse and stop.">;
}
//===----------------------------------------------------------------------===//
// CC1 Options
//===----------------------------------------------------------------------===//

View File

@ -37,7 +37,23 @@ class PrintPreprocessedAction : public PrescanAction {
void ExecuteAction() override;
};
class ParseSyntaxOnlyAction : public PrescanAction {
//===----------------------------------------------------------------------===//
// PrescanAndSema Actions
//===----------------------------------------------------------------------===//
class PrescanAndSemaAction : public FrontendAction {
void ExecuteAction() override = 0;
bool BeginSourceFileAction(CompilerInstance &ci) override;
};
class DebugUnparseWithSymbolsAction : public PrescanAndSemaAction {
void ExecuteAction() override;
};
class DebugUnparseAction : public PrescanAndSemaAction {
void ExecuteAction() override;
};
class ParseSyntaxOnlyAction : public PrescanAndSemaAction {
void ExecuteAction() override;
};

View File

@ -9,6 +9,7 @@
#define LLVM_FLANG_FRONTEND_FRONTENDOPTIONS_H
#include "flang/Common/Fortran-features.h"
#include "flang/Parser/unparse.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
@ -32,6 +33,13 @@ enum ActionKind {
/// Emit a .o file.
EmitObj,
/// Parse, unparse the parse-tree and output a Fortran source file
DebugUnparse,
/// Parse, resolve the sybmols, unparse the parse-tree and then output a
/// Fortran source file
DebugUnparseWithSymbols,
/// TODO: RunPreprocessor, EmitLLVM, EmitLLVMOnly,
/// EmitCodeGenOnly, EmitAssembly, (...)
};
@ -40,6 +48,10 @@ enum ActionKind {
/// \return True if the file extension should be processed as fixed form
bool isFixedFormSuffix(llvm::StringRef suffix);
// TODO: Find a more suitable location for this. Added for compability with
// f18.cpp (this is equivalent to `asFortran` defined there).
Fortran::parser::AnalyzedObjectsAsFortran getBasicAsFortran();
/// \param suffix The file extension
/// \return True if the file extension should be processed as free form
bool isFreeFormSuffix(llvm::StringRef suffix);

View File

@ -14,6 +14,7 @@ add_flang_library(flangFrontend
LINK_LIBS
FortranParser
FortranSemantics
FortranEvaluate
FortranCommon
clangBasic
clangDriver

View File

@ -110,6 +110,12 @@ static InputKind ParseFrontendArgs(FrontendOptions &opts,
case clang::driver::options::OPT_emit_obj:
opts.programAction_ = EmitObj;
break;
case clang::driver::options::OPT_fdebug_unparse:
opts.programAction_ = DebugUnparse;
break;
case clang::driver::options::OPT_fdebug_unparse_with_symbols:
opts.programAction_ = DebugUnparseWithSymbols;
break;
// TODO:
// case calng::driver::options::OPT_emit_llvm:

View File

@ -9,10 +9,13 @@
#include "flang/Frontend/FrontendActions.h"
#include "flang/Common/default-kinds.h"
#include "flang/Frontend/CompilerInstance.h"
#include "flang/Frontend/FrontendOptions.h"
#include "flang/Parser/parsing.h"
#include "flang/Parser/provenance.h"
#include "flang/Parser/source.h"
#include "flang/Parser/unparse.h"
#include "flang/Semantics/semantics.h"
#include "flang/Semantics/unparse-with-symbols.h"
using namespace Fortran::frontend;
@ -49,6 +52,75 @@ bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) {
return true;
}
bool PrescanAndSemaAction::BeginSourceFileAction(CompilerInstance &c1) {
CompilerInstance &ci = this->instance();
std::string currentInputPath{GetCurrentFileOrBufferName()};
Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) {
// Switch between fixed and free form format based on the input file
// extension.
//
// Ideally we should have all Fortran options set before entering this
// method (i.e. before processing any specific input files). However, we
// can't decide between fixed and free form based on the file extension
// earlier than this.
parserOptions.isFixedForm = currentInput().IsFixedForm();
}
// Prescan. In case of failure, report and return.
ci.parsing().Prescan(currentInputPath, parserOptions);
if (ci.parsing().messages().AnyFatalError()) {
const unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not scan %0");
ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
return false;
}
// Parse. In case of failure, report and return.
ci.parsing().Parse(llvm::outs());
if (ci.parsing().messages().AnyFatalError()) {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not parse %0");
ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
ci.parsing().messages().Emit(
llvm::errs(), this->instance().allCookedSources());
return false;
}
// Report the diagnostics from parsing
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
auto &parseTree{*ci.parsing().parseTree()};
// Prepare semantics
Fortran::semantics::Semantics semantics{ci.invocation().semanticsContext(),
parseTree, ci.parsing().cooked().AsCharBlock()};
// Run semantic checks
semantics.Perform();
// Report the diagnostics from the semantic checks
semantics.EmitMessages(ci.semaOutputStream());
if (semantics.AnyFatalError()) {
unsigned DiagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Semantic errors in %0");
ci.diagnostics().Report(DiagID) << GetCurrentFileOrBufferName();
return false;
}
return true;
}
void InputOutputTestAction::ExecuteAction() {
CompilerInstance &ci = instance();
@ -111,42 +183,25 @@ void PrintPreprocessedAction::ExecuteAction() {
}
}
void ParseSyntaxOnlyAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
void ParseSyntaxOnlyAction::ExecuteAction() {}
// Parse. In case of failure, report and return.
ci.parsing().Parse(llvm::outs());
void DebugUnparseAction::ExecuteAction() {
auto &parseTree{instance().parsing().parseTree()};
Fortran::parser::AnalyzedObjectsAsFortran asFortran =
Fortran::frontend::getBasicAsFortran();
if (ci.parsing().messages().AnyFatalError()) {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not parse %0");
ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
// TODO: Options should come from CompilerInvocation
Unparse(llvm::outs(), *parseTree,
/*encoding=*/Fortran::parser::Encoding::UTF_8,
/*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
/*preStatement=*/nullptr, &asFortran);
}
ci.parsing().messages().Emit(
llvm::errs(), this->instance().allCookedSources());
return;
}
void DebugUnparseWithSymbolsAction::ExecuteAction() {
auto &parseTree{*instance().parsing().parseTree()};
// Report the diagnostics from parsing
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
auto &parseTree{*ci.parsing().parseTree()};
// Prepare semantics
Fortran::semantics::Semantics semantics{ci.invocation().semanticsContext(),
parseTree, ci.parsing().cooked().AsCharBlock()};
// Run semantic checks
semantics.Perform();
// Report the diagnostics from the semantic checks
semantics.EmitMessages(ci.semaOutputStream());
if (semantics.AnyFatalError()) {
unsigned DiagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Semantic errors in %0");
ci.diagnostics().Report(DiagID) << GetCurrentFileOrBufferName();
}
Fortran::semantics::UnparseWithSymbols(
llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
}
void EmitObjAction::ExecuteAction() {

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "flang/Frontend/FrontendOptions.h"
#include "flang/Evaluate/expression.h"
using namespace Fortran::frontend;
@ -25,6 +26,34 @@ bool Fortran::frontend::isFreeFormSuffix(llvm::StringRef suffix) {
suffix == "f08" || suffix == "F08" || suffix == "f18" || suffix == "F18";
}
// TODO: This is a copy of `asFortran` from f18.cpp and is added here for
// compatiblity. It doesn't really belong here, but I couldn't find a better
// place. We should decide whether to add it to the Evaluate or Parse/Unparse
// APIs or some dedicated utility library in the driver.
Fortran::parser::AnalyzedObjectsAsFortran
Fortran::frontend::getBasicAsFortran() {
return Fortran::parser::AnalyzedObjectsAsFortran{
[](llvm::raw_ostream &o, const Fortran::evaluate::GenericExprWrapper &x) {
if (x.v) {
x.v->AsFortran(o);
} else {
o << "(bad expression)";
}
},
[](llvm::raw_ostream &o,
const Fortran::evaluate::GenericAssignmentWrapper &x) {
if (x.v) {
x.v->AsFortran(o);
} else {
o << "(bad assignment)";
}
},
[](llvm::raw_ostream &o, const Fortran::evaluate::ProcedureRef &x) {
x.AsFortran(o << "CALL ");
},
};
}
InputKind FrontendOptions::GetInputKindForExtension(llvm::StringRef extension) {
if (isFixedFormSuffix(extension) || isFreeFormSuffix(extension)) {
return Language::Fortran;

View File

@ -37,6 +37,12 @@ static std::unique_ptr<FrontendAction> CreateFrontendBaseAction(
case EmitObj:
return std::make_unique<EmitObjAction>();
break;
case DebugUnparse:
return std::make_unique<DebugUnparseAction>();
break;
case DebugUnparseWithSymbols:
return std::make_unique<DebugUnparseWithSymbolsAction>();
break;
default:
break;
// TODO:

View File

@ -1,4 +1,3 @@
add_flang_library(FortranParser
Fortran-parsers.cpp
char-buffer.cpp

View File

@ -46,6 +46,9 @@
! HELP-FC1-NEXT: -D <macro>=<value> Define <macro> to <value> (or 1 if <value> omitted)
! HELP-FC1-NEXT: -emit-obj Emit native object files
! HELP-FC1-NEXT: -E Only run the preprocessor
! HELP-FC1-NEXT: -fdebug-unparse-with-symbols
! HELP-FC1-NEXT: Unparse and stop.
! HELP-FC1-NEXT: -fdebug-unparse Unparse and stop.
! HELP-FC1-NEXT: -ffixed-form Process source files in fixed form
! HELP-FC1-NEXT: -ffixed-line-length=<value>
! HELP-FC1-NEXT: Use <value> as character line width in fixed mode

View File

@ -1,4 +1,4 @@
! RUN: %f18 -funparse %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
! CHECK: CALL foo("N","N")
#ifdef transpose
call foo('T',

View File

@ -1,4 +1,4 @@
! RUN: %f18 -funparse %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
#define pmk
#ifdef pmk // comment

View File

@ -1,5 +1,5 @@
! RUN: %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: end do
SUBROUTINE sub00(a,b,n,m)

View File

@ -1,5 +1,5 @@
! RUN: %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: end do
SUBROUTINE sub00(a,b,n,m)

View File

@ -1,5 +1,5 @@
! RUN: %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: 10 continue
! CHECK: end do

View File

@ -1,4 +1,4 @@
! RUN: %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK-NOT: do [1-9]
! Figure out how to also execute this test.

View File

@ -1,4 +1,4 @@
! RUN: %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: %f18 -fopenmp -funparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK-NOT: do *[1-9]

View File

@ -1,4 +1,4 @@
! RUN: %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK-NOT: Control flow escapes from CRITICAL
subroutine test1(a, i)

View File

@ -1,4 +1,4 @@
! RUN: %f18 -funparse %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
! Check the analyzed form of a defined operator or assignment.

View File

@ -1,6 +1,6 @@
! when the loops are not DO CONCURRENT
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK-NOT: image control statement not allowed in DO CONCURRENT
! CHECK-NOT: RETURN not allowed in DO CONCURRENT
! CHECK-NOT: call to impure procedure in DO CONCURRENT not allowed

View File

@ -1,4 +1,4 @@
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: Control flow escapes from DO CONCURRENT
! CHECK: branch into loop body from outside
! CHECK: the loop branched into

View File

@ -1,5 +1,5 @@
! C1122 The index-name shall be a named scalar variable of type integer.
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: Must have INTEGER type, but is REAL(4)
subroutine do_concurrent_test1(n)

View File

@ -1,4 +1,4 @@
! RUN: %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK-NOT: exit from DO CONCURRENT construct
subroutine do_concurrent_test1(n)

View File

@ -1,5 +1,5 @@
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: Label '0' is out of range
! CHECK: Label '100000' is out of range
! CHECK: Label '123456' is out of range

View File

@ -1,5 +1,5 @@
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: DO loop doesn't properly nest
! CHECK: DO loop conflicts
! CHECK: Label '30' cannot be found

View File

@ -1,5 +1,5 @@
! RUN: %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: branch into loop body from outside
! CHECK: do 10 i = 1, m
! CHECK: the loop branched into

View File

@ -1,5 +1,5 @@
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: Label '50' was not found
! CHECK: Label '55' is not in scope
! CHECK: Label '70' is not a branch target

View File

@ -1,5 +1,5 @@
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: Label '10' is not in scope
! CHECK: Label '20' was not found
! CHECK: Label '30' is not a branch target

View File

@ -1,5 +1,5 @@
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: Label '30' is not a branch target
! CHECK: Control flow use of '30'
! CHECK: Label '10' is not in scope

View File

@ -1,5 +1,5 @@
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: CYCLE construct-name is not in scope
! CHECK: IF construct name unexpected
! CHECK: unnamed IF statement

View File

@ -1,4 +1,4 @@
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: Label '60' was not found
subroutine s(a)

View File

@ -1,4 +1,4 @@
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: '60' not a FORMAT
! CHECK: data transfer use of '60'

View File

@ -1,4 +1,4 @@
! RUN: not %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: not %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: expected end of statement
subroutine s

View File

@ -1,4 +1,4 @@
! RUN: %f18 -funparse-with-symbols %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: branch into loop body from outside
! CHECK: the loop branched into

View File

@ -1,4 +1,4 @@
! RUN: %f18 -funparse %s 2>&1 | FileCheck %s
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
!CHECK-NOT: error:
module mm

View File

@ -74,10 +74,15 @@ tools = [
if config.include_flang_new_driver_test:
tools.append(ToolSubst('%flang-new', command=FindTool('flang-new'), unresolved='fatal'))
tools.append(ToolSubst('%flang', command=FindTool('flang-new'), unresolved='fatal'))
tools.append(ToolSubst('%flang_fc1', command=FindTool('flang-new'),
extra_args=['-fc1'], unresolved='fatal'))
else:
tools.append(ToolSubst('%flang', command=FindTool('f18'),
extra_args=["-intrinsic-module-directory "+config.flang_intrinsic_modules_dir],
unresolved='fatal'))
tools.append(ToolSubst('%flang_fc1', command=FindTool('f18'),
extra_args=["-intrinsic-module-directory "+config.flang_intrinsic_modules_dir],
unresolved='fatal'))
if config.flang_standalone_build:
llvm_config.add_tool_substitutions(tools, [config.flang_llvm_tools_dir])

View File

@ -540,9 +540,10 @@ int main(int argc, char *const argv[]) {
options.instrumentedParse = true;
} else if (arg == "-fdebug-no-semantics") {
driver.debugNoSemantics = true;
} else if (arg == "-funparse") {
} else if (arg == "-funparse" || arg == "-fdebug-unparse") {
driver.dumpUnparse = true;
} else if (arg == "-funparse-with-symbols") {
} else if (arg == "-funparse-with-symbols" ||
arg == "-fdebug-unparse-with-symbols") {
driver.dumpUnparseWithSymbols = true;
} else if (arg == "-funparse-typed-exprs-to-f18-fc") {
driver.unparseTypedExprsToF18_FC = true;