Skip to content

Commit cd98952

Browse files
banach-spacejeanPerier
authored andcommitted
[flang][driver] Add support for the -emit-llvm option
This patch adds support for the `-emit-llvm` option in the frontend driver. Similarly to Clang, `flang-new -fc1 -emit-llvm file.f` will generate an LLVM IR file. In order to better facilitate code re-use, common functionality for `-emit-llvm` and `-emit-mlir` is extracted into `CodeGenAction::BeginSourceFileAction`. The pass pipeline creation is extracted from tco.cpp and moved to flang/Tools/CLOptions.inc (see `createTCOPassPipeline`). Also, `fromDefaultKinds` in FrontendActions.cpp is deleted. We should be using the shared implementation from flang/Optimizer/Support/Utils.h instead (introduced in #1117).
1 parent 79c1f36 commit cd98952

File tree

13 files changed

+228
-75
lines changed

13 files changed

+228
-75
lines changed

clang/include/clang/Driver/Options.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,7 @@ def d_Flag : Flag<["-"], "d">, Group<d_Group>;
10631063
def d_Joined : Joined<["-"], "d">, Group<d_Group>;
10641064
def emit_ast : Flag<["-"], "emit-ast">,
10651065
HelpText<"Emit Clang AST files for source inputs">;
1066-
def emit_llvm : Flag<["-"], "emit-llvm">, Flags<[CC1Option]>, Group<Action_Group>,
1066+
def emit_llvm : Flag<["-"], "emit-llvm">, Flags<[CC1Option, FC1Option]>, Group<Action_Group>,
10671067
HelpText<"Use the LLVM representation for assembler and object files">;
10681068
def emit_interface_stubs : Flag<["-"], "emit-interface-stubs">, Flags<[CC1Option]>, Group<Action_Group>,
10691069
HelpText<"Generate Interface Stub Files.">;

flang/include/flang/Frontend/CompilerInstance.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#ifndef LLVM_FLANG_FRONTEND_COMPILERINSTANCE_H
99
#define LLVM_FLANG_FRONTEND_COMPILERINSTANCE_H
1010

11+
#include "mlir/IR/BuiltinOps.h"
1112
#include "flang/Frontend/CompilerInvocation.h"
1213
#include "flang/Frontend/FrontendAction.h"
1314
#include "flang/Frontend/PreprocessorOptions.h"
@@ -72,6 +73,10 @@ class CompilerInstance {
7273
/// facilitate this. It is optional and will normally be just a nullptr.
7374
std::unique_ptr<llvm::raw_pwrite_stream> outputStream_;
7475

76+
std::unique_ptr<mlir::MLIRContext> mlirCtx_;
77+
78+
std::unique_ptr<mlir::ModuleOp> mlirModule_;
79+
7580
public:
7681
explicit CompilerInstance();
7782

@@ -129,6 +134,22 @@ class CompilerInstance {
129134
semantics_ = std::move(semantics);
130135
}
131136

137+
/// }
138+
/// @name MLIR
139+
mlir::MLIRContext &mlirCtx() { return *mlirCtx_; }
140+
const mlir::MLIRContext &mlirCtx() const { return *mlirCtx_; }
141+
142+
void setMlirCtx(std::unique_ptr<mlir::MLIRContext> mlirCtx) {
143+
mlirCtx_ = std::move(mlirCtx);
144+
}
145+
146+
mlir::ModuleOp &mlirModule() { return *mlirModule_; }
147+
const mlir::ModuleOp &mlirModule() const { return *mlirModule_; }
148+
149+
void setMlirModule(std::unique_ptr<mlir::ModuleOp> module) {
150+
mlirModule_ = std::move(module);
151+
}
152+
132153
/// }
133154
/// @name High-Level Operations
134155
/// {

flang/include/flang/Frontend/FrontendActions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ class EmitMLIRAction : public CodeGenAction {
159159
void ExecuteAction() override;
160160
};
161161

162+
class EmitLLVMAction : public CodeGenAction {
163+
void ExecuteAction() override;
164+
};
165+
162166
} // namespace Fortran::frontend
163167

164168
#endif // LLVM_FLANG_FRONTEND_FRONTENDACTIONS_H

flang/include/flang/Frontend/FrontendOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ enum ActionKind {
3434
/// Emit a .mlir file
3535
EmitMLIR,
3636

37+
/// Emit a .llvm file
38+
EmitLLVM,
39+
3740
/// Emit a .o file.
3841
EmitObj,
3942

flang/include/flang/Tools/CLOptions.inc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
// This file defines some shared command-line options that can be used when
1010
// debugging the test tools. This file must be included into the tool.
1111

12+
#include "flang/Optimizer/CodeGen/CodeGen.h"
13+
#include "flang/Optimizer/Transforms/Passes.h"
14+
#include "mlir/Conversion/SCFToStandard/SCFToStandard.h"
15+
#include "mlir/Pass/PassManager.h"
16+
#include "mlir/Transforms/Passes.h"
17+
#include "llvm/Support/CommandLine.h"
18+
1219
#define DisableOption(DOName, DOOption, DODescription) \
1320
static llvm::cl::opt<bool> disable##DOName("disable-" DOOption, \
1421
llvm::cl::desc("disable " DODescription " pass"), llvm::cl::init(false), \
@@ -77,6 +84,35 @@ inline void addLLVMDialectToLLVMPass(
7784
addPassConditionally(pm, disableLlvmIrToLlvm,
7885
[&]() { return fir::createLLVMDialectToLLVMPass(output); });
7986
}
87+
88+
/// Create a pass pipeline for lowering from MLIR to LLVM IR
89+
///
90+
/// \param pm - MLIR pass manager that will hold the pipeline definition
91+
inline void createMLIRToLLVMPassPipeline(mlir::PassManager &pm)
92+
{
93+
// simplify the IR
94+
fir::addCSE(pm);
95+
pm.addNestedPass<mlir::FuncOp>(fir::createArrayValueCopyPass());
96+
pm.addNestedPass<mlir::FuncOp>(fir::createCharacterConversionPass());
97+
pm.addPass(mlir::createCanonicalizerPass());
98+
fir::addCSE(pm);
99+
pm.addPass(mlir::createInlinerPass());
100+
pm.addPass(mlir::createCSEPass());
101+
102+
// convert control flow to CFG form
103+
fir::addCfgConversionPass(pm);
104+
pm.addNestedPass<mlir::FuncOp>(fir::createControlFlowLoweringPass());
105+
pm.addPass(mlir::createLowerToCFGPass());
106+
107+
pm.addPass(mlir::createCanonicalizerPass());
108+
fir::addCSE(pm);
109+
110+
pm.addNestedPass<mlir::FuncOp>(fir::createAbstractResultOptPass());
111+
fir::addCodeGenRewritePass(pm);
112+
fir::addTargetRewritePass(pm);
113+
fir::addFIRToLLVMPass(pm);
114+
}
115+
80116
#undef FLANG_EXCLUDE_CODEGEN
81117
#endif
82118

flang/lib/Frontend/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ add_flang_library(flangFrontend
2929
FIRAnalysis
3030
FIRSupport
3131
FIRBuilder
32+
FIRCodeGen
33+
FIRTransforms
34+
MLIRTransforms
35+
MLIRLLVMToLLVMIRTranslation
36+
MLIRSCFToStandard
3237
${dialect_libs}
3338

3439
LINK_COMPONENTS

flang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ static bool ParseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
126126
case clang::driver::options::OPT_emit_mlir:
127127
opts.programAction = EmitMLIR;
128128
break;
129+
case clang::driver::options::OPT_emit_llvm:
130+
opts.programAction = EmitLLVM;
131+
break;
129132
case clang::driver::options::OPT_emit_obj:
130133
opts.programAction = EmitObj;
131134
break;

flang/lib/Frontend/FrontendActions.cpp

Lines changed: 116 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,21 @@
1010
#include "mlir/IR/Dialect.h"
1111
#include "mlir/Pass/PassManager.h"
1212
#include "mlir/Support/LogicalResult.h"
13+
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
1314
#include "flang/Common/default-kinds.h"
1415
#include "flang/Frontend/CompilerInstance.h"
1516
#include "flang/Frontend/FrontendOptions.h"
1617
#include "flang/Frontend/PreprocessorOptions.h"
1718
#include "flang/Lower/Bridge.h"
1819
#include "flang/Lower/PFTBuilder.h"
1920
#include "flang/Lower/Support/Verifier.h"
21+
#include "flang/Optimizer/CodeGen/CodeGen.h"
2022
#include "flang/Optimizer/Dialect/FIRType.h"
23+
#include "flang/Optimizer/Support//Utils.h"
24+
#include "flang/Optimizer/Support/FIRContext.h"
2125
#include "flang/Optimizer/Support/InitFIR.h"
2226
#include "flang/Optimizer/Support/KindMapping.h"
27+
#include "flang/Optimizer/Transforms/Passes.h"
2328
#include "flang/Parser/dump-parse-tree.h"
2429
#include "flang/Parser/parsing.h"
2530
#include "flang/Parser/provenance.h"
@@ -32,7 +37,6 @@
3237
#include "llvm/Support/ErrorHandling.h"
3338
#include "llvm/Support/raw_ostream.h"
3439
#include <clang/Basic/Diagnostic.h>
35-
#include <memory>
3640

3741
using namespace Fortran::frontend;
3842

@@ -55,7 +59,45 @@ bool PrescanAndSemaDebugAction::BeginSourceFileAction() {
5559
}
5660

5761
bool CodeGenAction::BeginSourceFileAction() {
58-
return RunPrescan() & RunParse() && RunSemanticChecks();
62+
bool res = RunPrescan() && RunParse() && RunSemanticChecks();
63+
if (!res)
64+
return res;
65+
66+
CompilerInstance &ci = this->instance();
67+
68+
// Load the MLIR dialects required by Flang
69+
mlir::DialectRegistry registry;
70+
ci.setMlirCtx(std::make_unique<mlir::MLIRContext>(registry));
71+
fir::support::registerNonCodegenDialects(registry);
72+
fir::support::loadNonCodegenDialects(ci.mlirCtx());
73+
74+
// Create a LoweringBridge
75+
auto &defKinds = ci.invocation().semanticsContext().defaultKinds();
76+
fir::KindMapping kindMap(&ci.mlirCtx(),
77+
llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)});
78+
auto lb = Fortran::lower::LoweringBridge::create(ci.mlirCtx(), defKinds,
79+
ci.invocation().semanticsContext().intrinsics(), ci.parsing().allCooked(),
80+
"", kindMap);
81+
82+
// Create a parse tree and lower it to FIR
83+
auto &parseTree{*ci.parsing().parseTree()};
84+
lb.lower(parseTree, ci.invocation().semanticsContext());
85+
ci.setMlirModule(std::make_unique<mlir::ModuleOp>(lb.getModule()));
86+
87+
// Run the default passes.
88+
mlir::PassManager pm(&ci.mlirCtx(), mlir::OpPassManager::Nesting::Implicit);
89+
pm.enableVerifier(/*verifyPasses=*/true);
90+
pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
91+
92+
if (mlir::failed(pm.run(ci.mlirModule()))) {
93+
unsigned diagID =
94+
ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
95+
"verification of lowering to FIR failed");
96+
ci.diagnostics().Report(diagID);
97+
return false;
98+
}
99+
100+
return true;
59101
}
60102

61103
//===----------------------------------------------------------------------===//
@@ -369,61 +411,13 @@ void GetSymbolsSourcesAction::ExecuteAction() {
369411
ci.semantics().DumpSymbolsSources(llvm::outs());
370412
}
371413

372-
// Translate front-end KINDs for use in the IR and code gen. Extracted from
373-
// bbc.cpp.
374-
static std::vector<fir::KindTy>
375-
fromDefaultKinds(const Fortran::common::IntrinsicTypeDefaultKinds &defKinds) {
376-
return {static_cast<fir::KindTy>(defKinds.GetDefaultKind(
377-
Fortran::common::TypeCategory::Character)),
378-
static_cast<fir::KindTy>(
379-
defKinds.GetDefaultKind(Fortran::common::TypeCategory::Complex)),
380-
static_cast<fir::KindTy>(defKinds.doublePrecisionKind()),
381-
static_cast<fir::KindTy>(
382-
defKinds.GetDefaultKind(Fortran::common::TypeCategory::Integer)),
383-
static_cast<fir::KindTy>(
384-
defKinds.GetDefaultKind(Fortran::common::TypeCategory::Logical)),
385-
static_cast<fir::KindTy>(
386-
defKinds.GetDefaultKind(Fortran::common::TypeCategory::Real))};
387-
}
388-
389414
void EmitMLIRAction::ExecuteAction() {
390415
CompilerInstance &ci = this->instance();
391416

392-
// Load the MLIR dialects required by Flang
393-
mlir::DialectRegistry registry;
394-
mlir::MLIRContext ctx(registry);
395-
fir::support::registerNonCodegenDialects(registry);
396-
fir::support::loadNonCodegenDialects(ctx);
397-
398-
// Create a LoweringBridge
399-
auto &defKinds = ci.invocation().semanticsContext().defaultKinds();
400-
fir::KindMapping kindMap(
401-
&ctx, llvm::ArrayRef<fir::KindTy>{fromDefaultKinds(defKinds)});
402-
auto lb = Fortran::lower::LoweringBridge::create(ctx, defKinds,
403-
ci.invocation().semanticsContext().intrinsics(), ci.parsing().allCooked(),
404-
"", kindMap);
405-
406-
// Create a parse tree and lower it to FIR
407-
auto &parseTree{*ci.parsing().parseTree()};
408-
lb.lower(parseTree, ci.invocation().semanticsContext());
409-
410-
// Run the default passes.
411-
mlir::PassManager pm(&ctx, mlir::OpPassManager::Nesting::Implicit);
412-
pm.enableVerifier(/*verifyPasses=*/true);
413-
pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
414-
mlir::ModuleOp mlirModule = lb.getModule();
415-
if (mlir::failed(pm.run(mlirModule))) {
416-
unsigned diagID =
417-
ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
418-
"verification of lowering to FIR failed");
419-
ci.diagnostics().Report(diagID);
420-
return;
421-
}
422-
423417
// Print the output. If a pre-defined output stream exists, dump the MLIR
424418
// content there.
425419
if (!ci.IsOutputStreamNull()) {
426-
mlirModule.print(ci.GetOutputStream());
420+
ci.mlirModule().print(ci.GetOutputStream());
427421
return;
428422
}
429423

@@ -437,7 +431,77 @@ void EmitMLIRAction::ExecuteAction() {
437431
return;
438432
}
439433

440-
mlirModule.print(*os);
434+
ci.mlirModule().print(*os);
435+
}
436+
437+
#include "flang/Tools/CLOptions.inc"
438+
439+
void EmitLLVMAction::ExecuteAction() {
440+
CompilerInstance &ci = this->instance();
441+
auto mlirMod = ci.mlirModule();
442+
443+
auto &ctx = ci.mlirCtx();
444+
fir::support::loadDialects(ctx);
445+
fir::support::registerLLVMTranslation(ctx);
446+
447+
// Set-up the pass manager
448+
fir::setTargetTriple(mlirMod, "native");
449+
auto &defKinds = ci.invocation().semanticsContext().defaultKinds();
450+
fir::KindMapping kindMap(&ci.mlirCtx(),
451+
llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)});
452+
fir::setKindMapping(mlirMod, kindMap);
453+
mlir::PassManager pm(&ci.mlirCtx(), mlir::OpPassManager::Nesting::Implicit);
454+
455+
pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
456+
pm.enableVerifier(/*verifyPasses=*/true);
457+
mlir::PassPipelineCLParser passPipeline("", "Compiler passes to run");
458+
459+
// Create the pass pipeline
460+
fir::createMLIRToLLVMPassPipeline(pm);
461+
462+
// Run the pass manager
463+
if (!mlir::succeeded(pm.run(mlirMod))) {
464+
unsigned diagID = ci.diagnostics().getCustomDiagID(
465+
clang::DiagnosticsEngine::Error, "Lowering to LLVM IR failed");
466+
ci.diagnostics().Report(diagID);
467+
}
468+
469+
// Translate to LLVM IR
470+
auto optName = mlirMod.getName();
471+
llvm::LLVMContext llvmCtx;
472+
auto llvmModule = (mlir::translateModuleToLLVMIR(
473+
mlirMod, llvmCtx, optName ? *optName : "FIRModule"));
474+
475+
if (!llvmModule) {
476+
unsigned diagID = ci.diagnostics().getCustomDiagID(
477+
clang::DiagnosticsEngine::Error, "failed to create the LLVM module");
478+
ci.diagnostics().Report(diagID);
479+
return;
480+
}
481+
482+
// Print the generated LLVM IR. If there is no pre-defined output stream to
483+
// print to, create an output file.
484+
std::unique_ptr<llvm::raw_ostream> os;
485+
if (ci.IsOutputStreamNull()) {
486+
// Lower from the LLVM dialect to LLVM IR
487+
os = ci.CreateDefaultOutputFile(
488+
/*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), "ll");
489+
if (!os) {
490+
unsigned diagID = ci.diagnostics().getCustomDiagID(
491+
clang::DiagnosticsEngine::Error, "failed to create the output file");
492+
ci.diagnostics().Report(diagID);
493+
return;
494+
}
495+
}
496+
497+
if (!ci.IsOutputStreamNull()) {
498+
llvmModule->print(
499+
ci.GetOutputStream(), /*AssemblyAnnotationWriter=*/nullptr);
500+
} else {
501+
llvmModule->print(*os, /*AssemblyAnnotationWriter=*/nullptr);
502+
}
503+
504+
return;
441505
}
442506

443507
void EmitObjAction::ExecuteAction() {

flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ static std::unique_ptr<FrontendAction> CreateFrontendBaseAction(
3535
return std::make_unique<ParseSyntaxOnlyAction>();
3636
case EmitMLIR:
3737
return std::make_unique<EmitMLIRAction>();
38+
case EmitLLVM:
39+
return std::make_unique<EmitLLVMAction>();
3840
case EmitObj:
3941
return std::make_unique<EmitObjAction>();
4042
case DebugUnparse:

flang/test/Driver/driver-help.f90

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
! HELP-FC1-NEXT:OPTIONS:
6666
! HELP-FC1-NEXT: -cpp Enable predefined and command line preprocessor macros
6767
! HELP-FC1-NEXT: -D <macro>=<value> Define <macro> to <value> (or 1 if <value> omitted)
68+
! HELP-FC1-NEXT: -emit-llvm Use the LLVM representation for assembler and object files
6869
! HELP-FC1-NEXT: -emit-mlir Build the parse tree, then lower it to MLIR and dump
6970
! HELP-FC1-NEXT: -emit-obj Emit native object files
7071
! HELP-FC1-NEXT: -E Only run the preprocessor

0 commit comments

Comments
 (0)