llvm/flang/lib/FIR/afforestation.cc
2019-03-11 08:57:41 -07:00

2186 lines
86 KiB
C++

// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "afforestation.h"
#include "builder.h"
#include "mixin.h"
#include "../evaluate/fold.h"
#include "../evaluate/tools.h"
#include "../parser/parse-tree-visitor.h"
#include "../semantics/expression.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_ostream.h"
namespace Fortran::FIR {
namespace {
template<typename A> Expression *ExprRef(const A &a) {
return &a.typedExpr.value();
}
struct LinearOp;
using LinearLabelRef = unsigned;
constexpr LinearLabelRef unspecifiedLabel{~0u};
llvm::raw_ostream *debugChannel;
llvm::raw_ostream &DebugChannel() {
return debugChannel ? *debugChannel : llvm::errs();
}
void SetDebugChannel(llvm::raw_ostream *output) { debugChannel = output; }
struct LinearLabelBuilder {
LinearLabelBuilder() : referenced(32), counter{0u} {}
LinearLabelRef getNext() {
LinearLabelRef next{counter++};
auto cap{referenced.capacity()};
if (cap < counter) referenced.reserve(2 * cap);
referenced[next] = false;
return next;
}
void setReferenced(LinearLabelRef label) { referenced[label] = true; }
bool isReferenced(LinearLabelRef label) const { return referenced[label]; }
std::vector<bool> referenced;
unsigned counter;
};
struct LinearLabel {
explicit LinearLabel(LinearLabelBuilder &builder)
: builder_{builder}, label_{builder.getNext()} {}
LinearLabel(const LinearLabel &that)
: builder_{that.builder_}, label_{that.label_} {}
LinearLabel &operator=(const LinearLabel &that) {
CHECK(&builder_ == &that.builder_);
label_ = that.label_;
return *this;
}
void setReferenced() const { builder_.setReferenced(label_); }
bool isReferenced() const { return builder_.isReferenced(label_); }
LinearLabelRef get() const { return label_; }
operator LinearLabelRef() const { return get(); }
private:
LinearLabelBuilder &builder_;
LinearLabelRef label_;
};
struct LinearGoto {
struct LinearArtificial {};
LinearGoto(LinearLabelRef dest) : u{LinearArtificial{}}, target{dest} {}
template<typename T>
LinearGoto(const T &stmt, LinearLabelRef dest) : u{&stmt}, target{dest} {}
std::variant<const parser::CycleStmt *, const parser::ExitStmt *,
const parser::GotoStmt *, LinearArtificial>
u;
LinearLabelRef target;
};
struct LinearReturn
: public SumTypeCopyMixin<std::variant<const parser::FailImageStmt *,
const parser::ReturnStmt *, const parser::StopStmt *>> {
SUM_TYPE_COPY_MIXIN(LinearReturn)
template<typename T> LinearReturn(const T &stmt) : SumTypeCopyMixin{&stmt} {}
};
struct LinearConditionalGoto {
template<typename T>
LinearConditionalGoto(const T &cond, LinearLabelRef tb, LinearLabelRef fb)
: u{&cond}, trueLabel{tb}, falseLabel{fb} {}
std::variant<const parser::Statement<parser::IfThenStmt> *,
const parser::Statement<parser::ElseIfStmt> *, const parser::IfStmt *,
const parser::Statement<parser::NonLabelDoStmt> *>
u;
LinearLabelRef trueLabel;
LinearLabelRef falseLabel;
};
struct LinearIndirectGoto {
LinearIndirectGoto(
const semantics::Symbol *symbol, std::vector<LinearLabelRef> &&labelRefs)
: symbol{symbol}, labelRefs{labelRefs} {}
const semantics::Symbol *symbol;
std::vector<LinearLabelRef> labelRefs;
};
struct LinearSwitchingIO {
template<typename T>
LinearSwitchingIO(const T &io, LinearLabelRef next,
std::optional<LinearLabelRef> errLab,
std::optional<LinearLabelRef> eorLab = std::nullopt,
std::optional<LinearLabelRef> endLab = std::nullopt)
: u{&io}, next{next}, errLabel{errLab}, eorLabel{eorLab}, endLabel{endLab} {
}
std::variant<const parser::ReadStmt *, const parser::WriteStmt *,
const parser::WaitStmt *, const parser::OpenStmt *,
const parser::CloseStmt *, const parser::BackspaceStmt *,
const parser::EndfileStmt *, const parser::RewindStmt *,
const parser::FlushStmt *, const parser::InquireStmt *>
u;
LinearLabelRef next;
std::optional<LinearLabelRef> errLabel;
std::optional<LinearLabelRef> eorLabel;
std::optional<LinearLabelRef> endLabel;
};
struct LinearSwitch {
template<typename T>
LinearSwitch(const T &sw, const std::vector<LinearLabelRef> &refs)
: u{&sw}, refs{refs} {}
std::variant<const parser::CallStmt *, const parser::ComputedGotoStmt *,
const parser::ArithmeticIfStmt *, const parser::CaseConstruct *,
const parser::SelectRankConstruct *, const parser::SelectTypeConstruct *>
u;
const std::vector<LinearLabelRef> refs;
};
struct LinearAction {
LinearAction(const parser::Statement<parser::ActionStmt> &stmt) : v{&stmt} {}
parser::CharBlock getSource() const { return v->source; }
const parser::Statement<parser::ActionStmt> *v;
};
using ConstructVariant = std::variant<const parser::AssociateConstruct *,
const parser::BlockConstruct *, const parser::CaseConstruct *,
const parser::ChangeTeamConstruct *, const parser::CriticalConstruct *,
const parser::DoConstruct *, const parser::IfConstruct *,
const parser::SelectRankConstruct *, const parser::SelectTypeConstruct *,
const parser::WhereConstruct *, const parser::ForallConstruct *,
const parser::CompilerDirective *, const parser::OpenMPConstruct *,
const parser::OpenMPEndLoopDirective *>;
struct LinearBeginConstruct : public SumTypeCopyMixin<ConstructVariant> {
SUM_TYPE_COPY_MIXIN(LinearBeginConstruct)
template<typename T>
LinearBeginConstruct(const T &c) : SumTypeCopyMixin{&c} {}
};
struct LinearEndConstruct : public SumTypeCopyMixin<ConstructVariant> {
SUM_TYPE_COPY_MIXIN(LinearEndConstruct)
template<typename T> LinearEndConstruct(const T &c) : SumTypeCopyMixin{&c} {}
};
struct LinearDoIncrement {
LinearDoIncrement(const parser::DoConstruct &stmt) : v{&stmt} {}
const parser::DoConstruct *v;
};
struct LinearDoCompare {
LinearDoCompare(const parser::DoConstruct &stmt) : v{&stmt} {}
const parser::DoConstruct *v;
};
template<typename CONSTRUCT>
const char *GetConstructName(const CONSTRUCT &construct) {
return std::visit(
common::visitors{
[](const parser::AssociateConstruct *) { return "ASSOCIATE"; },
[](const parser::BlockConstruct *) { return "BLOCK"; },
[](const parser::CaseConstruct *) { return "SELECT CASE"; },
[](const parser::ChangeTeamConstruct *) { return "CHANGE TEAM"; },
[](const parser::CriticalConstruct *) { return "CRITICAL"; },
[](const parser::DoConstruct *) { return "DO"; },
[](const parser::IfConstruct *) { return "IF"; },
[](const parser::SelectRankConstruct *) { return "SELECT RANK"; },
[](const parser::SelectTypeConstruct *) { return "SELECT TYPE"; },
[](const parser::WhereConstruct *) { return "WHERE"; },
[](const parser::ForallConstruct *) { return "FORALL"; },
[](const parser::CompilerDirective *) { return "<directive>"; },
[](const parser::OpenMPConstruct *) { return "<open-mp>"; },
[](const parser::OpenMPEndLoopDirective *) {
return "<open-mp-end-loop>";
}},
construct.u);
}
struct AnalysisData {
std::map<parser::Label, LinearLabel> labelMap;
std::vector<std::tuple<const parser::Name *, LinearLabelRef, LinearLabelRef>>
nameStack;
LinearLabelBuilder labelBuilder;
std::map<const semantics::Symbol *, std::set<parser::Label>> assignMap;
};
void AddAssign(AnalysisData &ad, const semantics::Symbol *symbol,
const parser::Label &label) {
ad.assignMap[symbol].insert(label);
}
std::vector<LinearLabelRef> GetAssign(
AnalysisData &ad, const semantics::Symbol *symbol) {
std::vector<LinearLabelRef> result;
for (auto lab : ad.assignMap[symbol]) {
result.emplace_back(lab);
}
return result;
}
LinearLabel BuildNewLabel(AnalysisData &ad) {
return LinearLabel{ad.labelBuilder};
}
LinearLabel FetchLabel(AnalysisData &ad, const parser::Label &label) {
auto iter{ad.labelMap.find(label)};
if (iter == ad.labelMap.end()) {
LinearLabel ll{ad.labelBuilder};
ll.setReferenced();
ad.labelMap.insert({label, ll});
return ll;
}
return iter->second;
}
std::tuple<const parser::Name *, LinearLabelRef, LinearLabelRef> FindStack(
const std::vector<std::tuple<const parser::Name *, LinearLabelRef,
LinearLabelRef>> &stack,
const parser::Name *key) {
for (auto iter{stack.rbegin()}, iend{stack.rend()}; iter != iend; ++iter) {
if (std::get<0>(*iter) == key) return *iter;
}
SEMANTICS_FAILED("construct name not on stack");
return {};
}
template<typename T> parser::Label GetErr(const T &stmt) {
if constexpr (std::is_same_v<T, parser::ReadStmt> ||
std::is_same_v<T, parser::WriteStmt>) {
for (const auto &control : stmt.controls) {
if (std::holds_alternative<parser::ErrLabel>(control.u)) {
return std::get<parser::ErrLabel>(control.u).v;
}
}
}
if constexpr (std::is_same_v<T, parser::WaitStmt> ||
std::is_same_v<T, parser::OpenStmt> ||
std::is_same_v<T, parser::CloseStmt> ||
std::is_same_v<T, parser::BackspaceStmt> ||
std::is_same_v<T, parser::EndfileStmt> ||
std::is_same_v<T, parser::RewindStmt> ||
std::is_same_v<T, parser::FlushStmt>) {
for (const auto &spec : stmt.v) {
if (std::holds_alternative<parser::ErrLabel>(spec.u)) {
return std::get<parser::ErrLabel>(spec.u).v;
}
}
}
if constexpr (std::is_same_v<T, parser::InquireStmt>) {
for (const auto &spec : std::get<std::list<parser::InquireSpec>>(stmt.u)) {
if (std::holds_alternative<parser::ErrLabel>(spec.u)) {
return std::get<parser::ErrLabel>(spec.u).v;
}
}
}
return 0;
}
template<typename T> parser::Label GetEor(const T &stmt) {
if constexpr (std::is_same_v<T, parser::ReadStmt> ||
std::is_same_v<T, parser::WriteStmt>) {
for (const auto &control : stmt.controls) {
if (std::holds_alternative<parser::EorLabel>(control.u)) {
return std::get<parser::EorLabel>(control.u).v;
}
}
}
if constexpr (std::is_same_v<T, parser::WaitStmt>) {
for (const auto &waitSpec : stmt.v) {
if (std::holds_alternative<parser::EorLabel>(waitSpec.u)) {
return std::get<parser::EorLabel>(waitSpec.u).v;
}
}
}
return 0;
}
template<typename T> parser::Label GetEnd(const T &stmt) {
if constexpr (std::is_same_v<T, parser::ReadStmt> ||
std::is_same_v<T, parser::WriteStmt>) {
for (const auto &control : stmt.controls) {
if (std::holds_alternative<parser::EndLabel>(control.u)) {
return std::get<parser::EndLabel>(control.u).v;
}
}
}
if constexpr (std::is_same_v<T, parser::WaitStmt>) {
for (const auto &waitSpec : stmt.v) {
if (std::holds_alternative<parser::EndLabel>(waitSpec.u)) {
return std::get<parser::EndLabel>(waitSpec.u).v;
}
}
}
return 0;
}
template<typename T>
void errLabelSpec(const T &s, std::list<LinearOp> &ops,
const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad) {
if (auto errLab{GetErr(s)}) {
std::optional<LinearLabelRef> errRef{FetchLabel(ad, errLab).get()};
LinearLabel next{BuildNewLabel(ad)};
ops.emplace_back(LinearSwitchingIO{s, next, errRef});
ops.emplace_back(next);
} else {
ops.emplace_back(LinearAction{ec});
}
}
template<typename T>
void threeLabelSpec(const T &s, std::list<LinearOp> &ops,
const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad) {
auto errLab{GetErr(s)};
auto eorLab{GetEor(s)};
auto endLab{GetEnd(s)};
if (errLab || eorLab || endLab) {
std::optional<LinearLabelRef> errRef{std::nullopt};
if (errLab) errRef = FetchLabel(ad, errLab).get();
std::optional<LinearLabelRef> eorRef{std::nullopt};
if (eorLab) eorRef = FetchLabel(ad, eorLab).get();
std::optional<LinearLabelRef> endRef{std::nullopt};
if (endLab) endRef = FetchLabel(ad, endLab).get();
LinearLabel next{BuildNewLabel(ad)};
ops.emplace_back(LinearSwitchingIO{s, next, errRef, eorRef, endRef});
ops.emplace_back(next);
} else {
ops.emplace_back(LinearAction{ec});
}
}
template<typename T>
std::vector<LinearLabelRef> toLabelRef(AnalysisData &ad, const T &labels) {
std::vector<LinearLabelRef> result;
for (auto label : labels) {
result.emplace_back(FetchLabel(ad, label).get());
}
CHECK(result.size() == labels.size());
return result;
}
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;
}
std::list<parser::Label> getAltReturnLabels(const parser::Call &call) {
std::list<parser::Label> result;
const auto &args{std::get<std::list<parser::ActualArgSpec>>(call.t)};
for (const auto &arg : args) {
const auto &actual{std::get<parser::ActualArg>(arg.t)};
if (const auto *p{std::get_if<parser::AltReturnSpec>(&actual.u)}) {
result.push_back(p->v);
}
}
return result;
}
LinearLabelRef NearestEnclosingDoConstruct(AnalysisData &ad) {
for (auto iterator{ad.nameStack.rbegin()}, endIterator{ad.nameStack.rend()};
iterator != endIterator; ++iterator) {
auto labelReference{std::get<2>(*iterator)};
if (labelReference != unspecifiedLabel) {
return labelReference;
}
}
SEMANTICS_FAILED("CYCLE|EXIT not in loop");
return unspecifiedLabel;
}
struct LinearOp
: public SumTypeMixin<std::variant<LinearLabel, LinearGoto, LinearReturn,
LinearConditionalGoto, LinearSwitchingIO, LinearSwitch, LinearAction,
LinearBeginConstruct, LinearEndConstruct, LinearIndirectGoto,
LinearDoIncrement, LinearDoCompare>> {
template<typename T> LinearOp(const T &thing) : SumTypeMixin{thing} {}
void dump() const;
static void Build(std::list<LinearOp> &ops,
const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad) {
std::visit(
common::visitors{
[&](const auto &s) { ops.emplace_back(LinearAction{ec}); },
[&](const common::Indirection<parser::CallStmt> &s) {
if (hasAltReturns(s.value())) {
auto next{BuildNewLabel(ad)};
auto labels{toLabelRef(ad, getAltReturnLabels(s.value().v))};
labels.push_back(next);
ops.emplace_back(LinearSwitch{s.value(), std::move(labels)});
ops.emplace_back(next);
} else {
ops.emplace_back(LinearAction{ec});
}
},
[&](const common::Indirection<parser::AssignStmt> &s) {
AddAssign(ad, std::get<parser::Name>(s.value().t).symbol,
std::get<parser::Label>(s.value().t));
ops.emplace_back(LinearAction{ec});
},
[&](const common::Indirection<parser::CycleStmt> &s) {
ops.emplace_back(LinearGoto{s.value(),
s.value().v ? std::get<2>(FindStack(
ad.nameStack, &s.value().v.value()))
: NearestEnclosingDoConstruct(ad)});
},
[&](const common::Indirection<parser::ExitStmt> &s) {
ops.emplace_back(LinearGoto{s.value(),
s.value().v ? std::get<1>(FindStack(
ad.nameStack, &s.value().v.value()))
: NearestEnclosingDoConstruct(ad)});
},
[&](const common::Indirection<parser::GotoStmt> &s) {
ops.emplace_back(
LinearGoto{s.value(), FetchLabel(ad, s.value().v).get()});
},
[&](const parser::FailImageStmt &s) {
ops.emplace_back(LinearReturn{s});
},
[&](const common::Indirection<parser::ReturnStmt> &s) {
ops.emplace_back(LinearReturn{s.value()});
},
[&](const common::Indirection<parser::StopStmt> &s) {
ops.emplace_back(LinearAction{ec});
ops.emplace_back(LinearReturn{s.value()});
},
[&](const common::Indirection<const parser::ReadStmt> &s) {
threeLabelSpec(s.value(), ops, ec, ad);
},
[&](const common::Indirection<const parser::WriteStmt> &s) {
threeLabelSpec(s.value(), ops, ec, ad);
},
[&](const common::Indirection<const parser::WaitStmt> &s) {
threeLabelSpec(s.value(), ops, ec, ad);
},
[&](const common::Indirection<const parser::OpenStmt> &s) {
errLabelSpec(s.value(), ops, ec, ad);
},
[&](const common::Indirection<const parser::CloseStmt> &s) {
errLabelSpec(s.value(), ops, ec, ad);
},
[&](const common::Indirection<const parser::BackspaceStmt> &s) {
errLabelSpec(s.value(), ops, ec, ad);
},
[&](const common::Indirection<const parser::EndfileStmt> &s) {
errLabelSpec(s.value(), ops, ec, ad);
},
[&](const common::Indirection<const parser::RewindStmt> &s) {
errLabelSpec(s.value(), ops, ec, ad);
},
[&](const common::Indirection<const parser::FlushStmt> &s) {
errLabelSpec(s.value(), ops, ec, ad);
},
[&](const common::Indirection<const parser::InquireStmt> &s) {
errLabelSpec(s.value(), ops, ec, ad);
},
[&](const common::Indirection<parser::ComputedGotoStmt> &s) {
auto next{BuildNewLabel(ad)};
auto labels{toLabelRef(
ad, std::get<std::list<parser::Label>>(s.value().t))};
labels.push_back(next);
ops.emplace_back(LinearSwitch{s.value(), std::move(labels)});
ops.emplace_back(next);
},
[&](const common::Indirection<parser::ArithmeticIfStmt> &s) {
ops.emplace_back(LinearSwitch{s.value(),
toLabelRef(ad,
std::list{std::get<1>(s.value().t),
std::get<2>(s.value().t),
std::get<3>(s.value().t)})});
},
[&](const common::Indirection<parser::AssignedGotoStmt> &s) {
ops.emplace_back(
LinearIndirectGoto{std::get<parser::Name>(s.value().t).symbol,
toLabelRef(ad,
std::get<std::list<parser::Label>>(s.value().t))});
},
[&](const common::Indirection<parser::IfStmt> &s) {
auto then{BuildNewLabel(ad)};
auto endif{BuildNewLabel(ad)};
ops.emplace_back(LinearConditionalGoto{s.value(), then, endif});
ops.emplace_back(then);
ops.emplace_back(LinearAction{ec});
ops.emplace_back(endif);
},
},
ec.statement.u);
}
};
template<typename STMTTYPE, typename CT>
Evaluation GetSwitchSelector(const CT *selectConstruct) {
const auto &selector{std::get<parser::Selector>(
std::get<parser::Statement<STMTTYPE>>(selectConstruct->t).statement.t)};
return std::visit(
common::visitors{
[](const parser::Expr &e) { return Evaluation{ExprRef(e)}; },
[](const parser::Variable &variable) {
return Evaluation{&variable};
},
},
selector.u);
}
Evaluation GetSwitchRankSelector(
const parser::SelectRankConstruct *selectRankConstruct) {
return GetSwitchSelector<parser::SelectRankStmt>(selectRankConstruct);
}
Evaluation GetSwitchTypeSelector(
const parser::SelectTypeConstruct *selectTypeConstruct) {
return GetSwitchSelector<parser::SelectTypeStmt>(selectTypeConstruct);
}
Evaluation GetSwitchCaseSelector(const parser::CaseConstruct *construct) {
auto &s{std::get<parser::Statement<parser::SelectCaseStmt>>(construct->t)};
return Evaluation{
ExprRef(std::get<parser::Scalar<parser::Expr>>(s.statement.t).thing)};
}
template<typename STMTTYPE, typename CT>
const std::optional<parser::Name> &GetSwitchAssociateName(
const CT *selectConstruct) {
return std::get<1>(
std::get<parser::Statement<STMTTYPE>>(selectConstruct->t).statement.t);
}
template<typename CONSTRUCT, typename GSF>
void DumpSwitchWithSelector(
const CONSTRUCT *construct, char const *const name, GSF getSelector) {
auto selector{getSelector(construct)};
DebugChannel() << name << "(" << selector.dump();
}
void LinearOp::dump() const {
std::visit(
common::visitors{
[](const LinearLabel &t) {
DebugChannel() << "label: " << t.get() << '\n';
},
[](const LinearGoto &t) {
DebugChannel() << "goto " << t.target << '\n';
},
[](const LinearReturn &) { DebugChannel() << "return\n"; },
[](const LinearConditionalGoto &t) {
DebugChannel() << "cbranch (?) " << t.trueLabel << ' '
<< t.falseLabel << '\n';
},
[](const LinearSwitchingIO &t) {
DebugChannel() << "io-op";
if (t.errLabel) DebugChannel() << " ERR=" << t.errLabel.value();
if (t.eorLabel) DebugChannel() << " EOR=" << t.eorLabel.value();
if (t.endLabel) DebugChannel() << " END=" << t.endLabel.value();
DebugChannel() << '\n';
},
[](const LinearSwitch &lswitch) {
DebugChannel() << "switch-";
std::visit(
common::visitors{
[](const parser::CaseConstruct *caseConstruct) {
DumpSwitchWithSelector(
caseConstruct, "case", GetSwitchCaseSelector);
},
[](const parser::SelectRankConstruct *selectRankConstruct) {
DumpSwitchWithSelector(
selectRankConstruct, "rank", GetSwitchRankSelector);
},
[](const parser::SelectTypeConstruct *selectTypeConstruct) {
DumpSwitchWithSelector(
selectTypeConstruct, "type", GetSwitchTypeSelector);
},
[](const parser::ComputedGotoStmt *computedGotoStmt) {
DebugChannel() << "igoto(?";
},
[](const parser::ArithmeticIfStmt *arithmeticIfStmt) {
DebugChannel() << "<=>(?";
},
[](const parser::CallStmt *callStmt) {
DebugChannel() << "alt-return(?";
},
},
lswitch.u);
DebugChannel() << ") [...]\n";
},
[](const LinearAction &t) {
DebugChannel() << "action: " << t.getSource().ToString() << '\n';
},
[](const LinearBeginConstruct &construct) {
DebugChannel() << "construct-" << GetConstructName(construct)
<< " {\n";
},
[](const LinearDoIncrement &) { DebugChannel() << "do increment\n"; },
[](const LinearDoCompare &) { DebugChannel() << "do compare\n"; },
[](const LinearEndConstruct &construct) {
DebugChannel() << "} construct-" << GetConstructName(construct)
<< "\n";
},
[](const LinearIndirectGoto &) { DebugChannel() << "igoto\n"; },
},
u);
}
} // end namespace
struct ControlFlowAnalyzer {
explicit ControlFlowAnalyzer(std::list<LinearOp> &ops, AnalysisData &ad)
: linearOps{ops}, ad{ad} {}
LinearLabel buildNewLabel() { return BuildNewLabel(ad); }
LinearOp findLabel(const parser::Label &lab) {
auto iter{ad.labelMap.find(lab)};
if (iter == ad.labelMap.end()) {
LinearLabel ll{ad.labelBuilder};
ad.labelMap.insert({lab, ll});
return {ll};
}
return {iter->second};
}
template<typename A> constexpr bool Pre(const A &) { return true; }
template<typename A> constexpr void Post(const A &) {}
template<typename A> bool Pre(const parser::Statement<A> &stmt) {
if (stmt.label) {
linearOps.emplace_back(findLabel(*stmt.label));
}
if constexpr (std::is_same_v<A, parser::ActionStmt>) {
LinearOp::Build(linearOps, stmt, ad);
}
return true;
}
// named constructs
template<typename T> bool linearConstruct(const T &construct) {
std::list<LinearOp> ops;
LinearLabel label{buildNewLabel()};
const parser::Name *name{getName(construct)};
ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel);
ops.emplace_back(LinearBeginConstruct{construct});
ControlFlowAnalyzer cfa{ops, ad};
Walk(std::get<parser::Block>(construct.t), cfa);
ops.emplace_back(label);
ops.emplace_back(LinearEndConstruct{construct});
linearOps.splice(linearOps.end(), ops);
ad.nameStack.pop_back();
return false;
}
bool Pre(const parser::AssociateConstruct &c) { return linearConstruct(c); }
bool Pre(const parser::ChangeTeamConstruct &c) { return linearConstruct(c); }
bool Pre(const parser::CriticalConstruct &c) { return linearConstruct(c); }
bool Pre(const parser::BlockConstruct &construct) {
std::list<LinearOp> ops;
LinearLabel label{buildNewLabel()};
const auto &optName{
std::get<parser::Statement<parser::BlockStmt>>(construct.t)
.statement.v};
const parser::Name *name{optName ? &*optName : nullptr};
ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel);
ops.emplace_back(LinearBeginConstruct{construct});
ControlFlowAnalyzer cfa{ops, ad};
Walk(std::get<parser::Block>(construct.t), cfa);
ops.emplace_back(LinearEndConstruct{construct});
ops.emplace_back(label);
linearOps.splice(linearOps.end(), ops);
ad.nameStack.pop_back();
return false;
}
bool Pre(const parser::DoConstruct &construct) {
std::list<LinearOp> ops;
LinearLabel backedgeLab{buildNewLabel()};
LinearLabel incrementLab{buildNewLabel()};
LinearLabel entryLab{buildNewLabel()};
LinearLabel exitLab{buildNewLabel()};
const parser::Name *name{getName(construct)};
LinearLabelRef exitOpRef{GetLabelRef(exitLab)};
ad.nameStack.emplace_back(name, exitOpRef, GetLabelRef(incrementLab));
ops.emplace_back(LinearBeginConstruct{construct});
ops.emplace_back(LinearGoto{GetLabelRef(backedgeLab)});
ops.emplace_back(incrementLab);
ops.emplace_back(LinearDoIncrement{construct});
ops.emplace_back(backedgeLab);
ops.emplace_back(LinearDoCompare{construct});
ops.emplace_back(LinearConditionalGoto{
std::get<parser::Statement<parser::NonLabelDoStmt>>(construct.t),
GetLabelRef(entryLab), exitOpRef});
ops.push_back(entryLab);
ControlFlowAnalyzer cfa{ops, ad};
Walk(std::get<parser::Block>(construct.t), cfa);
ops.emplace_back(LinearGoto{GetLabelRef(incrementLab)});
ops.emplace_back(LinearEndConstruct{construct});
ops.emplace_back(exitLab);
linearOps.splice(linearOps.end(), ops);
ad.nameStack.pop_back();
return false;
}
bool Pre(const parser::IfConstruct &construct) {
std::list<LinearOp> ops;
LinearLabel thenLab{buildNewLabel()};
LinearLabel elseLab{buildNewLabel()};
LinearLabel exitLab{buildNewLabel()};
const parser::Name *name{getName(construct)};
ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel);
ops.emplace_back(LinearBeginConstruct{construct});
ops.emplace_back(LinearConditionalGoto{
std::get<parser::Statement<parser::IfThenStmt>>(construct.t),
GetLabelRef(thenLab), GetLabelRef(elseLab)});
ops.emplace_back(thenLab);
ControlFlowAnalyzer cfa{ops, ad};
Walk(std::get<parser::Block>(construct.t), cfa);
LinearLabelRef exitOpRef{GetLabelRef(exitLab)};
ops.emplace_back(LinearGoto{exitOpRef});
for (const auto &elseIfBlock :
std::get<std::list<parser::IfConstruct::ElseIfBlock>>(construct.t)) {
ops.emplace_back(elseLab);
LinearLabel newThenLab{buildNewLabel()};
LinearLabel newElseLab{buildNewLabel()};
ops.emplace_back(LinearConditionalGoto{
std::get<parser::Statement<parser::ElseIfStmt>>(elseIfBlock.t),
GetLabelRef(newThenLab), GetLabelRef(newElseLab)});
ops.emplace_back(newThenLab);
Walk(std::get<parser::Block>(elseIfBlock.t), cfa);
ops.emplace_back(LinearGoto{exitOpRef});
elseLab = newElseLab;
}
ops.emplace_back(elseLab);
if (const auto &optElseBlock{
std::get<std::optional<parser::IfConstruct::ElseBlock>>(
construct.t)}) {
Walk(std::get<parser::Block>(optElseBlock->t), cfa);
}
ops.emplace_back(LinearGoto{exitOpRef});
ops.emplace_back(exitLab);
ops.emplace_back(LinearEndConstruct{construct});
linearOps.splice(linearOps.end(), ops);
ad.nameStack.pop_back();
return false;
}
template<typename A,
typename B = std::conditional_t<std::is_same_v<A, parser::CaseConstruct>,
parser::CaseConstruct::Case,
std::conditional_t<std::is_same_v<A, parser::SelectRankConstruct>,
parser::SelectRankConstruct::RankCase,
std::conditional_t<std::is_same_v<A, parser::SelectTypeConstruct>,
parser::SelectTypeConstruct::TypeCase, void>>>>
bool Multiway(const A &construct) {
std::list<LinearOp> ops;
LinearLabel exitLab{buildNewLabel()};
const parser::Name *name{getName(construct)};
ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel);
ops.emplace_back(LinearBeginConstruct{construct});
const auto N{std::get<std::list<B>>(construct.t).size()};
LinearLabelRef exitOpRef{GetLabelRef(exitLab)};
if (N > 0) {
typename std::list<B>::size_type i;
std::vector<LinearLabel> toLabels;
for (i = 0; i != N; ++i) {
toLabels.emplace_back(buildNewLabel());
}
std::vector<LinearLabelRef> targets;
for (i = 0; i != N; ++i) {
targets.emplace_back(GetLabelRef(toLabels[i]));
}
ops.emplace_back(LinearSwitch{construct, targets});
ControlFlowAnalyzer cfa{ops, ad};
i = 0;
for (const auto &caseBlock : std::get<std::list<B>>(construct.t)) {
ops.emplace_back(toLabels[i++]);
Walk(std::get<parser::Block>(caseBlock.t), cfa);
ops.emplace_back(LinearGoto{exitOpRef});
}
}
ops.emplace_back(exitLab);
ops.emplace_back(LinearEndConstruct{construct});
linearOps.splice(linearOps.end(), ops);
ad.nameStack.pop_back();
return false;
}
bool Pre(const parser::CaseConstruct &c) { return Multiway(c); }
bool Pre(const parser::SelectRankConstruct &c) { return Multiway(c); }
bool Pre(const parser::SelectTypeConstruct &c) { return Multiway(c); }
bool Pre(const parser::WhereConstruct &c) {
std::list<LinearOp> ops;
LinearLabel label{buildNewLabel()};
const parser::Name *name{getName(c)};
ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel);
ops.emplace_back(LinearBeginConstruct{c});
ControlFlowAnalyzer cfa{ops, ad};
Walk(std::get<std::list<parser::WhereBodyConstruct>>(c.t), cfa);
Walk(
std::get<std::list<parser::WhereConstruct::MaskedElsewhere>>(c.t), cfa);
Walk(std::get<std::optional<parser::WhereConstruct::Elsewhere>>(c.t), cfa);
ops.emplace_back(label);
ops.emplace_back(LinearEndConstruct{c});
linearOps.splice(linearOps.end(), ops);
ad.nameStack.pop_back();
return false;
}
bool Pre(const parser::ForallConstruct &construct) {
std::list<LinearOp> ops;
LinearLabel label{buildNewLabel()};
const parser::Name *name{getName(construct)};
ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel);
ops.emplace_back(LinearBeginConstruct{construct});
ControlFlowAnalyzer cfa{ops, ad};
Walk(std::get<std::list<parser::ForallBodyConstruct>>(construct.t), cfa);
ops.emplace_back(label);
ops.emplace_back(LinearEndConstruct{construct});
linearOps.splice(linearOps.end(), ops);
ad.nameStack.pop_back();
return false;
}
template<typename A> const parser::Name *getName(const A &a) {
const auto &optName{std::get<0>(std::get<0>(a.t).statement.t)};
return optName ? &*optName : nullptr;
}
LinearLabelRef GetLabelRef(const LinearLabel &label) {
label.setReferenced();
return label;
}
LinearLabelRef GetLabelRef(const parser::Label &label) {
return FetchLabel(ad, label);
}
std::list<LinearOp> &linearOps;
AnalysisData &ad;
};
struct SwitchArguments {
Evaluation exp;
LinearLabelRef defLab;
std::vector<SwitchStmt::ValueType> values;
std::vector<LinearLabelRef> labels;
};
struct SwitchCaseArguments {
Evaluation exp;
LinearLabelRef defLab;
std::vector<SwitchCaseStmt::ValueType> ranges;
std::vector<LinearLabelRef> labels;
};
struct SwitchRankArguments {
Evaluation exp;
LinearLabelRef defLab;
std::vector<SwitchRankStmt::ValueType> ranks;
std::vector<LinearLabelRef> labels;
};
struct SwitchTypeArguments {
Evaluation exp;
LinearLabelRef defLab;
std::vector<SwitchTypeStmt::ValueType> types;
std::vector<LinearLabelRef> labels;
};
template<typename T> bool IsDefault(const typename T::ValueType &valueType) {
return std::holds_alternative<typename T::Default>(valueType);
}
template<typename T>
void cleanupSwitchPairs(LinearLabelRef &defLab,
std::vector<typename T::ValueType> &values,
std::vector<LinearLabelRef> &labels) {
CHECK(values.size() == labels.size());
for (std::size_t i{0}, len{values.size()}; i < len; ++i) {
if (IsDefault<T>(values[i])) {
defLab = labels[i];
for (std::size_t j{i}; j < len - 1; ++j) {
values[j] = values[j + 1];
labels[j] = labels[j + 1];
}
values.pop_back();
labels.pop_back();
break;
}
}
}
static std::vector<SwitchCaseStmt::ValueType> populateSwitchValues(
const std::list<parser::CaseConstruct::Case> &list) {
std::vector<SwitchCaseStmt::ValueType> result;
for (auto &v : list) {
auto &caseSelector{std::get<parser::CaseSelector>(
std::get<parser::Statement<parser::CaseStmt>>(v.t).statement.t)};
if (std::holds_alternative<parser::Default>(caseSelector.u)) {
result.emplace_back(SwitchCaseStmt::Default{});
} else {
std::vector<SwitchCaseStmt::RangeAlternative> valueList;
for (auto &r :
std::get<std::list<parser::CaseValueRange>>(caseSelector.u)) {
std::visit(
common::visitors{
[&](const parser::CaseValue &caseValue) {
const auto &e{caseValue.thing.thing.value()};
valueList.emplace_back(SwitchCaseStmt::Exactly{ExprRef(e)});
},
[&](const parser::CaseValueRange::Range &range) {
if (range.lower.has_value()) {
if (range.upper.has_value()) {
valueList.emplace_back(SwitchCaseStmt::InclusiveRange{
ExprRef(range.lower->thing.thing.value()),
ExprRef(range.upper->thing.thing.value())});
} else {
valueList.emplace_back(SwitchCaseStmt::InclusiveAbove{
ExprRef(range.lower->thing.thing.value())});
}
} else {
valueList.emplace_back(SwitchCaseStmt::InclusiveBelow{
ExprRef(range.upper->thing.thing.value())});
}
},
},
r.u);
}
result.emplace_back(valueList);
}
}
return result;
}
static std::vector<SwitchRankStmt::ValueType> populateSwitchValues(
const std::list<parser::SelectRankConstruct::RankCase> &list) {
std::vector<SwitchRankStmt::ValueType> result;
for (auto &v : list) {
auto &rank{std::get<parser::SelectRankCaseStmt::Rank>(
std::get<parser::Statement<parser::SelectRankCaseStmt>>(v.t)
.statement.t)};
std::visit(
common::visitors{
[&](const parser::ScalarIntConstantExpr &exp) {
const auto &e{exp.thing.thing.thing.value()};
result.emplace_back(SwitchRankStmt::Exactly{ExprRef(e)});
},
[&](const parser::Star &) {
result.emplace_back(SwitchRankStmt::AssumedSize{});
},
[&](const parser::Default &) {
result.emplace_back(SwitchRankStmt::Default{});
},
},
rank.u);
}
return result;
}
static std::vector<SwitchTypeStmt::ValueType> populateSwitchValues(
const std::list<parser::SelectTypeConstruct::TypeCase> &list) {
std::vector<SwitchTypeStmt::ValueType> result;
for (auto &v : list) {
auto &guard{std::get<parser::TypeGuardStmt::Guard>(
std::get<parser::Statement<parser::TypeGuardStmt>>(v.t).statement.t)};
std::visit(
common::visitors{
[&](const parser::TypeSpec &typeSpec) {
result.emplace_back(
SwitchTypeStmt::TypeSpec{typeSpec.declTypeSpec});
},
[&](const parser::DerivedTypeSpec &derivedTypeSpec) {
result.emplace_back(
SwitchTypeStmt::DerivedTypeSpec{nullptr /*FIXME*/});
},
[&](const parser::Default &) {
result.emplace_back(SwitchTypeStmt::Default{});
},
},
guard.u);
}
return result;
}
static SwitchCaseArguments ComposeSwitchCaseArguments(
const parser::CaseConstruct *caseConstruct,
const std::vector<LinearLabelRef> &refs) {
auto &cases{
std::get<std::list<parser::CaseConstruct::Case>>(caseConstruct->t)};
SwitchCaseArguments result{GetSwitchCaseSelector(caseConstruct),
unspecifiedLabel, populateSwitchValues(cases), std::move(refs)};
cleanupSwitchPairs<SwitchCaseStmt>(
result.defLab, result.ranges, result.labels);
return result;
}
static SwitchRankArguments ComposeSwitchRankArguments(
const parser::SelectRankConstruct *selectRankConstruct,
const std::vector<LinearLabelRef> &refs) {
auto &ranks{std::get<std::list<parser::SelectRankConstruct::RankCase>>(
selectRankConstruct->t)};
SwitchRankArguments result{GetSwitchRankSelector(selectRankConstruct),
unspecifiedLabel, populateSwitchValues(ranks), std::move(refs)};
if (auto &name{GetSwitchAssociateName<parser::SelectRankStmt>(
selectRankConstruct)}) {
(void)name; // get rid of warning
// TODO: handle associate-name -> Add an assignment stmt?
}
cleanupSwitchPairs<SwitchRankStmt>(
result.defLab, result.ranks, result.labels);
return result;
}
static SwitchTypeArguments ComposeSwitchTypeArguments(
const parser::SelectTypeConstruct *selectTypeConstruct,
const std::vector<LinearLabelRef> &refs) {
auto &types{std::get<std::list<parser::SelectTypeConstruct::TypeCase>>(
selectTypeConstruct->t)};
SwitchTypeArguments result{GetSwitchTypeSelector(selectTypeConstruct),
unspecifiedLabel, populateSwitchValues(types), std::move(refs)};
if (auto &name{GetSwitchAssociateName<parser::SelectTypeStmt>(
selectTypeConstruct)}) {
(void)name; // get rid of warning
// TODO: handle associate-name -> Add an assignment stmt?
}
cleanupSwitchPairs<SwitchTypeStmt>(
result.defLab, result.types, result.labels);
return result;
}
static void buildMultiwayDefaultNext(SwitchArguments &result) {
result.defLab = result.labels.back();
result.labels.pop_back();
}
static SwitchArguments ComposeSwitchArgs(const LinearSwitch &op) {
SwitchArguments result{nullptr, unspecifiedLabel, {}, op.refs};
std::visit(
common::visitors{
[&](const parser::ComputedGotoStmt *c) {
const auto &e{std::get<parser::ScalarIntExpr>(c->t)};
result.exp = ExprRef(e.thing.thing.value());
buildMultiwayDefaultNext(result);
},
[&](const parser::ArithmeticIfStmt *c) {
result.exp = ExprRef(std::get<parser::Expr>(c->t));
},
[&](const parser::CallStmt *c) {
result.exp = nullptr; // fixme - result of call
buildMultiwayDefaultNext(result);
},
[](const auto *) { WRONG_PATH(); },
},
op.u);
return result;
}
template<typename T>
const T *FindReadWriteSpecifier(
const std::list<parser::IoControlSpec> &specifiers) {
for (const auto &specifier : specifiers) {
if (auto *result{std::get_if<T>(&specifier.u)}) {
return result;
}
}
return nullptr;
}
const parser::IoUnit *FindReadWriteIoUnit(
const std::optional<parser::IoUnit> &ioUnit,
const std::list<parser::IoControlSpec> &specifiers) {
if (ioUnit.has_value()) {
return &ioUnit.value();
}
if (const auto *result{FindReadWriteSpecifier<parser::IoUnit>(specifiers)}) {
return result;
}
SEMANTICS_FAILED("no UNIT spec");
return {};
}
const parser::Format *FindReadWriteFormat(
const std::optional<parser::Format> &format,
const std::list<parser::IoControlSpec> &specifiers) {
if (format.has_value()) {
return &format.value();
}
return FindReadWriteSpecifier<parser::Format>(specifiers);
}
static Expression AlwaysTrueExpression() {
auto result{common::SearchTypes(
evaluate::TypeKindVisitor<evaluate::TypeCategory::Logical,
evaluate::Constant, bool>{1, true})};
CHECK(result.has_value());
return {std::move(*result)};
}
// create an integer constant as an expression
static Expression CreateConstant(int value) {
auto result{common::SearchTypes(
evaluate::TypeKindVisitor<evaluate::TypeCategory::Integer,
evaluate::Constant, bool>{value, true})};
CHECK(result.has_value());
return {std::move(*result)};
}
static void CreateSwitchHelper(FIRBuilder *builder, const Evaluation &condition,
BasicBlock *defaultCase, const SwitchStmt::ValueSuccPairListType &rest) {
builder->CreateSwitch(condition, defaultCase, rest);
}
static void CreateSwitchCaseHelper(FIRBuilder *builder,
const Evaluation &condition, BasicBlock *defaultCase,
const SwitchCaseStmt::ValueSuccPairListType &rest) {
builder->CreateSwitchCase(condition, defaultCase, rest);
}
static void CreateSwitchRankHelper(FIRBuilder *builder,
const Evaluation &condition, BasicBlock *defaultCase,
const SwitchRankStmt::ValueSuccPairListType &rest) {
builder->CreateSwitchRank(condition, defaultCase, rest);
}
static void CreateSwitchTypeHelper(FIRBuilder *builder,
const Evaluation &condition, BasicBlock *defaultCase,
const SwitchTypeStmt::ValueSuccPairListType &rest) {
builder->CreateSwitchType(condition, defaultCase, rest);
}
class FortranIRLowering {
public:
using LabelMapType = std::map<LinearLabelRef, BasicBlock *>;
using Closure = std::function<void(const LabelMapType &)>;
FortranIRLowering(semantics::SemanticsContext &sc, bool debugLinearIR)
: fir_{new Program("program_name")}, semanticsContext_{sc},
debugLinearFIR_{debugLinearIR} {}
~FortranIRLowering() { CHECK(!builder_); }
template<typename A> constexpr bool Pre(const A &) { return true; }
template<typename A> constexpr void Post(const A &) {}
void Post(const parser::MainProgram &mainp) {
std::string mainName{"_MAIN"s};
if (auto &ps{
std::get<std::optional<parser::Statement<parser::ProgramStmt>>>(
mainp.t)}) {
mainName = ps->statement.v.ToString();
}
ProcessRoutine(mainp, mainName);
}
void Post(const parser::FunctionSubprogram &subp) {
ProcessRoutine(subp,
std::get<parser::Name>(
std::get<parser::Statement<parser::FunctionStmt>>(subp.t)
.statement.t)
.ToString());
}
void Post(const parser::SubroutineSubprogram &subp) {
ProcessRoutine(subp,
std::get<parser::Name>(
std::get<parser::Statement<parser::SubroutineStmt>>(subp.t)
.statement.t)
.ToString());
}
Program *program() { return fir_; }
template<typename T>
void ProcessRoutine(const T &here, const std::string &name) {
CHECK(!fir_->containsProcedure(name));
auto *subp{fir_->getOrInsertProcedure(name, nullptr, {})};
builder_ = new FIRBuilder(*CreateBlock(subp->getLastRegion()));
AnalysisData ad;
ControlFlowAnalyzer linearize{linearOperations_, ad};
Walk(here, linearize);
if (debugLinearFIR_) {
dumpLinearRepresentation();
}
ConstructFIR(ad);
DrawRemainingArcs();
Cleanup();
}
void dumpLinearRepresentation() const {
for (const auto &op : linearOperations_) {
op.dump();
}
DebugChannel() << "--- END ---\n";
}
template<typename A>
Statement *BindArrayWithBoundSpecifier(
const parser::DataRef &dataRef, const std::list<A> &bl) {
// TODO
return nullptr;
}
Statement *CreatePointerValue(const parser::PointerAssignmentStmt &stmt) {
auto &dataRef{std::get<parser::DataRef>(stmt.t)};
auto &bounds{std::get<parser::PointerAssignmentStmt::Bounds>(stmt.t)};
auto *remap{std::visit(
common::visitors{
[&](const std::list<parser::BoundsRemapping> &bl) -> Statement * {
if (bl.empty()) {
return nullptr;
}
return BindArrayWithBoundSpecifier(dataRef, bl);
},
[&](const std::list<parser::BoundsSpec> &bl) -> Statement * {
if (bl.empty()) {
return nullptr;
}
return BindArrayWithBoundSpecifier(dataRef, bl);
},
},
bounds.u)};
if (remap) {
return remap;
}
return builder_->CreateAddr(DataRefToExpression(dataRef));
}
Type CreateAllocationValue(const parser::Allocation *allocation,
const parser::AllocateStmt *statement) {
auto &obj{std::get<parser::AllocateObject>(allocation->t)};
(void)obj;
// TODO: build an expression for the allocation
return nullptr;
}
const AllocateInsn *CreateDeallocationValue(
const parser::AllocateObject *allocateObject,
const parser::DeallocateStmt *statement) {
// TODO: build an expression for the allocation
return nullptr;
}
// IO argument translations ...
IOCallArguments CreateBackspaceArguments(
const std::list<parser::PositionOrFlushSpec> &specifiers) {
return IOCallArguments{};
}
IOCallArguments CreateCloseArguments(
const std::list<parser::CloseStmt::CloseSpec> &specifiers) {
return IOCallArguments{};
}
IOCallArguments CreateEndfileArguments(
const std::list<parser::PositionOrFlushSpec> &specifiers) {
return IOCallArguments{};
}
IOCallArguments CreateFlushArguments(
const std::list<parser::PositionOrFlushSpec> &specifiers) {
return IOCallArguments{};
}
IOCallArguments CreateRewindArguments(
const std::list<parser::PositionOrFlushSpec> &specifiers) {
return IOCallArguments{};
}
IOCallArguments CreateInquireArguments(
const std::list<parser::InquireSpec> &specifiers) {
return IOCallArguments{};
}
IOCallArguments CreateInquireArguments(
const parser::InquireStmt::Iolength &iolength) {
return IOCallArguments{};
}
IOCallArguments CreateOpenArguments(
const std::list<parser::ConnectSpec> &specifiers) {
return IOCallArguments{};
}
IOCallArguments CreateWaitArguments(
const std::list<parser::WaitSpec> &specifiers) {
return IOCallArguments{};
}
IOCallArguments CreatePrintArguments(const parser::Format &format,
const std::list<parser::OutputItem> &outputs) {
return IOCallArguments{};
}
IOCallArguments CreateReadArguments(
const std::optional<parser::IoUnit> &ioUnit,
const std::optional<parser::Format> &format,
const std::list<parser::IoControlSpec> &controls,
const std::list<parser::InputItem> &inputs) {
return IOCallArguments{};
}
IOCallArguments CreateWriteArguments(
const std::optional<parser::IoUnit> &ioUnit,
const std::optional<parser::Format> &format,
const std::list<parser::IoControlSpec> &controls,
const std::list<parser::OutputItem> &outputs) {
return IOCallArguments{};
}
// Runtime argument translations ...
RuntimeCallArguments CreateEventPostArguments(
const parser::EventPostStmt &eventPostStatement) {
return RuntimeCallArguments{};
}
RuntimeCallArguments CreateEventWaitArguments(
const parser::EventWaitStmt &eventWaitStatement) {
return RuntimeCallArguments{};
}
RuntimeCallArguments CreateFailImageArguments(
const parser::FailImageStmt &failImageStatement) {
return RuntimeCallArguments{};
}
RuntimeCallArguments CreateFormTeamArguments(
const parser::FormTeamStmt &formTeamStatement) {
return RuntimeCallArguments{};
}
RuntimeCallArguments CreateLockArguments(
const parser::LockStmt &lockStatement) {
return RuntimeCallArguments{};
}
RuntimeCallArguments CreatePauseArguments(
const parser::PauseStmt &pauseStatement) {
return RuntimeCallArguments{};
}
RuntimeCallArguments CreateStopArguments(
const parser::StopStmt &stopStatement) {
return RuntimeCallArguments{};
}
RuntimeCallArguments CreateSyncAllArguments(
const parser::SyncAllStmt &syncAllStatement) {
return RuntimeCallArguments{};
}
RuntimeCallArguments CreateSyncImagesArguments(
const parser::SyncImagesStmt &syncImagesStatement) {
return RuntimeCallArguments{};
}
RuntimeCallArguments CreateSyncMemoryArguments(
const parser::SyncMemoryStmt &syncMemoryStatement) {
return RuntimeCallArguments{};
}
RuntimeCallArguments CreateSyncTeamArguments(
const parser::SyncTeamStmt &syncTeamStatement) {
return RuntimeCallArguments{};
}
RuntimeCallArguments CreateUnlockArguments(
const parser::UnlockStmt &unlockStatement) {
return RuntimeCallArguments{};
}
// CALL translations ...
const Value *CreateCalleeValue(
const parser::ProcedureDesignator &designator) {
return nullptr;
}
CallArguments CreateCallArguments(
const std::list<parser::ActualArgSpec> &arguments) {
return CallArguments{};
}
Expression VariableToExpression(const parser::Variable &var) {
auto maybe{semantics::AnalyzeVariable(semanticsContext_, var)};
return {std::move(*maybe)};
}
Expression DataRefToExpression(const parser::DataRef &dr) {
auto maybe{semantics::AnalyzeDataRef(semanticsContext_, dr)};
return {std::move(*maybe)};
}
Expression NameToExpression(const parser::Name &name) {
auto maybe{semantics::AnalyzeName(semanticsContext_, name)};
return {std::move(*maybe)};
}
void handleIntrinsicAssignmentStmt(const parser::AssignmentStmt &stmt) {
// TODO: check if allocation or reallocation should happen, etc.
auto *value{builder_->CreateExpr(ExprRef(std::get<parser::Expr>(stmt.t)))};
auto *addr{builder_->CreateAddr(
VariableToExpression(std::get<parser::Variable>(stmt.t)))};
builder_->CreateStore(addr, value);
}
void handleDefinedAssignmentStmt(const parser::AssignmentStmt &stmt) {
CHECK(false && "TODO defined assignment");
}
void handleAssignmentStmt(const parser::AssignmentStmt &stmt) {
// TODO: is this an intrinsic assignment or a defined assignment?
if (true) {
handleIntrinsicAssignmentStmt(stmt);
} else {
handleDefinedAssignmentStmt(stmt);
}
}
struct AllocOpts {
std::optional<Expression *> mold;
std::optional<Expression *> source;
std::optional<Expression> stat;
std::optional<Expression> errmsg;
};
void handleAllocateStmt(const parser::AllocateStmt &stmt) {
// extract options from list -> opts
AllocOpts opts;
for (auto &allocOpt : std::get<std::list<parser::AllocOpt>>(stmt.t)) {
std::visit(
common::visitors{
[&](const parser::AllocOpt::Mold &m) {
opts.mold = ExprRef(m.v.value());
},
[&](const parser::AllocOpt::Source &s) {
opts.source = ExprRef(s.v.value());
},
[&](const parser::StatOrErrmsg &var) {
std::visit(
common::visitors{
[&](const parser::StatVariable &sv) {
opts.stat = VariableToExpression(sv.v.thing.thing);
},
[&](const parser::MsgVariable &mv) {
opts.errmsg = VariableToExpression(mv.v.thing.thing);
},
},
var.u);
},
},
allocOpt.u);
}
// process the list of allocations
for (auto &allocation : std::get<std::list<parser::Allocation>>(stmt.t)) {
// TODO: add more arguments to builder as needed
builder_->CreateAlloc(CreateAllocationValue(&allocation, &stmt));
}
}
void handleActionStatement(
AnalysisData &ad, const parser::Statement<parser::ActionStmt> &stmt) {
std::visit(
common::visitors{
[&](const common::Indirection<parser::AllocateStmt> &s) {
handleAllocateStmt(s.value());
},
[&](const common::Indirection<parser::AssignmentStmt> &s) {
handleAssignmentStmt(s.value());
},
[&](const common::Indirection<parser::BackspaceStmt> &s) {
builder_->CreateIOCall(InputOutputCallBackspace,
CreateBackspaceArguments(s.value().v));
},
[&](const common::Indirection<parser::CallStmt> &s) {
builder_->CreateCall(nullptr,
CreateCalleeValue(
std::get<parser::ProcedureDesignator>(s.value().v.t)),
CreateCallArguments(
std::get<std::list<parser::ActualArgSpec>>(
s.value().v.t)));
},
[&](const common::Indirection<parser::CloseStmt> &s) {
builder_->CreateIOCall(
InputOutputCallClose, CreateCloseArguments(s.value().v));
},
[](const parser::ContinueStmt &) { WRONG_PATH(); },
[](const common::Indirection<parser::CycleStmt> &) {
WRONG_PATH();
},
[&](const common::Indirection<parser::DeallocateStmt> &s) {
for (auto &alloc :
std::get<std::list<parser::AllocateObject>>(s.value().t)) {
builder_->CreateDealloc(
CreateDeallocationValue(&alloc, &s.value()));
}
},
[&](const common::Indirection<parser::EndfileStmt> &s) {
builder_->CreateIOCall(
InputOutputCallEndfile, CreateEndfileArguments(s.value().v));
},
[&](const common::Indirection<parser::EventPostStmt> &s) {
builder_->CreateRuntimeCall(
RuntimeCallEventPost, CreateEventPostArguments(s.value()));
},
[&](const common::Indirection<parser::EventWaitStmt> &s) {
builder_->CreateRuntimeCall(
RuntimeCallEventWait, CreateEventWaitArguments(s.value()));
},
[](const common::Indirection<parser::ExitStmt> &) { WRONG_PATH(); },
[&](const parser::FailImageStmt &s) {
builder_->CreateRuntimeCall(
RuntimeCallFailImage, CreateFailImageArguments(s));
},
[&](const common::Indirection<parser::FlushStmt> &s) {
builder_->CreateIOCall(
InputOutputCallFlush, CreateFlushArguments(s.value().v));
},
[&](const common::Indirection<parser::FormTeamStmt> &s) {
builder_->CreateRuntimeCall(
RuntimeCallFormTeam, CreateFormTeamArguments(s.value()));
},
[](const common::Indirection<parser::GotoStmt> &) { WRONG_PATH(); },
[](const common::Indirection<parser::IfStmt> &) { WRONG_PATH(); },
[&](const common::Indirection<parser::InquireStmt> &s) {
std::visit(
common::visitors{
[&](const std::list<parser::InquireSpec> &specifiers) {
builder_->CreateIOCall(InputOutputCallInquire,
CreateInquireArguments(specifiers));
},
[&](const parser::InquireStmt::Iolength &iolength) {
builder_->CreateIOCall(InputOutputCallInquire,
CreateInquireArguments(iolength));
},
},
s.value().u);
},
[&](const common::Indirection<parser::LockStmt> &s) {
builder_->CreateRuntimeCall(
RuntimeCallLock, CreateLockArguments(s.value()));
},
[&](const common::Indirection<parser::NullifyStmt> &s) {
builder_->CreateNullify(&s.value());
},
[&](const common::Indirection<parser::OpenStmt> &s) {
builder_->CreateIOCall(
InputOutputCallOpen, CreateOpenArguments(s.value().v));
},
[&](const common::Indirection<parser::PointerAssignmentStmt> &s) {
auto *value{CreatePointerValue(s.value())};
auto *addr{builder_->CreateAddr(
ExprRef(std::get<parser::Expr>(s.value().t)))};
builder_->CreateStore(addr, value);
},
[&](const common::Indirection<parser::PrintStmt> &s) {
builder_->CreateIOCall(InputOutputCallPrint,
CreatePrintArguments(std::get<parser::Format>(s.value().t),
std::get<std::list<parser::OutputItem>>(s.value().t)));
},
[&](const common::Indirection<parser::ReadStmt> &s) {
builder_->CreateIOCall(InputOutputCallRead,
CreateReadArguments(s.value().iounit, s.value().format,
s.value().controls, s.value().items));
},
[](const common::Indirection<parser::ReturnStmt> &) {
WRONG_PATH();
},
[&](const common::Indirection<parser::RewindStmt> &s) {
builder_->CreateIOCall(
InputOutputCallRewind, CreateRewindArguments(s.value().v));
},
[&](const common::Indirection<parser::StopStmt> &s) {
builder_->CreateRuntimeCall(
RuntimeCallStop, CreateStopArguments(s.value()));
},
[&](const common::Indirection<parser::SyncAllStmt> &s) {
builder_->CreateRuntimeCall(
RuntimeCallSyncAll, CreateSyncAllArguments(s.value()));
},
[&](const common::Indirection<parser::SyncImagesStmt> &s) {
builder_->CreateRuntimeCall(
RuntimeCallSyncImages, CreateSyncImagesArguments(s.value()));
},
[&](const common::Indirection<parser::SyncMemoryStmt> &s) {
builder_->CreateRuntimeCall(
RuntimeCallSyncMemory, CreateSyncMemoryArguments(s.value()));
},
[&](const common::Indirection<parser::SyncTeamStmt> &s) {
builder_->CreateRuntimeCall(
RuntimeCallSyncTeam, CreateSyncTeamArguments(s.value()));
},
[&](const common::Indirection<parser::UnlockStmt> &s) {
builder_->CreateRuntimeCall(
RuntimeCallUnlock, CreateUnlockArguments(s.value()));
},
[&](const common::Indirection<parser::WaitStmt> &s) {
builder_->CreateIOCall(
InputOutputCallWait, CreateWaitArguments(s.value().v));
},
[](const common::Indirection<parser::WhereStmt> &) { /*fixme*/ },
[&](const common::Indirection<parser::WriteStmt> &s) {
builder_->CreateIOCall(InputOutputCallWrite,
CreateWriteArguments(s.value().iounit, s.value().format,
s.value().controls, s.value().items));
},
[](const common::Indirection<parser::ComputedGotoStmt> &) {
WRONG_PATH();
},
[](const common::Indirection<parser::ForallStmt> &) { /*fixme*/ },
[](const common::Indirection<parser::ArithmeticIfStmt> &) {
WRONG_PATH();
},
[&](const common::Indirection<parser::AssignStmt> &s) {
auto *addr{builder_->CreateAddr(
NameToExpression(std::get<parser::Name>(s.value().t)))};
auto *block{
blockMap_
.find(FetchLabel(ad, std::get<parser::Label>(s.value().t))
.get())
->second};
builder_->CreateStore(addr, block);
},
[](const common::Indirection<parser::AssignedGotoStmt> &) {
WRONG_PATH();
},
[&](const common::Indirection<parser::PauseStmt> &s) {
builder_->CreateRuntimeCall(
RuntimeCallPause, CreatePauseArguments(s.value()));
},
},
stmt.statement.u);
}
void handleLinearAction(const LinearAction &action, AnalysisData &ad) {
handleActionStatement(ad, *action.v);
}
// DO loop handlers
struct DoBoundsInfo {
Statement *doVariable;
Statement *lowerBound;
Statement *upperBound;
Statement *stepExpr;
Statement *condition;
};
void PushDoContext(const parser::NonLabelDoStmt *doStmt, Statement *doVar,
Statement *lowBound, Statement *upBound, Statement *stepExp) {
doMap_.emplace(doStmt, DoBoundsInfo{doVar, lowBound, upBound, stepExp});
}
void PopDoContext(const parser::NonLabelDoStmt *doStmt) {
doMap_.erase(doStmt);
}
template<typename T> DoBoundsInfo *GetBoundsInfo(const T &linearOp) {
auto *s{&std::get<parser::Statement<parser::NonLabelDoStmt>>(linearOp.v->t)
.statement};
auto iter{doMap_.find(s)};
if (iter != doMap_.end()) {
return &iter->second;
}
CHECK(false && "DO context not present");
return nullptr;
}
// do_var = do_var + e3
void handleLinearDoIncrement(const LinearDoIncrement &inc) {
auto *info{GetBoundsInfo(inc)};
auto *var{builder_->CreateLoad(info->doVariable)};
builder_->CreateIncrement(var, info->stepExpr);
}
// (e3 > 0 && do_var <= e2) || (e3 < 0 && do_var >= e2)
void handleLinearDoCompare(const LinearDoCompare &cmp) {
auto *info{GetBoundsInfo(cmp)};
auto *var{builder_->CreateLoad(info->doVariable)};
auto *cond{
builder_->CreateDoCondition(info->stepExpr, var, info->upperBound)};
info->condition = cond;
}
// InitiateConstruct - many constructs require some initial setup
void InitiateConstruct(const parser::AssociateStmt *stmt) {
for (auto &assoc : std::get<std::list<parser::Association>>(stmt->t)) {
auto &selector{std::get<parser::Selector>(assoc.t)};
auto *expr{builder_->CreateExpr(std::visit(
common::visitors{
[&](const parser::Variable &v) {
return VariableToExpression(v);
},
[](const parser::Expr &e) { return *ExprRef(e); },
},
selector.u))};
auto *name{builder_->CreateAddr(
NameToExpression(std::get<parser::Name>(assoc.t)))};
builder_->CreateStore(name, expr);
}
}
void InitiateConstruct(const parser::SelectCaseStmt *stmt) {
builder_->CreateExpr(
ExprRef(std::get<parser::Scalar<parser::Expr>>(stmt->t).thing));
}
void InitiateConstruct(const parser::ChangeTeamStmt *changeTeamStmt) {
// FIXME
}
void InitiateConstruct(const parser::NonLabelDoStmt *stmt) {
auto &ctrl{std::get<std::optional<parser::LoopControl>>(stmt->t)};
if (ctrl.has_value()) {
std::visit(
common::visitors{
[&](const parser::LoopBounds<parser::ScalarIntExpr> &bounds) {
auto *var = builder_->CreateAddr(
NameToExpression(bounds.name.thing.thing));
// evaluate e1, e2 [, e3] ...
auto *e1{builder_->CreateExpr(
ExprRef(bounds.lower.thing.thing.value()))};
auto *e2{builder_->CreateExpr(
ExprRef(bounds.upper.thing.thing.value()))};
Statement *e3;
if (bounds.step.has_value()) {
e3 = builder_->CreateExpr(
ExprRef(bounds.step->thing.thing.value()));
} else {
e3 = builder_->CreateExpr(CreateConstant(1));
}
builder_->CreateStore(var, e1);
PushDoContext(stmt, var, e1, e2, e3);
},
[&](const parser::ScalarLogicalExpr &whileExpr) {},
[&](const parser::LoopControl::Concurrent &cc) {},
},
ctrl->u);
} else {
// loop forever
}
}
// finish DO construct construction
void FinishConstruct(const parser::NonLabelDoStmt *stmt) {
auto &ctrl{std::get<std::optional<parser::LoopControl>>(stmt->t)};
if (ctrl.has_value()) {
std::visit(
common::visitors{
[&](const parser::LoopBounds<parser::ScalarIntExpr> &) {
PopDoContext(stmt);
},
[&](auto &) {
// do nothing
},
},
ctrl->u);
}
}
void InitiateConstruct(const parser::IfThenStmt *ifThenStmt) {
const auto &e{std::get<parser::ScalarLogicalExpr>(ifThenStmt->t).thing};
builder_->CreateExpr(ExprRef(e.thing.value()));
}
void InitiateConstruct(const parser::WhereConstructStmt *whereConstructStmt) {
const auto &e{std::get<parser::LogicalExpr>(whereConstructStmt->t)};
builder_->CreateExpr(ExprRef(e.thing.value()));
}
void InitiateConstruct(
const parser::ForallConstructStmt *forallConstructStmt) {
// FIXME
}
Statement *BuildLoopLatchExpression(const parser::NonLabelDoStmt *stmt) {
auto &loopCtrl{std::get<std::optional<parser::LoopControl>>(stmt->t)};
if (loopCtrl.has_value()) {
return std::visit(
common::visitors{
[&](const parser::LoopBounds<parser::ScalarIntExpr> &) {
return doMap_.find(stmt)->second.condition;
},
[&](const parser::ScalarLogicalExpr &sle) {
auto &exp{sle.thing.thing.value()};
SEMANTICS_CHECK(ExprRef(exp), "DO WHILE condition missing");
return builder_->CreateExpr(ExprRef(exp));
},
[&](const parser::LoopControl::Concurrent &concurrent) {
// FIXME - how do we want to lower DO CONCURRENT?
return builder_->CreateExpr(AlwaysTrueExpression());
},
},
loopCtrl->u);
}
return builder_->CreateExpr(AlwaysTrueExpression());
}
void ConstructFIR(AnalysisData &ad) {
for (auto iter{linearOperations_.begin()}, iend{linearOperations_.end()};
iter != iend; ++iter) {
const auto &op{*iter};
std::visit(
common::visitors{
[&](const LinearLabel &linearLabel) {
auto *newBlock{CreateBlock(builder_->GetCurrentRegion())};
blockMap_.insert({linearLabel.get(), newBlock});
if (builder_->GetInsertionPoint()) {
builder_->CreateBranch(newBlock);
}
builder_->SetInsertionPoint(newBlock);
},
[&](const LinearGoto &linearGoto) {
CheckInsertionPoint();
AddOrQueueBranch(linearGoto.target);
builder_->ClearInsertionPoint();
},
[&](const LinearIndirectGoto &linearIGoto) {
CheckInsertionPoint();
AddOrQueueIGoto(ad, linearIGoto.symbol, linearIGoto.labelRefs);
builder_->ClearInsertionPoint();
},
[&](const LinearReturn &linearReturn) {
CheckInsertionPoint();
std::visit(
common::visitors{
[&](const parser::FailImageStmt *s) {
builder_->CreateRuntimeCall(RuntimeCallFailImage,
CreateFailImageArguments(*s));
builder_->CreateUnreachable();
},
[&](const parser::ReturnStmt *s) {
if (s->v) {
builder_->CreateReturn(
ExprRef(s->v->thing.thing.value()));
} else {
builder_->CreateRetVoid();
}
},
[&](const parser::StopStmt *s) {
builder_->CreateRuntimeCall(
RuntimeCallStop, CreateStopArguments(*s));
builder_->CreateUnreachable();
},
},
linearReturn.u);
builder_->ClearInsertionPoint();
},
[&](const LinearConditionalGoto &linearConditionalGoto) {
CheckInsertionPoint();
std::visit(
common::visitors{
[&](const parser::Statement<parser::IfThenStmt> *s) {
const auto &exp{std::get<parser::ScalarLogicalExpr>(
s->statement.t)
.thing.thing.value()};
SEMANTICS_CHECK(ExprRef(exp),
"IF THEN condition expression missing");
auto *cond{builder_->CreateExpr(ExprRef(exp))};
AddOrQueueCGoto(cond, linearConditionalGoto.trueLabel,
linearConditionalGoto.falseLabel);
},
[&](const parser::Statement<parser::ElseIfStmt> *s) {
const auto &exp{std::get<parser::ScalarLogicalExpr>(
s->statement.t)
.thing.thing.value()};
SEMANTICS_CHECK(ExprRef(exp),
"ELSE IF condition expression missing");
auto *cond{builder_->CreateExpr(ExprRef(exp))};
AddOrQueueCGoto(cond, linearConditionalGoto.trueLabel,
linearConditionalGoto.falseLabel);
},
[&](const parser::IfStmt *s) {
const auto &exp{
std::get<parser::ScalarLogicalExpr>(s->t)
.thing.thing.value()};
SEMANTICS_CHECK(
ExprRef(exp), "IF condition expression missing");
auto *cond{builder_->CreateExpr(ExprRef(exp))};
AddOrQueueCGoto(cond, linearConditionalGoto.trueLabel,
linearConditionalGoto.falseLabel);
},
[&](const parser::Statement<parser::NonLabelDoStmt>
*s) {
AddOrQueueCGoto(
BuildLoopLatchExpression(&s->statement),
linearConditionalGoto.trueLabel,
linearConditionalGoto.falseLabel);
}},
linearConditionalGoto.u);
builder_->ClearInsertionPoint();
},
[&](const LinearSwitchingIO &linearIO) {
CheckInsertionPoint();
AddOrQueueSwitch<SwitchStmt>(
nullptr, linearIO.next, {}, {}, CreateSwitchHelper);
builder_->ClearInsertionPoint();
},
[&](const LinearSwitch &linearSwitch) {
CheckInsertionPoint();
std::visit(
common::visitors{
[&](auto) {
auto args{ComposeSwitchArgs(linearSwitch)};
AddOrQueueSwitch<SwitchStmt>(args.exp, args.defLab,
args.values, args.labels, CreateSwitchHelper);
},
[&](const parser::CaseConstruct *caseConstruct) {
auto args{ComposeSwitchCaseArguments(
caseConstruct, linearSwitch.refs)};
AddOrQueueSwitch<SwitchCaseStmt>(args.exp,
args.defLab, args.ranges, args.labels,
CreateSwitchCaseHelper);
},
[&](const parser::SelectRankConstruct
*selectRankConstruct) {
auto args{ComposeSwitchRankArguments(
selectRankConstruct, linearSwitch.refs)};
AddOrQueueSwitch<SwitchRankStmt>(args.exp,
args.defLab, args.ranks, args.labels,
CreateSwitchRankHelper);
},
[&](const parser::SelectTypeConstruct
*selectTypeConstruct) {
auto args{ComposeSwitchTypeArguments(
selectTypeConstruct, linearSwitch.refs)};
AddOrQueueSwitch<SwitchTypeStmt>(args.exp,
args.defLab, args.types, args.labels,
CreateSwitchTypeHelper);
},
},
linearSwitch.u);
builder_->ClearInsertionPoint();
},
[&](const LinearAction &action) {
CheckInsertionPoint();
handleLinearAction(action, ad);
},
[&](const LinearDoIncrement &inc) {
CheckInsertionPoint();
handleLinearDoIncrement(inc);
},
[&](const LinearDoCompare &cmp) {
CheckInsertionPoint();
handleLinearDoCompare(cmp);
},
[&](const LinearBeginConstruct &linearConstruct) {
std::visit(
common::visitors{
[&](const parser::AssociateConstruct *construct) {
const auto &statement{std::get<
parser::Statement<parser::AssociateStmt>>(
construct->t)};
const auto &position{statement.source};
EnterRegion(position);
InitiateConstruct(&statement.statement);
},
[&](const parser::BlockConstruct *construct) {
EnterRegion(
std::get<parser::Statement<parser::BlockStmt>>(
construct->t)
.source);
},
[&](const parser::CaseConstruct *construct) {
InitiateConstruct(
&std::get<
parser::Statement<parser::SelectCaseStmt>>(
construct->t)
.statement);
},
[&](const parser::ChangeTeamConstruct *construct) {
const auto &statement{std::get<
parser::Statement<parser::ChangeTeamStmt>>(
construct->t)};
EnterRegion(statement.source);
InitiateConstruct(&statement.statement);
},
[&](const parser::DoConstruct *construct) {
const auto &statement{std::get<
parser::Statement<parser::NonLabelDoStmt>>(
construct->t)};
EnterRegion(statement.source);
InitiateConstruct(&statement.statement);
},
[&](const parser::IfConstruct *construct) {
InitiateConstruct(
&std::get<parser::Statement<parser::IfThenStmt>>(
construct->t)
.statement);
},
[&](const parser::SelectRankConstruct *construct) {
const auto &statement{std::get<
parser::Statement<parser::SelectRankStmt>>(
construct->t)};
EnterRegion(statement.source);
},
[&](const parser::SelectTypeConstruct *construct) {
const auto &statement{std::get<
parser::Statement<parser::SelectTypeStmt>>(
construct->t)};
EnterRegion(statement.source);
},
[&](const parser::WhereConstruct *construct) {
InitiateConstruct(
&std::get<parser::Statement<
parser::WhereConstructStmt>>(construct->t)
.statement);
},
[&](const parser::ForallConstruct *construct) {
InitiateConstruct(
&std::get<parser::Statement<
parser::ForallConstructStmt>>(construct->t)
.statement);
},
[](const parser::CriticalConstruct *) { /*fixme*/ },
[](const parser::CompilerDirective *) { /*fixme*/ },
[](const parser::OpenMPConstruct *) { /*fixme*/ },
[](const parser::OpenMPEndLoopDirective
*) { /*fixme*/ },
},
linearConstruct.u);
auto next{iter};
const auto &nextOp{*(++next)};
std::visit(
common::visitors{
[](const auto &) {},
[&](const LinearLabel &linearLabel) {
blockMap_.insert({linearLabel.get(),
builder_->GetInsertionPoint()});
++iter;
},
},
nextOp.u);
},
[&](const LinearEndConstruct &linearConstruct) {
std::visit(
common::visitors{
[](const auto &) {},
[&](const parser::BlockConstruct *) { ExitRegion(); },
[&](const parser::DoConstruct *crct) {
const auto &statement{std::get<
parser::Statement<parser::NonLabelDoStmt>>(
crct->t)};
FinishConstruct(&statement.statement);
ExitRegion();
},
[&](const parser::AssociateConstruct *) {
ExitRegion();
},
[&](const parser::ChangeTeamConstruct *) {
ExitRegion();
},
[&](const parser::SelectTypeConstruct *) {
ExitRegion();
},
},
linearConstruct.u);
},
},
op.u);
}
}
void EnterRegion(const parser::CharBlock &pos) {
auto *region{builder_->GetCurrentRegion()};
auto *scope{semanticsContext_.globalScope().FindScope(pos)};
auto *newRegion{Region::Create(region->getParent(), scope, region)};
auto *block{CreateBlock(newRegion)};
CheckInsertionPoint();
builder_->CreateBranch(block);
builder_->SetInsertionPoint(block);
}
void ExitRegion() {
builder_->SetCurrentRegion(builder_->GetCurrentRegion()->GetEnclosing());
}
void CheckInsertionPoint() {
if (!builder_->GetInsertionPoint()) {
builder_->SetInsertionPoint(CreateBlock(builder_->GetCurrentRegion()));
}
}
void AddOrQueueBranch(LinearLabelRef dest) {
auto iter{blockMap_.find(dest)};
if (iter != blockMap_.end()) {
builder_->CreateBranch(iter->second);
} else {
using namespace std::placeholders;
controlFlowEdgesToAdd_.emplace_back(std::bind(
[](FIRBuilder *builder, BasicBlock *block, LinearLabelRef dest,
const LabelMapType &map) {
builder->SetInsertionPoint(block);
CHECK(map.find(dest) != map.end());
builder->CreateBranch(map.find(dest)->second);
},
builder_, builder_->GetInsertionPoint(), dest, _1));
}
}
void AddOrQueueCGoto(Statement *condition, LinearLabelRef trueBlock,
LinearLabelRef falseBlock) {
auto trueIter{blockMap_.find(trueBlock)};
auto falseIter{blockMap_.find(falseBlock)};
if (trueIter != blockMap_.end() && falseIter != blockMap_.end()) {
builder_->CreateConditionalBranch(
condition, trueIter->second, falseIter->second);
} else {
using namespace std::placeholders;
controlFlowEdgesToAdd_.emplace_back(std::bind(
[](FIRBuilder *builder, BasicBlock *block, Statement *expr,
LinearLabelRef trueDest, LinearLabelRef falseDest,
const LabelMapType &map) {
builder->SetInsertionPoint(block);
CHECK(map.find(trueDest) != map.end());
CHECK(map.find(falseDest) != map.end());
builder->CreateConditionalBranch(
expr, map.find(trueDest)->second, map.find(falseDest)->second);
},
builder_, builder_->GetInsertionPoint(), condition, trueBlock,
falseBlock, _1));
}
}
template<typename SWITCHTYPE, typename F>
void AddOrQueueSwitch(const Evaluation &condition,
LinearLabelRef defaultLabel,
const std::vector<typename SWITCHTYPE::ValueType> &values,
const std::vector<LinearLabelRef> &labels, F function) {
auto defer{false};
auto defaultIter{blockMap_.find(defaultLabel)};
typename SWITCHTYPE::ValueSuccPairListType cases;
if (defaultIter == blockMap_.end()) {
defer = true;
} else {
CHECK(values.size() == labels.size());
auto valiter{values.begin()};
for (auto lab : labels) {
auto labIter{blockMap_.find(lab)};
if (labIter == blockMap_.end()) {
defer = true;
break;
} else {
cases.emplace_back(*valiter++, labIter->second);
}
}
}
if (defer) {
using namespace std::placeholders;
controlFlowEdgesToAdd_.emplace_back(std::bind(
[](FIRBuilder *builder, BasicBlock *block, const Evaluation &expr,
LinearLabelRef defaultDest,
const std::vector<typename SWITCHTYPE::ValueType> &values,
const std::vector<LinearLabelRef> &labels, F function,
const LabelMapType &map) {
builder->SetInsertionPoint(block);
typename SWITCHTYPE::ValueSuccPairListType cases;
auto valiter{values.begin()};
for (auto &lab : labels) {
cases.emplace_back(*valiter++, map.find(lab)->second);
}
function(builder, expr, map.find(defaultDest)->second, cases);
},
builder_, builder_->GetInsertionPoint(), condition, defaultLabel,
values, labels, function, _1));
} else {
function(builder_, condition, defaultIter->second, cases);
}
}
Variable *ConvertToVariable(const semantics::Symbol *symbol) {
// FIXME: how to convert semantics::Symbol to evaluate::Variable?
return new Variable(symbol);
}
void AddOrQueueIGoto(AnalysisData &ad, const semantics::Symbol *symbol,
const std::vector<LinearLabelRef> &labels) {
auto useLabels{labels.empty() ? GetAssign(ad, symbol) : labels};
auto defer{false};
IndirectBranchStmt::TargetListType blocks;
for (auto lab : useLabels) {
auto iter{blockMap_.find(lab)};
if (iter == blockMap_.end()) {
defer = true;
break;
} else {
blocks.push_back(iter->second);
}
}
if (defer) {
using namespace std::placeholders;
controlFlowEdgesToAdd_.emplace_back(std::bind(
[](FIRBuilder *builder, BasicBlock *block, Variable *variable,
const std::vector<LinearLabelRef> &fixme,
const LabelMapType &map) {
builder->SetInsertionPoint(block);
builder->CreateIndirectBr(variable, {}); // FIXME
},
builder_, builder_->GetInsertionPoint(), nullptr /*symbol*/,
useLabels, _1));
} else {
builder_->CreateIndirectBr(ConvertToVariable(symbol), blocks);
}
}
void DrawRemainingArcs() {
for (auto &arc : controlFlowEdgesToAdd_) {
arc(blockMap_);
}
}
BasicBlock *CreateBlock(Region *region) { return BasicBlock::Create(region); }
void Cleanup() {
delete builder_;
builder_ = nullptr;
linearOperations_.clear();
controlFlowEdgesToAdd_.clear();
blockMap_.clear();
}
FIRBuilder *builder_{nullptr};
Program *fir_;
std::list<LinearOp> linearOperations_;
std::list<Closure> controlFlowEdgesToAdd_;
std::map<const parser::NonLabelDoStmt *, DoBoundsInfo> doMap_;
LabelMapType blockMap_;
semantics::SemanticsContext &semanticsContext_;
bool debugLinearFIR_;
};
Program *CreateFortranIR(const parser::Program &program,
semantics::SemanticsContext &semanticsContext, bool debugLinearIR) {
FortranIRLowering converter{semanticsContext, debugLinearIR};
Walk(program, converter);
return converter.program();
}
void SetDebugChannel(const std::string &filename) {
std::error_code ec;
SetDebugChannel(
new llvm::raw_fd_ostream(filename, ec, llvm::sys::fs::F_None));
CHECK(!ec);
}
}