MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
nest.cpp
Go to the documentation of this file.
1#include "mim/nest.h"
2
3#include <queue>
4
5#include "mim/world.h"
6
7namespace mim {
8
10 : world_(r->world())
11 , root_(make_node(r)) {
12 populate(false);
13 analyze();
14}
15
16Nest::Nest(World& world, fe::View<Def*> muts, bool transitive)
17 : world_(world)
18 , root_(make_node(nullptr)) {
19 for (auto mut : muts)
20 make_node(mut, root_);
21 populate(transitive);
22 analyze();
23}
24
26 : Nest(world, world.externals().mutate(), true) {}
27
28void Nest::populate(bool transitive) {
29 std::queue<Node*> queue;
30
31 if (root()->mut())
32 queue.push(root_);
33 else
34 for (auto child : root_->children_.mut_nodes())
35 queue.push(child);
36
37 while (!queue.empty()) {
38 auto curr_node = fe::pop(queue);
39 for (auto op : curr_node->mut()->deps()) {
40 for (auto local_mut : op->local_muts()) {
41 if (node(local_mut)) continue;
42 if (!contains(local_mut)) {
43 if (transitive && local_mut->is_closed()) queue.push(make_node(local_mut, root_));
44 continue;
45 }
46
47 queue.push(make_node(local_mut, innermost(local_mut)));
48 }
49 }
50 }
51}
52
53Nest::Node* Nest::innermost(Node* res, Vars vars) const {
54 for (auto fv : vars)
55 if (auto n = node(fv->binder()); n && n->level() > res->level()) res = n;
56 return res;
57}
58
59Nest::Node* Nest::innermost(const Def* def) const {
60 if (auto mut = def->isa_mut()) return innermost(root_, mut->free_vars());
61 auto res = innermost(root_, def->local_vars());
62 for (auto mut : def->local_muts())
63 res = innermost(res, mut->free_vars());
64 return res;
65}
66
67Nest::Node* Nest::make_node(Def* mut, Node* inest) {
68 auto node = std::unique_ptr<Node>(new Node(*this, mut, inest)); // can't use make_unique - c'tor is private
69 auto res = node.get();
70 mut2node_.emplace(mut, std::move(node));
71 if (mut) {
72 if (auto var = mut->has_var()) vars_ = world().vars().insert(vars_, var);
73 }
74 return res;
75}
76
77void Nest::analyze() {
78 auto path = fe::Vector<Node*>();
79 calc_sibl_deps(root_, path);
80 calc_SCCs(root_);
81
82 size_t n = 0;
83 if (root_->mut()) {
84 number(root_, n);
85 } else {
86 for (auto child : root_->children_.mut_nodes())
87 number(child, n);
88 root_->postorder_number_ = ++n;
89 }
90
91 for (auto& [mut, node] : mut2node_)
92 if (mut && node->children().num() != 0) calc_dominance(node.get());
93}
94
95/// @p path holds the Node%s from the root() down to @p curr.
96void Nest::calc_sibl_deps(Node* curr, fe::Vector<Node*>& path) {
97 path.emplace_back(curr);
98 if (curr->mut()) {
99 for (auto op : curr->mut()->deps()) {
100 for (auto local_mut : op->local_muts()) {
101 if (auto local_node = node(local_mut)) {
102 if (local_node->is_root()) local_node->recursive_ = true;
103 if (local_node == curr) {
104 local_node->link(local_node);
105 } else if (auto inest = local_node->inest_) {
106 // Only the child of inest that leads to curr depends on local_node.
107 if (auto l = inest->level() + 1; l < path.size() && path[l - 1] == inest) {
108 assert(inest->children().contains(path[l]->mut()));
109 path[l]->link(local_node);
110 }
111 }
112 }
113 }
114 }
115 }
116
117 for (auto child : curr->children_.mut_nodes())
118 calc_sibl_deps(child, path);
119 path.pop_back();
120}
121
122void Nest::calc_SCCs(Node* curr) {
123 Node::Stack stack;
124 for (uint32_t i = 0; auto child : curr->children_.mut_nodes())
125 if (child->idx_ == Node::Unvisited) i = child->tarjan(i, stack);
126
127 for (auto child : curr->children_.mut_nodes()) {
128 child->loop_depth_ = child->is_recursive() ? curr->loop_depth() + 1 : curr->loop_depth();
129 calc_SCCs(child);
130 }
131}
132
133uint32_t Nest::Node::tarjan(uint32_t i, Stack& stack) {
134 this->idx_ = this->low_ = i++;
135 this->on_stack_ = true;
136 stack.emplace(this);
137
138 for (auto dep : this->sibl_deps_.nodes_) {
139 if (dep->idx_ == Unvisited) i = dep->tarjan(i, stack);
140 if (dep->on_stack_) this->low_ = std::min(this->low_, dep->low_);
141 }
142
143 if (this->idx_ == this->low_) {
144 inest_->topo_.emplace_front(std::make_unique<SCC>());
145 SCC* scc = inest_->topo_.front().get();
146 Node* node;
147 int num = 0;
148 do {
149 node = fe::pop(stack);
150 node->on_stack_ = false;
151 node->recursive_ = true;
152 node->low_ = this->idx_;
153 ++num;
154
155 scc->emplace(node);
156 node->scc_ = scc;
157 } while (node != this);
158
159 if (num == 1 && !this->sibl_deps_.contains(this)) this->recursive_ = false;
160 }
161
162 return i;
163}
164
165void Nest::number(Node* node, size_t& n) {
166 if (node->postorder_number_) return; // already visited
167 node->postorder_number_ = 0; // mark in progress
168 for (auto op : node->mut()->deps())
169 for (auto mut : op->local_muts())
170 if (auto succ = this->node(mut)) number(succ, n);
171 node->postorder_number_ = ++n;
172}
173
174const Nest::Node* Nest::lca(const Node* n, const Node* m) {
175 while (n != m)
176 if (n->postorder_number_ < m->postorder_number_)
177 n = n->idom_ ? n->idom_ : n->inest();
178 else if (m->postorder_number_ < n->postorder_number_)
179 m = m->idom_ ? m->idom_ : m->inest();
180
181 return n;
182}
183
184void Nest::sibl_post_order(Node* node, ankerl::unordered_dense::set<const Node*>& visited, fe::Vector<Node*>& nodes) {
185 if (!visited.insert(node).second) return;
186 for (auto succ : node->sibl_deps_.nodes_)
187 sibl_post_order(succ, visited, nodes);
188 nodes.push_back(node);
189}
190
191/// Calculates dominance among the children of @p parent using Cooper-Harvey-Kennedy algorithm
192/// from Cooper et al, "A Simple, Fast Dominance Algorithm".
193/// https://www.clear.rice.edu/comp512/Lectures/Papers/TR06-33870-Dom.pdf
194void Nest::calc_dominance(Node* parent) {
195 // Initialize entry nodes directly referenced by the parent
196 for (auto op : parent->mut()->deps()) {
197 for (auto local_mut : op->local_muts())
198 if (auto node = this->node(local_mut); node && node->inest_ == parent) node->idom_ = parent;
199 }
200
201 // Holds all siblings in reverse post-order coming from the parent
202 ankerl::unordered_dense::set<const Node*> visited;
203 fe::Vector<Node*> nodes;
204 for (auto op : parent->mut()->deps()) {
205 for (auto mut : op->local_muts())
206 if (auto node = this->node(mut); node && node->inest_ == parent) sibl_post_order(node, visited, nodes);
207 }
208
209 // Actual dominance algorithm
210 for (bool todo = true; todo;) {
211 todo = false;
212 for (auto node : nodes | std::views::reverse) {
213 // skip entry nodes
214 if (node->idom_ == parent) continue;
215
216 const Node* new_idom = nullptr;
217 for (auto user : node->sibl_rev_deps_.nodes_)
218 if (user->idom_) new_idom = new_idom ? lca(new_idom, user) : user;
219 if (node->idom_ != new_idom) {
220 node->idom_ = new_idom;
221 todo = true;
222 }
223 }
224 }
225}
226
227} // namespace mim
Base class for all Defs.
Definition def.h:289
GIDSet< const Node * > SCC
Strongly Connected Component.
Definition nest.h:125
const SCC & scc() const
The SCC among its siblings this Node belongs to; the Nest::root() has none.
Definition nest.h:135
const Node * inest() const
Immediate nester/parent of this Node.
Definition nest.h:33
const Node * inest(const Def *def) const
The innermost Node binding a free Var of def, or the root() if there is none.
Definition nest.h:211
auto nodes() const
Definition nest.h:205
World & world() const
Definition nest.h:194
static const Node * lca(const Node *n, const Node *m)
Least common ancestor of n and m.
Definition nest.cpp:174
auto muts() const
Definition nest.h:204
const Node * root() const
Definition nest.h:195
Nest(Def *root)
Definition nest.cpp:9
Vars vars() const
All Vars occurring in this Nest.
Definition nest.h:196
bool contains(const Def *def) const
Definition nest.h:197
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:43
auto & vars()
Definition world.h:714
Definition ast.h:16
fe::Patricia< const Var, DefKey >::Set Vars
Definition def.h:115
Node
Definition def.h:123