diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -1,8 +1,12 @@ #include "flang/Lower/Bridge.h" #include "flang/Evaluate/common.h" +#include "flang/Lower/FIRBuilder.h" +#include "flang/Lower/Mangler.h" #include "flang/Lower/PFTBuilder.h" +#include "flang/Optimizer/Support/InternalNames.h" #include "flang/Semantics/symbol.h" #include "llvm/Support/CommandLine.h" +#include static llvm::cl::opt dumpBeforeFir( "fdebug-dump-pre-fir", llvm::cl::init(false), @@ -29,25 +33,27 @@ void run(Fortran::lower::pft::Program &pft) { // do translation for (auto &u : pft.getUnits()) { - std::visit(Fortran::common::visitors{ - [&](Fortran::lower::pft::FunctionLikeUnit &f) { - mlir::emitError(toLocation(), "FUNCTION not handled"); - llvm::report_fatal_error("not yet implemented"); - }, - [&](Fortran::lower::pft::ModuleLikeUnit &m) { - mlir::emitError(toLocation(), "MODULE not handled"); - llvm::report_fatal_error("not yet implemented"); - }, - [&](Fortran::lower::pft::BlockDataUnit &) { - mlir::emitError(toLocation(), "BLOCK DATA not handled"); - llvm::report_fatal_error("not yet implemented"); - }, - }, - u); + std::visit( + Fortran::common::visitors{ + [&](Fortran::lower::pft::FunctionLikeUnit &f) { lowerFunc(f); }, + [&](Fortran::lower::pft::ModuleLikeUnit &m) { + mlir::emitError(toLocation(), "MODULE not handled"); + llvm::report_fatal_error("not yet implemented"); + }, + [&](Fortran::lower::pft::BlockDataUnit &) { + mlir::emitError(toLocation(), "BLOCK DATA not handled"); + llvm::report_fatal_error("not yet implemented"); + }, + }, + u); } } -private: + /// Generate a dummy location. + mlir::Location genLocation() { + // 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) { if (const auto *cooked = bridge.getCookedSource()) { @@ -63,11 +69,94 @@ } mlir::MLIRContext &getMLIRContext() { return bridge.getMLIRContext(); } + mlir::ModuleOp &getModuleOp() { return bridge.getModule(); } - /// Generate a dummy location. - mlir::Location genLocation() { - // Note: builder may not be instantiated yet - return mlir::UnknownLoc::get(&getMLIRContext()); + std::string mangleName(const Fortran::semantics::Symbol &symbol) { + return Fortran::lower::mangle::mangleName(uniquer, symbol); + } + +private: + /// END of program + /// + /// Generate the cleanup block before the program exits + void genExitRoutine() { builder->create(toLocation()); } + + void genFIRProcedureExit(Fortran::lower::pft::FunctionLikeUnit &funit, + const Fortran::semantics::Symbol &symbol) { + if (auto *finalBlock = funit.finalBlock) { + // The current block must end with a terminator. + if (blockIsUnterminated()) + builder->create(toLocation(), finalBlock); + // Set insertion point to final block. + builder->setInsertionPoint(finalBlock, finalBlock->end()); + } + genExitRoutine(); + } + + /// Prepare to translate a new function + void startNewFunction(Fortran::lower::pft::FunctionLikeUnit &funit) { + assert(!builder && "expected nullptr"); + auto name = funit.isMainProgram() ? fir::NameUniquer::doProgramEntry().str() + : mangleName(funit.getSubprogramSymbol()); + auto module = getModuleOp(); + mlir::FuncOp func = + Fortran::lower::FirOpBuilder::getNamedFunction(module, name); + if (!func) { + mlir::Location loc = genLocation(funit.getStartingSourceLoc()); + mlir::FunctionType ty = + mlir::FunctionType::get({}, {}, &getMLIRContext()); + func = + Fortran::lower::FirOpBuilder::createFunction(loc, module, name, ty); + } + builder = std::make_unique( + func, bridge.getKindMap()); + assert(builder && "FirOpBuilder did not instantiate"); + builder->createBlock(&func.front()); + + assert(funit.getOrderedSymbolTable().empty()); + assert(funit.evaluationList.empty()); + + assert(funit.getOrderedSymbolTable().empty()); + } + + /// Return the predicate: "current block does not have a terminator branch". + bool blockIsUnterminated() { + auto *currentBlock = builder->getBlock(); + return currentBlock->empty() || currentBlock->back().isKnownNonTerminator(); + } + + /// Emit return and cleanup after the function has been translated. + void endNewFunction(Fortran::lower::pft::FunctionLikeUnit &funit) { + setCurrentPosition( + Fortran::lower::pft::FunctionLikeUnit::stmtSourceLoc(funit.endStmt)); + + if (funit.isMainProgram()) + genExitRoutine(); + else + genFIRProcedureExit(funit, funit.getSubprogramSymbol()); + + builder.reset(); + } + + /// Lower a procedure-like construct + void lowerFunc(Fortran::lower::pft::FunctionLikeUnit &funit) { + startNewFunction(funit); + // lower this procedure + if (!funit.evaluationList.empty()) { + mlir::emitError(toLocation(), "only empty functions are handled"); + llvm::report_fatal_error("not yet implemented"); + } + endNewFunction(funit); + // recursively lower internal procedures + if (!funit.nestedFunctions.empty()) { + mlir::emitError(toLocation(), "only empty functions are handled"); + llvm::report_fatal_error("not yet implemented"); + } + } + + void setCurrentPosition(const Fortran::parser::CharBlock &position) { + if (position != Fortran::parser::CharBlock{}) + currentPosition = position; } /// Convert a parser CharBlock to a Location @@ -80,6 +169,7 @@ [[maybe_unused]] Fortran::lower::LoweringBridge &bridge; [[maybe_unused]] fir::NameUniquer &uniquer; Fortran::evaluate::FoldingContext foldingContext; + std::unique_ptr builder; Fortran::parser::CharBlock currentPosition; }; } // namespace diff --git a/flang/test/Lower/empty-program.f90 b/flang/test/Lower/empty-program.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/empty-program.f90 @@ -0,0 +1,10 @@ +!RUN: bbc %s -o - | FileCheck %s --check-prefix CHECK-FIR +!RUN: bbc %s -o %t | tco %t | FileCheck %s --check-prefix CHECK-LLVM +program test +end program test +!CHECK-FIR-LABEL: func @_QQmain() { +!CHECK-FIR-NEXT: return +!CHECK-FIR-NEXT: } +!CHECK-LLVM-LABEL: define void @_QQmain() !dbg ![[#]] { +!CHECK-LLVM-NEXT: ret void +!CHECK-LLVM-NEXT: } diff --git a/flang/test/Lower/empty-subroutines.f90 b/flang/test/Lower/empty-subroutines.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/empty-subroutines.f90 @@ -0,0 +1,20 @@ +!RUN: bbc %s -o - | FileCheck %s --check-prefix CHECK-FIR +!RUN: bbc %s -o %t | tco %t | FileCheck %s --check-prefix CHECK-LLVM + +subroutine test +end subroutine test +!CHECK-FIR-LABEL: func @_QPtest() { +!CHECK-FIR-NEXT: return +!CHECK-FIR-NEXT: } +!CHECK-LLVM-LABEL: define void @_QPtest() !dbg ![[#]] { +!CHECK-LLVM-NEXT: ret void +!CHECK-LLVM-NEXT: } + +subroutine test2 +end subroutine test2 +!CHECK-FIR-LABEL: func @_QPtest2() { +!CHECK-FIR-NEXT: return +!CHECK-FIR-NEXT: } +!CHECK-LLVM-LABEL: define void @_QPtest2() !dbg ![[#]] { +!CHECK-LLVM-NEXT: ret void +!CHECK-LLVM-NEXT: }