forked from OSchip/llvm-project
698 lines
25 KiB
C++
698 lines
25 KiB
C++
//===-- 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 <algorithm>
|
|
#include <cassert>
|
|
#include <utility>
|
|
|
|
namespace Fortran::lower {
|
|
namespace {
|
|
|
|
/// Helpers to unveil parser node inside parser::Statement<>,
|
|
/// parser::UnlabeledStatement, and common::Indirection<>
|
|
template <typename A>
|
|
struct RemoveIndirectionHelper {
|
|
using Type = A;
|
|
static constexpr const Type &unwrap(const A &a) { return a; }
|
|
};
|
|
template <typename A>
|
|
struct RemoveIndirectionHelper<common::Indirection<A>> {
|
|
using Type = A;
|
|
static constexpr const Type &unwrap(const common::Indirection<A> &a) {
|
|
return a.value();
|
|
}
|
|
};
|
|
|
|
template <typename A>
|
|
const auto &removeIndirection(const A &a) {
|
|
return RemoveIndirectionHelper<A>::unwrap(a);
|
|
}
|
|
|
|
template <typename A>
|
|
struct UnwrapStmt {
|
|
static constexpr bool isStmt{false};
|
|
};
|
|
template <typename A>
|
|
struct UnwrapStmt<parser::Statement<A>> {
|
|
static constexpr bool isStmt{true};
|
|
using Type = typename RemoveIndirectionHelper<A>::Type;
|
|
constexpr UnwrapStmt(const parser::Statement<A> &a)
|
|
: unwrapped{removeIndirection(a.statement)}, pos{a.source}, lab{a.label} {
|
|
}
|
|
const Type &unwrapped;
|
|
parser::CharBlock pos;
|
|
std::optional<parser::Label> lab;
|
|
};
|
|
template <typename A>
|
|
struct UnwrapStmt<parser::UnlabeledStatement<A>> {
|
|
static constexpr bool isStmt{true};
|
|
using Type = typename RemoveIndirectionHelper<A>::Type;
|
|
constexpr UnwrapStmt(const parser::UnlabeledStatement<A> &a)
|
|
: unwrapped{removeIndirection(a.statement)}, pos{a.source} {}
|
|
const Type &unwrapped;
|
|
parser::CharBlock pos;
|
|
std::optional<parser::Label> 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<pft::Program> result() { return std::move(pgm); }
|
|
|
|
template <typename A>
|
|
constexpr bool Pre(const A &a) {
|
|
bool visit{true};
|
|
if constexpr (pft::isFunctionLike<A>) {
|
|
return enterFunc(a);
|
|
} else if constexpr (pft::isConstruct<A>) {
|
|
return enterConstruct(a);
|
|
} else if constexpr (UnwrapStmt<A>::isStmt) {
|
|
using T = typename UnwrapStmt<A>::Type;
|
|
// Node "a" being visited has one of the following types:
|
|
// Statement<T>, Statement<Indirection<T>, UnlabeledStatement<T>,
|
|
// or UnlabeledStatement<Indirection<T>>
|
|
auto stmt{UnwrapStmt<A>(a)};
|
|
if constexpr (pft::isConstructStmt<T> || pft::isOtherStmt<T>) {
|
|
addEval(pft::Evaluation{stmt.unwrapped, parents.back(), stmt.pos,
|
|
stmt.lab});
|
|
visit = false;
|
|
} else if constexpr (std::is_same_v<T, parser::ActionStmt>) {
|
|
addEval(makeEvalAction(stmt.unwrapped, stmt.pos, stmt.lab));
|
|
visit = false;
|
|
}
|
|
}
|
|
return visit;
|
|
}
|
|
|
|
template <typename A>
|
|
constexpr void Post(const A &) {
|
|
if constexpr (pft::isFunctionLike<A>) {
|
|
exitFunc();
|
|
} else if constexpr (pft::isConstruct<A>) {
|
|
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<parser::ForallAssignmentStmt>
|
|
&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<parser::ForallAssignmentStmt> &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<parser::AssignmentStmt> &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<parser::Label> 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 <typename A>
|
|
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 <typename A>
|
|
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 <typename A>
|
|
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 <typename A>
|
|
A &addUnit(A &&unit) {
|
|
pgm->getUnits().emplace_back(std::move(unit));
|
|
return std::get<A>(pgm->getUnits().back());
|
|
}
|
|
|
|
template <typename A>
|
|
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<pft::Program> 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<pft::FunctionLikeUnit> *funclist{nullptr};
|
|
/// evallist is a stack of pointer to FunctionLikeUnit::evals (or
|
|
/// Evaluation::subs) that are being build.
|
|
std::vector<pft::EvaluationCollection *> evallist;
|
|
std::vector<pft::ParentType> parents;
|
|
};
|
|
|
|
template <typename Label, typename A>
|
|
constexpr bool hasLabel(const A &stmt) {
|
|
auto isLabel{
|
|
[](const auto &v) { return std::holds_alternative<Label>(v.u); }};
|
|
if constexpr (std::is_same_v<A, parser::ReadStmt> ||
|
|
std::is_same_v<A, parser::WriteStmt>) {
|
|
return std::any_of(std::begin(stmt.controls), std::end(stmt.controls),
|
|
isLabel);
|
|
}
|
|
if constexpr (std::is_same_v<A, parser::WaitStmt>) {
|
|
return std::any_of(std::begin(stmt.v), std::end(stmt.v), isLabel);
|
|
}
|
|
if constexpr (std::is_same_v<Label, parser::ErrLabel>) {
|
|
if constexpr (common::HasMember<
|
|
A, std::tuple<parser::OpenStmt, parser::CloseStmt,
|
|
parser::BackspaceStmt, parser::EndfileStmt,
|
|
parser::RewindStmt, parser::FlushStmt>>)
|
|
return std::any_of(std::begin(stmt.v), std::end(stmt.v), isLabel);
|
|
if constexpr (std::is_same_v<A, parser::InquireStmt>) {
|
|
const auto &specifiers{std::get<std::list<parser::InquireSpec>>(stmt.u)};
|
|
return std::any_of(std::begin(specifiers), std::end(specifiers), isLabel);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool hasAltReturns(const parser::CallStmt &callStmt) {
|
|
const auto &args{std::get<std::list<parser::ActualArgSpec>>(callStmt.v.t)};
|
|
for (const auto &arg : args) {
|
|
const auto &actual{std::get<parser::ActualArg>(arg.t)};
|
|
if (std::holds_alternative<parser::AltReturnSpec>(actual.u))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Determine if `callStmt` has alternate returns and if so set `e` to be the
|
|
/// origin of a switch-like control flow
|
|
///
|
|
/// \param cstr points to the current construct. It may be null at the top-level
|
|
/// of a FunctionLikeUnit.
|
|
void altRet(pft::Evaluation &evaluation, const parser::CallStmt &callStmt,
|
|
pft::Evaluation *cstr) {
|
|
if (hasAltReturns(callStmt))
|
|
evaluation.setCFG(pft::CFGAnnotation::Switch, cstr);
|
|
}
|
|
|
|
/// \param cstr points to the current construct. It may be null at the top-level
|
|
/// of a FunctionLikeUnit.
|
|
void annotateEvalListCFG(pft::EvaluationCollection &evaluationCollection,
|
|
pft::Evaluation *cstr) {
|
|
bool nextIsTarget = false;
|
|
for (auto &eval : evaluationCollection) {
|
|
eval.isTarget = nextIsTarget;
|
|
nextIsTarget = false;
|
|
if (auto *subs{eval.getConstructEvals()}) {
|
|
annotateEvalListCFG(*subs, &eval);
|
|
// assume that the entry and exit are both possible branch targets
|
|
nextIsTarget = true;
|
|
}
|
|
|
|
if (eval.isActionOrGenerated() && eval.lab.has_value())
|
|
eval.isTarget = true;
|
|
eval.visit(common::visitors{
|
|
[&](const parser::CallStmt &statement) {
|
|
altRet(eval, statement, cstr);
|
|
},
|
|
[&](const parser::CycleStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::Goto, cstr);
|
|
},
|
|
[&](const parser::ExitStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::Goto, cstr);
|
|
},
|
|
[&](const parser::FailImageStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::Terminate, cstr);
|
|
},
|
|
[&](const parser::GotoStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::Goto, cstr);
|
|
},
|
|
[&](const parser::IfStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::CondGoto, cstr);
|
|
},
|
|
[&](const parser::ReturnStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::Return, cstr);
|
|
},
|
|
[&](const parser::StopStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::Terminate, cstr);
|
|
},
|
|
[&](const parser::ArithmeticIfStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::Switch, cstr);
|
|
},
|
|
[&](const parser::AssignedGotoStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::IndGoto, cstr);
|
|
},
|
|
[&](const parser::ComputedGotoStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::Switch, cstr);
|
|
},
|
|
[&](const parser::WhereStmt &) {
|
|
// fir.loop + fir.where around the next stmt
|
|
eval.isTarget = true;
|
|
eval.setCFG(pft::CFGAnnotation::Iterative, cstr);
|
|
},
|
|
[&](const parser::ForallStmt &) {
|
|
// fir.loop around the next stmt
|
|
eval.isTarget = true;
|
|
eval.setCFG(pft::CFGAnnotation::Iterative, cstr);
|
|
},
|
|
[&](pft::CGJump &) { eval.setCFG(pft::CFGAnnotation::Goto, cstr); },
|
|
[&](const parser::SelectCaseStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::Switch, cstr);
|
|
},
|
|
[&](const parser::NonLabelDoStmt &) {
|
|
eval.isTarget = true;
|
|
eval.setCFG(pft::CFGAnnotation::Iterative, cstr);
|
|
},
|
|
[&](const parser::EndDoStmt &) {
|
|
eval.isTarget = true;
|
|
eval.setCFG(pft::CFGAnnotation::Goto, cstr);
|
|
},
|
|
[&](const parser::IfThenStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::CondGoto, cstr);
|
|
},
|
|
[&](const parser::ElseIfStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::CondGoto, cstr);
|
|
},
|
|
[&](const parser::SelectRankStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::Switch, cstr);
|
|
},
|
|
[&](const parser::SelectTypeStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::Switch, cstr);
|
|
},
|
|
[&](const parser::WhereConstruct &) {
|
|
// mark the WHERE as if it were a DO loop
|
|
eval.isTarget = true;
|
|
eval.setCFG(pft::CFGAnnotation::Iterative, cstr);
|
|
},
|
|
[&](const parser::WhereConstructStmt &) {
|
|
eval.setCFG(pft::CFGAnnotation::CondGoto, cstr);
|
|
},
|
|
[&](const parser::MaskedElsewhereStmt &) {
|
|
eval.isTarget = true;
|
|
eval.setCFG(pft::CFGAnnotation::CondGoto, cstr);
|
|
},
|
|
[&](const parser::ForallConstructStmt &) {
|
|
eval.isTarget = true;
|
|
eval.setCFG(pft::CFGAnnotation::Iterative, cstr);
|
|
},
|
|
|
|
[&](const auto &stmt) {
|
|
// Handle statements with similar impact on control flow
|
|
using IoStmts = std::tuple<parser::BackspaceStmt, parser::CloseStmt,
|
|
parser::EndfileStmt, parser::FlushStmt,
|
|
parser::InquireStmt, parser::OpenStmt,
|
|
parser::ReadStmt, parser::RewindStmt,
|
|
parser::WaitStmt, parser::WriteStmt>;
|
|
|
|
using TargetStmts =
|
|
std::tuple<parser::EndAssociateStmt, parser::EndBlockStmt,
|
|
parser::CaseStmt, parser::EndSelectStmt,
|
|
parser::EndChangeTeamStmt, parser::EndCriticalStmt,
|
|
parser::ElseStmt, parser::EndIfStmt,
|
|
parser::SelectRankCaseStmt, parser::TypeGuardStmt,
|
|
parser::ElsewhereStmt, parser::EndWhereStmt,
|
|
parser::EndForallStmt>;
|
|
|
|
using DoNothingConstructStmts =
|
|
std::tuple<parser::BlockStmt, parser::AssociateStmt,
|
|
parser::CriticalStmt, parser::ChangeTeamStmt>;
|
|
|
|
using A = std::decay_t<decltype(stmt)>;
|
|
if constexpr (common::HasMember<A, IoStmts>) {
|
|
if (hasLabel<parser::ErrLabel>(stmt) ||
|
|
hasLabel<parser::EorLabel>(stmt) ||
|
|
hasLabel<parser::EndLabel>(stmt))
|
|
eval.setCFG(pft::CFGAnnotation::IoSwitch, cstr);
|
|
} else if constexpr (common::HasMember<A, TargetStmts>) {
|
|
eval.isTarget = true;
|
|
} else if constexpr (common::HasMember<A, DoNothingConstructStmts>) {
|
|
// Explicitly do nothing for these construct statements
|
|
} else {
|
|
static_assert(!pft::isConstructStmt<A>,
|
|
"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 << "<<End" << name << ">>\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<parser::ProgramStmt> *statement) {
|
|
unitKind = "Program";
|
|
name = statement->statement.v.ToString();
|
|
},
|
|
[&](const parser::Statement<parser::FunctionStmt> *statement) {
|
|
unitKind = "Function";
|
|
name =
|
|
std::get<parser::Name>(statement->statement.t).ToString();
|
|
header = statement->source.ToString();
|
|
},
|
|
[&](const parser::Statement<parser::SubroutineStmt> *statement) {
|
|
unitKind = "Subroutine";
|
|
name =
|
|
std::get<parser::Name>(statement->statement.t).ToString();
|
|
header = statement->source.ToString();
|
|
},
|
|
[&](const parser::Statement<parser::MpSubprogramStmt>
|
|
*statement) {
|
|
unitKind = "MpSubprogram";
|
|
name = statement->statement.v.ToString();
|
|
header = statement->source.ToString();
|
|
},
|
|
[&](auto *) {},
|
|
},
|
|
*functionLikeUnit.beginStmt);
|
|
} else {
|
|
unitKind = "Program";
|
|
name = "<anonymous>";
|
|
}
|
|
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 <typename T>
|
|
std::size_t getNodeIndex(const T &node) {
|
|
auto addr{static_cast<const void *>(&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<const void *, std::size_t> nodeIndexes;
|
|
std::size_t nextIndex{1}; // 0 is the root
|
|
};
|
|
|
|
template <typename A, typename T>
|
|
pft::FunctionLikeUnit::FunctionStatement getFunctionStmt(const T &func) {
|
|
return pft::FunctionLikeUnit::FunctionStatement{
|
|
&std::get<parser::Statement<A>>(func.t)};
|
|
}
|
|
template <typename A, typename T>
|
|
pft::ModuleLikeUnit::ModuleStatement getModuleStmt(const T &mod) {
|
|
return pft::ModuleLikeUnit::ModuleStatement{
|
|
&std::get<parser::Statement<A>>(mod.t)};
|
|
}
|
|
|
|
} // namespace
|
|
|
|
pft::FunctionLikeUnit::FunctionLikeUnit(const parser::MainProgram &func,
|
|
const pft::ParentType &parent)
|
|
: ProgramUnit{func, parent} {
|
|
auto &ps{
|
|
std::get<std::optional<parser::Statement<parser::ProgramStmt>>>(func.t)};
|
|
if (ps.has_value()) {
|
|
const parser::Statement<parser::ProgramStmt> &statement{ps.value()};
|
|
beginStmt = &statement;
|
|
}
|
|
endStmt = getFunctionStmt<parser::EndProgramStmt>(func);
|
|
}
|
|
|
|
pft::FunctionLikeUnit::FunctionLikeUnit(const parser::FunctionSubprogram &func,
|
|
const pft::ParentType &parent)
|
|
: ProgramUnit{func, parent},
|
|
beginStmt{getFunctionStmt<parser::FunctionStmt>(func)},
|
|
endStmt{getFunctionStmt<parser::EndFunctionStmt>(func)} {}
|
|
|
|
pft::FunctionLikeUnit::FunctionLikeUnit(
|
|
const parser::SubroutineSubprogram &func, const pft::ParentType &parent)
|
|
: ProgramUnit{func, parent},
|
|
beginStmt{getFunctionStmt<parser::SubroutineStmt>(func)},
|
|
endStmt{getFunctionStmt<parser::EndSubroutineStmt>(func)} {}
|
|
|
|
pft::FunctionLikeUnit::FunctionLikeUnit(
|
|
const parser::SeparateModuleSubprogram &func, const pft::ParentType &parent)
|
|
: ProgramUnit{func, parent},
|
|
beginStmt{getFunctionStmt<parser::MpSubprogramStmt>(func)},
|
|
endStmt{getFunctionStmt<parser::EndMpSubprogramStmt>(func)} {}
|
|
|
|
pft::ModuleLikeUnit::ModuleLikeUnit(const parser::Module &m,
|
|
const pft::ParentType &parent)
|
|
: ProgramUnit{m, parent}, beginStmt{getModuleStmt<parser::ModuleStmt>(m)},
|
|
endStmt{getModuleStmt<parser::EndModuleStmt>(m)} {}
|
|
|
|
pft::ModuleLikeUnit::ModuleLikeUnit(const parser::Submodule &m,
|
|
const pft::ParentType &parent)
|
|
: ProgramUnit{m, parent}, beginStmt{getModuleStmt<parser::SubmoduleStmt>(
|
|
m)},
|
|
endStmt{getModuleStmt<parser::EndSubmoduleStmt>(m)} {}
|
|
|
|
pft::BlockDataUnit::BlockDataUnit(const parser::BlockData &bd,
|
|
const pft::ParentType &parent)
|
|
: ProgramUnit{bd, parent} {}
|
|
|
|
std::unique_ptr<pft::Program> 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
|