From 96f7b6c808bd3a0ef6c9027600e278e478a1064d Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Wed, 20 Feb 2019 13:07:49 -0800 Subject: [PATCH] [flang] Fortran IR: part 4. Dotty graph visualization and other miscellaneous headers. Original-commit: flang-compiler/f18@d7d9515f20557608ad7df45ef90647a318441e68 Reviewed-on: https://github.com/flang-compiler/f18/pull/296 Tree-same-pre-rewrite: false --- flang/lib/IntermediateRepresentation/common.h | 107 ++++++++++++++++++ .../lib/IntermediateRepresentation/graph-writer.cc | 122 +++++++++++++++++++++ .../lib/IntermediateRepresentation/graph-writer.h | 93 ++++++++++++++++ flang/lib/IntermediateRepresentation/mixin.h | 114 +++++++++++++++++++ 4 files changed, 436 insertions(+) create mode 100644 flang/lib/IntermediateRepresentation/common.h create mode 100644 flang/lib/IntermediateRepresentation/graph-writer.cc create mode 100644 flang/lib/IntermediateRepresentation/graph-writer.h create mode 100644 flang/lib/IntermediateRepresentation/mixin.h diff --git a/flang/lib/IntermediateRepresentation/common.h b/flang/lib/IntermediateRepresentation/common.h new file mode 100644 index 0000000..8066c6a --- /dev/null +++ b/flang/lib/IntermediateRepresentation/common.h @@ -0,0 +1,107 @@ +// 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_COMMON_H_ +#define FORTRAN_INTERMEDIATEREPRESENTATION_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" + +// Some useful, self-documenting macros for failure modes +#define STRINGIFY(X) #X +#define LINE2STRING(X) STRINGIFY(X) +#define AT_HERE " at " __FILE__ "(" LINE2STRING(__LINE__) ")" +#define DIE Fortran::common::die +#define SEMANTICS_FAILED(STRING) DIE("semantics bug: " STRING AT_HERE) +#define SEMANTICS_CHECK(CONDITION, STRING) \ + if (CONDITION) { \ + } else { \ + DIE("semantics bug: " STRING AT_HERE); \ + } +#define WRONG_PATH() DIE("control should not reach here" AT_HERE) + +namespace Fortran::IntermediateRepresentation { +class Statement; +class BasicBlock; +struct Program; +struct GraphWriter; + +struct Attribute { + enum { IntentIn, IntentOut, IntentInOut } attribute; + unsigned short position; +}; +using FunctionType = evaluate::SomeType; // TODO: what should this be? +using AttributeList = std::vector; +enum struct LinkageTypes { Public, Hidden, External }; +using Expression = evaluate::GenericExprWrapper; +#if 0 +struct Variable { + // TODO: should semantics::Symbol be removed? + template struct GVT { + using type = + std::variant...>; + }; + Variable(const semantics::Symbol *symbol) : u{symbol} {} + common::OverMembers::type u; +}; +#endif +using Variable = const semantics::Symbol *; +using PathVariable = const parser::Variable; +using Scope = const semantics::Scope; +using Value = Expression; +using PHIPair = std::pair; +using CallArguments = std::vector; + +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; +} + +#endif diff --git a/flang/lib/IntermediateRepresentation/graph-writer.cc b/flang/lib/IntermediateRepresentation/graph-writer.cc new file mode 100644 index 0000000..dc843a3 --- /dev/null +++ b/flang/lib/IntermediateRepresentation/graph-writer.cc @@ -0,0 +1,122 @@ +// 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" + +namespace Fortran::IntermediateRepresentation { + +std::optional GraphWriter::defaultOutput_{std::nullopt}; + +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 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> 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 color) { + output_ << " " << block_id(block) << " [label = \""; + if (isEntry_) { + output_ << "<>\\n"; + } + output_ << block_id(block) << '(' << reinterpret_cast(&block) + << ")\\n"; + for (auto &action : block.getSublist(static_cast(nullptr))) { + output_ << action.dump() << "\\n"; + } + if (isExit_) { + output_ << "<>"; + } + output_ << "\",shape=rectangle"; + if (color) { + output_ << ",color=" << *color; + } + output_ << "];\n"; +} + +void GraphWriter::dumpInternalEdges(BasicBlock &block, + std::set &nodeSet, + std::list> &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}); + } + } +} + +} diff --git a/flang/lib/IntermediateRepresentation/graph-writer.h b/flang/lib/IntermediateRepresentation/graph-writer.h new file mode 100644 index 0000000..d9d2ea6 --- /dev/null +++ b/flang/lib/IntermediateRepresentation/graph-writer.h @@ -0,0 +1,93 @@ +// 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_GRAPH_WRITER_H_ +#define FORTRAN_INTERMEDIATEREPRESENTATION_GRAPH_WRITER_H_ + +#include "program.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include + +namespace Fortran::IntermediateRepresentation { + +struct GraphWriter { + 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() { + if (defaultOutput_) { + delete *defaultOutput_; + defaultOutput_ = std::nullopt; + } + } + 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 color = std::nullopt); + void dumpInternalEdges(BasicBlock &block, std::set &nodeSet, + std::list> &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_.value() : llvm::outs(); + } + + unsigned count_{0u}; + llvm::raw_ostream &output_; + unsigned blockNum_{0u}; + bool isEntry_{false}; + bool isExit_{false}; + std::map blockIds_; + static std::optional defaultOutput_; +}; + +} + +#endif diff --git a/flang/lib/IntermediateRepresentation/mixin.h b/flang/lib/IntermediateRepresentation/mixin.h new file mode 100644 index 0000000..a970d82 --- /dev/null +++ b/flang/lib/IntermediateRepresentation/mixin.h @@ -0,0 +1,114 @@ +// 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_MIXIN_H_ +#define FORTRAN_INTERMEDIATEREPRESENTATION_MIXIN_H_ + +#include "llvm/ADT/ilist.h" +#include +#include +#include +#include + +namespace Fortran::IntermediateRepresentation { + +template struct SumTypeMixin {}; +template // T must be std::optional<...> +struct SumTypeMixin>> { + template SumTypeMixin(A &&x) : u{std::move(x)} {} + using SumTypeTrait = std::true_type; + SumTypeMixin(SumTypeMixin &&) = default; + SumTypeMixin &operator=(SumTypeMixin &&) = default; + SumTypeMixin(const SumTypeMixin &) = delete; + SumTypeMixin &operator=(const SumTypeMixin &) = delete; + SumTypeMixin() = delete; + T u; +}; + +template struct SumTypeCopyMixin {}; +template // T must be std::optional<...> +struct SumTypeCopyMixin>> { + using CopyableSumTypeTrait = std::true_type; + SumTypeCopyMixin(SumTypeCopyMixin &&) = default; + SumTypeCopyMixin &operator=(SumTypeCopyMixin &&) = default; + SumTypeCopyMixin(const SumTypeCopyMixin &) = default; + SumTypeCopyMixin &operator=(const SumTypeCopyMixin &) = default; + SumTypeCopyMixin() = delete; + T u; +}; +#define SUM_TYPE_COPY_MIXIN(Derived) \ + Derived(const Derived &derived) : SumTypeCopyMixin(derived) {} \ + Derived &operator=(const Derived &derived) { \ + SumTypeCopyMixin::operator=(derived); \ + return *this; \ + } + +template struct ProductTypeMixin {}; +template // T must be std::tuple<...> +struct ProductTypeMixin>> { + template ProductTypeMixin(A &&x) : t{std::move(x)} {} + using ProductTypeTrait = std::true_type; + ProductTypeMixin(ProductTypeMixin &&) = default; + ProductTypeMixin &operator=(ProductTypeMixin &&) = default; + ProductTypeMixin(const ProductTypeMixin &) = delete; + ProductTypeMixin &operator=(const ProductTypeMixin &) = delete; + ProductTypeMixin() = delete; + T t; +}; + +template struct MaybeMixin {}; +template // T must be std::optional<...> +struct MaybeMixin, T>>> { + template MaybeMixin(A &&x) : o{std::move(x)} {} + using MaybeTrait = std::true_type; + MaybeMixin(MaybeMixin &&) = default; + MaybeMixin &operator=(MaybeMixin &&) = default; + MaybeMixin(const MaybeMixin &) = delete; + MaybeMixin &operator=(const MaybeMixin &) = delete; + MaybeMixin() = delete; + T o; +}; + +template 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 &getList() { return parent->getSublist(this); } +}; + +template +C Zip(C out, A first, A last, B other) { + std::transform(first, last, other, out, + [](auto &&a, auto &&b) -> std::pair { + return {a, b}; + }); + return out; +} +template 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; +} + +} + +#endif -- 2.7.4