//===-- lib/Lower/PFTBuilder.cc -------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "flang/Lower/PFTBuilder.h" #include "flang/Parser/dump-parse-tree.h" #include "flang/Parser/parse-tree-visitor.h" #include "llvm/ADT/DenseMap.h" #include #include #include namespace Fortran::lower { namespace { /// Helpers to unveil parser node inside parser::Statement<>, /// parser::UnlabeledStatement, and common::Indirection<> template struct RemoveIndirectionHelper { using Type = A; static constexpr const Type &unwrap(const A &a) { return a; } }; template struct RemoveIndirectionHelper> { using Type = A; static constexpr const Type &unwrap(const common::Indirection &a) { return a.value(); } }; template const auto &removeIndirection(const A &a) { return RemoveIndirectionHelper::unwrap(a); } template struct UnwrapStmt { static constexpr bool isStmt{false}; }; template struct UnwrapStmt> { static constexpr bool isStmt{true}; using Type = typename RemoveIndirectionHelper::Type; constexpr UnwrapStmt(const parser::Statement &a) : unwrapped{removeIndirection(a.statement)}, pos{a.source}, lab{a.label} { } const Type &unwrapped; parser::CharBlock pos; std::optional lab; }; template struct UnwrapStmt> { static constexpr bool isStmt{true}; using Type = typename RemoveIndirectionHelper::Type; constexpr UnwrapStmt(const parser::UnlabeledStatement &a) : unwrapped{removeIndirection(a.statement)}, pos{a.source} {} const Type &unwrapped; parser::CharBlock pos; std::optional lab; }; /// The instantiation of a parse tree visitor (Pre and Post) is extremely /// expensive in terms of compile and link time, so one goal here is to limit /// the bridge to one such instantiation. class PFTBuilder { public: PFTBuilder() : pgm{new pft::Program}, parents{*pgm.get()} {} /// Get the result std::unique_ptr result() { return std::move(pgm); } template constexpr bool Pre(const A &a) { bool visit{true}; if constexpr (pft::isFunctionLike) { return enterFunc(a); } else if constexpr (pft::isConstruct) { return enterConstruct(a); } else if constexpr (UnwrapStmt::isStmt) { using T = typename UnwrapStmt::Type; // Node "a" being visited has one of the following types: // Statement, Statement, UnlabeledStatement, // or UnlabeledStatement> auto stmt{UnwrapStmt(a)}; if constexpr (pft::isConstructStmt || pft::isOtherStmt) { addEval(pft::Evaluation{stmt.unwrapped, parents.back(), stmt.pos, stmt.lab}); visit = false; } else if constexpr (std::is_same_v) { addEval(makeEvalAction(stmt.unwrapped, stmt.pos, stmt.lab)); visit = false; } } return visit; } template constexpr void Post(const A &) { if constexpr (pft::isFunctionLike) { exitFunc(); } else if constexpr (pft::isConstruct) { exitConstruct(); } } // Module like bool Pre(const parser::Module &node) { return enterModule(node); } bool Pre(const parser::Submodule &node) { return enterModule(node); } void Post(const parser::Module &) { exitModule(); } void Post(const parser::Submodule &) { exitModule(); } // Block data bool Pre(const parser::BlockData &node) { addUnit(pft::BlockDataUnit{node, parents.back()}); return false; } // Get rid of production wrapper bool Pre(const parser::UnlabeledStatement &statement) { addEval(std::visit( [&](const auto &x) { return pft::Evaluation{x, parents.back(), statement.source, {}}; }, statement.statement.u)); return false; } bool Pre(const parser::Statement &statement) { addEval(std::visit( [&](const auto &x) { return pft::Evaluation{x, parents.back(), statement.source, statement.label}; }, statement.statement.u)); return false; } bool Pre(const parser::WhereBodyConstruct &whereBody) { return std::visit( common::visitors{ [&](const parser::Statement &stmt) { // Not caught as other AssignmentStmt because it is not // wrapped in a parser::ActionStmt. addEval(pft::Evaluation{stmt.statement, parents.back(), stmt.source, stmt.label}); return false; }, [&](const auto &) { return true; }, }, whereBody.u); } private: // ActionStmt has a couple of non-conforming cases, which get handled // explicitly here. The other cases use an Indirection, which we discard in // the PFT. pft::Evaluation makeEvalAction(const parser::ActionStmt &statement, parser::CharBlock pos, std::optional lab) { return std::visit( common::visitors{ [&](const auto &x) { return pft::Evaluation{removeIndirection(x), parents.back(), pos, lab}; }, }, statement.u); } // When we enter a function-like structure, we want to build a new unit and // set the builder's cursors to point to it. template bool enterFunc(const A &func) { auto &unit = addFunc(pft::FunctionLikeUnit{func, parents.back()}); funclist = &unit.funcs; pushEval(&unit.evals); parents.emplace_back(unit); return true; } /// Make funclist to point to current parent function list if it exists. void setFunctListToParentFuncs() { if (!parents.empty()) { std::visit(common::visitors{ [&](pft::FunctionLikeUnit *p) { funclist = &p->funcs; }, [&](pft::ModuleLikeUnit *p) { funclist = &p->funcs; }, [&](auto *) { funclist = nullptr; }, }, parents.back().p); } } void exitFunc() { popEval(); parents.pop_back(); setFunctListToParentFuncs(); } // When we enter a construct structure, we want to build a new construct and // set the builder's evaluation cursor to point to it. template bool enterConstruct(const A &construct) { auto &con = addEval(pft::Evaluation{construct, parents.back()}); con.subs.reset(new pft::EvaluationCollection); pushEval(con.subs.get()); parents.emplace_back(con); return true; } void exitConstruct() { popEval(); parents.pop_back(); } // When we enter a module structure, we want to build a new module and // set the builder's function cursor to point to it. template bool enterModule(const A &func) { auto &unit = addUnit(pft::ModuleLikeUnit{func, parents.back()}); funclist = &unit.funcs; parents.emplace_back(unit); return true; } void exitModule() { parents.pop_back(); setFunctListToParentFuncs(); } template A &addUnit(A &&unit) { pgm->getUnits().emplace_back(std::move(unit)); return std::get(pgm->getUnits().back()); } template A &addFunc(A &&func) { if (funclist) { funclist->emplace_back(std::move(func)); return funclist->back(); } return addUnit(std::move(func)); } /// move the Evaluation to the end of the current list pft::Evaluation &addEval(pft::Evaluation &&eval) { assert(funclist && "not in a function"); assert(evallist.size() > 0); evallist.back()->emplace_back(std::move(eval)); return evallist.back()->back(); } /// push a new list on the stack of Evaluation lists void pushEval(pft::EvaluationCollection *eval) { assert(funclist && "not in a function"); assert(eval && eval->empty() && "evaluation list isn't correct"); evallist.emplace_back(eval); } /// pop the current list and return to the last Evaluation list void popEval() { assert(funclist && "not in a function"); evallist.pop_back(); } std::unique_ptr pgm; /// funclist points to FunctionLikeUnit::funcs list (resp. /// ModuleLikeUnit::funcs) when building a FunctionLikeUnit (resp. /// ModuleLikeUnit) to store internal procedures (resp. module procedures). /// Otherwise (e.g. when building the top level Program), it is null. std::list *funclist{nullptr}; /// evallist is a stack of pointer to FunctionLikeUnit::evals (or /// Evaluation::subs) that are being build. std::vector evallist; std::vector parents; }; template constexpr bool hasLabel(const A &stmt) { auto isLabel{ [](const auto &v) { return std::holds_alternative, "All ConstructStmts impact on the control flow " "should be explicitly handled"); } /* else do nothing */ }, }); } } /// Annotate the PFT with CFG source decorations (see CFGAnnotation) and mark /// potential branch targets inline void annotateFuncCFG(pft::FunctionLikeUnit &functionLikeUnit) { annotateEvalListCFG(functionLikeUnit.evals, nullptr); for (auto &internalFunc : functionLikeUnit.funcs) annotateFuncCFG(internalFunc); } class PFTDumper { public: void dumpPFT(llvm::raw_ostream &outputStream, pft::Program &pft) { for (auto &unit : pft.getUnits()) { std::visit(common::visitors{ [&](pft::BlockDataUnit &unit) { outputStream << getNodeIndex(unit) << " "; outputStream << "BlockData: "; outputStream << "\nEndBlockData\n\n"; }, [&](pft::FunctionLikeUnit &func) { dumpFunctionLikeUnit(outputStream, func); }, [&](pft::ModuleLikeUnit &unit) { dumpModuleLikeUnit(outputStream, unit); }, }, unit); } resetIndexes(); } llvm::StringRef evalName(pft::Evaluation &eval) { return eval.visit(common::visitors{ [](const pft::CGJump) { return "CGJump"; }, [](const auto &parseTreeNode) { return parser::ParseTreeDumper::GetNodeName(parseTreeNode); }, }); } void dumpEvalList(llvm::raw_ostream &outputStream, pft::EvaluationCollection &evaluationCollection, int indent = 1) { static const std::string white{" ++"}; std::string indentString{white.substr(0, indent * 2)}; for (pft::Evaluation &eval : evaluationCollection) { outputStream << indentString << getNodeIndex(eval) << " "; llvm::StringRef name{evalName(eval)}; if (auto *subs{eval.getConstructEvals()}) { outputStream << "<<" << name << ">>"; outputStream << "\n"; dumpEvalList(outputStream, *subs, indent + 1); outputStream << indentString << "<>\n"; } else { outputStream << name; outputStream << ": " << eval.pos.ToString() + "\n"; } } } void dumpFunctionLikeUnit(llvm::raw_ostream &outputStream, pft::FunctionLikeUnit &functionLikeUnit) { outputStream << getNodeIndex(functionLikeUnit) << " "; llvm::StringRef unitKind{}; std::string name{}; std::string header{}; if (functionLikeUnit.beginStmt) { std::visit( common::visitors{ [&](const parser::Statement *statement) { unitKind = "Program"; name = statement->statement.v.ToString(); }, [&](const parser::Statement *statement) { unitKind = "Function"; name = std::get(statement->statement.t).ToString(); header = statement->source.ToString(); }, [&](const parser::Statement *statement) { unitKind = "Subroutine"; name = std::get(statement->statement.t).ToString(); header = statement->source.ToString(); }, [&](const parser::Statement *statement) { unitKind = "MpSubprogram"; name = statement->statement.v.ToString(); header = statement->source.ToString(); }, [&](auto *) {}, }, *functionLikeUnit.beginStmt); } else { unitKind = "Program"; name = ""; } outputStream << unitKind << ' ' << name; if (header.size()) outputStream << ": " << header; outputStream << '\n'; dumpEvalList(outputStream, functionLikeUnit.evals); if (!functionLikeUnit.funcs.empty()) { outputStream << "\nContains\n"; for (auto &func : functionLikeUnit.funcs) dumpFunctionLikeUnit(outputStream, func); outputStream << "EndContains\n"; } outputStream << "End" << unitKind << ' ' << name << "\n\n"; } void dumpModuleLikeUnit(llvm::raw_ostream &outputStream, pft::ModuleLikeUnit &moduleLikeUnit) { outputStream << getNodeIndex(moduleLikeUnit) << " "; outputStream << "ModuleLike: "; outputStream << "\nContains\n"; for (auto &func : moduleLikeUnit.funcs) dumpFunctionLikeUnit(outputStream, func); outputStream << "EndContains\nEndModuleLike\n\n"; } template std::size_t getNodeIndex(const T &node) { auto addr{static_cast(&node)}; auto it{nodeIndexes.find(addr)}; if (it != nodeIndexes.end()) { return it->second; } nodeIndexes.try_emplace(addr, nextIndex); return nextIndex++; } std::size_t getNodeIndex(const pft::Program &) { return 0; } void resetIndexes() { nodeIndexes.clear(); nextIndex = 1; } private: llvm::DenseMap nodeIndexes; std::size_t nextIndex{1}; // 0 is the root }; template pft::FunctionLikeUnit::FunctionStatement getFunctionStmt(const T &func) { return pft::FunctionLikeUnit::FunctionStatement{ &std::get>(func.t)}; } template pft::ModuleLikeUnit::ModuleStatement getModuleStmt(const T &mod) { return pft::ModuleLikeUnit::ModuleStatement{ &std::get>(mod.t)}; } } // namespace pft::FunctionLikeUnit::FunctionLikeUnit(const parser::MainProgram &func, const pft::ParentType &parent) : ProgramUnit{func, parent} { auto &ps{ std::get>>(func.t)}; if (ps.has_value()) { const parser::Statement &statement{ps.value()}; beginStmt = &statement; } endStmt = getFunctionStmt(func); } pft::FunctionLikeUnit::FunctionLikeUnit(const parser::FunctionSubprogram &func, const pft::ParentType &parent) : ProgramUnit{func, parent}, beginStmt{getFunctionStmt(func)}, endStmt{getFunctionStmt(func)} {} pft::FunctionLikeUnit::FunctionLikeUnit( const parser::SubroutineSubprogram &func, const pft::ParentType &parent) : ProgramUnit{func, parent}, beginStmt{getFunctionStmt(func)}, endStmt{getFunctionStmt(func)} {} pft::FunctionLikeUnit::FunctionLikeUnit( const parser::SeparateModuleSubprogram &func, const pft::ParentType &parent) : ProgramUnit{func, parent}, beginStmt{getFunctionStmt(func)}, endStmt{getFunctionStmt(func)} {} pft::ModuleLikeUnit::ModuleLikeUnit(const parser::Module &m, const pft::ParentType &parent) : ProgramUnit{m, parent}, beginStmt{getModuleStmt(m)}, endStmt{getModuleStmt(m)} {} pft::ModuleLikeUnit::ModuleLikeUnit(const parser::Submodule &m, const pft::ParentType &parent) : ProgramUnit{m, parent}, beginStmt{getModuleStmt( m)}, endStmt{getModuleStmt(m)} {} pft::BlockDataUnit::BlockDataUnit(const parser::BlockData &bd, const pft::ParentType &parent) : ProgramUnit{bd, parent} {} std::unique_ptr createPFT(const parser::Program &root) { PFTBuilder walker; Walk(root, walker); return walker.result(); } void annotateControl(pft::Program &pft) { for (auto &unit : pft.getUnits()) { std::visit(common::visitors{ [](pft::BlockDataUnit &) {}, [](pft::FunctionLikeUnit &func) { annotateFuncCFG(func); }, [](pft::ModuleLikeUnit &unit) { for (auto &func : unit.funcs) annotateFuncCFG(func); }, }, unit); } } /// Dump a PFT. void dumpPFT(llvm::raw_ostream &outputStream, pft::Program &pft) { PFTDumper{}.dumpPFT(outputStream, pft); } } // namespace Fortran::lower