forked from OSchip/llvm-project
[flang] remove FIR
Original-commit: flang-compiler/f18@008ad16e28 Reviewed-on: https://github.com/flang-compiler/f18/pull/489
This commit is contained in:
parent
6bbcc8c1d8
commit
5c978b94a2
|
@ -14,6 +14,5 @@
|
|||
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(evaluate)
|
||||
add_subdirectory(FIR)
|
||||
add_subdirectory(parser)
|
||||
add_subdirectory(semantics)
|
||||
|
|
|
@ -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
|
@ -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_
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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"; }
|
||||
}
|
|
@ -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
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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 ®ion, 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});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 ®ion) {
|
||||
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 ®ion, 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
|
|
@ -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_
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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 ®ionList_.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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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_
|
|
@ -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_
|
Loading…
Reference in New Issue