[flang] Initial lowering for empty program
This patch enable lowering from Fortran to FIR for a basic empty
program. It brings all the infrastructure needed for that. As discussed
previously, this is the first patch for lowering and follow up patches
should be smaller.
With this patch we can lower the following code:
```
program basic
end program
```
To a the FIR equivalent:
```
func @_QQmain() {
return
}
```
Follow up patch will add lowering of more complex constructs.
Reviewed By: kiranchandramohan, schweitz, PeteSteinfeld
Differential Revision: https://reviews.llvm.org/D118436
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
new file mode 100644
index 0000000..20756b9
--- /dev/null
+++ b/flang/lib/Lower/Bridge.cpp
@@ -0,0 +1,306 @@
+//===-- Bridge.cpp -- bridge to lower to MLIR -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Lower/Bridge.h"
+#include "flang/Evaluate/tools.h"
+#include "flang/Lower/CallInterface.h"
+#include "flang/Lower/Mangler.h"
+#include "flang/Lower/PFTBuilder.h"
+#include "flang/Lower/SymbolMap.h"
+#include "flang/Lower/Todo.h"
+#include "flang/Optimizer/Support/FIRContext.h"
+#include "mlir/IR/PatternMatch.h"
+#include "mlir/Transforms/RegionUtils.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "flang-lower-bridge"
+
+static llvm::cl::opt<bool> dumpBeforeFir(
+ "fdebug-dump-pre-fir", llvm::cl::init(false),
+ llvm::cl::desc("dump the Pre-FIR tree prior to FIR generation"));
+
+//===----------------------------------------------------------------------===//
+// FirConverter
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+/// Traverse the pre-FIR tree (PFT) to generate the FIR dialect of MLIR.
+class FirConverter : public Fortran::lower::AbstractConverter {
+public:
+ explicit FirConverter(Fortran::lower::LoweringBridge &bridge)
+ : bridge{bridge}, foldingContext{bridge.createFoldingContext()} {}
+ virtual ~FirConverter() = default;
+
+ /// Convert the PFT to FIR.
+ void run(Fortran::lower::pft::Program &pft) {
+ // Primary translation pass.
+ for (Fortran::lower::pft::Program::Units &u : pft.getUnits()) {
+ std::visit(
+ Fortran::common::visitors{
+ [&](Fortran::lower::pft::FunctionLikeUnit &f) { lowerFunc(f); },
+ [&](Fortran::lower::pft::ModuleLikeUnit &m) {},
+ [&](Fortran::lower::pft::BlockDataUnit &b) {},
+ [&](Fortran::lower::pft::CompilerDirectiveUnit &d) {
+ setCurrentPosition(
+ d.get<Fortran::parser::CompilerDirective>().source);
+ mlir::emitWarning(toLocation(),
+ "ignoring all compiler directives");
+ },
+ },
+ u);
+ }
+ }
+
+ //===--------------------------------------------------------------------===//
+ // AbstractConverter overrides
+ //===--------------------------------------------------------------------===//
+
+ mlir::Value getSymbolAddress(Fortran::lower::SymbolRef sym) override final {
+ return lookupSymbol(sym).getAddr();
+ }
+
+ mlir::Value genExprAddr(const Fortran::lower::SomeExpr &expr,
+ mlir::Location *loc = nullptr) override final {
+ TODO_NOLOC("Not implemented. Needed for more complex expression lowering");
+ }
+ mlir::Value genExprValue(const Fortran::lower::SomeExpr &expr,
+ mlir::Location *loc = nullptr) override final {
+ TODO_NOLOC("Not implemented. Needed for more complex expression lowering");
+ }
+
+ Fortran::evaluate::FoldingContext &getFoldingContext() override final {
+ return foldingContext;
+ }
+
+ mlir::Type genType(const Fortran::evaluate::DataRef &) override final {
+ TODO_NOLOC("Not implemented. Needed for more complex expression lowering");
+ }
+ mlir::Type genType(const Fortran::lower::SomeExpr &) override final {
+ TODO_NOLOC("Not implemented. Needed for more complex expression lowering");
+ }
+ mlir::Type genType(Fortran::lower::SymbolRef) override final {
+ TODO_NOLOC("Not implemented. Needed for more complex expression lowering");
+ }
+ mlir::Type genType(Fortran::common::TypeCategory tc) override final {
+ TODO_NOLOC("Not implemented. Needed for more complex expression lowering");
+ }
+ mlir::Type genType(Fortran::common::TypeCategory tc,
+ int kind) override final {
+ TODO_NOLOC("Not implemented. Needed for more complex expression lowering");
+ }
+ mlir::Type genType(const Fortran::lower::pft::Variable &) override final {
+ TODO_NOLOC("Not implemented. Needed for more complex expression lowering");
+ }
+
+ void setCurrentPosition(const Fortran::parser::CharBlock &position) {
+ if (position != Fortran::parser::CharBlock{})
+ currentPosition = position;
+ }
+
+ //===--------------------------------------------------------------------===//
+ // Utility methods
+ //===--------------------------------------------------------------------===//
+
+ /// Convert a parser CharBlock to a Location
+ mlir::Location toLocation(const Fortran::parser::CharBlock &cb) {
+ return genLocation(cb);
+ }
+
+ mlir::Location toLocation() { return toLocation(currentPosition); }
+ void setCurrentEval(Fortran::lower::pft::Evaluation &eval) {
+ evalPtr = &eval;
+ }
+
+ mlir::Location getCurrentLocation() override final { return toLocation(); }
+
+ /// Generate a dummy location.
+ mlir::Location genUnknownLocation() override final {
+ // Note: builder may not be instantiated yet
+ return mlir::UnknownLoc::get(&getMLIRContext());
+ }
+
+ /// Generate a `Location` from the `CharBlock`.
+ mlir::Location
+ genLocation(const Fortran::parser::CharBlock &block) override final {
+ if (const Fortran::parser::AllCookedSources *cooked =
+ bridge.getCookedSource()) {
+ if (std::optional<std::pair<Fortran::parser::SourcePosition,
+ Fortran::parser::SourcePosition>>
+ loc = cooked->GetSourcePositionRange(block)) {
+ // loc is a pair (begin, end); use the beginning position
+ Fortran::parser::SourcePosition &filePos = loc->first;
+ return mlir::FileLineColLoc::get(&getMLIRContext(), filePos.file.path(),
+ filePos.line, filePos.column);
+ }
+ }
+ return genUnknownLocation();
+ }
+
+ fir::FirOpBuilder &getFirOpBuilder() override final { return *builder; }
+
+ mlir::ModuleOp &getModuleOp() override final { return bridge.getModule(); }
+
+ mlir::MLIRContext &getMLIRContext() override final {
+ return bridge.getMLIRContext();
+ }
+ std::string
+ mangleName(const Fortran::semantics::Symbol &symbol) override final {
+ return Fortran::lower::mangle::mangleName(symbol);
+ }
+
+ const fir::KindMapping &getKindMap() override final {
+ return bridge.getKindMap();
+ }
+
+ /// Return the predicate: "current block does not have a terminator branch".
+ bool blockIsUnterminated() {
+ mlir::Block *currentBlock = builder->getBlock();
+ return currentBlock->empty() ||
+ !currentBlock->back().hasTrait<mlir::OpTrait::IsTerminator>();
+ }
+
+ /// Emit return and cleanup after the function has been translated.
+ void endNewFunction(Fortran::lower::pft::FunctionLikeUnit &funit) {
+ setCurrentPosition(Fortran::lower::pft::stmtSourceLoc(funit.endStmt));
+ if (funit.isMainProgram())
+ genExitRoutine();
+ funit.finalBlock = nullptr;
+ LLVM_DEBUG(llvm::dbgs() << "*** Lowering result:\n\n"
+ << *builder->getFunction() << '\n');
+ delete builder;
+ builder = nullptr;
+ localSymbols.clear();
+ }
+
+ /// Prepare to translate a new function
+ void startNewFunction(Fortran::lower::pft::FunctionLikeUnit &funit) {
+ assert(!builder && "expected nullptr");
+ Fortran::lower::CalleeInterface callee(funit, *this);
+ mlir::FuncOp func = callee.addEntryBlockAndMapArguments();
+ func.setVisibility(mlir::SymbolTable::Visibility::Public);
+ builder = new fir::FirOpBuilder(func, bridge.getKindMap());
+ assert(builder && "FirOpBuilder did not instantiate");
+ builder->setInsertionPointToStart(&func.front());
+ }
+
+ /// Lower a procedure (nest).
+ void lowerFunc(Fortran::lower::pft::FunctionLikeUnit &funit) {
+ setCurrentPosition(funit.getStartingSourceLoc());
+ for (int entryIndex = 0, last = funit.entryPointList.size();
+ entryIndex < last; ++entryIndex) {
+ funit.setActiveEntry(entryIndex);
+ startNewFunction(funit); // the entry point for lowering this procedure
+ endNewFunction(funit);
+ }
+ funit.setActiveEntry(0);
+ for (Fortran::lower::pft::FunctionLikeUnit &f : funit.nestedFunctions)
+ lowerFunc(f); // internal procedure
+ }
+
+private:
+ FirConverter() = delete;
+ FirConverter(const FirConverter &) = delete;
+ FirConverter &operator=(const FirConverter &) = delete;
+
+ //===--------------------------------------------------------------------===//
+ // Helper member functions
+ //===--------------------------------------------------------------------===//
+
+ /// Find the symbol in the local map or return null.
+ Fortran::lower::SymbolBox
+ lookupSymbol(const Fortran::semantics::Symbol &sym) {
+ if (Fortran::lower::SymbolBox v = localSymbols.lookupSymbol(sym))
+ return v;
+ return {};
+ }
+
+ //===--------------------------------------------------------------------===//
+ // Termination of symbolically referenced execution units
+ //===--------------------------------------------------------------------===//
+
+ /// END of program
+ ///
+ /// Generate the cleanup block before the program exits
+ void genExitRoutine() {
+ if (blockIsUnterminated())
+ builder->create<mlir::ReturnOp>(toLocation());
+ }
+ void genFIR(const Fortran::parser::EndProgramStmt &) { genExitRoutine(); }
+
+ //===--------------------------------------------------------------------===//
+
+ Fortran::lower::LoweringBridge &bridge;
+ Fortran::evaluate::FoldingContext foldingContext;
+ fir::FirOpBuilder *builder = nullptr;
+ Fortran::lower::pft::Evaluation *evalPtr = nullptr;
+ Fortran::lower::SymMap localSymbols;
+ Fortran::parser::CharBlock currentPosition;
+};
+
+} // namespace
+
+Fortran::evaluate::FoldingContext
+Fortran::lower::LoweringBridge::createFoldingContext() const {
+ return {getDefaultKinds(), getIntrinsicTable()};
+}
+
+void Fortran::lower::LoweringBridge::lower(
+ const Fortran::parser::Program &prg,
+ const Fortran::semantics::SemanticsContext &semanticsContext) {
+ std::unique_ptr<Fortran::lower::pft::Program> pft =
+ Fortran::lower::createPFT(prg, semanticsContext);
+ if (dumpBeforeFir)
+ Fortran::lower::dumpPFT(llvm::errs(), *pft);
+ FirConverter converter{*this};
+ converter.run(*pft);
+}
+
+Fortran::lower::LoweringBridge::LoweringBridge(
+ mlir::MLIRContext &context,
+ const Fortran::common::IntrinsicTypeDefaultKinds &defaultKinds,
+ const Fortran::evaluate::IntrinsicProcTable &intrinsics,
+ const Fortran::parser::AllCookedSources &cooked, llvm::StringRef triple,
+ fir::KindMapping &kindMap)
+ : defaultKinds{defaultKinds}, intrinsics{intrinsics}, cooked{&cooked},
+ context{context}, kindMap{kindMap} {
+ // Register the diagnostic handler.
+ context.getDiagEngine().registerHandler([](mlir::Diagnostic &diag) {
+ llvm::raw_ostream &os = llvm::errs();
+ switch (diag.getSeverity()) {
+ case mlir::DiagnosticSeverity::Error:
+ os << "error: ";
+ break;
+ case mlir::DiagnosticSeverity::Remark:
+ os << "info: ";
+ break;
+ case mlir::DiagnosticSeverity::Warning:
+ os << "warning: ";
+ break;
+ default:
+ break;
+ }
+ if (!diag.getLocation().isa<UnknownLoc>())
+ os << diag.getLocation() << ": ";
+ os << diag << '\n';
+ os.flush();
+ return mlir::success();
+ });
+
+ // Create the module and attach the attributes.
+ module = std::make_unique<mlir::ModuleOp>(
+ mlir::ModuleOp::create(mlir::UnknownLoc::get(&context)));
+ assert(module.get() && "module was not created");
+ fir::setTargetTriple(*module.get(), triple);
+ fir::setKindMapping(*module.get(), kindMap);
+}