-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[flang][driver] Separate the actions of the -emit-fir
and -emit-mlir
options
#139857
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…ir` options. This patch split `-emit-fir` and `-emit-mlir` option in Flang's frontend driver. A new parent class for code-gen frontend actions is introduced:`CodeGenAction`. For the `-emit-mlir` option, we aim to generate a file using the core MLIR dialects. Currently, FIR Dialect is directly lowered to LLVM Dialect, but in the future, we hope to gradually separate the pipeline into FIR → MLIR (core dialects) → LLVM. For now, this option temporarily generate a file using the LLVM dialect.
@llvm/pr-subscribers-clang @llvm/pr-subscribers-flang-fir-hlfir Author: MingYan (NexMing) ChangesThis patch split A new parent class for code-gen frontend actions is introduced: For the Full diff: https://github.com/llvm/llvm-project/pull/139857.diff 10 Files Affected:
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index bd8df8f6a749a..00cc05a2bd1a6 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -7193,7 +7193,8 @@ defm analyzed_objects_for_unparse : OptOutFC1FFlag<"analyzed-objects-for-unparse
def emit_fir : Flag<["-"], "emit-fir">, Group<Action_Group>,
HelpText<"Build the parse tree, then lower it to FIR">;
-def emit_mlir : Flag<["-"], "emit-mlir">, Alias<emit_fir>;
+def emit_mlir : Flag<["-"], "emit-mlir">, Group<Action_Group>,
+ HelpText<"Build the parse tree, then lower it to core MLIR">;
def emit_hlfir : Flag<["-"], "emit-hlfir">, Group<Action_Group>,
HelpText<"Build the parse tree, then lower it to HLFIR">;
diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h
index f9a45bd6c0a56..b651a234b5849 100644
--- a/flang/include/flang/Frontend/FrontendActions.h
+++ b/flang/include/flang/Frontend/FrontendActions.h
@@ -179,6 +179,7 @@ enum class BackendActionTy {
Backend_EmitBC, ///< Emit LLVM bitcode files
Backend_EmitLL, ///< Emit human-readable LLVM assembly
Backend_EmitFIR, ///< Emit FIR files, possibly lowering via HLFIR
+ Backend_EmitMLIR, ///< Emit MLIR files, possibly lowering via FIR
Backend_EmitHLFIR, ///< Emit HLFIR files before any passes run
};
@@ -216,6 +217,11 @@ class CodeGenAction : public FrontendAction {
/// Runs pass pipeline to lower HLFIR into FIR
void lowerHLFIRToFIR();
+ /// Runs pass pipeline to lower FIR into core MLIR
+ /// TODO: Some operations currently do not have corresponding representations
+ /// in the core MLIR dialects, so we lower them directly to the LLVM dialect.
+ void lowerFIRToMLIR();
+
/// Generates an LLVM IR module from CodeGenAction::mlirModule and saves it
/// in CodeGenAction::llvmModule.
void generateLLVMIR();
@@ -232,6 +238,11 @@ class EmitFIRAction : public CodeGenAction {
EmitFIRAction() : CodeGenAction(BackendActionTy::Backend_EmitFIR) {}
};
+class EmitMLIRAction : public CodeGenAction {
+public:
+ EmitMLIRAction() : CodeGenAction(BackendActionTy::Backend_EmitMLIR) {}
+};
+
class EmitHLFIRAction : public CodeGenAction {
public:
EmitHLFIRAction() : CodeGenAction(BackendActionTy::Backend_EmitHLFIR) {}
diff --git a/flang/include/flang/Frontend/FrontendOptions.h b/flang/include/flang/Frontend/FrontendOptions.h
index 0bd2e621813ca..69bc5691430a8 100644
--- a/flang/include/flang/Frontend/FrontendOptions.h
+++ b/flang/include/flang/Frontend/FrontendOptions.h
@@ -37,6 +37,9 @@ enum ActionKind {
/// Emit FIR mlir file
EmitFIR,
+ /// Emit core MLIR mlir file
+ EmitMLIR,
+
/// Emit HLFIR mlir file
EmitHLFIR,
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 238079a09ef3a..2a4b6e9d884af 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -572,6 +572,9 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
case clang::driver::options::OPT_emit_fir:
opts.programAction = EmitFIR;
break;
+ case clang::driver::options::OPT_emit_mlir:
+ opts.programAction = EmitMLIR;
+ break;
case clang::driver::options::OPT_emit_hlfir:
opts.programAction = EmitHLFIR;
break;
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index e5a15c555fa5e..9ba98873042f8 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -635,6 +635,49 @@ void CodeGenAction::lowerHLFIRToFIR() {
}
}
+void CodeGenAction::lowerFIRToMLIR() {
+ assert(mlirModule && "The MLIR module has not been generated yet.");
+
+ CompilerInstance &ci = this->getInstance();
+ CompilerInvocation &invoc = ci.getInvocation();
+ const CodeGenOptions &opts = invoc.getCodeGenOpts();
+ const auto &mathOpts = invoc.getLoweringOpts().getMathOptions();
+ llvm::OptimizationLevel level = mapToLevel(opts);
+ mlir::DefaultTimingManager &timingMgr = ci.getTimingManager();
+ mlir::TimingScope &timingScopeRoot = ci.getTimingScopeRoot();
+
+ fir::support::loadDialects(*mlirCtx);
+ mlir::DialectRegistry registry;
+ fir::support::registerNonCodegenDialects(registry);
+ fir::support::addFIRExtensions(registry);
+ mlirCtx->appendDialectRegistry(registry);
+ fir::support::registerLLVMTranslation(*mlirCtx);
+
+ // Set-up the MLIR pass manager
+ mlir::PassManager pm((*mlirModule)->getName(),
+ mlir::OpPassManager::Nesting::Implicit);
+
+ pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
+ pm.enableVerifier(/*verifyPasses=*/true);
+
+ MLIRToLLVMPassPipelineConfig config(level, opts, mathOpts);
+ fir::registerDefaultInlinerPass(config);
+
+ // Create the pass pipeline
+ fir::createDefaultFIROptimizerPassPipeline(pm, config);
+ fir::createDefaultFIRCodeGenPassPipeline(pm, config);
+ (void)mlir::applyPassManagerCLOptions(pm);
+
+ mlir::TimingScope timingScopeMLIRPasses = timingScopeRoot.nest(
+ mlir::TimingIdentifier::get(timingIdMLIRPasses, timingMgr));
+ pm.enableTiming(timingScopeMLIRPasses);
+ if (!mlir::succeeded(pm.run(*mlirModule))) {
+ unsigned diagID = ci.getDiagnostics().getCustomDiagID(
+ clang::DiagnosticsEngine::Error, "Lowering to FIR failed");
+ ci.getDiagnostics().Report(diagID);
+ }
+}
+
static std::optional<std::pair<unsigned, unsigned>>
getAArch64VScaleRange(CompilerInstance &ci) {
const auto &langOpts = ci.getInvocation().getLangOpts();
@@ -836,6 +879,7 @@ getOutputStream(CompilerInstance &ci, llvm::StringRef inFile,
return ci.createDefaultOutputFile(
/*Binary=*/false, inFile, /*extension=*/"ll");
case BackendActionTy::Backend_EmitFIR:
+ case BackendActionTy::Backend_EmitMLIR:
case BackendActionTy::Backend_EmitHLFIR:
return ci.createDefaultOutputFile(
/*Binary=*/false, inFile, /*extension=*/"mlir");
@@ -1242,10 +1286,14 @@ void CodeGenAction::executeAction() {
}
}
- if (action == BackendActionTy::Backend_EmitFIR) {
+ if (action == BackendActionTy::Backend_EmitFIR ||
+ action == BackendActionTy::Backend_EmitMLIR) {
if (loweringOpts.getLowerToHighLevelFIR()) {
lowerHLFIRToFIR();
}
+ if (action == BackendActionTy::Backend_EmitMLIR) {
+ lowerFIRToMLIR();
+ }
mlirModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
return;
}
diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 09ac129d3e689..0c4195ec2ac2e 100644
--- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -43,6 +43,8 @@ createFrontendAction(CompilerInstance &ci) {
return std::make_unique<ParseSyntaxOnlyAction>();
case EmitFIR:
return std::make_unique<EmitFIRAction>();
+ case EmitMLIR:
+ return std::make_unique<EmitMLIRAction>();
case EmitHLFIR:
return std::make_unique<EmitHLFIRAction>();
case EmitLLVM:
diff --git a/flang/test/Driver/emit-fir.f90 b/flang/test/Driver/emit-fir.f90
new file mode 100644
index 0000000000000..4230c4b7ab434
--- /dev/null
+++ b/flang/test/Driver/emit-fir.f90
@@ -0,0 +1,30 @@
+! Test the `-emit-fir` option
+
+! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
+
+! Verify that an `.mlir` file is created when `-emit-fir` 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-fir emit-fir.f90 && ls emit-fir.mlir
+
+! CHECK: module attributes {
+! CHECK-SAME: dlti.dl_spec =
+! CHECK-SAME: llvm.data_layout =
+! CHECK-LABEL: func @_QQmain() {
+! CHECK-NEXT: fir.dummy_scope
+! CHECK-NEXT: return
+! CHECK-NEXT: }
+! CHECK-NEXT: func.func private @_FortranAProgramStart(i32, !llvm.ptr, !llvm.ptr, !llvm.ptr)
+! CHECK-NEXT: func.func private @_FortranAProgramEndStatement()
+! CHECK-NEXT: func.func @main(%arg0: i32, %arg1: !llvm.ptr, %arg2: !llvm.ptr) -> i32 {
+! CHECK-NEXT: %c0_i32 = arith.constant 0 : i32
+! CHECK-NEXT: %0 = fir.zero_bits !fir.ref<tuple<i32, !fir.ref<!fir.array<0xtuple<!fir.ref<i8>, !fir.ref<i8>>>>>>
+! CHECK-NEXT: fir.call @_FortranAProgramStart(%arg0, %arg1, %arg2, %0) {{.*}} : (i32, !llvm.ptr, !llvm.ptr, !fir.ref<tuple<i32, !fir.ref<!fir.array<0xtuple<!fir.ref<i8>, !fir.ref<i8>>>>>>)
+! CHECK-NEXT: fir.call @_QQmain() fastmath<contract> : () -> ()
+! CHECK-NEXT: fir.call @_FortranAProgramEndStatement() {{.*}} : () -> ()
+! CHECK-NEXT: return %c0_i32 : i32
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+
+end program
diff --git a/flang/test/Driver/emit-mlir.f90 b/flang/test/Driver/emit-mlir.f90
index de5a62d6bc7f3..cced4b0e37017 100644
--- a/flang/test/Driver/emit-mlir.f90
+++ b/flang/test/Driver/emit-mlir.f90
@@ -1,7 +1,6 @@
! Test the `-emit-mlir` option
! 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.
@@ -9,23 +8,22 @@
! RUN: cp %s .
! RUN: %flang_fc1 -emit-mlir emit-mlir.f90 && ls emit-mlir.mlir
-! CHECK: module attributes {
-! CHECK-SAME: dlti.dl_spec =
-! CHECK-SAME: llvm.data_layout =
-! CHECK-LABEL: func @_QQmain() {
-! CHECK-NEXT: fir.dummy_scope
-! CHECK-NEXT: return
-! CHECK-NEXT: }
-! CHECK-NEXT: func.func private @_FortranAProgramStart(i32, !llvm.ptr, !llvm.ptr, !llvm.ptr)
-! CHECK-NEXT: func.func private @_FortranAProgramEndStatement()
-! CHECK-NEXT: func.func @main(%arg0: i32, %arg1: !llvm.ptr, %arg2: !llvm.ptr) -> i32 {
-! CHECK-NEXT: %c0_i32 = arith.constant 0 : i32
-! CHECK-NEXT: %0 = fir.zero_bits !fir.ref<tuple<i32, !fir.ref<!fir.array<0xtuple<!fir.ref<i8>, !fir.ref<i8>>>>>>
-! CHECK-NEXT: fir.call @_FortranAProgramStart(%arg0, %arg1, %arg2, %0) {{.*}} : (i32, !llvm.ptr, !llvm.ptr, !fir.ref<tuple<i32, !fir.ref<!fir.array<0xtuple<!fir.ref<i8>, !fir.ref<i8>>>>>>)
-! CHECK-NEXT: fir.call @_QQmain() fastmath<contract> : () -> ()
-! CHECK-NEXT: fir.call @_FortranAProgramEndStatement() {{.*}} : () -> ()
-! CHECK-NEXT: return %c0_i32 : i32
-! CHECK-NEXT: }
-! CHECK-NEXT: }
+! CHECK-LABEL: llvm.func @_QQmain() {
+! CHECK: llvm.return
+! CHECK: }
+! CHECK: llvm.func @_FortranAProgramStart(i32, !llvm.ptr, !llvm.ptr, !llvm.ptr) attributes {sym_visibility = "private"}
+! CHECK: llvm.func @_FortranAProgramEndStatement() attributes {sym_visibility = "private"}
+
+! CHECK-LABEL: llvm.func @main(
+! CHECK-SAME: %[[ARG0:.*]]: i32,
+! CHECK-SAME: %[[ARG1:.*]]: !llvm.ptr,
+! CHECK-SAME: %[[ARG2:.*]]: !llvm.ptr) -> i32 {
+! CHECK: %[[VAL_0:.*]] = llvm.mlir.constant(0 : i32) : i32
+! CHECK: %[[VAL_1:.*]] = llvm.mlir.zero : !llvm.ptr
+! CHECK: llvm.call @_FortranAProgramStart(%[[ARG0]], %[[ARG1]], %[[ARG2]], %[[VAL_1]]) {fastmathFlags = #llvm.fastmath<contract>} : (i32, !llvm.ptr, !llvm.ptr, !llvm.ptr) -> ()
+! CHECK: llvm.call @_QQmain() {fastmathFlags = #llvm.fastmath<contract>} : () -> ()
+! CHECK: llvm.call @_FortranAProgramEndStatement() {fastmathFlags = #llvm.fastmath<contract>} : () -> ()
+! CHECK: llvm.return %[[VAL_0]] : i32
+! CHECK: }
end program
diff --git a/flang/test/Fir/non-trivial-procedure-binding-description.f90 b/flang/test/Fir/non-trivial-procedure-binding-description.f90
index 668928600157b..79bb4dbb3521e 100644
--- a/flang/test/Fir/non-trivial-procedure-binding-description.f90
+++ b/flang/test/Fir/non-trivial-procedure-binding-description.f90
@@ -1,5 +1,5 @@
-! RUN: %flang_fc1 -emit-mlir %s -o - | FileCheck %s --check-prefix=BEFORE
-! RUN: %flang_fc1 -emit-mlir %s -o - | fir-opt --abstract-result | FileCheck %s --check-prefix=AFTER
+! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s --check-prefix=BEFORE
+! RUN: %flang_fc1 -emit-fir %s -o - | fir-opt --abstract-result | FileCheck %s --check-prefix=AFTER
module a
type f
contains
diff --git a/flang/test/Lower/unsigned-ops.f90 b/flang/test/Lower/unsigned-ops.f90
index f61f10656159a..fa1eb47b26b00 100644
--- a/flang/test/Lower/unsigned-ops.f90
+++ b/flang/test/Lower/unsigned-ops.f90
@@ -1,4 +1,4 @@
-! RUN: %flang_fc1 -funsigned -emit-mlir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -funsigned -emit-fir %s -o - | FileCheck %s
unsigned function f01(u, v)
unsigned, intent(in) :: u, v
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code changes look okay here but I think the naming should reflect that the lowering to upstream MLIR dialects will be considered experimental (and may remain so forever, if it cannot be made to fully conform to Fortran semantics or show runtime performance improvements that justify the added complexity and compile time).
Instead of re-purposing -emit-mlir
, how about -emit-experimental-mlir
?
Thank you for upstreaming your work aligning flang with upstream mlir dialects. I am personally very interested in this. But I think making such a significant change to the lowering pipeline is going to need a lot broader consensus amongst flang contributors and strong technical justification.
For now I am very happy to see any work in this direction under experimental options 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I understand the goal, I also have some issues with the genericity of the name while there is no single MLIR core dialect. How would you make a difference between lowering to different level of representation that the core dialect offer: affine, linalg, scf, cf, memref, tensor, llvm dialect?
I have no problem having some -emit-llvm-dialect
option though, on the contrary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to prefer @jeanPerier's suggestion of having the option name reflect that only the LLVM dialect is being used.
In the future, do you intend to provide a way to choose the set of dialects to use? In that case, we could consider something a bit more general like @tblah's suggestion.
CompilerInstance &ci = this->getInstance(); | ||
CompilerInvocation &invoc = ci.getInvocation(); | ||
const CodeGenOptions &opts = invoc.getCodeGenOpts(); | ||
const auto &mathOpts = invoc.getLoweringOpts().getMathOptions(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use a more concrete type instead of auto
here?
Should this instead be a "A new code-gen action class is introduced: |
Currently, using the LLVM dialect is only a temporary solution and doesn’t align with my long-term goals. In the future, I plan to implement conversions from FIR to scf, memref, and other core dialects. I personally prefer @tblah’s suggestion, but as @jeanPerier pointed out, I’m indeed not yet sure how to make a difference between the different levels of representation that the core dialects. I think the appropriate core MLIR dialects are those that most closely reflect the original FIR representation. Do you have any suggestions on how to approach this? |
Who said it is a temporary solution? Can you point to an RFC? |
This specific patch wasn't discussed in the RFC but there is some discussion here https://discourse.llvm.org/t/rfc-add-fir-affine-optimization-fir-pass-pipeline/86190/5 |
So is that only for the affine pipeline? |
It's not just about Affine. In the future, I plan to implement conversions from FIR to scf, memref, and other core dialects. The FIR → Affine → FIR path is part of my experimental roadmap for exploring Fortran optimizations. |
I am not exactly clear on what sort of suggestions you are looking for. I haven't thought a lot about this either, so here are just my initial thoughts: One could have some standard "sets" of dialects that could be chosen. This may be similar to the various optimization levels available, -O3 for maximum performance, -Os to optimize for size etc. Each "set" may have its own strengths - maybe some enable better optimizations for certain codes. Others may contain information that is useful for more sophisticated tooling, rather than standard compilation. This may allow us to defer development of a truly flexible approach until we have a better sense of what is useful. As someone who works in a research lab, having the ability to experiment with lowering using the various MLIR dialects sounds great. However, there is a tension between that and the requirements for a production-quality compiler and the ability to maintain this code. Apologies if this is obvious, but I am just trying to provide some broader context for the concerns/interests of folks in the community. Does that help at all? |
This patch split
-emit-fir
and-emit-mlir
option in Flang's frontend driver.A new code-gen action class is introduced:
EmitMLIRAction
.For the
-emit-mlir
option, we aim to generate a file using the core MLIR dialects. Currently, FIR Dialect is directly lowered to LLVM Dialect, but in the future, we hope to gradually separate the pipeline into FIR → MLIR (core dialects) → LLVM. For now, this option temporarily generate a file using the LLVM dialect.