//===-- 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 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().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> 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(); } /// 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(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 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()) os << diag.getLocation() << ": "; os << diag << '\n'; os.flush(); return mlir::success(); }); // Create the module and attach the attributes. module = std::make_unique( mlir::ModuleOp::create(mlir::UnknownLoc::get(&context))); assert(module.get() && "module was not created"); fir::setTargetTriple(*module.get(), triple); fir::setKindMapping(*module.get(), kindMap); }