Thorin 1.9.0
The Higher ORder INtermediate representation
Loading...
Searching...
No Matches
cfg.cpp
Go to the documentation of this file.
2
3#include <fstream>
4#include <memory>
5
6#include "thorin/world.h"
7
12#include "thorin/util/util.h"
13
14namespace thorin {
15
16//------------------------------------------------------------------------------
17
18uint64_t CFNode::gid_counter_ = 0;
19
20void CFNode::link(const CFNode* other) const {
21 this->succs_.emplace(other);
22 other->preds_.emplace(this);
23}
24
25std::ostream& operator<<(std::ostream& os, const CFNode* n) { return os << n->mut(); }
26
27//------------------------------------------------------------------------------
28
29CFA::CFA(const Scope& scope)
30 : scope_(scope)
31 , entry_(node(scope.entry()))
32 , exit_(node(scope.exit())) {
33 std::queue<Def*> cfg_queue;
34 MutSet cfg_done;
35
36 auto cfg_enqueue = [&](Def* mut) {
37 if (mut->is_set() && cfg_done.emplace(mut).second) cfg_queue.push(mut);
38 };
39
40 cfg_enqueue(scope.entry());
41
42 while (!cfg_queue.empty()) {
43 auto src = pop(cfg_queue);
44 std::queue<const Def*> queue;
45 DefSet done;
46
47 auto enqueue = [&](const Def* def) {
48 if (def->isa<Var>()) return;
49 // TODO maybe optimize a little bit by using the order
50 if (scope.bound(def) && done.emplace(def).second) {
51 if (auto dst = def->isa_mut()) {
52 cfg_enqueue(dst);
53 node(src)->link(node(dst));
54 } else
55 queue.push(def);
56 }
57 };
58
59 queue.push(src);
60
61 while (!queue.empty()) {
62 auto def = pop(queue);
63 for (auto op : def->ops()) enqueue(op);
64 }
65 }
66
67 link_to_exit();
68 verify();
69}
70
71const CFNode* CFA::node(Def* mut) {
72 auto&& n = nodes_[mut];
73 if (n == nullptr) n = new CFNode(mut);
74 return n;
75}
76
78 for (const auto& p : nodes_) delete p.second;
79}
80
81const F_CFG& CFA::f_cfg() const { return lazy_init(this, f_cfg_); }
82const B_CFG& CFA::b_cfg() const { return lazy_init(this, b_cfg_); }
83
84void CFA::link_to_exit() {
85 using CFNodeSet = thorin::GIDSet<const CFNode*>;
86
87 CFNodeSet reachable;
88 std::queue<const CFNode*> queue;
89
90 // first, link all nodes without succs to exit
91 for (auto p : nodes()) {
92 auto n = p.second;
93 if (n != exit() && n->succs().empty()) n->link(exit());
94 }
95
96 auto backwards_reachable = [&](const CFNode* n) {
97 auto enqueue = [&](const CFNode* n) {
98 if (reachable.emplace(n).second) queue.push(n);
99 };
100
101 enqueue(n);
102
103 while (!queue.empty())
104 for (auto pred : pop(queue)->preds()) enqueue(pred);
105 };
106
107 std::stack<const CFNode*> stack;
108 CFNodeSet on_stack;
109
110 auto push = [&](const CFNode* n) {
111 if (on_stack.emplace(n).second) {
112 stack.push(n);
113 return true;
114 }
115
116 return false;
117 };
118
119 backwards_reachable(exit());
120 push(entry());
121
122 while (!stack.empty()) {
123 auto n = stack.top();
124
125 bool todo = false;
126 for (auto succ : n->succs()) todo |= push(succ);
127
128 if (!todo) {
129 if (!reachable.contains(n)) {
130 n->link(exit());
131 backwards_reachable(n);
132 }
133
134 stack.pop();
135 }
136 }
137}
138
139void CFA::verify() {
140 bool error = false;
141 for (const auto& p : nodes()) {
142 auto in = p.second;
143 if (in != entry() && in->preds_.size() == 0) {
144 world().VLOG("missing predecessors: {}", in->mut());
145 error = true;
146 }
147 }
148
149 if (error) {
150 // TODO
151 assert(false && "CFG not sound");
152 }
153}
154
155//------------------------------------------------------------------------------
156
157template<bool forward>
159 : cfa_(cfa)
160 , rpo_(*this) {
161 auto index = post_order_visit(entry(), size());
162 assert_unused(index == 0);
163}
164
165template<bool forward> size_t CFG<forward>::post_order_visit(const CFNode* n, size_t i) {
166 auto& n_index = forward ? n->f_index_ : n->b_index_;
167 n_index = size_t(-2);
168
169 for (auto succ : succs(n))
170 if (index(succ) == size_t(-1)) i = post_order_visit(succ, i);
171
172 n_index = i - 1;
173 rpo_[n] = n;
174 return n_index;
175}
176
177// clang-format off
178template<bool forward> const CFNodes& CFG<forward>::preds(const CFNode* n) const { assert(n != nullptr); return forward ? n->preds() : n->succs(); }
179template<bool forward> const CFNodes& CFG<forward>::succs(const CFNode* n) const { assert(n != nullptr); return forward ? n->succs() : n->preds(); }
180template<bool forward> const DomTreeBase<forward>& CFG<forward>::domtree() const { return lazy_init(this, domtree_); }
181template<bool forward> const LoopTree<forward>& CFG<forward>::looptree() const { return lazy_init(this, looptree_); }
182template<bool forward> const DomFrontierBase<forward>& CFG<forward>::domfrontier() const { return lazy_init(this, domfrontier_); }
183// clang-format on
184
185template class CFG<true>;
186template class CFG<false>;
187
188//------------------------------------------------------------------------------
189
190} // namespace thorin
Control Flow Analysis.
Definition cfg.h:62
CFA(const CFA &)=delete
World & world() const
Definition cfg.h:71
const MutMap< const CFNode * > & nodes() const
Definition cfg.h:73
const Scope & scope() const
Definition cfg.h:70
const B_CFG & b_cfg() const
Definition cfg.cpp:82
const F_CFG & f_cfg() const
Definition cfg.cpp:81
A Control-Flow Graph.
Definition cfg.h:116
const LoopTree< forward > & looptree() const
Definition cfg.cpp:181
size_t size() const
Definition cfg.h:127
const CFNodes & preds(const CFNode *n) const
Definition cfg.cpp:178
const CFNode * entry() const
Definition cfg.h:136
static size_t index(const CFNode *n)
Definition cfg.h:150
CFG(const CFG &)=delete
const DomTreeBase< forward > & domtree() const
Definition cfg.cpp:180
const CFNodes & succs(const CFNode *n) const
Definition cfg.cpp:179
const DomFrontierBase< forward > & domfrontier() const
Definition cfg.cpp:182
A Control-Flow Node.
Definition cfg.h:27
Def * mut() const
Definition cfg.h:34
Base class for all Defs.
Definition def.h:222
A Dominance Frontier Graph.
Definition domfrontier.h:12
A Dominance Tree.
Definition domtree.h:11
Calculates a loop nesting forest rooted at LoopTree::root_.
Definition looptree.h:16
A Scope represents a region of Defs that are live from the view of an entry's Var.
Definition scope.h:22
Def * entry() const
Definition scope.h:33
bool bound(const Def *def) const
Definition scope.h:40
Definition cfg.h:11
void error(const Def *def, const char *fmt, Args &&... args)
Definition def.h:622
GIDSet< const Def * > DefSet
Definition def.h:60
GIDSet< Def * > MutSet
Definition def.h:70
GIDSet< const CFNode * > CFNodes
Definition cfg.h:22
std::ostream & operator<<(std::ostream &, const CFNode *)
Definition cfg.cpp:25
auto pop(S &s) -> decltype(s.top(), typename S::value_type())
Definition util.h:78
absl::flat_hash_set< K, GIDHash< K >, GIDEq< K > > GIDSet
Definition util.h:180
T & lazy_init(const This *self, std::unique_ptr< T > &ptr)
Definition util.h:21