diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -5268,9 +5268,12 @@ 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, - HelpText<"Build the parse tree, then lower it to MLIR">; -def emit_fir : Flag<["-"], "emit-fir">, Alias; +def emit_fir : Flag<["-"], "emit-fir">, Group, + HelpText<"Build the parse tree, then lower it to FIR">; +def emit_mlir : Flag<["-"], "emit-mlir">, Alias; + +def emit_hlfir : Flag<["-"], "emit-hlfir">, Group, + HelpText<"Build the parse tree, then lower it to HLFIR">; } // let Flags = [FC1Option, FlangOnlyOption] diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h --- a/flang/include/flang/Frontend/FrontendActions.h +++ b/flang/include/flang/Frontend/FrontendActions.h @@ -191,7 +191,8 @@ Backend_EmitObj, ///< Emit native object files Backend_EmitBC, ///< Emit LLVM bitcode files Backend_EmitLL, ///< Emit human-readable LLVM assembly - Backend_EmitMLIR ///< Emit MLIR files + Backend_EmitFIR, ///< Emit FIR files, possibly lowering via HLFIR + Backend_EmitHLFIR, ///< Emit HLFIR files before any passes run }; /// Abstract base class for actions that generate code (MLIR, LLVM IR, assembly @@ -224,6 +225,9 @@ /// Embeds offload objects given with specified with -fembed-offload-object void embedOffloadObjects(); + /// Runs pass pipeline to lower HLFIR into FIR + void lowerHLFIRToFIR(); + /// Generates an LLVM IR module from CodeGenAction::mlirModule and saves it /// in CodeGenAction::llvmModule. void generateLLVMIR(); @@ -236,9 +240,14 @@ ~CodeGenAction() override; }; -class EmitMLIRAction : public CodeGenAction { +class EmitFIRAction : public CodeGenAction { +public: + EmitFIRAction() : CodeGenAction(BackendActionTy::Backend_EmitFIR) {} +}; + +class EmitHLFIRAction : public CodeGenAction { public: - EmitMLIRAction() : CodeGenAction(BackendActionTy::Backend_EmitMLIR) {} + EmitHLFIRAction() : CodeGenAction(BackendActionTy::Backend_EmitHLFIR) {} }; class EmitLLVMAction : public CodeGenAction { diff --git a/flang/include/flang/Frontend/FrontendOptions.h b/flang/include/flang/Frontend/FrontendOptions.h --- a/flang/include/flang/Frontend/FrontendOptions.h +++ b/flang/include/flang/Frontend/FrontendOptions.h @@ -34,8 +34,11 @@ /// -fsyntax-only ParseSyntaxOnly, - /// Emit a .mlir file - EmitMLIR, + /// Emit FIR mlir file + EmitFIR, + + /// Emit HLFIR mlir file + EmitHLFIR, /// Emit an .ll file EmitLLVM, diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -306,8 +306,11 @@ case clang::driver::options::OPT_fsyntax_only: opts.programAction = ParseSyntaxOnly; break; - case clang::driver::options::OPT_emit_mlir: - opts.programAction = EmitMLIR; + case clang::driver::options::OPT_emit_fir: + opts.programAction = EmitFIR; + break; + case clang::driver::options::OPT_emit_hlfir: + opts.programAction = EmitHLFIR; break; case clang::driver::options::OPT_emit_llvm: opts.programAction = EmitLLVM; @@ -911,7 +914,8 @@ } // -flang-experimental-hlfir - if (args.hasArg(clang::driver::options::OPT_flang_experimental_hlfir)) { + if (args.hasArg(clang::driver::options::OPT_flang_experimental_hlfir) || + args.hasArg(clang::driver::options::OPT_emit_hlfir)) { res.loweringOpts.setLowerToHighLevelFIR(true); } diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -647,6 +647,34 @@ } } +// Lower using HLFIR then run the FIR to HLFIR pipeline +void CodeGenAction::lowerHLFIRToFIR() { + assert(mlirModule && "The MLIR module has not been generated yet."); + + CompilerInstance &ci = this->getInstance(); + auto opts = ci.getInvocation().getCodeGenOpts(); + llvm::OptimizationLevel level = mapToLevel(opts); + + fir::support::loadDialects(*mlirCtx); + + // Set-up the MLIR pass manager + mlir::PassManager pm((*mlirModule)->getName(), + mlir::OpPassManager::Nesting::Implicit); + + pm.addPass(std::make_unique()); + pm.enableVerifier(/*verifyPasses=*/true); + + // Create the pass pipeline + fir::createHLFIRToFIRPassPipeline(pm, level); + (void)mlir::applyPassManagerCLOptions(pm); + + if (!mlir::succeeded(pm.run(*mlirModule))) { + unsigned diagID = ci.getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, "Lowering to FIR failed"); + ci.getDiagnostics().Report(diagID); + } +} + // Lower the previously generated MLIR module into an LLVM IR module void CodeGenAction::generateLLVMIR() { assert(mlirModule && "The MLIR module has not been generated yet."); @@ -751,7 +779,9 @@ case BackendActionTy::Backend_EmitLL: return ci.createDefaultOutputFile( /*Binary=*/false, inFile, /*extension=*/"ll"); - case BackendActionTy::Backend_EmitMLIR: + case BackendActionTy::Backend_EmitFIR: + LLVM_FALLTHROUGH; + case BackendActionTy::Backend_EmitHLFIR: return ci.createDefaultOutputFile( /*Binary=*/false, inFile, /*extension=*/"mlir"); case BackendActionTy::Backend_EmitBC: @@ -914,7 +944,17 @@ } } - if (action == BackendActionTy::Backend_EmitMLIR) { + if (action == BackendActionTy::Backend_EmitFIR) { + if (ci.getInvocation().getLoweringOpts().getLowerToHighLevelFIR()) { + lowerHLFIRToFIR(); + } + mlirModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream()); + return; + } + + if (action == BackendActionTy::Backend_EmitHLFIR) { + assert(ci.getInvocation().getLoweringOpts().getLowerToHighLevelFIR() && + "Lowering must have been configured to emit HLFIR"); mlirModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream()); return; } diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp --- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -40,8 +40,10 @@ return std::make_unique(); case ParseSyntaxOnly: return std::make_unique(); - case EmitMLIR: - return std::make_unique(); + case EmitFIR: + return std::make_unique(); + case EmitHLFIR: + return std::make_unique(); case EmitLLVM: return std::make_unique(); case EmitLLVMBitcode: diff --git a/flang/test/Driver/driver-help.f90 b/flang/test/Driver/driver-help.f90 --- a/flang/test/Driver/driver-help.f90 +++ b/flang/test/Driver/driver-help.f90 @@ -94,9 +94,10 @@ ! HELP-FC1-NEXT:OPTIONS: ! HELP-FC1-NEXT: -cpp Enable predefined and command line preprocessor macros ! HELP-FC1-NEXT: -D = Define to (or 1 if omitted) +! HELP-FC1-NEXT: -emit-fir Build the parse tree, then lower it to FIR +! HELP-FC1-NEXT: -emit-hlfir Build the parse tree, then lower it to HLFIR ! HELP-FC1-NEXT: -emit-llvm-bc Build ASTs then convert to LLVM, emit .bc file ! HELP-FC1-NEXT: -emit-llvm Use the LLVM representation for assembler and object files -! 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/HLFIR/flang-experimental-hlfir-flag.f90 b/flang/test/HLFIR/flang-experimental-hlfir-flag.f90 --- a/flang/test/HLFIR/flang-experimental-hlfir-flag.f90 +++ b/flang/test/HLFIR/flang-experimental-hlfir-flag.f90 @@ -1,19 +1,38 @@ -! Test -flang-experimental-hlfir flag -! RUN: %flang_fc1 -flang-experimental-hlfir -emit-fir -o - %s | FileCheck %s -! RUN: %flang_fc1 -emit-fir -o - %s | FileCheck %s --check-prefix NO-HLFIR +! Test -flang-experimental-hlfir, -emit-hlfir, -emit-fir flags +! RUN: %flang_fc1 -emit-hlfir -o - %s | FileCheck --check-prefix HLFIR --check-prefix ALL %s +! RUN: %flang_fc1 -emit-hlfir -flang-experimental-hlfir -o - %s | FileCheck --check-prefix HLFIR --check-prefix ALL %s +! RUN: %flang_fc1 -emit-fir -o - %s | FileCheck %s --check-prefix NO-HLFIR --check-prefix ALL +! RUN: %flang_fc1 -emit-fir -flang-experimental-hlfir -o - %s | FileCheck --check-prefix FIR --check-prefix ALL %s + +! | Action | -flang-experimental-hlfir? | Result | +! | =========== | ========================== | =============================== | +! | -emit-hlfir | N | Outputs HLFIR | +! | -emit-hlfir | Y | Outputs HLFIR | +! | -emit-fir | N | Outputs FIR, using old lowering | +! | -emit-fir | Y | Outputs FIR, lowering via HLFIR | subroutine test(a, res) real :: a(:), res res = SUM(a) end subroutine -! CHECK-LABEL: func.func @_QPtest -! CHECK: %[[A:.*]]: !fir.box> -! CHECK: %[[RES:.*]]: !fir.ref -! CHECK-DAG: %[[A_VAR:.*]]:2 = hlfir.declare %[[A]] -! CHECK-DAG: %[[RES_VAR:.*]]:2 = hlfir.declare %[[RES]] -! CHECK-NEXT: %[[SUM_RES:.*]] = hlfir.sum %[[A_VAR]]#0 -! CHECK-NEXT: hlfir.assign %[[SUM_RES]] to %[[RES_VAR]]#0 -! CHECK-NEXT: return -! CHECK-NEXT: } +! ALL-LABEL: func.func @_QPtest +! ALL: %[[A:.*]]: !fir.box> +! ALL: %[[RES:.*]]: !fir.ref + +! HLFIR: %[[A_VAR:.*]]:2 = hlfir.declare %[[A]] +! fir.declare is only generated via the hlfir -> fir lowering +! FIR: %[[A_VAR:.*]] = fir.declare %[[A]] +! NO-HLFIR-NOT: fir.declare + +! HLFIR-DAG: %[[RES_VAR:.*]]:2 = hlfir.declare %[[RES]] +! FIR: %[[RES_VAR:.*]] = fir.declare %[[RES]] +! NO-HLFIR-NOT: fir.declare + +! HLFIR-NEXT: %[[SUM_RES:.*]] = hlfir.sum %[[A_VAR]]#0 +! HLFIR-NEXT: hlfir.assign %[[SUM_RES]] to %[[RES_VAR]]#0 +! FIR-NOT: hlfir. +! NO-HLFIR-NOT: hlfir. + +! ALL: return +! ALL-NEXT: } -! NO-HLFIR-NOT: hlfir.