[flang] remove FIR

Original-commit: flang-compiler/f18@008ad16e28
Reviewed-on: https://github.com/flang-compiler/f18/pull/489
This commit is contained in:
Eric Schweitz 2019-06-07 10:17:41 -07:00
parent 6bbcc8c1d8
commit 5c978b94a2
23 changed files with 0 additions and 4766 deletions

View File

@ -14,6 +14,5 @@
add_subdirectory(common)
add_subdirectory(evaluate)
add_subdirectory(FIR)
add_subdirectory(parser)
add_subdirectory(semantics)

View File

@ -1,35 +0,0 @@
# 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.
add_library(FortranFIR
basicblock.cc
builder.cc
flattened.cc
graph-writer.cc
procedure.cc
program.cc
region.cc
statements.cc
)
target_link_libraries(FortranFIR
FortranCommon
)
install (TARGETS FortranFIR
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)

File diff suppressed because it is too large Load Diff

View File

@ -1,37 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_AFFORESTATION_H_
#define FORTRAN_FIR_AFFORESTATION_H_
#include <string>
namespace Fortran::parser {
struct Program;
}
namespace Fortran::semantics {
class SemanticsContext;
}
namespace Fortran::FIR {
class Program;
void SetDebugChannel(const std::string &filename);
Program *CreateFortranIR(const parser::Program &program,
semantics::SemanticsContext &semanticsContext, bool debugLinearIR = false);
}
#endif // FORTRAN_FIR_AFFORESTATION_H_

View File

@ -1,59 +0,0 @@
// 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 "basicblock.h"
#include "statements.h"
namespace Fortran::FIR {
BasicBlock::BasicBlock(Region *parentRegion, BasicBlock *insertBefore)
: ChildMixin{parentRegion} {
parent->insertBefore(this, insertBefore);
}
BasicBlock::~BasicBlock() { statementList_.clear(); }
void BasicBlock::insertBefore(Statement *stmt, Statement *before) {
if (before) {
statementList_.insert(before->getIterator(), stmt);
} else {
statementList_.push_back(stmt);
}
}
void BasicBlock::addPred(BasicBlock *bb) {
for (auto *p : preds_) {
if (p == bb) {
return;
}
}
preds_.push_back(bb);
}
const Statement *BasicBlock::terminator() const {
if (statementList_.empty()) {
return nullptr;
}
const auto &lastStmt{statementList_.back()};
return std::visit(
[&lastStmt](auto stmt) -> const Statement * {
if constexpr (std::is_base_of_v<TerminatorStmt_impl,
std::decay_t<decltype(stmt)>>) {
return &lastStmt;
}
return nullptr;
},
lastStmt.u);
}
}

View File

@ -1,72 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_BASICBLOCK_H_
#define FORTRAN_FIR_BASICBLOCK_H_
#include "region.h"
#include <iostream>
namespace Fortran::FIR {
class Region;
class Statement;
class BasicBlock final : public llvm::ilist_node<BasicBlock>,
public ChildMixin<BasicBlock, Region>,
public Value_impl {
public:
using StatementListType = llvm::iplist<Statement>;
using iterator = StatementListType::iterator;
using const_iterator = StatementListType::const_iterator;
using reverse_iterator = StatementListType::reverse_iterator;
using const_reverse_iterator = StatementListType::const_reverse_iterator;
BasicBlock(const BasicBlock &) = delete;
BasicBlock &operator=(const BasicBlock &) = delete;
~BasicBlock();
// callback to allow general access to contained sublist(s)
StatementListType &getSublist(Statement *) { return Statements(); }
void insertBefore(Statement *stmt, Statement *before = nullptr);
static BasicBlock *Create(
Region *parentRegion, BasicBlock *insertBefore = nullptr) {
return new BasicBlock(parentRegion, insertBefore);
}
const Statement *terminator() const;
Statement *terminator() {
return const_cast<Statement *>(
const_cast<const BasicBlock *>(this)->terminator());
}
void SetRegion(Region *region) { parent = region; }
Region *GetRegion() const { return parent; }
void addPred(BasicBlock *bb);
std::vector<BasicBlock *> &preds() { return preds_; }
StatementListType &Statements() { return statementList_; }
BasicBlock *SplitEdge(BasicBlock *toBlock) { return nullptr; }
private:
StatementListType statementList_;
std::vector<BasicBlock *> preds_;
explicit BasicBlock(Region *parentRegion, BasicBlock *insertBefore);
};
inline std::list<BasicBlock *> pred_list(BasicBlock &block) {
return std::list<BasicBlock *>{block.preds().begin(), block.preds().end()};
}
}
#endif

View File

@ -1,21 +0,0 @@
// 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 "builder.h"
#include <ostream>
namespace Fortran::FIR {
void FIRBuilder::dump() const { std::cerr << "builder state:\n"; }
}

View File

@ -1,167 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_BUILDER_H_
#define FORTRAN_FIR_BUILDER_H_
#include "statements.h"
#include <initializer_list>
namespace Fortran::FIR {
/// Helper class for building FIR statements
struct FIRBuilder {
explicit FIRBuilder(BasicBlock &block)
: cursorRegion_{block.getParent()}, cursorBlock_{&block} {}
template<typename A> Statement *Insert(A &&s) {
CHECK(GetInsertionPoint());
auto *statement{Statement::Create(GetInsertionPoint(), std::forward<A>(s))};
return statement;
}
template<typename A, typename B> QualifiedStmt<A> QualifiedInsert(B &&s) {
CHECK(GetInsertionPoint());
auto *statement{Statement::Create(GetInsertionPoint(), std::forward<B>(s))};
return QualifiedStmtCreate<A, B>(statement);
}
template<typename A> Statement *InsertTerminator(A &&s) {
auto *stmt{Insert(std::forward<A>(s))};
for (auto *block : s.succ_blocks()) {
block->addPred(GetInsertionPoint());
}
return stmt;
}
// manage the insertion point
void SetInsertionPoint(BasicBlock *bb) {
cursorBlock_ = bb;
cursorRegion_ = bb->getParent();
}
void ClearInsertionPoint() { cursorBlock_ = nullptr; }
BasicBlock *GetInsertionPoint() const { return cursorBlock_; }
// create the various statements
QualifiedStmt<Addressable_impl> CreateAddr(const Expression *e) {
return QualifiedInsert<Addressable_impl>(LocateExprStmt::Create(e));
}
QualifiedStmt<Addressable_impl> CreateAddr(Expression &&e) {
return QualifiedInsert<Addressable_impl>(
LocateExprStmt::Create(std::move(e)));
}
QualifiedStmt<AllocateInsn> CreateAlloc(Type type) {
return QualifiedInsert<AllocateInsn>(AllocateInsn::Create(type));
}
Statement *CreateBranch(BasicBlock *block) {
return InsertTerminator(BranchStmt::Create(block));
}
Statement *CreateCall(
const FunctionType *type, const Value callee, CallArguments &&args) {
return Insert(CallStmt::Create(type, callee, std::move(args)));
}
Statement *CreateConditionalBranch(
Statement *cond, BasicBlock *trueBlock, BasicBlock *falseBlock) {
return InsertTerminator(BranchStmt::Create(cond, trueBlock, falseBlock));
}
Statement *CreateDealloc(QualifiedStmt<AllocateInsn> alloc) {
return Insert(DeallocateInsn::Create(alloc));
}
Statement *CreateExpr(const Expression *e) {
return Insert(ApplyExprStmt::Create(e));
}
Statement *CreateExpr(Expression &&e) {
return Insert(ApplyExprStmt::Create(std::move(e)));
}
ApplyExprStmt *MakeAsExpr(const Expression *e) {
return GetApplyExpr(CreateExpr(e));
}
QualifiedStmt<ApplyExprStmt> QualifiedCreateExpr(const Expression *e) {
return QualifiedInsert<ApplyExprStmt>(ApplyExprStmt::Create(e));
}
QualifiedStmt<ApplyExprStmt> QualifiedCreateExpr(Expression &&e) {
return QualifiedInsert<ApplyExprStmt>(ApplyExprStmt::Create(std::move(e)));
}
Statement *CreateIndirectBr(Variable *v, const std::vector<BasicBlock *> &p) {
return InsertTerminator(IndirectBranchStmt::Create(v, p));
}
Statement *CreateIOCall(InputOutputCallType c, IOCallArguments &&a) {
return Insert(IORuntimeStmt::Create(c, std::move(a)));
}
Statement *CreateLoad(Statement *addr) {
return Insert(LoadInsn::Create(addr));
}
QualifiedStmt<Addressable_impl> CreateLocal(
Type type, const Expression &expr, int alignment = 0) {
return QualifiedInsert<Addressable_impl>(
AllocateLocalInsn::Create(type, expr, alignment));
}
Statement *CreateNullify(Statement *s) {
return Insert(DisassociateInsn::Create(s));
}
Statement *CreateReturn(QualifiedStmt<ApplyExprStmt> expr) {
return InsertTerminator(ReturnStmt::Create(expr));
}
Statement *CreateRuntimeCall(
RuntimeCallType call, RuntimeCallArguments &&arguments) {
return Insert(RuntimeStmt::Create(call, std::move(arguments)));
}
Statement *CreateStore(
QualifiedStmt<Addressable_impl> addr, Statement *value) {
return Insert(StoreInsn::Create(addr, value));
}
Statement *CreateStore(
QualifiedStmt<Addressable_impl> addr, BasicBlock *value) {
return Insert(StoreInsn::Create(addr, value));
}
Statement *CreateSwitch(
Value cond, const SwitchStmt::ValueSuccPairListType &pairs) {
return InsertTerminator(SwitchStmt::Create(cond, pairs));
}
Statement *CreateSwitchCase(
Value cond, const SwitchCaseStmt::ValueSuccPairListType &pairs) {
return InsertTerminator(SwitchCaseStmt::Create(cond, pairs));
}
Statement *CreateSwitchType(
Value cond, const SwitchTypeStmt::ValueSuccPairListType &pairs) {
return InsertTerminator(SwitchTypeStmt::Create(cond, pairs));
}
Statement *CreateSwitchRank(
Value cond, const SwitchRankStmt::ValueSuccPairListType &pairs) {
return InsertTerminator(SwitchRankStmt::Create(cond, pairs));
}
Statement *CreateUnreachable() {
return InsertTerminator(UnreachableStmt::Create());
}
void PushBlock(BasicBlock *block) { blockStack_.push_back(block); }
BasicBlock *PopBlock() {
auto *block{blockStack_.back()};
blockStack_.pop_back();
return block;
}
void dump() const;
void SetCurrentRegion(Region *region) { cursorRegion_ = region; }
Region *GetCurrentRegion() const { return cursorRegion_; }
private:
Region *cursorRegion_;
BasicBlock *cursorBlock_;
std::vector<BasicBlock *> blockStack_;
};
}
#endif

View File

@ -1,114 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_COMMON_H_
#define FORTRAN_FIR_COMMON_H_
#include "../common/idioms.h"
#include "../common/indirection.h"
#include "../evaluate/expression.h"
#include "../evaluate/type.h"
#include "../evaluate/variable.h"
#include "../parser/parse-tree.h"
#include "../semantics/symbol.h"
#include "llvm/Support/raw_ostream.h"
// Some useful, self-documenting macros for failure modes
#define SEMANTICS_FAILED(STRING) DIE("semantics bug: " STRING)
#define SEMANTICS_CHECK(CONDITION, STRING) \
if (CONDITION) { \
} else { \
DIE("semantics bug: " STRING); \
}
#define WRONG_PATH() DIE("control should not reach here")
namespace Fortran::FIR {
CLASS_TRAIT(ValueTrait)
class Value_impl {
public:
using ValueTrait = std::true_type;
std::string dump() const { return {}; }
};
struct Nothing {};
constexpr Nothing NOTHING{};
class Value;
class Statement;
class BasicBlock;
class Region;
class Procedure;
class Program;
class GraphWriter;
class DataObject;
struct Attribute {
enum { IntentIn, IntentOut, IntentInOut, Value } attribute;
unsigned short position;
};
using FunctionType = evaluate::SomeType; // TODO: what should this be?
using AttributeList = std::vector<Attribute>;
enum struct LinkageTypes { Public, Hidden, External };
using Expression = evaluate::Expr<evaluate::SomeType>;
using Variable = const semantics::Symbol *;
using PathVariable = const parser::Variable;
using Scope = const semantics::Scope;
using PHIPair = std::pair<Value, BasicBlock *>;
using CallArguments = std::vector<Expression>;
using TypeRep = semantics::DeclTypeSpec; // FIXME
using Type = const TypeRep *;
enum InputOutputCallType {
InputOutputCallBackspace = 11,
InputOutputCallClose,
InputOutputCallEndfile,
InputOutputCallFlush,
InputOutputCallInquire,
InputOutputCallOpen,
InputOutputCallPrint,
InputOutputCallRead,
InputOutputCallRewind,
InputOutputCallWait,
InputOutputCallWrite,
InputOutputCallSIZE = InputOutputCallWrite - InputOutputCallBackspace + 1
};
using IOCallArguments = CallArguments;
enum RuntimeCallType {
RuntimeCallFailImage = 31,
RuntimeCallStop,
RuntimeCallPause,
RuntimeCallFormTeam,
RuntimeCallEventPost,
RuntimeCallEventWait,
RuntimeCallSyncAll,
RuntimeCallSyncImages,
RuntimeCallSyncMemory,
RuntimeCallSyncTeam,
RuntimeCallLock,
RuntimeCallUnlock,
RuntimeCallSIZE = RuntimeCallUnlock - RuntimeCallFailImage + 1
};
using RuntimeCallArguments = CallArguments;
llvm::raw_ostream &DebugChannel();
void SetDebugChannel(const std::string &filename);
}
#endif // FORTRAN_FIR_COMMON_H_

View File

@ -1,801 +0,0 @@
// 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 "flattened.h"
#include "../parser/parse-tree-visitor.h"
namespace Fortran::FIR {
namespace flat {
LabelBuilder::LabelBuilder() : referenced(32), counter{0u} {}
LabelRef LabelBuilder::getNext() {
LabelRef next{counter++};
auto cap{referenced.capacity()};
if (cap < counter) {
referenced.reserve(2 * cap);
}
referenced[next] = false;
return next;
}
void LabelBuilder::setReferenced(LabelRef label) { referenced[label] = true; }
bool LabelBuilder::isReferenced(LabelRef label) const {
return referenced[label];
}
LabelOp::LabelOp(LabelBuilder &builder)
: builder_{builder}, label_{builder.getNext()} {}
LabelOp::LabelOp(const LabelOp &that)
: builder_{that.builder_}, label_{that.label_} {}
LabelOp &LabelOp::operator=(const LabelOp &that) {
CHECK(&builder_ == &that.builder_);
label_ = that.label_;
return *this;
}
void LabelOp::setReferenced() const { builder_.setReferenced(label_); }
bool LabelOp::isReferenced() const { return builder_.isReferenced(label_); }
static void AddAssign(AnalysisData &ad, const semantics::Symbol *symbol,
const parser::Label &label) {
ad.assignMap[symbol].insert(label);
}
std::vector<LabelRef> GetAssign(
AnalysisData &ad, const semantics::Symbol *symbol) {
std::vector<LabelRef> result;
for (auto lab : ad.assignMap[symbol]) {
result.emplace_back(lab);
}
return result;
}
static std::tuple<const parser::Name *, LabelRef, LabelRef> FindStack(
const std::vector<std::tuple<const parser::Name *, LabelRef, LabelRef>>
&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 {};
}
LabelOp FetchLabel(AnalysisData &ad, const parser::Label &label) {
auto iter{ad.labelMap.find(label)};
if (iter == ad.labelMap.end()) {
LabelOp ll{ad.labelBuilder};
ll.setReferenced();
ad.labelMap.insert({label, ll});
return ll;
}
return iter->second;
}
static LabelOp BuildNewLabel(AnalysisData &ad) {
return LabelOp{ad.labelBuilder};
}
template<typename A> parser::Label GetErr(const A &stmt) {
if constexpr (std::is_same_v<A, parser::ReadStmt> ||
std::is_same_v<A, 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<A, parser::WaitStmt> ||
std::is_same_v<A, parser::OpenStmt> ||
std::is_same_v<A, parser::CloseStmt> ||
std::is_same_v<A, parser::BackspaceStmt> ||
std::is_same_v<A, parser::EndfileStmt> ||
std::is_same_v<A, parser::RewindStmt> ||
std::is_same_v<A, 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<A, 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 A> parser::Label GetEor(const A &stmt) {
if constexpr (std::is_same_v<A, parser::ReadStmt> ||
std::is_same_v<A, 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<A, 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 A> parser::Label GetEnd(const A &stmt) {
if constexpr (std::is_same_v<A, parser::ReadStmt> ||
std::is_same_v<A, 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<A, 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 A>
void errLabelSpec(const A &s, std::list<Op> &ops,
const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad) {
if (auto errLab{GetErr(s)}) {
std::optional<LabelRef> errRef{FetchLabel(ad, errLab).get()};
LabelOp next{BuildNewLabel(ad)};
ops.emplace_back(SwitchIOOp{s, next, ec.source, errRef});
ops.emplace_back(next);
} else {
ops.emplace_back(ActionOp{ec});
}
}
template<typename A>
void threeLabelSpec(const A &s, std::list<Op> &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<LabelRef> errRef;
if (errLab) {
errRef = FetchLabel(ad, errLab).get();
}
std::optional<LabelRef> eorRef;
if (eorLab) {
eorRef = FetchLabel(ad, eorLab).get();
}
std::optional<LabelRef> endRef;
if (endLab) {
endRef = FetchLabel(ad, endLab).get();
}
auto next{BuildNewLabel(ad)};
ops.emplace_back(SwitchIOOp{s, next, ec.source, errRef, eorRef, endRef});
ops.emplace_back(next);
} else {
ops.emplace_back(ActionOp{ec});
}
}
template<typename A>
std::vector<LabelRef> toLabelRef(AnalysisData &ad, const A &labels) {
std::vector<LabelRef> result;
for (auto label : labels) {
result.emplace_back(FetchLabel(ad, label).get());
}
CHECK(result.size() == labels.size());
return result;
}
template<typename A>
std::vector<LabelRef> toLabelRef(
const LabelOp &next, AnalysisData &ad, const A &labels) {
std::vector<LabelRef> result;
result.emplace_back(next);
auto refs{toLabelRef(ad, labels)};
result.insert(result.end(), refs.begin(), refs.end());
CHECK(result.size() == labels.size() + 1);
return result;
}
static 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;
}
static 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;
}
static LabelRef 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;
}
template<typename A> std::string GetSource(const A *s) {
return s->source.ToString();
}
template<typename A, typename B> std::string GetSource(const B *s) {
return GetSource(&std::get<parser::Statement<A>>(s->t));
}
void LabelOp::dump() const { DebugChannel() << "label_" << get() << ":\n"; }
void GotoOp::dump() const {
DebugChannel() << "\tgoto %label_" << target << " ["
<< std::visit(
common::visitors{
[](ArtificialJump) { return ""s; },
[&](auto *) { return GetSource(this); },
},
u)
<< "]\n";
}
void ReturnOp::dump() const {
DebugChannel() << "\treturn [" << GetSource(this) << "]\n";
}
void ConditionalGotoOp::dump() const {
DebugChannel() << "\tcbranch .T.: %label_" << trueLabel << " .F.: %label_"
<< falseLabel << " ["
<< std::visit(
common::visitors{
[](const parser::IfStmt *) { return "if-stmt"s; },
[&](auto *s) { return GetSource(s); },
},
u)
<< "]\n";
}
void SwitchIOOp::dump() const {
DebugChannel() << "\tio-call";
if (errLabel.has_value()) {
DebugChannel() << " ERR: %label_" << errLabel.value();
}
if (eorLabel.has_value()) {
DebugChannel() << " EOR: %label_" << eorLabel.value();
}
if (endLabel.has_value()) {
DebugChannel() << " END: %label_" << endLabel.value();
}
DebugChannel() << " [" << GetSource(this) << "]\n";
}
void SwitchOp::dump() const {
DebugChannel() << "\tswitch [" << GetSource(this) << "]\n";
}
void ActionOp::dump() const { DebugChannel() << '\t' << GetSource(v) << '\n'; }
template<typename A> std::string dumpConstruct(const A &a) {
return std::visit(
common::visitors{
[](const parser::AssociateConstruct *c) {
return GetSource<parser::AssociateStmt>(c);
},
[](const parser::BlockConstruct *c) {
return GetSource<parser::BlockStmt>(c);
},
[](const parser::CaseConstruct *c) {
return GetSource<parser::SelectCaseStmt>(c);
},
[](const parser::ChangeTeamConstruct *c) {
return GetSource<parser::ChangeTeamStmt>(c);
},
[](const parser::CriticalConstruct *c) {
return GetSource<parser::CriticalStmt>(c);
},
[](const parser::DoConstruct *c) {
return GetSource<parser::NonLabelDoStmt>(c);
},
[](const parser::IfConstruct *c) {
return GetSource<parser::IfThenStmt>(c);
},
[](const parser::SelectRankConstruct *c) {
return GetSource<parser::SelectRankStmt>(c);
},
[](const parser::SelectTypeConstruct *c) {
return GetSource<parser::SelectTypeStmt>(c);
},
[](const parser::WhereConstruct *c) {
return GetSource<parser::WhereConstructStmt>(c);
},
[](const parser::ForallConstruct *c) {
return GetSource(
&std::get<parser::Statement<parser::ForallConstructStmt>>(
c->t));
},
[](const parser::CompilerDirective *c) { return GetSource(c); },
[](const parser::OpenMPConstruct *) { return "openmp"s; },
[](const parser::OpenMPEndLoopDirective *) {
return "openmp end loop"s;
},
},
a);
}
void BeginOp::dump() const {
DebugChannel() << "\t[" << dumpConstruct(u) << "] {\n";
}
void EndOp::dump() const {
DebugChannel() << "\t} [" << dumpConstruct(u) << "]\n";
}
void IndirectGotoOp::dump() const {
DebugChannel() << "\tindirect-goto " << symbol->name().ToString() << ':';
for (auto lab : labelRefs) {
DebugChannel() << ' ' << lab;
}
DebugChannel() << '\n';
}
void DoIncrementOp::dump() const {
using A = parser::Statement<parser::NonLabelDoStmt>;
DebugChannel() << "\tincrement [" << GetSource(&std::get<A>(v->t)) << "]\n";
}
void DoCompareOp::dump() const {
using A = parser::Statement<parser::NonLabelDoStmt>;
DebugChannel() << "\tcompare [" << GetSource(&std::get<A>(v->t)) << "]\n";
}
void Op::Build(std::list<Op> &ops,
const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad) {
std::visit(
common::visitors{
[&](const auto &s) { ops.emplace_back(ActionOp{ec}); },
[&](const common::Indirection<parser::CallStmt> &s) {
if (hasAltReturns(s.value())) {
auto next{BuildNewLabel(ad)};
auto alts{getAltReturnLabels(s.value().v)};
auto labels{toLabelRef(next, ad, alts)};
ops.emplace_back(
SwitchOp{s.value(), std::move(labels), ec.source});
ops.emplace_back(next);
} else {
ops.emplace_back(ActionOp{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(ActionOp{ec});
},
[&](const common::Indirection<parser::CycleStmt> &s) {
ops.emplace_back(GotoOp{s.value(),
s.value().v
? std::get<2>(FindStack(ad.nameStack, &s.value().v.value()))
: NearestEnclosingDoConstruct(ad),
ec.source});
},
[&](const common::Indirection<parser::ExitStmt> &s) {
ops.emplace_back(GotoOp{s.value(),
s.value().v
? std::get<1>(FindStack(ad.nameStack, &s.value().v.value()))
: NearestEnclosingDoConstruct(ad),
ec.source});
},
[&](const common::Indirection<parser::GotoStmt> &s) {
ops.emplace_back(GotoOp{
s.value(), FetchLabel(ad, s.value().v).get(), ec.source});
},
[&](const parser::FailImageStmt &s) {
ops.emplace_back(ReturnOp{s, ec.source});
},
[&](const common::Indirection<parser::ReturnStmt> &s) {
ops.emplace_back(ReturnOp{s.value(), ec.source});
},
[&](const common::Indirection<parser::StopStmt> &s) {
ops.emplace_back(ActionOp{ec});
ops.emplace_back(ReturnOp{s.value(), ec.source});
},
[&](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(
next, ad, std::get<std::list<parser::Label>>(s.value().t))};
ops.emplace_back(SwitchOp{s.value(), std::move(labels), ec.source});
ops.emplace_back(next);
},
[&](const common::Indirection<parser::ArithmeticIfStmt> &s) {
ops.emplace_back(SwitchOp{s.value(),
toLabelRef(ad,
std::list{std::get<1>(s.value().t),
std::get<2>(s.value().t), std::get<3>(s.value().t)}),
ec.source});
},
[&](const common::Indirection<parser::AssignedGotoStmt> &s) {
ops.emplace_back(
IndirectGotoOp{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(ConditionalGotoOp{s.value(), then, endif});
ops.emplace_back(then);
ops.emplace_back(ActionOp{ec});
ops.emplace_back(endif);
},
},
ec.statement.u);
}
template<typename> struct ElementMap;
template<> struct ElementMap<parser::CaseConstruct> {
using type = parser::CaseConstruct::Case;
};
template<> struct ElementMap<parser::SelectRankConstruct> {
using type = parser::SelectRankConstruct::RankCase;
};
template<> struct ElementMap<parser::SelectTypeConstruct> {
using type = parser::SelectTypeConstruct::TypeCase;
};
struct ControlFlowAnalyzer {
explicit ControlFlowAnalyzer(std::list<Op> &ops, AnalysisData &ad)
: linearOps{ops}, ad{ad} {}
LabelOp buildNewLabel() { return BuildNewLabel(ad); }
Op findLabel(const parser::Label &lab) {
auto iter{ad.labelMap.find(lab)};
if (iter == ad.labelMap.end()) {
LabelOp 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>) {
Op::Build(linearOps, stmt, ad);
}
return true;
}
template<typename A>
void appendIfLabeled(const parser::Statement<A> &stmt, std::list<Op> &ops) {
if (stmt.label) {
ops.emplace_back(findLabel(*stmt.label));
}
}
// named constructs
template<typename A> bool linearConstruct(const A &construct) {
std::list<Op> ops;
LabelOp label{buildNewLabel()};
const parser::Name *name{getName(construct)};
ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel);
appendIfLabeled(std::get<0>(construct.t), ops);
ops.emplace_back(BeginOp{construct});
ControlFlowAnalyzer cfa{ops, ad};
Walk(std::get<parser::Block>(construct.t), cfa);
ops.emplace_back(label);
appendIfLabeled(std::get<2>(construct.t), ops);
ops.emplace_back(EndOp{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<Op> ops;
LabelOp 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);
appendIfLabeled(
std::get<parser::Statement<parser::BlockStmt>>(construct.t), ops);
ops.emplace_back(BeginOp{construct});
ControlFlowAnalyzer cfa{ops, ad};
Walk(std::get<parser::Block>(construct.t), cfa);
appendIfLabeled(
std::get<parser::Statement<parser::EndBlockStmt>>(construct.t), ops);
ops.emplace_back(EndOp{construct});
ops.emplace_back(label);
linearOps.splice(linearOps.end(), ops);
ad.nameStack.pop_back();
return false;
}
bool Pre(const parser::DoConstruct &construct) {
std::list<Op> ops;
LabelOp backedgeLab{buildNewLabel()};
LabelOp incrementLab{buildNewLabel()};
LabelOp entryLab{buildNewLabel()};
LabelOp exitLab{buildNewLabel()};
const parser::Name *name{getName(construct)};
LabelRef exitOpRef{GetLabelRef(exitLab)};
ad.nameStack.emplace_back(name, exitOpRef, GetLabelRef(incrementLab));
appendIfLabeled(
std::get<parser::Statement<parser::NonLabelDoStmt>>(construct.t), ops);
ops.emplace_back(BeginOp{construct});
ops.emplace_back(GotoOp{GetLabelRef(backedgeLab)});
ops.emplace_back(incrementLab);
ops.emplace_back(DoIncrementOp{construct});
ops.emplace_back(backedgeLab);
ops.emplace_back(DoCompareOp{construct});
ops.emplace_back(ConditionalGotoOp{
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);
appendIfLabeled(
std::get<parser::Statement<parser::EndDoStmt>>(construct.t), ops);
ops.emplace_back(GotoOp{GetLabelRef(incrementLab)});
ops.emplace_back(EndOp{construct});
ops.emplace_back(exitLab);
linearOps.splice(linearOps.end(), ops);
ad.nameStack.pop_back();
return false;
}
bool Pre(const parser::IfConstruct &construct) {
std::list<Op> ops;
LabelOp thenLab{buildNewLabel()};
LabelOp elseLab{buildNewLabel()};
LabelOp exitLab{buildNewLabel()};
const parser::Name *name{getName(construct)};
ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel);
appendIfLabeled(
std::get<parser::Statement<parser::IfThenStmt>>(construct.t), ops);
ops.emplace_back(BeginOp{construct});
ops.emplace_back(ConditionalGotoOp{
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);
LabelRef exitOpRef{GetLabelRef(exitLab)};
ops.emplace_back(GotoOp{exitOpRef});
for (const auto &elseIfBlock :
std::get<std::list<parser::IfConstruct::ElseIfBlock>>(construct.t)) {
appendIfLabeled(
std::get<parser::Statement<parser::ElseIfStmt>>(elseIfBlock.t), ops);
ops.emplace_back(elseLab);
LabelOp newThenLab{buildNewLabel()};
LabelOp newElseLab{buildNewLabel()};
ops.emplace_back(ConditionalGotoOp{
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(GotoOp{exitOpRef});
elseLab = newElseLab;
}
ops.emplace_back(elseLab);
if (const auto &optElseBlock{
std::get<std::optional<parser::IfConstruct::ElseBlock>>(
construct.t)}) {
appendIfLabeled(
std::get<parser::Statement<parser::ElseStmt>>(optElseBlock->t), ops);
Walk(std::get<parser::Block>(optElseBlock->t), cfa);
}
ops.emplace_back(GotoOp{exitOpRef});
ops.emplace_back(exitLab);
appendIfLabeled(
std::get<parser::Statement<parser::EndIfStmt>>(construct.t), ops);
ops.emplace_back(EndOp{construct});
linearOps.splice(linearOps.end(), ops);
ad.nameStack.pop_back();
return false;
}
template<typename A> bool Multiway(const A &construct) {
using B = typename ElementMap<A>::type;
std::list<Op> ops;
LabelOp exitLab{buildNewLabel()};
const parser::Name *name{getName(construct)};
ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel);
appendIfLabeled(std::get<0>(construct.t), ops);
ops.emplace_back(BeginOp{construct});
const auto N{std::get<std::list<B>>(construct.t).size()};
LabelRef exitOpRef{GetLabelRef(exitLab)};
if (N > 0) {
typename std::list<B>::size_type i;
std::vector<LabelOp> toLabels;
for (i = 0; i != N; ++i) {
toLabels.emplace_back(buildNewLabel());
}
std::vector<LabelRef> targets;
for (i = 0; i != N; ++i) {
targets.emplace_back(GetLabelRef(toLabels[i]));
}
ops.emplace_back(
SwitchOp{construct, targets, std::get<0>(construct.t).source});
ControlFlowAnalyzer cfa{ops, ad};
i = 0;
for (const auto &caseBlock : std::get<std::list<B>>(construct.t)) {
ops.emplace_back(toLabels[i++]);
appendIfLabeled(std::get<0>(caseBlock.t), ops);
Walk(std::get<parser::Block>(caseBlock.t), cfa);
ops.emplace_back(GotoOp{exitOpRef});
}
}
ops.emplace_back(exitLab);
appendIfLabeled(std::get<2>(construct.t), ops);
ops.emplace_back(EndOp{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<Op> ops;
LabelOp label{buildNewLabel()};
const parser::Name *name{getName(c)};
ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel);
appendIfLabeled(
std::get<parser::Statement<parser::WhereConstructStmt>>(c.t), ops);
ops.emplace_back(BeginOp{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);
appendIfLabeled(
std::get<parser::Statement<parser::EndWhereStmt>>(c.t), ops);
ops.emplace_back(EndOp{c});
linearOps.splice(linearOps.end(), ops);
ad.nameStack.pop_back();
return false;
}
bool Pre(const parser::ForallConstruct &construct) {
std::list<Op> ops;
LabelOp label{buildNewLabel()};
const parser::Name *name{getName(construct)};
ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel);
appendIfLabeled(
std::get<parser::Statement<parser::ForallConstructStmt>>(construct.t),
ops);
ops.emplace_back(BeginOp{construct});
ControlFlowAnalyzer cfa{ops, ad};
Walk(std::get<std::list<parser::ForallBodyConstruct>>(construct.t), cfa);
ops.emplace_back(label);
appendIfLabeled(
std::get<parser::Statement<parser::EndForallStmt>>(construct.t), ops);
ops.emplace_back(EndOp{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;
}
LabelRef GetLabelRef(const LabelOp &label) {
label.setReferenced();
return label;
}
LabelRef GetLabelRef(const parser::Label &label) {
return FetchLabel(ad, label);
}
std::list<Op> &linearOps;
AnalysisData &ad;
};
}
void dump(const std::list<flat::Op> &ops) {
for (auto &op : ops) {
op.dump();
}
}
template<typename A>
void CreateFlatIR(const A &ptree, std::list<flat::Op> &ops, AnalysisData &ad) {
flat::ControlFlowAnalyzer linearize{ops, ad};
Walk(ptree, linearize);
}
#define INSTANTIATE_EXPLICITLY(T) \
template void CreateFlatIR<parser::T>( \
const parser::T &, std::list<flat::Op> &, AnalysisData &)
INSTANTIATE_EXPLICITLY(MainProgram);
INSTANTIATE_EXPLICITLY(FunctionSubprogram);
INSTANTIATE_EXPLICITLY(SubroutineSubprogram);
}

View File

@ -1,264 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_FLATTENED_H_
#define FORTRAN_FIR_FLATTENED_H_
#include "common.h"
#include "mixin.h"
#include "../parser/parse-tree.h"
#include <list>
#include <map>
#include <set>
#include <vector>
namespace Fortran::FIR {
struct AnalysisData;
namespace flat {
// This is a flattened, linearized representation of the parse tree. It captures
// the executable specification of the input program. The flattened IR can be
// used to construct the Fortran IR.
struct Op;
struct LabelOp;
struct GotoOp;
struct ReturnOp;
struct ConditionalGotoOp;
struct SwitchIOOp;
struct SwitchOp;
struct ActionOp;
struct BeginOp;
struct EndOp;
struct IndirectGotoOp;
struct DoIncrementOp;
struct DoCompareOp;
using LabelRef = unsigned;
constexpr LabelRef unspecifiedLabel{~0u};
using Location = parser::CharBlock;
struct LabelBuilder;
// target for a control-flow edge
struct LabelOp {
explicit LabelOp(LabelBuilder &builder);
LabelOp(const LabelOp &that);
LabelOp &operator=(const LabelOp &that);
void setReferenced() const;
bool isReferenced() const;
LabelRef get() const { return label_; }
operator LabelRef() const { return get(); }
void dump() const;
private:
LabelBuilder &builder_;
LabelRef label_;
};
struct ArtificialJump {};
constexpr ArtificialJump ARTIFICIAL{};
// a source of an absolute control flow edge
struct GotoOp
: public SumTypeCopyMixin<const parser::CycleStmt *, const parser::ExitStmt *,
const parser::GotoStmt *, ArtificialJump> {
template<typename A>
GotoOp(const A &stmt, LabelRef dest, const Location &source)
: SumTypeCopyMixin{&stmt}, target{dest}, source{source} {}
explicit GotoOp(LabelRef dest) : SumTypeCopyMixin{ARTIFICIAL}, target{dest} {}
void dump() const;
LabelRef target;
Location source;
};
// control exits the procedure
struct ReturnOp : public SumTypeCopyMixin<const parser::FailImageStmt *,
const parser::ReturnStmt *, const parser::StopStmt *> {
template<typename A>
ReturnOp(const A &stmt, const Location &source)
: SumTypeCopyMixin{&stmt}, source{source} {}
void dump() const;
Location source;
};
// two-way branch based on a condition
struct ConditionalGotoOp
: public SumTypeCopyMixin<const parser::Statement<parser::IfThenStmt> *,
const parser::Statement<parser::ElseIfStmt> *, const parser::IfStmt *,
const parser::Statement<parser::NonLabelDoStmt> *> {
template<typename A>
ConditionalGotoOp(const A &cond, LabelRef tb, LabelRef fb)
: SumTypeCopyMixin{&cond}, trueLabel{tb}, falseLabel{fb} {}
void dump() const;
LabelRef trueLabel;
LabelRef falseLabel;
};
// multi-way branch based on a target-value of a variable
struct IndirectGotoOp {
IndirectGotoOp(
const semantics::Symbol *symbol, std::vector<LabelRef> &&labelRefs)
: symbol{symbol}, labelRefs{labelRefs} {}
void dump() const;
const semantics::Symbol *symbol;
std::vector<LabelRef> labelRefs;
};
// intrinsic IO operations can return with an implied multi-way branch
struct SwitchIOOp
: public SumTypeCopyMixin<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 *> {
template<typename A>
SwitchIOOp(const A &io, LabelRef next, const Location &source,
std::optional<LabelRef> errLab,
std::optional<LabelRef> eorLab = std::nullopt,
std::optional<LabelRef> endLab = std::nullopt)
: SumTypeCopyMixin{&io}, next{next}, source{source}, errLabel{errLab},
eorLabel{eorLab}, endLabel{endLab} {}
void dump() const;
LabelRef next;
Location source;
std::optional<LabelRef> errLabel;
std::optional<LabelRef> eorLabel;
std::optional<LabelRef> endLabel;
};
// multi-way branch based on conditions
struct SwitchOp
: public SumTypeCopyMixin<const parser::CallStmt *,
const parser::ComputedGotoStmt *, const parser::ArithmeticIfStmt *,
const parser::CaseConstruct *, const parser::SelectRankConstruct *,
const parser::SelectTypeConstruct *> {
template<typename A>
SwitchOp(
const A &sw, const std::vector<LabelRef> &refs, const Location &source)
: SumTypeCopyMixin{&sw}, refs{refs}, source{source} {}
void dump() const;
const std::vector<LabelRef> refs;
Location source;
};
// a compute step
struct ActionOp {
ActionOp(const parser::Statement<parser::ActionStmt> &stmt) : v{&stmt} {}
void dump() const;
const parser::Statement<parser::ActionStmt> *v;
};
#define CONSTRUCT_TYPES \
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 *
// entry into a Fortran construct
struct BeginOp : public SumTypeCopyMixin<CONSTRUCT_TYPES> {
SUM_TYPE_COPY_MIXIN(BeginOp)
void dump() const;
template<typename A> BeginOp(const A &c) : SumTypeCopyMixin{&c} {}
};
// exit from a Fortran construct
struct EndOp : public SumTypeCopyMixin<CONSTRUCT_TYPES> {
SUM_TYPE_COPY_MIXIN(EndOp)
void dump() const;
template<typename A> EndOp(const A &c) : SumTypeCopyMixin{&c} {}
};
struct DoIncrementOp {
DoIncrementOp(const parser::DoConstruct &stmt) : v{&stmt} {}
void dump() const;
const parser::DoConstruct *v;
};
struct DoCompareOp {
DoCompareOp(const parser::DoConstruct &stmt) : v{&stmt} {}
void dump() const;
const parser::DoConstruct *v;
};
// the flat FIR is a list of Ops, where an Op is any of ...
struct Op : public SumTypeMixin<LabelOp, GotoOp, ReturnOp, ConditionalGotoOp,
SwitchIOOp, SwitchOp, ActionOp, BeginOp, EndOp, IndirectGotoOp,
DoIncrementOp, DoCompareOp> {
template<typename A> Op(const A &thing) : SumTypeMixin{thing} {}
void dump() const {
std::visit([](const auto &op) { op.dump(); }, u);
}
static void Build(std::list<Op> &ops,
const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad);
};
// helper to build unique labels
struct LabelBuilder {
LabelBuilder();
LabelRef getNext();
void setReferenced(LabelRef label);
bool isReferenced(LabelRef label) const;
std::vector<bool> referenced;
unsigned counter;
};
LabelOp FetchLabel(AnalysisData &ad, const parser::Label &label);
std::vector<LabelRef> GetAssign(
AnalysisData &ad, const semantics::Symbol *symbol);
}
struct AnalysisData {
std::map<parser::Label, flat::LabelOp> labelMap;
std::vector<std::tuple<const parser::Name *, flat::LabelRef, flat::LabelRef>>
nameStack;
flat::LabelBuilder labelBuilder;
std::map<const semantics::Symbol *, std::set<parser::Label>> assignMap;
};
// entry-point into building the flat IR
template<typename A>
void CreateFlatIR(const A &ptree, std::list<flat::Op> &ops, AnalysisData &ad);
#define EXPLICIT_INSTANTIATION(T) \
extern template void CreateFlatIR<parser::T>( \
const parser::T &, std::list<flat::Op> &, AnalysisData &)
EXPLICIT_INSTANTIATION(MainProgram);
EXPLICIT_INSTANTIATION(FunctionSubprogram);
EXPLICIT_INSTANTIATION(SubroutineSubprogram);
// dump flat IR
void dump(const std::list<flat::Op> &ops);
}
#endif // FORTRAN_FIR_FLATTENED_H_

View File

@ -1,127 +0,0 @@
// 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 "graph-writer.h"
#include "statements.h"
namespace Fortran::FIR {
llvm::raw_ostream *GraphWriter::defaultOutput_;
GraphWriter::~GraphWriter() {
if (defaultOutput_) {
defaultOutput_->flush();
}
}
void GraphWriter::dumpHeader() { output_ << "digraph G {\n"; }
void GraphWriter::dumpFooter() { output_ << "}\n"; }
void GraphWriter::dump(Program &program) {
dumpHeader();
for (auto iter{program.procedureMap_.begin()},
iend{program.procedureMap_.end()};
iter != iend; ++iter) {
dump(*iter->getValue(), true);
}
dumpFooter();
}
void GraphWriter::dump(Procedure &procedure, bool box) {
if (box) {
output_ << "subgraph cluster" << counter()
<< " {\n node[style=filled];\n color=red;\n";
}
for (auto iter{procedure.regionList_.begin()},
iend{procedure.regionList_.end()};
iter != iend; ++iter) {
dump(*iter, true);
}
if (box) {
output_ << " label = \"procedure";
if (procedure.getName().empty()) {
output_ << '#' << counter();
} else {
output_ << ": " << procedure.getName().str();
}
output_ << "\"\n}\n";
}
}
void GraphWriter::dump(Region &region, bool box) {
if (box) {
output_ << " subgraph cluster" << counter()
<< " {\n node[style=filled];\n";
}
for (auto iter{region.begin()}, iend{region.end()}; iter != iend; ++iter) {
dump(*iter, true);
}
std::set<BasicBlock *> myNodes;
auto blocks{region.getBlocks()};
auto iend{blocks.end()};
auto iexit{iend};
--iexit;
auto ientry{blocks.begin()};
for (auto iter{ientry}; iter != iend; ++iter) {
isEntry_ = iter == ientry && region.IsOutermost();
isExit_ = iter == iexit && region.IsOutermost();
dump(**iter);
myNodes.insert(*iter);
}
std::list<std::pair<BasicBlock *, BasicBlock *>> emitAfter;
for (auto iter{blocks.begin()}, iend{blocks.end()}; iter != iend; ++iter) {
dumpInternalEdges(**iter, myNodes, emitAfter);
}
if (box) {
output_ << " style=dashed;\n color=blue;\n label = \"region#"
<< counter() << "\\nvariables: {...}\\n\"\n }\n";
}
for (auto pair : emitAfter) {
output_ << " " << block_id(*pair.first) << " -> " << block_id(*pair.second)
<< ";\n";
}
}
void GraphWriter::dump(BasicBlock &block, std::optional<const char *> color) {
output_ << " " << block_id(block) << " [label = \"";
if (isEntry_) {
output_ << "<<ENTRY>>\\n";
}
output_ << block_id(block) << '(' << ToString(&block) << ")\\n";
for (auto &action : block.getSublist(static_cast<Statement *>(nullptr))) {
output_ << action.dump() << "\\n";
}
if (isExit_) {
output_ << "<<EXIT>>";
}
output_ << "\",shape=rectangle";
if (color) {
output_ << ",color=" << *color;
}
output_ << "];\n";
}
void GraphWriter::dumpInternalEdges(BasicBlock &block,
std::set<BasicBlock *> &nodeSet,
std::list<std::pair<BasicBlock *, BasicBlock *>> &emitAfter) {
for (auto succ : succ_list(block)) {
if (nodeSet.count(succ)) {
output_ << " " << block_id(block) << " -> " << block_id(*succ)
<< ";\n";
} else {
emitAfter.push_back({&block, succ});
}
}
}
}

View File

@ -1,89 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_GRAPH_WRITER_H_
#define FORTRAN_FIR_GRAPH_WRITER_H_
#include "program.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <string>
namespace Fortran::FIR {
class GraphWriter {
public:
static void setOutput(llvm::raw_ostream *output) { defaultOutput_ = output; }
static void setOutput(const std::string &filename) {
std::error_code ec;
setOutput(new llvm::raw_fd_ostream(filename, ec, llvm::sys::fs::F_None));
CHECK(!ec);
}
static void print(Program &program) {
GraphWriter writer{getOutput()};
writer.dump(program);
}
static void print(Procedure &procedure) {
GraphWriter writer{getOutput()};
writer.dump(procedure);
}
static void print(Region &region) {
GraphWriter writer{getOutput()};
writer.dump(region);
}
private:
GraphWriter(llvm::raw_ostream &output) : output_{output} {}
~GraphWriter();
void dump(Program &program);
void dump(Procedure &procedure, bool box = false);
void dump(Region &region, bool box = false);
void dumpHeader();
void dumpFooter();
unsigned counter() { return count_++; }
void dump(
BasicBlock &block, std::optional<const char *> color = std::nullopt);
void dumpInternalEdges(BasicBlock &block, std::set<BasicBlock *> &nodeSet,
std::list<std::pair<BasicBlock *, BasicBlock *>> &emitAfter);
std::string block_id(BasicBlock &block) {
unsigned num;
if (blockIds_.count(&block)) {
num = blockIds_[&block];
} else {
blockIds_[&block] = num = blockNum_++;
}
std::ostringstream buffer;
buffer << "BB_" << num;
return buffer.str();
}
static llvm::raw_ostream &getOutput() {
return defaultOutput_ ? *defaultOutput_ : llvm::outs();
}
unsigned count_{0u};
llvm::raw_ostream &output_;
unsigned blockNum_{0u};
bool isEntry_{false};
bool isExit_{false};
std::map<BasicBlock *, unsigned> blockIds_;
static llvm::raw_ostream *defaultOutput_;
};
}
#endif

View File

@ -1,133 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_MIXIN_H_
#define FORTRAN_FIR_MIXIN_H_
// Mixin classes are "partial" classes (not used standalone) that can be used to
// add a repetitive (ad hoc) interface (and implementation) to a class. It's
// better to think of these as "included in" a class, rather than as an
// "inherited from" base class.
#include "llvm/ADT/ilist.h"
#include <optional>
#include <tuple>
#include <type_traits>
#include <variant>
namespace Fortran::FIR {
// implementation of a (moveable) sum type (variant)
template<typename... Ts> struct SumTypeMixin {
using SumTypeTrait = std::true_type;
template<typename A> SumTypeMixin(const A &x) : u{x} {}
template<typename A> SumTypeMixin(A &&x) : u{std::forward<A>(x)} {}
SumTypeMixin(SumTypeMixin &&) = default;
SumTypeMixin &operator=(SumTypeMixin &&) = default;
SumTypeMixin(const SumTypeMixin &) = delete;
SumTypeMixin &operator=(const SumTypeMixin &) = delete;
SumTypeMixin() = delete;
std::variant<Ts...> u;
};
// implementation of a copyable sum type
template<typename... Ts> struct SumTypeCopyMixin {
using CopyableSumTypeTrait = std::true_type;
template<typename A> SumTypeCopyMixin(const A &x) : u{x} {}
template<typename A> SumTypeCopyMixin(A &&x) : u{std::forward<A>(x)} {}
SumTypeCopyMixin(SumTypeCopyMixin &&) = default;
SumTypeCopyMixin &operator=(SumTypeCopyMixin &&) = default;
SumTypeCopyMixin(const SumTypeCopyMixin &) = default;
SumTypeCopyMixin &operator=(const SumTypeCopyMixin &) = default;
SumTypeCopyMixin() = delete;
std::variant<Ts...> u;
};
#define SUM_TYPE_COPY_MIXIN(DT) \
DT(const DT &derived) : SumTypeCopyMixin(derived.u) {} \
DT(DT &&derived) : SumTypeCopyMixin(std::move(derived.u)) {} \
DT &operator=(const DT &derived) { \
SumTypeCopyMixin::operator=(derived.u); \
return *this; \
} \
DT &operator=(DT &&derived) { \
SumTypeCopyMixin::operator=(std::move(derived.u)); \
return *this; \
}
// implementation of a (moveable) product type (tuple)
template<typename... Ts> struct ProductTypeMixin {
using ProductTypeTrait = std::true_type;
ProductTypeMixin(const Ts &... x) : t{x...} {}
template<typename... As>
ProductTypeMixin(As &&... x) : t{std::forward<As>(x)...} {}
ProductTypeMixin(ProductTypeMixin &&) = default;
ProductTypeMixin &operator=(ProductTypeMixin &&) = default;
ProductTypeMixin(const ProductTypeMixin &) = delete;
ProductTypeMixin &operator=(const ProductTypeMixin &) = delete;
ProductTypeMixin() = delete;
std::tuple<Ts...> t;
};
// implementation of a (moveable) maybe type
template<typename T> struct MaybeMixin {
using MaybeTrait = std::true_type;
MaybeMixin(const T &x) : o{x} {}
MaybeMixin(T &&x) : o{std::move(x)} {}
MaybeMixin(MaybeMixin &&) = default;
MaybeMixin &operator=(MaybeMixin &&) = default;
MaybeMixin(const MaybeMixin &) = delete;
MaybeMixin &operator=(const MaybeMixin &) = delete;
MaybeMixin() = delete;
std::optional<T> o;
};
// implementation of a child type (composable hierarchy)
template<typename T, typename P> struct ChildMixin {
protected:
P *parent;
public:
ChildMixin(P *p) : parent{p} {}
inline const P *getParent() const { return parent; }
inline P *getParent() { return parent; }
llvm::iplist<T> &getList() { return parent->getSublist(this); }
};
// zip :: ([a],[b]) -> [(a,b)]
template<typename A, typename B, typename C>
C Zip(C out, A first, A last, B other) {
std::transform(first, last, other, out,
[](auto &&a, auto &&b) -> std::pair<decltype(a), decltype(b)> {
return {a, b};
});
return out;
}
// unzip :: [(a,b)] -> ([a],[b])
template<typename A, typename B> B &Unzip(B &out, A first, A last) {
std::transform(first, last, std::back_inserter(out.first),
[](auto &&a) -> decltype(a.first) { return a.first; });
std::transform(first, last, std::back_inserter(out.second),
[](auto &&a) -> decltype(a.second) { return a.second; });
return out;
}
template<typename A, typename B> B &UnzipSnd(B &out, A first, A last) {
std::transform(first, last, std::back_inserter(out.second),
[](auto &&a) -> decltype(a.second) { return a.second; });
return out;
}
}
#endif // FORTRAN_FIR_COMMON_H_

View File

@ -1,89 +0,0 @@
// 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 "procedure.h"
#include "statements.h"
namespace Fortran::FIR {
Procedure::Procedure(Program *program, FunctionType *ty, LinkageTypes lt,
unsigned addrSpace, const llvm::Twine &n, Procedure *before)
: ChildMixin{program}, procType_{ty}, linkage_{lt},
addressSpace_{addrSpace}, name_{n.str()} {
Region::Create(this);
parent->insertBefore(this, before);
}
Procedure::~Procedure() { regionList_.clear(); }
Region *Procedure::insertBefore(Region *region, Region *before) {
if (before) {
regionList_.insert(before->getIterator(), region);
} else {
regionList_.push_back(region);
}
return region;
}
template<typename T>
static void AddCountScopes(
unsigned count, BasicBlock *block, T callback, semantics::Scope *scope) {
for (; count; --count) {
block->insertBefore(
Statement::Create(block, callback(scope)), block->terminator());
}
}
inline static void AddEnterScopes(unsigned count, BasicBlock *block) {
AddCountScopes(
count, block, ScopeEnterStmt::Create, /*TODO: thread scope? */ nullptr);
}
inline static void AddExitScopes(unsigned count, BasicBlock *block) {
AddCountScopes(
count, block, ScopeExitStmt::Create, /*TODO: thread scope? */ nullptr);
}
static bool DistinctScopes(Region *region1, Region *region2) {
return region1->HasScope() && region2->HasScope() &&
region1->GetScope() != region2->GetScope();
}
void Procedure::FlattenRegions() {
for (auto &block : GetBlocks()) {
auto *region{block.GetRegion()};
if (!region->IsOutermost()) {
for (auto *succ : succ_list(block)) {
auto *succRegion{succ->GetRegion()};
if (succRegion != region && DistinctScopes(succRegion, region)) {
if (IsAncestor(region, succRegion)) {
AddEnterScopes(
RegionDepth(region, succRegion), succ->SplitEdge(&block));
} else if (IsAncestor(succRegion, region)) {
AddExitScopes(
RegionDepth(succRegion, region), block.SplitEdge(succ));
} else {
// TODO: edge to a cousin region
CHECK(false);
}
}
}
}
}
}
Value Procedure::CreateIntrinsicProcedure(int value) {
return {NOTHING}; // FIXME
}
}

View File

@ -1,77 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_PROCEDURE_H_
#define FORTRAN_FIR_PROCEDURE_H_
#include "program.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
namespace Fortran::FIR {
class Procedure final : public llvm::ilist_node<Procedure>,
public ChildMixin<Procedure, Program>,
public Value_impl {
public:
friend GraphWriter;
friend Program;
friend Region;
using BasicBlockListType = llvm::iplist<BasicBlock>;
using RegionListType = llvm::iplist<Region>;
using iterator = BasicBlockListType::iterator;
using const_iterator = BasicBlockListType::const_iterator;
using reverse_iterator = BasicBlockListType::reverse_iterator;
using const_reverse_iterator = BasicBlockListType::const_reverse_iterator;
Procedure(const Procedure &) = delete;
Procedure &operator=(const Procedure &) = delete;
~Procedure();
BasicBlockListType &GetBlocks() { return basicBlockList_; }
BasicBlockListType &getSublist(BasicBlock *) { return GetBlocks(); }
RegionListType &GetRegions() { return regionList_; }
RegionListType &getSublist(Region *) { return GetRegions(); }
iterator begin() { return basicBlockList_.begin(); }
const_iterator begin() const { return basicBlockList_.begin(); }
iterator end() { return basicBlockList_.end(); }
const_iterator end() const { return basicBlockList_.end(); }
Region *getLastRegion() { return &regionList_.back(); }
BasicBlock *GetEntryBlock() { return &basicBlockList_.front(); }
static Procedure *Create(Program *prog, FunctionType *ty,
LinkageTypes linkage, unsigned addrSpace = 0u,
const llvm::Twine &name = "", Procedure *before = nullptr) {
return new Procedure(prog, ty, linkage, addrSpace, name, before);
}
void setParent(Program *p) { parent = p; }
bool hasName() const { return !getName().empty(); }
llvm::StringRef getName() const { return name_; }
void FlattenRegions();
static Value CreateIntrinsicProcedure(int value);
private:
RegionListType regionList_;
BasicBlockListType basicBlockList_;
FunctionType *procType_;
LinkageTypes linkage_;
unsigned addressSpace_;
const std::string name_;
explicit Procedure(Program *program, FunctionType *ty, LinkageTypes lt,
unsigned addrSpace, const llvm::Twine &n, Procedure *before);
Region *insertBefore(Region *region, Region *before = nullptr);
};
}
#endif

View File

@ -1,47 +0,0 @@
// 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 "program.h"
#include "basicblock.h"
namespace Fortran::FIR {
Program::Program(llvm::StringRef id) : name_{id} {}
void Program::insertBefore(Procedure *subprog, Procedure *before) {
if (before) {
procedureList_.insert(before->getIterator(), subprog);
} else {
procedureList_.push_back(subprog);
}
}
Procedure *Program::getOrInsertProcedure(
llvm::StringRef name, FunctionType *procTy, AttributeList attrs) {
llvm::StringMapEntry<Procedure *> *entry{nullptr};
if (!name.empty()) {
auto iter{procedureMap_.find(name)};
if (iter != procedureMap_.end()) {
return iter->getValue();
}
entry = &*procedureMap_.insert({name, nullptr}).first;
name = entry->getKey();
}
auto *subp{Procedure::Create(this, procTy, LinkageTypes::Public, 0u, name)};
if (entry) {
entry->setValue(subp);
}
return subp;
}
}

View File

@ -1,60 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_PROGRAM_H_
#define FORTRAN_FIR_PROGRAM_H_
#include "value.h"
#include "../evaluate/type.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include <string>
namespace Fortran::FIR {
class Procedure;
class GraphWriter;
// FIR is a composable hierarchy of owned objects meant to represent a Fortran
// compilation unit operationally. At this point, the top-level object is a
// Program. A Program owns a list of Procedures and a list of data objects, all
// with process lifetimes (to-do). These objects are referenced by pointers. A
// Procedure owns a list of BasicBlocks. A BasicBlock is referenced by a
// pointer. A BasicBlock owns a list of Statements. A Statement is referenced
// by a pointer.
class Program final {
public:
friend GraphWriter;
using ProcedureListType = llvm::iplist<Procedure>;
using ProcedureMapType = llvm::StringMap<Procedure *>;
explicit Program(llvm::StringRef id);
void insertBefore(Procedure *subprog, Procedure *before = nullptr);
ProcedureListType &getSublist(Procedure *) { return procedureList_; }
bool containsProcedure(llvm::StringRef name) {
return procedureMap_.find(name) != procedureMap_.end();
}
std::string getName() const { return name_; }
Procedure *getOrInsertProcedure(
llvm::StringRef name, FunctionType *procTy, AttributeList attrs);
private:
ProcedureListType procedureList_;
ProcedureMapType procedureMap_;
const std::string name_;
};
}
#endif

View File

@ -1,51 +0,0 @@
// 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 "region.h"
#include "basicblock.h"
namespace Fortran::FIR {
Region::Region(
Procedure *procedure, Scope *scope, Region *inRegion, Region *insertBefore)
: ChildMixin{procedure}, basicBlockList_{procedure->GetBlocks()},
enclosingRegion_{inRegion}, scope_{scope} {
if (enclosingRegion_) {
enclosingRegion_->getSublist(static_cast<Region *>(nullptr))
.push_back(this);
} else {
parent->insertBefore(this, insertBefore);
}
}
Region::~Region() { basicBlockList_.clear(); }
void Region::insertBefore(BasicBlock *block, BasicBlock *before) {
if (before) {
basicBlockList_.insert(before->getIterator(), block);
} else {
basicBlockList_.push_back(block);
}
}
std::vector<BasicBlock *> Region::getBlocks() {
std::vector<BasicBlock *> result;
for (auto &block : basicBlockList_) {
if (block.getParent() == this) {
result.push_back(&block);
}
}
return result;
}
}

View File

@ -1,90 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_REGION_H_
#define FORTRAN_FIR_REGION_H_
#include "procedure.h"
#include "../semantics/semantics.h"
namespace Fortran::FIR {
class Procedure;
class BasicBlock;
class Region final : public llvm::ilist_node<Region>,
public ChildMixin<Region, Procedure> {
public:
friend Procedure;
friend BasicBlock;
using BasicBlockListType = llvm::iplist<BasicBlock>;
using AllocatableListType = std::list<const semantics::Symbol *>;
using SubregionListType = llvm::iplist<Region>;
using iterator = SubregionListType::iterator;
using const_iterator = SubregionListType::const_iterator;
using reverse_iterator = SubregionListType::reverse_iterator;
using const_reverse_iterator = SubregionListType::const_reverse_iterator;
Region(const Region &) = delete;
Region &operator=(const Region &) = delete;
~Region();
std::vector<BasicBlock *> getBlocks();
std::vector<BasicBlock *> getSublist(BasicBlock *) { return getBlocks(); }
SubregionListType &getSublist(Region *) { return subregionList_; }
iterator begin() { return subregionList_.begin(); }
const_iterator begin() const { return subregionList_.begin(); }
iterator end() { return subregionList_.end(); }
const_iterator end() const { return subregionList_.end(); }
Region *GetEnclosing() const { return enclosingRegion_; }
bool IsOutermost() const { return !GetEnclosing(); }
static Region *Create(Procedure *procedure, Scope *scope = nullptr,
Region *inRegion = nullptr, Region *insertBefore = nullptr) {
return new Region(procedure, scope, inRegion, insertBefore);
}
bool HasScope() const { return scope_.has_value(); }
Scope *GetScope() const { return scope_ ? scope_.value() : nullptr; }
private:
BasicBlockListType &basicBlockList_;
AllocatableListType allocatableList_;
SubregionListType subregionList_; // direct descendants
Region *enclosingRegion_; // parent in nesting tree
std::optional<Scope *> scope_;
explicit Region(Procedure *procedure, Scope *scope, Region *inRegion,
Region *insertBefore);
void insertBefore(BasicBlock *block, BasicBlock *before);
};
inline bool IsAncestor(const Region *fromRegion, const Region *toRegion) {
CHECK(fromRegion && toRegion);
for (const auto *region{fromRegion->GetEnclosing()}; region;
region = region->GetEnclosing()) {
if (region == toRegion) return true;
}
return false;
}
inline unsigned RegionDepth(const Region *fromRegion, const Region *toRegion) {
CHECK(IsAncestor(fromRegion, toRegion));
unsigned result{0u};
for (const auto *region{fromRegion}; region != toRegion;
region = region->GetEnclosing()) {
++result;
}
return result;
}
}
#endif

View File

@ -1,212 +0,0 @@
// 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 "statements.h"
namespace Fortran::FIR {
TerminatorStmt_impl::~TerminatorStmt_impl() = default;
Addressable_impl *GetAddressable(Statement *stmt) {
return std::visit(
[](auto &s) -> Addressable_impl * {
if constexpr (std::is_base_of_v<Addressable_impl,
std::decay_t<decltype(s)>>) {
return &s;
}
return nullptr;
},
stmt->u);
}
static std::string dump(const Expression &e) {
std::stringstream stringStream;
stringStream << e;
return stringStream.str();
}
BranchStmt::BranchStmt(const std::optional<Value> &cond, BasicBlock *trueBlock,
BasicBlock *falseBlock)
: condition_{cond}, succs_{trueBlock, falseBlock} {
CHECK(succs_[TrueIndex]);
if (cond) {
CHECK(condition_);
CHECK(succs_[FalseIndex]);
}
}
template<typename L>
static std::list<BasicBlock *> SuccBlocks(
const typename L::ValueSuccPairListType &valueSuccPairList) {
std::pair<std::list<typename L::ValueType>, std::list<BasicBlock *>> result;
UnzipSnd(result, valueSuccPairList.begin(), valueSuccPairList.end());
return result.second;
}
ReturnStmt::ReturnStmt(QualifiedStmt<ApplyExprStmt> exp) : value_{exp} {}
ReturnStmt::ReturnStmt() : value_{QualifiedStmt<ApplyExprStmt>{nullptr}} {}
SwitchStmt::SwitchStmt(const Value &cond, const ValueSuccPairListType &args)
: condition_{cond} {
valueSuccPairs_.insert(valueSuccPairs_.end(), args.begin(), args.end());
}
std::list<BasicBlock *> SwitchStmt::succ_blocks() const {
return SuccBlocks<SwitchStmt>(valueSuccPairs_);
}
BasicBlock *SwitchStmt::defaultSucc() const {
CHECK(IsNothing(valueSuccPairs_[0].first));
return valueSuccPairs_[0].second;
}
SwitchCaseStmt::SwitchCaseStmt(Value cond, const ValueSuccPairListType &args)
: condition_{cond} {
valueSuccPairs_.insert(valueSuccPairs_.end(), args.begin(), args.end());
}
std::list<BasicBlock *> SwitchCaseStmt::succ_blocks() const {
return SuccBlocks<SwitchCaseStmt>(valueSuccPairs_);
}
BasicBlock *SwitchCaseStmt::defaultSucc() const {
CHECK(std::holds_alternative<Default>(valueSuccPairs_[0].first));
return valueSuccPairs_[0].second;
}
SwitchTypeStmt::SwitchTypeStmt(Value cond, const ValueSuccPairListType &args)
: condition_{cond} {
valueSuccPairs_.insert(valueSuccPairs_.end(), args.begin(), args.end());
}
std::list<BasicBlock *> SwitchTypeStmt::succ_blocks() const {
return SuccBlocks<SwitchTypeStmt>(valueSuccPairs_);
}
BasicBlock *SwitchTypeStmt::defaultSucc() const {
CHECK(std::holds_alternative<Default>(valueSuccPairs_[0].first));
return valueSuccPairs_[0].second;
}
SwitchRankStmt ::SwitchRankStmt(Value cond, const ValueSuccPairListType &args)
: condition_{cond} {
valueSuccPairs_.insert(valueSuccPairs_.end(), args.begin(), args.end());
}
std::list<BasicBlock *> SwitchRankStmt::succ_blocks() const {
return SuccBlocks<SwitchRankStmt>(valueSuccPairs_);
}
BasicBlock *SwitchRankStmt::defaultSucc() const {
CHECK(std::holds_alternative<Default>(valueSuccPairs_[0].first));
return valueSuccPairs_[0].second;
}
// check LoadInsn constraints
static void CheckLoadInsn(const Value &v) {
std::visit(
common::visitors{
[](DataObject *) { /* ok */ },
[](Statement *s) { CHECK(GetAddressable(s)); },
[](auto) { CHECK(!"invalid load input"); },
},
v.u);
}
LoadInsn::LoadInsn(const Value &addr) : address_{addr} {
CheckLoadInsn(address_);
}
LoadInsn::LoadInsn(Value &&addr) : address_{std::move(addr)} {
CheckLoadInsn(address_);
}
LoadInsn::LoadInsn(Statement *addr) : address_{addr} {
CHECK(GetAddressable(addr));
}
// Store ctor
StoreInsn::StoreInsn(QualifiedStmt<Addressable_impl> addr, Value val)
: address_{addr}, value_{val} {
CHECK(address_);
CHECK(!IsNothing(value_));
}
// dump is intended for debugging rather than idiomatic FIR output
std::string Statement::dump() const {
return std::visit(
common::visitors{
[](const ReturnStmt &s) { return "return " + ToString(s.value()); },
[](const BranchStmt &s) {
if (s.hasCondition()) {
return "cgoto (" + s.getCond().dump() + ") " +
ToString(s.getTrueSucc()) + ", " + ToString(s.getFalseSucc());
}
return "goto " + ToString(s.getTrueSucc());
},
[](const SwitchStmt &s) {
return "switch (" + s.getCond().dump() + ")";
},
[](const SwitchCaseStmt &s) {
return "switch-case (" + s.getCond().dump() + ")";
},
[](const SwitchTypeStmt &s) {
return "switch-type (" + s.getCond().dump() + ")";
},
[](const SwitchRankStmt &s) {
return "switch-rank (" + s.getCond().dump() + ")";
},
[](const IndirectBranchStmt &s) {
std::string targets;
for (auto *b : s.succ_blocks()) {
targets += " " + ToString(b);
}
return "igoto (" + ToString(s.variable()) + ")" + targets;
},
[](const UnreachableStmt &) { return "unreachable"s; },
[&](const ApplyExprStmt &e) {
return '%' + ToString(&u) + ": eval " + FIR::dump(e.expression());
},
[&](const LocateExprStmt &e) {
return '%' + ToString(&u) + ": addr-of " +
FIR::dump(e.expression());
},
[](const AllocateInsn &) { return "alloc"s; },
[](const DeallocateInsn &s) {
return "dealloc (" + ToString(s.alloc()) + ")";
},
[&](const AllocateLocalInsn &insn) {
return '%' + ToString(&u) + ": alloca " +
FIR::dump(insn.variable());
},
[&](const LoadInsn &insn) {
return '%' + ToString(&u) + ": load " + insn.address().dump();
},
[](const StoreInsn &insn) {
std::string value{insn.value().dump()};
return "store " + value + " to " +
FIR::dump(insn.address()->address());
},
[](const DisassociateInsn &) { return "NULLIFY"s; },
[&](const CallStmt &) { return '%' + ToString(&u) + ": call"s; },
[](const RuntimeStmt &) { return "runtime-call()"s; },
[](const IORuntimeStmt &) { return "io-call()"s; },
[](const ScopeEnterStmt &) { return "scopeenter"s; },
[](const ScopeExitStmt &) { return "scopeexit"s; },
[](const PHIStmt &) { return "PHI"s; },
},
u);
}
std::string Value::dump() const {
return std::visit(
common::visitors{
[](const Nothing &) { return "<none>"s; },
[](const DataObject *obj) { return "obj_" + ToString(obj); },
[](const Statement *s) { return "stmt_" + ToString(s); },
[](const BasicBlock *bb) { return "block_" + ToString(bb); },
[](const Procedure *p) { return "proc_" + ToString(p); },
},
u);
}
}

View File

@ -1,625 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_STATEMENTS_H_
#define FORTRAN_FIR_STATEMENTS_H_
#include "basicblock.h"
#include <initializer_list>
namespace Fortran::FIR {
class ReturnStmt;
class BranchStmt;
class SwitchStmt;
class SwitchCaseStmt;
class SwitchTypeStmt;
class SwitchRankStmt;
class IndirectBranchStmt;
class UnreachableStmt;
class ApplyExprStmt;
class LocateExprStmt;
class AllocateInsn;
class DeallocateInsn;
class AllocateLocalInsn;
class LoadInsn;
class StoreInsn;
class DisassociateInsn;
class CallStmt;
class RuntimeStmt;
class IORuntimeStmt;
class ScopeEnterStmt;
class ScopeExitStmt;
class PHIStmt;
class Statement;
CLASS_TRAIT(StatementTrait)
CLASS_TRAIT(TerminatorTrait)
CLASS_TRAIT(ActionTrait)
class Stmt_impl {
public:
using StatementTrait = std::true_type;
};
// Some uses of a Statement should be constrained. These contraints are imposed
// at compile-time with a QualifiedStmt<A> wrapper.
template<typename A> class QualifiedStmt {
public:
template<typename T, typename U,
std::enable_if_t<std::is_base_of_v<T, U>, int>>
friend QualifiedStmt<T> QualifiedStmtCreate(Statement *s);
QualifiedStmt() = delete;
// create a stub, where stmt == nullptr
QualifiedStmt(std::nullptr_t) : stmt{nullptr} {}
operator Statement *() const { return stmt; }
operator bool() const { return stmt; }
operator A *() const;
Statement *stmt;
private:
QualifiedStmt(Statement *stmt) : stmt{stmt} {}
};
template<typename A, typename B,
std::enable_if_t<std::is_base_of_v<A, B>, int> = 0>
QualifiedStmt<A> QualifiedStmtCreate(Statement *s) {
return QualifiedStmt<A>{s};
}
// Every basic block must end in a terminator
class TerminatorStmt_impl : virtual public Stmt_impl {
public:
virtual std::list<BasicBlock *> succ_blocks() const = 0;
virtual ~TerminatorStmt_impl();
using TerminatorTrait = std::true_type;
};
// Transfer control out of the current procedure
class ReturnStmt : public TerminatorStmt_impl {
public:
static ReturnStmt Create(QualifiedStmt<ApplyExprStmt> stmt) {
return ReturnStmt{stmt};
}
static ReturnStmt Create() { return ReturnStmt{}; }
std::list<BasicBlock *> succ_blocks() const override { return {}; }
bool has_value() const { return value_; }
Statement *value() const { return value_; }
private:
QualifiedStmt<ApplyExprStmt> value_;
explicit ReturnStmt(QualifiedStmt<ApplyExprStmt> exp);
explicit ReturnStmt();
};
// Encodes two-way conditional branch and one-way absolute branch
class BranchStmt : public TerminatorStmt_impl {
public:
static BranchStmt Create(
Value condition, BasicBlock *trueBlock, BasicBlock *falseBlock) {
return BranchStmt{condition, trueBlock, falseBlock};
}
static BranchStmt Create(BasicBlock *succ) {
return BranchStmt{std::nullopt, succ, nullptr};
}
bool hasCondition() const { return condition_.has_value(); }
Value getCond() const { return condition_.value(); }
std::list<BasicBlock *> succ_blocks() const override {
if (hasCondition()) {
return {succs_[TrueIndex], succs_[FalseIndex]};
}
return {succs_[TrueIndex]};
}
BasicBlock *getTrueSucc() const { return succs_[TrueIndex]; }
BasicBlock *getFalseSucc() const { return succs_[FalseIndex]; }
private:
explicit BranchStmt(const std::optional<Value> &condition,
BasicBlock *trueBlock, BasicBlock *falseBlock);
static constexpr int TrueIndex{0};
static constexpr int FalseIndex{1};
std::optional<Value> condition_;
BasicBlock *succs_[2];
};
// Switch on an expression into a set of constant values
class SwitchStmt : public TerminatorStmt_impl {
public:
using ValueType = Value;
using ValueSuccPairType = std::pair<ValueType, BasicBlock *>;
using ValueSuccPairListType = std::vector<ValueSuccPairType>;
static SwitchStmt Create(
const Value &switchEval, const ValueSuccPairListType &args) {
return SwitchStmt{switchEval, args};
}
BasicBlock *defaultSucc() const;
std::list<BasicBlock *> succ_blocks() const override;
Value getCond() const { return condition_; }
private:
explicit SwitchStmt(
const Value &condition, const ValueSuccPairListType &args);
Value condition_;
ValueSuccPairListType valueSuccPairs_;
};
// Switch on an expression into a set of value (open or closed) ranges
class SwitchCaseStmt : public TerminatorStmt_impl {
public:
struct Default {};
struct Exactly { // selector == v
ApplyExprStmt *v;
};
struct InclusiveAbove { // v <= selector
ApplyExprStmt *v;
};
struct InclusiveBelow { // selector <= v
ApplyExprStmt *v;
};
struct InclusiveRange { // lower <= selector <= upper
ApplyExprStmt *lower;
ApplyExprStmt *upper;
};
using RangeAlternative =
std::variant<Exactly, InclusiveAbove, InclusiveBelow, InclusiveRange>;
using ValueType = std::variant<Default, std::vector<RangeAlternative>>;
using ValueSuccPairType = std::pair<ValueType, BasicBlock *>;
using ValueSuccPairListType = std::vector<ValueSuccPairType>;
static SwitchCaseStmt Create(
Value switchEval, const ValueSuccPairListType &args) {
return SwitchCaseStmt{switchEval, args};
}
BasicBlock *defaultSucc() const;
std::list<BasicBlock *> succ_blocks() const override;
Value getCond() const { return condition_; }
private:
explicit SwitchCaseStmt(Value condition, const ValueSuccPairListType &args);
Value condition_;
ValueSuccPairListType valueSuccPairs_;
};
// Switch on the TYPE of the selector into a set of TYPES, etc.
class SwitchTypeStmt : public TerminatorStmt_impl {
public:
struct Default {};
struct TypeSpec {
Type v;
};
struct DerivedTypeSpec {
Type v;
};
using ValueType = std::variant<Default, TypeSpec, DerivedTypeSpec>;
using ValueSuccPairType = std::pair<ValueType, BasicBlock *>;
using ValueSuccPairListType = std::vector<ValueSuccPairType>;
static SwitchTypeStmt Create(
Value switchEval, const ValueSuccPairListType &args) {
return SwitchTypeStmt{switchEval, args};
}
BasicBlock *defaultSucc() const;
std::list<BasicBlock *> succ_blocks() const override;
Value getCond() const { return condition_; }
private:
explicit SwitchTypeStmt(Value condition, const ValueSuccPairListType &args);
Value condition_;
ValueSuccPairListType valueSuccPairs_;
};
// Switch on the RANK of the selector into a set of constant integers, etc.
class SwitchRankStmt : public TerminatorStmt_impl {
public:
struct Default {}; // RANK DEFAULT
struct AssumedSize {}; // RANK(*)
struct Exactly { // RANK(n)
Expression *v;
};
using ValueType = std::variant<Exactly, AssumedSize, Default>;
using ValueSuccPairType = std::pair<ValueType, BasicBlock *>;
using ValueSuccPairListType = std::vector<ValueSuccPairType>;
static SwitchRankStmt Create(
Value switchEval, const ValueSuccPairListType &args) {
return SwitchRankStmt{switchEval, args};
}
BasicBlock *defaultSucc() const;
std::list<BasicBlock *> succ_blocks() const override;
Value getCond() const { return condition_; }
private:
explicit SwitchRankStmt(Value condition, const ValueSuccPairListType &args);
Value condition_;
ValueSuccPairListType valueSuccPairs_;
};
class IndirectBranchStmt : public TerminatorStmt_impl {
public:
using TargetListType = std::vector<BasicBlock *>;
static IndirectBranchStmt Create(
Variable *variable, const TargetListType &potentialTargets) {
return IndirectBranchStmt{variable, potentialTargets};
}
Variable *variable() const { return variable_; }
std::list<BasicBlock *> succ_blocks() const override {
return {potentialTargets_.begin(), potentialTargets_.end()};
}
private:
explicit IndirectBranchStmt(
Variable *variable, const TargetListType &potentialTargets)
: variable_{variable}, potentialTargets_{potentialTargets} {}
Variable *variable_;
TargetListType potentialTargets_;
};
// This statement is not reachable
class UnreachableStmt : public TerminatorStmt_impl {
public:
static UnreachableStmt Create() { return UnreachableStmt{}; }
std::list<BasicBlock *> succ_blocks() const override { return {}; }
private:
explicit UnreachableStmt() = default;
};
class ActionStmt_impl : virtual public Stmt_impl {
public:
using ActionTrait = std::true_type;
protected:
ActionStmt_impl() : type{std::nullopt} {}
// TODO: DynamicType is a placeholder for now
std::optional<evaluate::DynamicType> type;
};
// Compute the value of an expression
class ApplyExprStmt : public ActionStmt_impl {
public:
static ApplyExprStmt Create(const Expression *e) { return ApplyExprStmt{*e}; }
static ApplyExprStmt Create(Expression &&e) {
return ApplyExprStmt{std::move(e)};
}
Expression expression() const { return expression_; }
private:
explicit ApplyExprStmt(const Expression &e) : expression_{e} {}
explicit ApplyExprStmt(Expression &&e) : expression_{std::move(e)} {}
Expression expression_;
};
// Base class of all addressable statements
class Addressable_impl : public ActionStmt_impl {
public:
Expression address() const { return addrExpr_.value(); }
protected:
Addressable_impl() : addrExpr_{std::nullopt} {}
explicit Addressable_impl(const Expression &ae) : addrExpr_{ae} {}
std::optional<Expression> addrExpr_;
};
// Compute the location of an expression
class LocateExprStmt : public Addressable_impl {
public:
static LocateExprStmt Create(const Expression *e) {
return LocateExprStmt(*e);
}
static LocateExprStmt Create(Expression &&e) { return LocateExprStmt(e); }
const Expression &expression() const { return addrExpr_.value(); }
private:
explicit LocateExprStmt(const Expression &e) : Addressable_impl{e} {}
};
// has memory effect
class MemoryStmt_impl : public ActionStmt_impl {
protected:
MemoryStmt_impl() {}
};
// Allocate storage (per ALLOCATE)
class AllocateInsn : public Addressable_impl, public MemoryStmt_impl {
public:
static AllocateInsn Create(Type type, int alignment = 0) {
return AllocateInsn{type, alignment};
}
Type type() const { return type_; }
int alignment() const { return alignment_; }
private:
explicit AllocateInsn(Type type, int alignment)
: type_{type}, alignment_{alignment} {}
Type type_;
int alignment_;
};
// Deallocate storage (per DEALLOCATE)
class DeallocateInsn : public MemoryStmt_impl {
public:
static DeallocateInsn Create(QualifiedStmt<AllocateInsn> alloc) {
return DeallocateInsn{alloc};
}
Statement *alloc() const { return alloc_; }
private:
explicit DeallocateInsn(QualifiedStmt<AllocateInsn> alloc) : alloc_{alloc} {}
QualifiedStmt<AllocateInsn> alloc_;
};
// Allocate space for a temporary by its Type. The lifetime of the temporary
// will not exceed that of the containing Procedure.
class AllocateLocalInsn : public Addressable_impl, public MemoryStmt_impl {
public:
static AllocateLocalInsn Create(
Type type, const Expression &expr, int alignment = 0) {
return AllocateLocalInsn{type, alignment, expr};
}
Type type() const { return type_; }
int alignment() const { return alignment_; }
Expression variable() const { return addrExpr_.value(); }
private:
explicit AllocateLocalInsn(Type type, int alignment, const Expression &expr)
: Addressable_impl{expr}, type_{type}, alignment_{alignment} {}
Type type_;
int alignment_;
};
// Load value(s) from a location
class LoadInsn : public MemoryStmt_impl {
public:
static LoadInsn Create(const Value &addr) { return LoadInsn{addr}; }
static LoadInsn Create(Value &&addr) { return LoadInsn{addr}; }
static LoadInsn Create(Statement *addr) { return LoadInsn{addr}; }
Value address() const { return address_; }
private:
explicit LoadInsn(const Value &addr);
explicit LoadInsn(Value &&addr);
explicit LoadInsn(Statement *addr);
Value address_;
};
// Store value(s) from an applied expression to a location
class StoreInsn : public MemoryStmt_impl {
public:
static StoreInsn Create(QualifiedStmt<Addressable_impl> addr, Value value) {
return StoreInsn{addr, value};
}
Addressable_impl *address() const { return address_; }
Value value() const { return value_; }
private:
explicit StoreInsn(QualifiedStmt<Addressable_impl> addr, Value val);
QualifiedStmt<Addressable_impl> address_;
Value value_;
};
// NULLIFY - make pointer object disassociated
class DisassociateInsn : public ActionStmt_impl {
public:
static DisassociateInsn Create(Statement *s) { return DisassociateInsn{s}; }
Statement *disassociate() { return disassociate_; }
private:
DisassociateInsn(Statement *s) : disassociate_{s} {}
Statement *disassociate_;
};
// base class for all call-like IR statements
class CallStmt_impl : public ActionStmt_impl {
public:
Value Callee() const { return callee_; }
unsigned NumArgs() const { return arguments_.size(); }
protected:
CallStmt_impl(
const FunctionType *functionType, Value callee, CallArguments &&arguments)
: functionType_{functionType}, callee_{callee}, arguments_{arguments} {}
const FunctionType *functionType_;
Value callee_;
CallArguments arguments_;
};
// CALL statements and function references
// A CallStmt has pass-by-value semantics. Pass-by-reference must be done
// explicitly by passing addresses of objects or temporaries.
class CallStmt : public CallStmt_impl {
public:
static CallStmt Create(
const FunctionType *type, Value callee, CallArguments &&arguments) {
return CallStmt{type, callee, std::move(arguments)};
}
private:
explicit CallStmt(
const FunctionType *functionType, Value callee, CallArguments &&arguments)
: CallStmt_impl{functionType, callee, std::move(arguments)} {}
};
// Miscellaneous statements that turn into runtime calls
class RuntimeStmt : public CallStmt_impl {
public:
static RuntimeStmt Create(
RuntimeCallType call, RuntimeCallArguments &&argument) {
return RuntimeStmt{call, std::move(argument)};
}
RuntimeCallType call() const { return call_; }
private:
explicit RuntimeStmt(RuntimeCallType call, RuntimeCallArguments &&arguments)
: CallStmt_impl{nullptr, Procedure::CreateIntrinsicProcedure(call),
std::move(arguments)},
call_{call} {}
RuntimeCallType call_;
};
// The 13 Fortran I/O statements. Will be lowered to whatever becomes of the
// I/O runtime.
class IORuntimeStmt : public CallStmt_impl {
public:
static IORuntimeStmt Create(
InputOutputCallType call, IOCallArguments &&arguments) {
return IORuntimeStmt{call, std::move(arguments)};
}
InputOutputCallType call() const { return call_; }
private:
explicit IORuntimeStmt(InputOutputCallType call, IOCallArguments &&arguments)
: CallStmt_impl{nullptr, Procedure::CreateIntrinsicProcedure(call),
std::move(arguments)},
call_{call} {}
InputOutputCallType call_;
};
class ScopeStmt_impl : public ActionStmt_impl {
public:
Scope *GetScope() const { return scope; }
protected:
ScopeStmt_impl(Scope *scope) : scope{nullptr} {}
Scope *scope;
};
// From the CFG document
class ScopeEnterStmt : public ScopeStmt_impl {
public:
static ScopeEnterStmt Create(Scope *scope) { return ScopeEnterStmt{scope}; }
private:
ScopeEnterStmt(Scope *scope) : ScopeStmt_impl{scope} {}
};
// From the CFG document
class ScopeExitStmt : public ScopeStmt_impl {
public:
static ScopeExitStmt Create(Scope *scope) { return ScopeExitStmt{scope}; }
private:
ScopeExitStmt(Scope *scope) : ScopeStmt_impl{scope} {}
};
// From the CFG document to support SSA
class PHIStmt : public ActionStmt_impl {
public:
static PHIStmt Create(unsigned numReservedValues) {
return PHIStmt{numReservedValues};
}
private:
PHIStmt(unsigned size) : inputs_(size) {}
std::vector<PHIPair> inputs_;
};
// Sum type over all statement classes
class Statement : public SumTypeMixin<ReturnStmt, //
BranchStmt, //
SwitchStmt, //
SwitchCaseStmt, //
SwitchTypeStmt, //
SwitchRankStmt, //
IndirectBranchStmt, //
UnreachableStmt, //
ApplyExprStmt, //
LocateExprStmt, //
AllocateInsn, //
DeallocateInsn, //
AllocateLocalInsn, //
LoadInsn, //
StoreInsn, //
DisassociateInsn, //
CallStmt, //
RuntimeStmt, //
IORuntimeStmt, //
ScopeEnterStmt, //
ScopeExitStmt, //
PHIStmt //
>,
public Value_impl,
public ChildMixin<Statement, BasicBlock>,
public llvm::ilist_node<Statement> {
public:
template<typename A> static Statement *Create(BasicBlock *p, A &&t) {
return new Statement(p, std::forward<A>(t));
}
std::string dump() const; // LLVM expected name
void Dump(std::ostream &os) const { os << dump(); }
private:
template<typename A>
explicit Statement(BasicBlock *p, A &&t)
: SumTypeMixin{std::forward<A>(t)}, ChildMixin{p} {
parent->insertBefore(this);
}
};
template<typename A> inline QualifiedStmt<A>::operator A *() const {
return reinterpret_cast<A *>(&stmt->u);
}
inline std::list<BasicBlock *> succ_list(BasicBlock &block) {
if (auto *terminator{block.terminator()}) {
return reinterpret_cast<const TerminatorStmt_impl *>(&terminator->u)
->succ_blocks();
}
// CHECK(false && "block does not have terminator");
return {};
}
inline ApplyExprStmt *GetApplyExpr(Statement *stmt) {
return std::get_if<ApplyExprStmt>(&stmt->u);
}
inline AllocateLocalInsn *GetLocal(Statement *stmt) {
return std::get_if<AllocateLocalInsn>(&stmt->u);
}
Addressable_impl *GetAddressable(Statement *stmt);
template<typename A> std::string ToString(const A *a) {
std::stringstream ss;
ss << std::hex << reinterpret_cast<std::intptr_t>(a);
return ss.str();
}
}
#endif // FORTRAN_FIR_STATEMENTS_H_

View File

@ -1,53 +0,0 @@
// 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.
#ifndef FORTRAN_FIR_VALUE_H_
#define FORTRAN_FIR_VALUE_H_
#include "common.h"
#include "mixin.h"
#include "../common/idioms.h"
#include <string>
namespace Fortran::FIR {
class Statement;
class BasicBlock;
class Procedure;
class DataObject;
class Value : public SumTypeCopyMixin<Nothing, DataObject *, Statement *,
BasicBlock *, Procedure *> {
public:
SUM_TYPE_COPY_MIXIN(Value)
template<typename A> Value(A *a) : SumTypeCopyMixin{a} {}
Value(const Nothing &n) : SumTypeCopyMixin{n} {}
Value() : SumTypeCopyMixin{NOTHING} {}
std::string dump() const;
};
inline bool IsNothing(Value value) {
return std::holds_alternative<Nothing>(value.u);
}
inline bool IsStatement(Value value) {
return std::holds_alternative<Statement *>(value.u);
}
inline bool IsBasicBlock(Value value) {
return std::holds_alternative<BasicBlock *>(value.u);
}
}
#endif // FORTRAN_FIR_VALUE_H_