aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrzej Warzynski <andrzej.warzynski@arm.com>2022-02-03 17:00:27 +0000
committerAndrzej Warzynski <andrzej.warzynski@arm.com>2022-02-09 08:35:48 +0000
commit69c3309d4545cd729c41b102303686e19c92f55c (patch)
tree208bf3b689f4e1a29e1e4c3c0359645224a8e25d
parent68c1eeb4bad18753dbaa053a6c919c8f1a23fb9c (diff)
downloadllvm-69c3309d4545cd729c41b102303686e19c92f55c.zip
llvm-69c3309d4545cd729c41b102303686e19c92f55c.tar.gz
llvm-69c3309d4545cd729c41b102303686e19c92f55c.tar.bz2
[flang][driver] Add support for `-emit-mlir`
This patch adds support for generating MLIR files in Flang's frontend driver (i.e. `flang-new -fc1`). `-emit-fir` is added as an alias for `-emit-mlir`. We may want to decide to split the two in the future. A new parent class for code-gen frontend actions is introduced: `CodeGenAction`. We will be using this class to encapsulate logic shared between all code-generation actions, but not required otherwise. For now, it will: * run prescanning, parsing and semantic checks, * lower the input to MLIR. `EmitObjAction` is updated to inherit from this class. This means that the behaviour of `flang-new -fc1 -emit-obj` is also updated (previously, it would just exit immediately). This change required `flang/test/Driver/syntax-only.f90` to be updated. For `-emit-fir`, a specialisation of `CodeGenAction` is introduced: `EmitMLIRAction`. The key logic for this class is implemented in `EmitMLIRAction::ExecuteAction`. Differential Revision: https://reviews.llvm.org/D118985
-rw-r--r--clang/include/clang/Driver/Options.td4
-rw-r--r--flang/include/flang/Frontend/CompilerInstance.h7
-rw-r--r--flang/include/flang/Frontend/FrontendActions.h35
-rw-r--r--flang/include/flang/Frontend/FrontendOptions.h3
-rw-r--r--flang/lib/Frontend/CMakeLists.txt15
-rw-r--r--flang/lib/Frontend/CompilerInvocation.cpp3
-rw-r--r--flang/lib/Frontend/FrontendActions.cpp74
-rw-r--r--flang/lib/FrontendTool/CMakeLists.txt3
-rw-r--r--flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp2
-rw-r--r--flang/test/Driver/driver-help.f901
-rw-r--r--flang/test/Driver/emit-mlir.f9027
-rw-r--r--flang/test/Driver/syntax-only.f9024
12 files changed, 184 insertions, 14 deletions
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 1900d0f..a64a676 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4759,6 +4759,10 @@ def fno_reformat : Flag<["-"], "fno-reformat">, Group<Preprocessor_Group>,
HelpText<"Dump the cooked character stream in -E mode">;
defm analyzed_objects_for_unparse : OptOutFC1FFlag<"analyzed-objects-for-unparse", "", "Do not use the analyzed objects when unparsing">;
+def emit_mlir : Flag<["-"], "emit-mlir">, Group<Action_Group>,
+ HelpText<"Build the parse tree, then lower it to MLIR">;
+def emit_fir : Flag<["-"], "emit-fir">, Alias<emit_mlir>;
+
}
//===----------------------------------------------------------------------===//
diff --git a/flang/include/flang/Frontend/CompilerInstance.h b/flang/include/flang/Frontend/CompilerInstance.h
index ce1e46c..29a1bc7 100644
--- a/flang/include/flang/Frontend/CompilerInstance.h
+++ b/flang/include/flang/Frontend/CompilerInstance.h
@@ -250,6 +250,13 @@ public:
void WriteOutputStream(const std::string &message) {
*outputStream_ << message;
}
+
+ /// Get the user specified output stream.
+ llvm::raw_pwrite_stream &GetOutputStream() {
+ assert(outputStream_ &&
+ "Compiler instance has no user-specified output stream!");
+ return *outputStream_;
+ }
};
} // end namespace Fortran::frontend
diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h
index 1e17f19..e3def74 100644
--- a/flang/include/flang/Frontend/FrontendActions.h
+++ b/flang/include/flang/Frontend/FrontendActions.h
@@ -11,6 +11,8 @@
#include "flang/Frontend/FrontendAction.h"
#include "flang/Semantics/semantics.h"
+
+#include "mlir/IR/BuiltinOps.h"
#include <memory>
namespace Fortran::frontend {
@@ -34,10 +36,6 @@ class InputOutputTestAction : public FrontendAction {
void ExecuteAction() override;
};
-class EmitObjAction : public FrontendAction {
- void ExecuteAction() override;
-};
-
class InitOnlyAction : public FrontendAction {
void ExecuteAction() override;
};
@@ -146,6 +144,35 @@ class DebugDumpAllAction : public PrescanAndSemaDebugAction {
void ExecuteAction() override;
};
+//===----------------------------------------------------------------------===//
+// CodeGen Actions
+//===----------------------------------------------------------------------===//
+/// Abstract base class for actions that generate code (MLIR, LLVM IR, assembly
+/// and machine code). Every action that inherits from this class will at
+/// least run the prescanning, parsing, semantic checks and lower the parse
+/// tree to an MLIR module.
+class CodeGenAction : public FrontendAction {
+
+ void ExecuteAction() override = 0;
+ /// Runs prescan, parsing, sema and lowers to MLIR.
+ bool BeginSourceFileAction() override;
+
+protected:
+ /// @name MLIR
+ /// {
+ std::unique_ptr<mlir::ModuleOp> mlirModule;
+ std::unique_ptr<mlir::MLIRContext> mlirCtx;
+ /// }
+};
+
+class EmitMLIRAction : public CodeGenAction {
+ void ExecuteAction() override;
+};
+
+class EmitObjAction : public CodeGenAction {
+ void ExecuteAction() override;
+};
+
} // namespace Fortran::frontend
#endif // LLVM_FLANG_FRONTEND_FRONTENDACTIONS_H
diff --git a/flang/include/flang/Frontend/FrontendOptions.h b/flang/include/flang/Frontend/FrontendOptions.h
index 26a5728..0ff8d0a 100644
--- a/flang/include/flang/Frontend/FrontendOptions.h
+++ b/flang/include/flang/Frontend/FrontendOptions.h
@@ -31,6 +31,9 @@ enum ActionKind {
/// -fsyntax-only
ParseSyntaxOnly,
+ /// Emit a .mlir file
+ EmitMLIR,
+
/// Emit a .o file.
EmitObj,
diff --git a/flang/lib/Frontend/CMakeLists.txt b/flang/lib/Frontend/CMakeLists.txt
index abaa77f..95606d2 100644
--- a/flang/lib/Frontend/CMakeLists.txt
+++ b/flang/lib/Frontend/CMakeLists.txt
@@ -1,3 +1,5 @@
+get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
+
add_flang_library(flangFrontend
CompilerInstance.cpp
CompilerInvocation.cpp
@@ -10,6 +12,10 @@ add_flang_library(flangFrontend
DEPENDS
clangBasic
+ FIRBuilder
+ FIRDialect
+ FIRSupport
+ ${dialect_libs}
LINK_LIBS
FortranParser
@@ -19,6 +25,15 @@ add_flang_library(flangFrontend
FortranLower
clangBasic
clangDriver
+ FIRDialect
+ FIRSupport
+ FIRBuilder
+ FIRCodeGen
+ FIRTransforms
+ MLIRTransforms
+ MLIRLLVMToLLVMIRTranslation
+ MLIRSCFToControlFlow
+ ${dialect_libs}
LINK_COMPONENTS
Option
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index dcfa741..af59cb6 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -134,6 +134,9 @@ static bool ParseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
case clang::driver::options::OPT_fsyntax_only:
opts.programAction = ParseSyntaxOnly;
break;
+ case clang::driver::options::OPT_emit_mlir:
+ opts.programAction = EmitMLIR;
+ break;
case clang::driver::options::OPT_emit_obj:
opts.programAction = EmitObj;
break;
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index cdc2031..d981faa 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -11,7 +11,12 @@
#include "flang/Frontend/CompilerInstance.h"
#include "flang/Frontend/FrontendOptions.h"
#include "flang/Frontend/PreprocessorOptions.h"
+#include "flang/Lower/Bridge.h"
#include "flang/Lower/PFTBuilder.h"
+#include "flang/Lower/Support/Verifier.h"
+#include "flang/Optimizer/Support/InitFIR.h"
+#include "flang/Optimizer/Support/KindMapping.h"
+#include "flang/Optimizer/Support/Utils.h"
#include "flang/Parser/dump-parse-tree.h"
#include "flang/Parser/parsing.h"
#include "flang/Parser/provenance.h"
@@ -20,6 +25,9 @@
#include "flang/Semantics/runtime-type-info.h"
#include "flang/Semantics/semantics.h"
#include "flang/Semantics/unparse-with-symbols.h"
+
+#include "mlir/IR/Dialect.h"
+#include "mlir/Pass/PassManager.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorHandling.h"
#include <clang/Basic/Diagnostic.h>
@@ -45,6 +53,49 @@ bool PrescanAndSemaDebugAction::BeginSourceFileAction() {
return RunPrescan() && RunParse() && (RunSemanticChecks() || true);
}
+bool CodeGenAction::BeginSourceFileAction() {
+ bool res = RunPrescan() && RunParse() && RunSemanticChecks();
+ if (!res)
+ return res;
+
+ CompilerInstance &ci = this->instance();
+
+ // Load the MLIR dialects required by Flang
+ mlir::DialectRegistry registry;
+ mlirCtx = std::make_unique<mlir::MLIRContext>(registry);
+ fir::support::registerNonCodegenDialects(registry);
+ fir::support::loadNonCodegenDialects(*mlirCtx);
+
+ // Create a LoweringBridge
+ const common::IntrinsicTypeDefaultKinds &defKinds =
+ ci.invocation().semanticsContext().defaultKinds();
+ fir::KindMapping kindMap(mlirCtx.get(),
+ llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)});
+ lower::LoweringBridge lb = Fortran::lower::LoweringBridge::create(*mlirCtx,
+ defKinds, ci.invocation().semanticsContext().intrinsics(),
+ ci.parsing().allCooked(), /*triple=*/"native", kindMap);
+
+ // Create a parse tree and lower it to FIR
+ Fortran::parser::Program &parseTree{*ci.parsing().parseTree()};
+ lb.lower(parseTree, ci.invocation().semanticsContext());
+ mlirModule = std::make_unique<mlir::ModuleOp>(lb.getModule());
+
+ // Run the default passes.
+ mlir::PassManager pm(mlirCtx.get(), mlir::OpPassManager::Nesting::Implicit);
+ pm.enableVerifier(/*verifyPasses=*/true);
+ pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
+
+ if (mlir::failed(pm.run(*mlirModule))) {
+ unsigned diagID =
+ ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
+ "verification of lowering to FIR failed");
+ ci.diagnostics().Report(diagID);
+ return false;
+ }
+
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// Custom ExecuteAction
//===----------------------------------------------------------------------===//
@@ -356,6 +407,29 @@ void GetSymbolsSourcesAction::ExecuteAction() {
ci.semantics().DumpSymbolsSources(llvm::outs());
}
+void EmitMLIRAction::ExecuteAction() {
+ CompilerInstance &ci = this->instance();
+
+ // Print the output. If a pre-defined output stream exists, dump the MLIR
+ // content there.
+ if (!ci.IsOutputStreamNull()) {
+ mlirModule->print(ci.GetOutputStream());
+ return;
+ }
+
+ // ... otherwise, print to a file.
+ std::unique_ptr<llvm::raw_pwrite_stream> os{ci.CreateDefaultOutputFile(
+ /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), "mlir")};
+ if (!os) {
+ unsigned diagID = ci.diagnostics().getCustomDiagID(
+ clang::DiagnosticsEngine::Error, "failed to create the output file");
+ ci.diagnostics().Report(diagID);
+ return;
+ }
+
+ mlirModule->print(*os);
+}
+
void EmitObjAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
unsigned DiagID = ci.diagnostics().getCustomDiagID(
diff --git a/flang/lib/FrontendTool/CMakeLists.txt b/flang/lib/FrontendTool/CMakeLists.txt
index 65e1dd5..1a29f4a 100644
--- a/flang/lib/FrontendTool/CMakeLists.txt
+++ b/flang/lib/FrontendTool/CMakeLists.txt
@@ -2,6 +2,9 @@ add_flang_library(flangFrontendTool
ExecuteCompilerInvocation.cpp
DEPENDS
+ # This makes sure that the MLIR dependencies of flangFrontend (which are
+ # transitively required here) are generated before this target is build.
+ flangFrontend
clangBasic
LINK_LIBS
diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 677f8cd..d69242f 100644
--- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -33,6 +33,8 @@ static std::unique_ptr<FrontendAction> CreateFrontendBaseAction(
return std::make_unique<PrintPreprocessedAction>();
case ParseSyntaxOnly:
return std::make_unique<ParseSyntaxOnlyAction>();
+ case EmitMLIR:
+ return std::make_unique<EmitMLIRAction>();
case EmitObj:
return std::make_unique<EmitObjAction>();
case DebugUnparse:
diff --git a/flang/test/Driver/driver-help.f90 b/flang/test/Driver/driver-help.f90
index 627fef3..622548d 100644
--- a/flang/test/Driver/driver-help.f90
+++ b/flang/test/Driver/driver-help.f90
@@ -65,6 +65,7 @@
! HELP-FC1-NEXT:OPTIONS:
! HELP-FC1-NEXT: -cpp Enable predefined and command line preprocessor macros
! HELP-FC1-NEXT: -D <macro>=<value> Define <macro> to <value> (or 1 if <value> omitted)
+! HELP-FC1-NEXT: -emit-mlir Build the parse tree, then lower it to MLIR
! HELP-FC1-NEXT: -emit-obj Emit native object files
! HELP-FC1-NEXT: -E Only run the preprocessor
! HELP-FC1-NEXT: -falternative-parameter-statement
diff --git a/flang/test/Driver/emit-mlir.f90 b/flang/test/Driver/emit-mlir.f90
new file mode 100644
index 0000000..006774a
--- /dev/null
+++ b/flang/test/Driver/emit-mlir.f90
@@ -0,0 +1,27 @@
+! Test the `-emit-mlir` option
+
+!-------------
+! RUN COMMANDS
+!-------------
+! RUN: %flang_fc1 -emit-mlir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
+
+! Verify that an `.mlir` file is created when `-emit-mlir` is used. Do it in a temporary directory, which will be cleaned up by the
+! LIT runner.
+! RUN: rm -rf %t-dir && mkdir -p %t-dir && cd %t-dir
+! RUN: cp %s .
+! RUN: %flang_fc1 -emit-mlir emit-mlir.f90 && ls emit-mlir.mlir
+
+!----------------
+! EXPECTED OUTPUT
+!----------------
+! CHECK: module attributes {
+! CHECK-LABEL: func @_QQmain() {
+! CHECK-NEXT: return
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+
+!------
+! INPUT
+!------
+end program
diff --git a/flang/test/Driver/syntax-only.f90 b/flang/test/Driver/syntax-only.f90
index 8ecfc2c..a97abee 100644
--- a/flang/test/Driver/syntax-only.f90
+++ b/flang/test/Driver/syntax-only.f90
@@ -1,24 +1,28 @@
-! Verify that the compiler driver correctly processes `-fsyntax-only`. By
-! default it will try to run code-generation, but that's not supported yet. We
-! don't need to test the frontend driver here - it runs `-fsyntax-only` by
-! default.
+! Verify that the driver correctly processes `-fsyntax-only`.
+!
+! By default, the compiler driver (`flang`) will create actions/phases to
+! generate machine code (i.e. object files). The `-fsyntax-only` flag is a
+! "phase-control" flag that controls this behavior and makes the driver stop
+! once the semantic checks have been run. The frontend driver (`flang -fc1`)
+! runs `-fsyntax-only` by default (i.e. that's the default action), so the flag
+! can be skipped.
!-----------
! RUN LINES
!-----------
-! RUN: not %flang -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=FSYNTAX_ONLY
+! RUN: %flang -fsyntax-only %s 2>&1 | FileCheck %s --allow-empty
+! RUN: %flang_fc1 %s 2>&1 | FileCheck %s --allow-empty
+
! RUN: not %flang %s 2>&1 | FileCheck %s --check-prefix=NO_FSYNTAX_ONLY
+! RUN: not %flang_fc1 -emit-obj %s 2>&1 | FileCheck %s --check-prefix=NO_FSYNTAX_ONLY
!-----------------
! EXPECTED OUTPUT
!-----------------
-! FSYNTAX_ONLY: IF statement is not allowed in IF statement
-! FSYNTAX_ONLY_NEXT: Semantic errors in {{.*}}syntax-only.f90
-
+! CHECK-NOT: error
! NO_FSYNTAX_ONLY: error: code-generation is not available yet
!-------
! INPUT
!-------
-IF (A > 0.0) IF (B < 0.0) A = LOG (A)
-END
+end program