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