[flang] Fortran IR: staged pull request for the "upper layers" of the IR. The

Fortran IR is hierarchical: A Program contains Procedures.  Procedures
contain Basic Blocks. Groups of Basic Blocks can be grouped as Regions.
This structure follows those one finds in SIL and LLVM IR, etc.

Original-commit: flang-compiler/f18@e2291016df
Reviewed-on: https://github.com/flang-compiler/f18/pull/293
Tree-same-pre-rewrite: false
This commit is contained in:
Eric Schweitz 2019-02-20 11:46:41 -08:00
parent f425c4064d
commit 52d7de892e
8 changed files with 535 additions and 0 deletions

View file

@ -0,0 +1,57 @@
// 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 "stmt.h"
namespace Fortran::IntermediateRepresentation {
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 : predecessors_) {
if (p == bb) return;
}
predecessors_.push_back(bb);
}
const Statement *BasicBlock::getTerminator() const {
if (statementList_.empty()) {
return nullptr;
}
const auto &lastStmt{statementList_.back()};
return std::visit(
[&lastStmt](auto stmt) {
if constexpr (std::is_base_of_v<TerminatorStmt_impl, decltype(stmt)>) {
return &lastStmt;
}
return static_cast<const Statement *>(nullptr);
},
lastStmt.u);
}
}

View file

@ -0,0 +1,69 @@
// 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_INTERMEDIATEREPRESENTATION_BASICBLOCK_H_
#define FORTRAN_INTERMEDIATEREPRESENTATION_BASICBLOCK_H_
#include "mixin.h"
#include "region.h"
#include <iostream>
namespace Fortran::IntermediateRepresentation {
struct Region;
struct Statement;
struct BasicBlock final : public llvm::ilist_node<BasicBlock>,
public ChildMixin<BasicBlock, Region> {
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();
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 *getTerminator() const;
Statement *getTerminator() {
return const_cast<Statement *>(
const_cast<const BasicBlock *>(this)->getTerminator());
}
void SetRegion(Region *region) { parent = region; }
Region *GetRegion() const { return parent; }
void addPred(BasicBlock *bb);
std::vector<BasicBlock *> &Predecessors() { return predecessors_; }
StatementListType &Statements() { return statementList_; }
BasicBlock *SplitEdge(BasicBlock *toBlock) { return nullptr; }
private:
StatementListType statementList_;
std::vector<BasicBlock *> predecessors_;
explicit BasicBlock(Region *parentRegion, BasicBlock *insertBefore);
};
inline std::list<BasicBlock *> pred_list(BasicBlock &block) {
return std::list<BasicBlock *>{
block.Predecessors().begin(), block.Predecessors().end()};
}
}
#endif

View file

@ -0,0 +1,86 @@
// 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"
namespace Fortran::IntermediateRepresentation {
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(
new Statement(block, callback(scope)), block->getTerminator());
}
}
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 *successor : succ_list(block)) {
auto *successorRegion{successor->GetRegion()};
if (successorRegion != region &&
DistinctScopes(successorRegion, region)) {
if (IsAncestor(region, successorRegion)) {
AddEnterScopes(RegionDepth(region, successorRegion),
successor->SplitEdge(&block));
} else if (IsAncestor(successorRegion, region)) {
AddExitScopes(RegionDepth(successorRegion, region),
block.SplitEdge(successor));
} else {
// TODO: edge to a cousin region
CHECK(false);
}
}
}
}
}
}
}

View file

@ -0,0 +1,80 @@
// 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_INTERMEDIATEREPRESENTATION_PROCEDURE_H_
#define FORTRAN_INTERMEDIATEREPRESENTATION_PROCEDURE_H_
#include "mixin.h"
#include "program.h"
#include "region.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
namespace Fortran::IntermediateRepresentation {
struct Program;
struct Region;
struct GraphWriter;
struct Procedure final : public llvm::ilist_node<Procedure>,
public ChildMixin<Procedure, Program> {
friend GraphWriter;
friend Program;
friend Region;
using BasicBlockListType = llvm::iplist<BasicBlock>;
using RegionListType = llvm::iplist<Region>;
using iterator = BasicBlockListType::iterator;
using const_iterator = BasicBlockListType::const_iterator;
using reverse_iterator = BasicBlockListType::reverse_iterator;
using const_reverse_iterator = BasicBlockListType::const_reverse_iterator;
Procedure(const Procedure &) = delete;
Procedure &operator=(const Procedure &) = delete;
~Procedure();
BasicBlockListType &GetBlocks() { return basicBlockList_; }
BasicBlockListType &getSublist(BasicBlock *) { return GetBlocks(); }
RegionListType &GetRegions() { return regionList_; }
RegionListType &getSublist(Region *) { return GetRegions(); }
iterator begin() { return basicBlockList_.begin(); }
const_iterator begin() const { return basicBlockList_.begin(); }
iterator end() { return basicBlockList_.end(); }
const_iterator end() const { return basicBlockList_.end(); }
Region *getLastRegion() { return &regionList_.back(); }
BasicBlock *StartBlock() { 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();
private:
RegionListType regionList_;
BasicBlockListType basicBlockList_;
FunctionType *procType_;
LinkageTypes linkage_;
unsigned addressSpace_;
const std::string name_;
explicit Procedure(Program *program, FunctionType *ty, LinkageTypes lt,
unsigned addrSpace, const llvm::Twine &n, Procedure *before);
Region *insertBefore(Region *region, Region *before = nullptr);
};
}
#endif

View file

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

View file

@ -0,0 +1,55 @@
// 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_INTERMEDIATEREPRESENTATION_PROGRAM_H_
#define FORTRAN_INTERMEDIATEREPRESENTATION_PROGRAM_H_
#include "common.h"
#include "procedure.h"
#include "../evaluate/type.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include <string>
namespace Fortran::IntermediateRepresentation {
struct Procedure;
struct GraphWriter;
struct Program final {
friend GraphWriter;
using ProcedureListType = llvm::iplist<Procedure>;
using ProcedureMapType = llvm::StringMap<Procedure *>;
explicit Program(llvm::StringRef id);
~Program();
void insertBefore(Procedure *subprog, Procedure *before = nullptr);
ProcedureListType &getSublist(Procedure *) { return procedureList_; }
bool containsProcedure(llvm::StringRef name) {
return procedureMap_.find(name) != procedureMap_.end();
}
std::string getName() const { return name_; }
Procedure *getOrInsertProcedure(
llvm::StringRef name, FunctionType *procTy, AttributeList attrs);
private:
ProcedureListType procedureList_;
ProcedureMapType procedureMap_;
const std::string name_;
};
}
#endif

View file

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

View file

@ -0,0 +1,91 @@
// 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_INTERMEDIATEREPRESENTATION_REGION_H_
#define FORTRAN_INTERMEDIATEREPRESENTATION_REGION_H_
#include "procedure.h"
#include "stmt.h"
#include "../semantics/semantics.h"
namespace Fortran::IntermediateRepresentation {
struct Procedure;
struct BasicBlock;
struct Region final : public llvm::ilist_node<Region>,
public ChildMixin<Region, Procedure> {
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() == nullptr; }
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