af375b2560
Original-commit: flang-compiler/f18@6bc660c355 Reviewed-on: https://github.com/flang-compiler/f18/pull/399 Tree-same-pre-rewrite: false
1476 lines
58 KiB
C++
1476 lines
58 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 "flattened.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"
|
|
|
|
namespace Fortran::FIR {
|
|
namespace {
|
|
Expression *ExprRef(const parser::Expr &a) { return &a.typedExpr.get()->v; }
|
|
Expression *ExprRef(const common::Indirection<parser::Expr> &a) {
|
|
return &a.value().typedExpr.get()->v;
|
|
}
|
|
|
|
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>
|
|
void DumpSwitchWithSelector(
|
|
const CONSTRUCT *construct, char const *const name) {
|
|
/// auto selector{getSelector(construct)};
|
|
DebugChannel() << name << "("; // << selector.dump()
|
|
}
|
|
} // end namespace
|
|
|
|
template<typename T> struct SwitchArgs {
|
|
Value exp;
|
|
std::vector<T> values;
|
|
std::vector<flat::LabelRef> labels;
|
|
};
|
|
using SwitchArguments = SwitchArgs<SwitchStmt::ValueType>;
|
|
using SwitchCaseArguments = SwitchArgs<SwitchCaseStmt::ValueType>;
|
|
using SwitchRankArguments = SwitchArgs<SwitchRankStmt::ValueType>;
|
|
using SwitchTypeArguments = SwitchArgs<SwitchTypeStmt::ValueType>;
|
|
|
|
template<typename T> bool IsDefault(const typename T::ValueType &valueType) {
|
|
return std::holds_alternative<typename T::Default>(valueType);
|
|
}
|
|
|
|
// move the default case to be first
|
|
template<typename T>
|
|
void cleanupSwitchPairs(std::vector<typename T::ValueType> &values,
|
|
std::vector<flat::LabelRef> &labels) {
|
|
CHECK(values.size() == labels.size());
|
|
for (std::size_t i{1}, len{values.size()}; i < len; ++i) {
|
|
if (IsDefault<T>(values[i])) {
|
|
auto v{values[0]};
|
|
values[0] = values[i];
|
|
values[i] = v;
|
|
auto w{labels[0]};
|
|
labels[0] = labels[i];
|
|
labels[i] = w;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static std::vector<SwitchCaseStmt::ValueType> populateSwitchValues(
|
|
FIRBuilder *builder, 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()};
|
|
auto *app{builder->MakeAsExpr(ExprRef(e))};
|
|
valueList.emplace_back(SwitchCaseStmt::Exactly{app});
|
|
},
|
|
[&](const parser::CaseValueRange::Range &range) {
|
|
if (range.lower.has_value()) {
|
|
if (range.upper.has_value()) {
|
|
auto *appl{builder->MakeAsExpr(
|
|
ExprRef(range.lower->thing.thing))};
|
|
auto *apph{builder->MakeAsExpr(
|
|
ExprRef(range.upper->thing.thing))};
|
|
valueList.emplace_back(
|
|
SwitchCaseStmt::InclusiveRange{appl, apph});
|
|
} else {
|
|
auto *app{builder->MakeAsExpr(
|
|
ExprRef(range.lower->thing.thing))};
|
|
valueList.emplace_back(
|
|
SwitchCaseStmt::InclusiveAbove{app});
|
|
}
|
|
} else {
|
|
auto *app{
|
|
builder->MakeAsExpr(ExprRef(range.upper->thing.thing))};
|
|
valueList.emplace_back(SwitchCaseStmt::InclusiveBelow{app});
|
|
}
|
|
},
|
|
},
|
|
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 &spec) {
|
|
result.emplace_back(SwitchTypeStmt::TypeSpec{spec.declTypeSpec});
|
|
},
|
|
[&](const parser::DerivedTypeSpec &spec) {
|
|
result.emplace_back(
|
|
SwitchTypeStmt::DerivedTypeSpec{nullptr /* FIXME */});
|
|
},
|
|
[&](const parser::Default &) {
|
|
result.emplace_back(SwitchTypeStmt::Default{});
|
|
},
|
|
},
|
|
guard.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() {
|
|
using A = evaluate::Type<evaluate::TypeCategory::Logical, 1>;
|
|
return {evaluate::AsGenericExpr(evaluate::Constant<A>{true})};
|
|
}
|
|
|
|
// create an integer constant as an expression
|
|
static Expression CreateConstant(int64_t value) {
|
|
using A = evaluate::SubscriptInteger;
|
|
return {evaluate::AsGenericExpr(evaluate::Constant<A>{value})};
|
|
}
|
|
|
|
static void CreateSwitchHelper(FIRBuilder *builder, Value condition,
|
|
const SwitchStmt::ValueSuccPairListType &rest) {
|
|
builder->CreateSwitch(condition, rest);
|
|
}
|
|
static void CreateSwitchCaseHelper(FIRBuilder *builder, Value condition,
|
|
const SwitchCaseStmt::ValueSuccPairListType &rest) {
|
|
builder->CreateSwitchCase(condition, rest);
|
|
}
|
|
static void CreateSwitchRankHelper(FIRBuilder *builder, Value condition,
|
|
const SwitchRankStmt::ValueSuccPairListType &rest) {
|
|
builder->CreateSwitchRank(condition, rest);
|
|
}
|
|
static void CreateSwitchTypeHelper(FIRBuilder *builder, Value condition,
|
|
const SwitchTypeStmt::ValueSuccPairListType &rest) {
|
|
builder->CreateSwitchType(condition, rest);
|
|
}
|
|
|
|
static Expression getApplyExpr(Statement *s) {
|
|
return GetApplyExpr(s)->expression();
|
|
}
|
|
static Expression getLocalVariable(Statement *s) {
|
|
return GetLocal(s)->variable();
|
|
}
|
|
|
|
// create a new temporary name (as heap garbage)
|
|
static parser::CharBlock NewTemporaryName() {
|
|
constexpr int SizeMagicValue{32};
|
|
static int counter;
|
|
char cache[SizeMagicValue];
|
|
int bytesWritten{snprintf(cache, SizeMagicValue, ".t%d", counter++)};
|
|
CHECK(bytesWritten < SizeMagicValue);
|
|
auto len{strlen(cache)};
|
|
char *name{new char[len]}; // XXX: add these to a pool?
|
|
memcpy(name, cache, len);
|
|
return {name, name + len};
|
|
}
|
|
|
|
static TypeRep GetDefaultIntegerType(semantics::SemanticsContext &c) {
|
|
evaluate::ExpressionAnalyzer analyzer{c};
|
|
return c.MakeNumericType(common::TypeCategory::Integer,
|
|
analyzer.GetDefaultKind(common::TypeCategory::Integer));
|
|
}
|
|
|
|
/*static*/ TypeRep GetDefaultLogicalType(semantics::SemanticsContext &c) {
|
|
evaluate::ExpressionAnalyzer analyzer{c};
|
|
return c.MakeLogicalType(
|
|
analyzer.GetDefaultKind(common::TypeCategory::Logical));
|
|
}
|
|
|
|
class FortranIRLowering {
|
|
public:
|
|
using LabelMapType = std::map<flat::LabelRef, 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_; }
|
|
|
|
// convert a parse tree data reference to an Expression
|
|
template<typename A> Expression ToExpression(const A &a) {
|
|
return {std::move(semantics::AnalyzeExpr(semanticsContext_, a).value())};
|
|
}
|
|
|
|
TypeRep GetDefaultIntegerType() {
|
|
return FIR::GetDefaultIntegerType(semanticsContext_);
|
|
}
|
|
// build a simple arithmetic Expression
|
|
template<template<typename> class OPR>
|
|
Expression ConsExpr(Expression e1, Expression e2) {
|
|
evaluate::ExpressionAnalyzer context{semanticsContext_};
|
|
ConformabilityCheck(context.GetContextualMessages(), e1, e2);
|
|
return evaluate::NumericOperation<OPR>(context.GetContextualMessages(),
|
|
std::move(e1), std::move(e2),
|
|
context.GetDefaultKind(common::TypeCategory::Real))
|
|
.value();
|
|
}
|
|
Expression ConsExpr(
|
|
common::RelationalOperator op, Expression e1, Expression e2) {
|
|
evaluate::ExpressionAnalyzer context{semanticsContext_};
|
|
return evaluate::AsGenericExpr(evaluate::Relate(
|
|
context.GetContextualMessages(), op, std::move(e1), std::move(e2))
|
|
.value());
|
|
}
|
|
parser::Name MakeTemp(Type tempType) {
|
|
auto name{NewTemporaryName()};
|
|
auto details{semantics::ObjectEntityDetails{true}};
|
|
details.set_type(std::move(*tempType));
|
|
auto *sym{&semanticsContext_.globalScope().MakeSymbol(
|
|
name, {}, std::move(details))};
|
|
return {name, sym};
|
|
}
|
|
QualifiedStmt<Addressable_impl> CreateTemp(TypeRep &&spec) {
|
|
TypeRep declSpec{std::move(spec)};
|
|
auto temp{MakeTemp(&declSpec)};
|
|
auto expr{ToExpression(temp)};
|
|
auto *localType{temp.symbol->get<semantics::ObjectEntityDetails>().type()};
|
|
return builder_->CreateLocal(localType, expr);
|
|
}
|
|
|
|
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;
|
|
CreateFlatIR(here, linearOperations_, ad);
|
|
if (debugLinearFIR_) {
|
|
DebugChannel() << "define @" << name << "(...) {\n";
|
|
dump(linearOperations_);
|
|
DebugChannel() << "}\n";
|
|
}
|
|
ConstructFIR(ad);
|
|
DrawRemainingArcs();
|
|
Cleanup();
|
|
}
|
|
|
|
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(ToExpression(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;
|
|
}
|
|
QualifiedStmt<AllocateInsn> CreateDeallocationValue(
|
|
const parser::AllocateObject *allocateObject,
|
|
const parser::DeallocateStmt *statement) {
|
|
// TODO: build an expression for the deallocation
|
|
return QualifiedStmt<AllocateInsn>{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 NOTHING;
|
|
}
|
|
CallArguments CreateCallArguments(
|
|
const std::list<parser::ActualArgSpec> &arguments) {
|
|
return CallArguments{};
|
|
}
|
|
|
|
template<typename STMTTYPE, typename CT>
|
|
Statement *GetSwitchSelector(const CT *selectConstruct) {
|
|
return std::visit(
|
|
common::visitors{
|
|
[&](const parser::Expr &e) {
|
|
return builder_->CreateExpr(ExprRef(e));
|
|
},
|
|
[&](const parser::Variable &v) {
|
|
return builder_->CreateExpr(ToExpression(v));
|
|
},
|
|
},
|
|
std::get<parser::Selector>(
|
|
std::get<parser::Statement<STMTTYPE>>(selectConstruct->t)
|
|
.statement.t)
|
|
.u);
|
|
}
|
|
Statement *GetSwitchRankSelector(
|
|
const parser::SelectRankConstruct *selectRankConstruct) {
|
|
return GetSwitchSelector<parser::SelectRankStmt>(selectRankConstruct);
|
|
}
|
|
Statement *GetSwitchTypeSelector(
|
|
const parser::SelectTypeConstruct *selectTypeConstruct) {
|
|
return GetSwitchSelector<parser::SelectTypeStmt>(selectTypeConstruct);
|
|
}
|
|
Statement *GetSwitchCaseSelector(const parser::CaseConstruct *construct) {
|
|
using A = parser::Statement<parser::SelectCaseStmt>;
|
|
const auto &x{std::get<parser::Scalar<parser::Expr>>(
|
|
std::get<A>(construct->t).statement.t)};
|
|
return builder_->CreateExpr(ExprRef(x.thing));
|
|
}
|
|
|
|
SwitchArguments ComposeIOSwitchArgs(const flat::SwitchIOOp &IOp) {
|
|
return {}; // FIXME
|
|
}
|
|
SwitchArguments ComposeSwitchArgs(const flat::SwitchOp &op) {
|
|
return std::visit(
|
|
common::visitors{
|
|
[&](const parser::ComputedGotoStmt *c) {
|
|
const auto &e{std::get<parser::ScalarIntExpr>(c->t)};
|
|
auto *exp{builder_->CreateExpr(ExprRef(e.thing.thing))};
|
|
return SwitchArguments{exp, {}, op.refs};
|
|
},
|
|
[&](const parser::ArithmeticIfStmt *c) {
|
|
const auto &e{std::get<parser::Expr>(c->t)};
|
|
auto *exp{builder_->CreateExpr(ExprRef(e))};
|
|
return SwitchArguments{exp, {}, op.refs};
|
|
},
|
|
[&](const parser::CallStmt *c) {
|
|
auto exp{NOTHING}; // fixme - result of call
|
|
return SwitchArguments{exp, {}, op.refs};
|
|
},
|
|
[](const auto *) {
|
|
WRONG_PATH();
|
|
return SwitchArguments{};
|
|
},
|
|
},
|
|
op.u);
|
|
}
|
|
|
|
SwitchCaseArguments ComposeSwitchCaseArguments(
|
|
const parser::CaseConstruct *caseConstruct,
|
|
const std::vector<flat::LabelRef> &refs) {
|
|
using A = std::list<parser::CaseConstruct::Case>;
|
|
auto &cases{std::get<A>(caseConstruct->t)};
|
|
SwitchCaseArguments result{GetSwitchCaseSelector(caseConstruct),
|
|
populateSwitchValues(builder_, cases), std::move(refs)};
|
|
cleanupSwitchPairs<SwitchCaseStmt>(result.values, result.labels);
|
|
return result;
|
|
}
|
|
SwitchRankArguments ComposeSwitchRankArguments(
|
|
const parser::SelectRankConstruct *crct,
|
|
const std::vector<flat::LabelRef> &refs) {
|
|
auto &ranks{
|
|
std::get<std::list<parser::SelectRankConstruct::RankCase>>(crct->t)};
|
|
SwitchRankArguments result{GetSwitchRankSelector(crct),
|
|
populateSwitchValues(ranks), std::move(refs)};
|
|
if (auto &name{GetSwitchAssociateName<parser::SelectRankStmt>(crct)}) {
|
|
(void)name; // get rid of warning
|
|
// TODO: handle associate-name -> Add an assignment stmt?
|
|
}
|
|
cleanupSwitchPairs<SwitchRankStmt>(result.values, result.labels);
|
|
return result;
|
|
}
|
|
SwitchTypeArguments ComposeSwitchTypeArguments(
|
|
const parser::SelectTypeConstruct *selectTypeConstruct,
|
|
const std::vector<flat::LabelRef> &refs) {
|
|
auto &types{std::get<std::list<parser::SelectTypeConstruct::TypeCase>>(
|
|
selectTypeConstruct->t)};
|
|
SwitchTypeArguments result{GetSwitchTypeSelector(selectTypeConstruct),
|
|
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.values, result.labels);
|
|
return result;
|
|
}
|
|
|
|
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(ToExpression(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);
|
|
},
|
|
[&](const parser::AllocOpt::Source &s) {
|
|
opts.source = *ExprRef(s.v);
|
|
},
|
|
[&](const parser::StatOrErrmsg &var) {
|
|
std::visit(
|
|
common::visitors{
|
|
[&](const parser::StatVariable &sv) {
|
|
opts.stat = ToExpression(sv.v.thing.thing);
|
|
},
|
|
[&](const parser::MsgVariable &mv) {
|
|
opts.errmsg = ToExpression(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) {
|
|
for (auto &obj : s.value().v) {
|
|
std::visit(
|
|
common::visitors{
|
|
[&](const parser::Name &n) {
|
|
auto s{builder_->CreateAddr(ToExpression(n))};
|
|
builder_->CreateNullify(s);
|
|
},
|
|
[&](const parser::StructureComponent &sc) {
|
|
auto s{builder_->CreateAddr(ToExpression(sc))};
|
|
builder_->CreateNullify(s);
|
|
},
|
|
},
|
|
obj.u);
|
|
}
|
|
},
|
|
[&](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(
|
|
ToExpression(std::get<parser::Name>(s.value().t)))};
|
|
auto *block{blockMap_
|
|
.find(flat::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 flat::ActionOp &action, AnalysisData &ad) {
|
|
handleActionStatement(ad, *action.v);
|
|
}
|
|
|
|
// DO loop handlers
|
|
struct DoBoundsInfo {
|
|
QualifiedStmt<Addressable_impl> doVariable;
|
|
QualifiedStmt<Addressable_impl> counter;
|
|
Statement *stepExpr;
|
|
Statement *condition;
|
|
};
|
|
void PushDoContext(const parser::NonLabelDoStmt *doStmt,
|
|
QualifiedStmt<Addressable_impl> doVar = nullptr,
|
|
QualifiedStmt<Addressable_impl> counter = nullptr,
|
|
Statement *stepExp = nullptr) {
|
|
doMap_.emplace(doStmt, DoBoundsInfo{doVar, counter, 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;
|
|
}
|
|
|
|
void handleLinearDoIncrement(const flat::DoIncrementOp &inc) {
|
|
auto *info{GetBoundsInfo(inc)};
|
|
if (info->doVariable) {
|
|
if (info->stepExpr) {
|
|
// evaluate: do_var = do_var + e3; counter--
|
|
auto *incremented{builder_->CreateExpr(
|
|
ConsExpr<evaluate::Add>(GetAddressable(info->doVariable)->address(),
|
|
GetApplyExpr(info->stepExpr)->expression()))};
|
|
builder_->CreateStore(info->doVariable, incremented);
|
|
auto *decremented{builder_->CreateExpr(ConsExpr<evaluate::Subtract>(
|
|
GetAddressable(info->counter)->address(), CreateConstant(1)))};
|
|
builder_->CreateStore(info->counter, decremented);
|
|
}
|
|
}
|
|
}
|
|
|
|
// is (counter > 0)?
|
|
void handleLinearDoCompare(const flat::DoCompareOp &cmp) {
|
|
auto *info{GetBoundsInfo(cmp)};
|
|
if (info->doVariable) {
|
|
if (info->stepExpr) {
|
|
Expression compare{ConsExpr(common::RelationalOperator::GT,
|
|
getLocalVariable(info->counter), CreateConstant(0))};
|
|
auto *cond{builder_->CreateExpr(&compare)};
|
|
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 ToExpression(v); },
|
|
[](const parser::Expr &e) { return *ExprRef(e); },
|
|
},
|
|
selector.u))};
|
|
auto name{
|
|
builder_->CreateAddr(ToExpression(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::IfThenStmt *ifThenStmt) {
|
|
const auto &e{std::get<parser::ScalarLogicalExpr>(ifThenStmt->t).thing};
|
|
builder_->CreateExpr(ExprRef(e.thing));
|
|
}
|
|
void InitiateConstruct(const parser::WhereConstructStmt *whereConstructStmt) {
|
|
const auto &e{std::get<parser::LogicalExpr>(whereConstructStmt->t)};
|
|
builder_->CreateExpr(ExprRef(e.thing));
|
|
}
|
|
void InitiateConstruct(
|
|
const parser::ForallConstructStmt *forallConstructStmt) {
|
|
// 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 name{builder_->CreateAddr(
|
|
ToExpression(bounds.name.thing.thing))};
|
|
// evaluate e1, e2 [, e3] ...
|
|
auto *e1{
|
|
builder_->CreateExpr(ExprRef(bounds.lower.thing.thing))};
|
|
auto *e2{
|
|
builder_->CreateExpr(ExprRef(bounds.upper.thing.thing))};
|
|
Statement *e3;
|
|
if (bounds.step.has_value()) {
|
|
e3 = builder_->CreateExpr(ExprRef(bounds.step->thing.thing));
|
|
} else {
|
|
e3 = builder_->CreateExpr(CreateConstant(1));
|
|
}
|
|
// name <- e1
|
|
builder_->CreateStore(name, e1);
|
|
auto tripCounter{CreateTemp(GetDefaultIntegerType())};
|
|
// See 11.1.7.4.1, para. 1, item (3)
|
|
// totalTrips ::= iteration count = a
|
|
// where a = (e2 - e1 + e3) / e3 if a > 0 and 0 otherwise
|
|
Expression tripExpr{ConsExpr<evaluate::Divide>(
|
|
ConsExpr<evaluate::Add>(
|
|
ConsExpr<evaluate::Subtract>(
|
|
getApplyExpr(e2), getApplyExpr(e1)),
|
|
getApplyExpr(e3)),
|
|
getApplyExpr(e3))};
|
|
auto *totalTrips{builder_->CreateExpr(&tripExpr)};
|
|
builder_->CreateStore(tripCounter, totalTrips);
|
|
PushDoContext(stmt, name, tripCounter, e3);
|
|
},
|
|
[&](const parser::ScalarLogicalExpr &expr) {
|
|
// See 11.1.7.4.1, para. 2
|
|
// See BuildLoopLatchExpression()
|
|
PushDoContext(stmt);
|
|
},
|
|
[&](const parser::LoopControl::Concurrent &cc) {
|
|
// See 11.1.7.4.2
|
|
// FIXME
|
|
},
|
|
},
|
|
ctrl->u);
|
|
} else {
|
|
// loop forever (See 11.1.7.4.1, para. 2)
|
|
PushDoContext(stmt);
|
|
}
|
|
}
|
|
|
|
// finish DO construct construction
|
|
void FinishConstruct(const parser::NonLabelDoStmt *stmt) {
|
|
auto &ctrl{std::get<std::optional<parser::LoopControl>>(stmt->t)};
|
|
if (ctrl.has_value()) {
|
|
using A = parser::LoopBounds<parser::ScalarIntExpr>;
|
|
if (std::holds_alternative<A>(ctrl->u)) {
|
|
PopDoContext(stmt);
|
|
}
|
|
}
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
template<typename SWITCHTYPE, typename F>
|
|
void AddOrQueueSwitch(Value condition,
|
|
const std::vector<typename SWITCHTYPE::ValueType> &values,
|
|
const std::vector<flat::LabelRef> &labels, F function) {
|
|
auto defer{false};
|
|
typename SWITCHTYPE::ValueSuccPairListType cases;
|
|
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, Value expr,
|
|
const std::vector<typename SWITCHTYPE::ValueType> &values,
|
|
const std::vector<flat::LabelRef> &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, cases);
|
|
},
|
|
builder_, builder_->GetInsertionPoint(), condition, values, labels,
|
|
function, _1));
|
|
} else {
|
|
function(builder_, condition, cases);
|
|
}
|
|
}
|
|
|
|
void ConstructFIR(AnalysisData &ad) {
|
|
for (auto iter{linearOperations_.begin()}, iend{linearOperations_.end()};
|
|
iter != iend; ++iter) {
|
|
const auto &op{*iter};
|
|
std::visit(
|
|
common::visitors{
|
|
[&](const flat::LabelOp &op) {
|
|
auto *newBlock{CreateBlock(builder_->GetCurrentRegion())};
|
|
blockMap_.insert({op.get(), newBlock});
|
|
if (builder_->GetInsertionPoint()) {
|
|
builder_->CreateBranch(newBlock);
|
|
}
|
|
builder_->SetInsertionPoint(newBlock);
|
|
},
|
|
[&](const flat::GotoOp &op) {
|
|
CheckInsertionPoint();
|
|
AddOrQueueBranch(op.target);
|
|
builder_->ClearInsertionPoint();
|
|
},
|
|
[&](const flat::IndirectGotoOp &op) {
|
|
CheckInsertionPoint();
|
|
AddOrQueueIGoto(ad, op.symbol, op.labelRefs);
|
|
builder_->ClearInsertionPoint();
|
|
},
|
|
[&](const flat::ReturnOp &op) {
|
|
CheckInsertionPoint();
|
|
std::visit(
|
|
common::visitors{
|
|
[&](const parser::FailImageStmt *s) {
|
|
builder_->CreateRuntimeCall(RuntimeCallFailImage,
|
|
CreateFailImageArguments(*s));
|
|
builder_->CreateUnreachable();
|
|
},
|
|
[&](const parser::ReturnStmt *s) {
|
|
// alt-return
|
|
if (s->v) {
|
|
auto *exp{ExprRef(s->v->thing.thing)};
|
|
auto app{builder_->QualifiedCreateExpr(exp)};
|
|
builder_->CreateReturn(app);
|
|
} else {
|
|
auto zero{builder_->QualifiedCreateExpr(
|
|
CreateConstant(0))};
|
|
builder_->CreateReturn(zero);
|
|
}
|
|
},
|
|
[&](const parser::StopStmt *s) {
|
|
builder_->CreateRuntimeCall(
|
|
RuntimeCallStop, CreateStopArguments(*s));
|
|
builder_->CreateUnreachable();
|
|
},
|
|
},
|
|
op.u);
|
|
builder_->ClearInsertionPoint();
|
|
},
|
|
[&](const flat::ConditionalGotoOp &cop) {
|
|
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, cop.trueLabel, cop.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, cop.trueLabel, cop.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, cop.trueLabel, cop.falseLabel);
|
|
},
|
|
[&](const parser::Statement<parser::NonLabelDoStmt>
|
|
*s) {
|
|
AddOrQueueCGoto(
|
|
BuildLoopLatchExpression(&s->statement),
|
|
cop.trueLabel, cop.falseLabel);
|
|
}},
|
|
cop.u);
|
|
builder_->ClearInsertionPoint();
|
|
},
|
|
[&](const flat::SwitchIOOp &IOp) {
|
|
CheckInsertionPoint();
|
|
auto args{ComposeIOSwitchArgs(IOp)};
|
|
AddOrQueueSwitch<SwitchStmt>(
|
|
args.exp, args.values, args.labels, CreateSwitchHelper);
|
|
builder_->ClearInsertionPoint();
|
|
},
|
|
[&](const flat::SwitchOp &sop) {
|
|
CheckInsertionPoint();
|
|
std::visit(
|
|
common::visitors{
|
|
[&](auto) {
|
|
auto args{ComposeSwitchArgs(sop)};
|
|
AddOrQueueSwitch<SwitchStmt>(args.exp, args.values,
|
|
args.labels, CreateSwitchHelper);
|
|
},
|
|
[&](const parser::CaseConstruct *crct) {
|
|
auto args{ComposeSwitchCaseArguments(crct, sop.refs)};
|
|
AddOrQueueSwitch<SwitchCaseStmt>(args.exp,
|
|
args.values, args.labels, CreateSwitchCaseHelper);
|
|
},
|
|
[&](const parser::SelectRankConstruct *crct) {
|
|
auto args{ComposeSwitchRankArguments(crct, sop.refs)};
|
|
AddOrQueueSwitch<SwitchRankStmt>(args.exp,
|
|
args.values, args.labels, CreateSwitchRankHelper);
|
|
},
|
|
[&](const parser::SelectTypeConstruct *crct) {
|
|
auto args{ComposeSwitchTypeArguments(crct, sop.refs)};
|
|
AddOrQueueSwitch<SwitchTypeStmt>(args.exp,
|
|
args.values, args.labels, CreateSwitchTypeHelper);
|
|
},
|
|
},
|
|
sop.u);
|
|
builder_->ClearInsertionPoint();
|
|
},
|
|
[&](const flat::ActionOp &action) {
|
|
CheckInsertionPoint();
|
|
handleLinearAction(action, ad);
|
|
},
|
|
[&](const flat::DoIncrementOp &inc) {
|
|
CheckInsertionPoint();
|
|
handleLinearDoIncrement(inc);
|
|
},
|
|
[&](const flat::DoCompareOp &cmp) {
|
|
CheckInsertionPoint();
|
|
handleLinearDoCompare(cmp);
|
|
},
|
|
[&](const flat::BeginOp &con) {
|
|
std::visit(
|
|
common::visitors{
|
|
[&](const parser::AssociateConstruct *crct) {
|
|
using A = parser::Statement<parser::AssociateStmt>;
|
|
const auto &statement{std::get<A>(crct->t)};
|
|
const auto &position{statement.source};
|
|
EnterRegion(position);
|
|
InitiateConstruct(&statement.statement);
|
|
},
|
|
[&](const parser::BlockConstruct *crct) {
|
|
using A = parser::Statement<parser::BlockStmt>;
|
|
EnterRegion(std::get<A>(crct->t).source);
|
|
},
|
|
[&](const parser::CaseConstruct *crct) {
|
|
using A = parser::Statement<parser::SelectCaseStmt>;
|
|
InitiateConstruct(&std::get<A>(crct->t).statement);
|
|
},
|
|
[&](const parser::ChangeTeamConstruct *crct) {
|
|
using A = parser::Statement<parser::ChangeTeamStmt>;
|
|
const auto &statement{std::get<A>(crct->t)};
|
|
EnterRegion(statement.source);
|
|
InitiateConstruct(&statement.statement);
|
|
},
|
|
[&](const parser::DoConstruct *crct) {
|
|
using A = parser::Statement<parser::NonLabelDoStmt>;
|
|
const auto &statement{std::get<A>(crct->t)};
|
|
EnterRegion(statement.source);
|
|
InitiateConstruct(&statement.statement);
|
|
},
|
|
[&](const parser::IfConstruct *crct) {
|
|
using A = parser::Statement<parser::IfThenStmt>;
|
|
InitiateConstruct(&std::get<A>(crct->t).statement);
|
|
},
|
|
[&](const parser::SelectRankConstruct *crct) {
|
|
using A = parser::Statement<parser::SelectRankStmt>;
|
|
const auto &statement{std::get<A>(crct->t)};
|
|
EnterRegion(statement.source);
|
|
},
|
|
[&](const parser::SelectTypeConstruct *crct) {
|
|
using A = parser::Statement<parser::SelectTypeStmt>;
|
|
const auto &statement{std::get<A>(crct->t)};
|
|
EnterRegion(statement.source);
|
|
},
|
|
[&](const parser::WhereConstruct *crct) {
|
|
using A =
|
|
parser::Statement<parser::WhereConstructStmt>;
|
|
InitiateConstruct(&std::get<A>(crct->t).statement);
|
|
},
|
|
[&](const parser::ForallConstruct *crct) {
|
|
using A =
|
|
parser::Statement<parser::ForallConstructStmt>;
|
|
InitiateConstruct(&std::get<A>(crct->t).statement);
|
|
},
|
|
[](const parser::CriticalConstruct *) { /*fixme*/ },
|
|
[](const parser::CompilerDirective *) { /*fixme*/ },
|
|
[](const parser::OpenMPConstruct *) { /*fixme*/ },
|
|
[](const parser::OpenMPEndLoopDirective
|
|
*) { /*fixme*/ },
|
|
},
|
|
con.u);
|
|
auto next{iter};
|
|
const auto &nextOp{*(++next)};
|
|
if (auto *op{std::get_if<flat::LabelOp>(&nextOp.u)}) {
|
|
blockMap_.insert({op->get(), builder_->GetInsertionPoint()});
|
|
++iter;
|
|
}
|
|
},
|
|
[&](const flat::EndOp &con) {
|
|
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();
|
|
},
|
|
},
|
|
con.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(flat::LabelRef 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, flat::LabelRef dest,
|
|
const LabelMapType &map) {
|
|
builder->SetInsertionPoint(block);
|
|
CHECK(map.find(dest) != map.end() && "no destination");
|
|
builder->CreateBranch(map.find(dest)->second);
|
|
},
|
|
builder_, builder_->GetInsertionPoint(), dest, _1));
|
|
}
|
|
}
|
|
|
|
void AddOrQueueCGoto(Statement *condition, flat::LabelRef trueBlock,
|
|
flat::LabelRef 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,
|
|
flat::LabelRef trueDest, flat::LabelRef 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));
|
|
}
|
|
}
|
|
|
|
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<flat::LabelRef> &labels) {
|
|
auto useLabels{labels.empty() ? flat::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<flat::LabelRef> &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<flat::Op> 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();
|
|
}
|
|
|
|
// debug channel
|
|
llvm::raw_ostream *debugChannel;
|
|
|
|
llvm::raw_ostream &DebugChannel() {
|
|
return debugChannel ? *debugChannel : llvm::errs();
|
|
}
|
|
|
|
static void SetDebugChannel(llvm::raw_ostream *output) {
|
|
debugChannel = output;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|