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();
13}
14
15Nest::Nest(fe::View<Def*> muts)
16 : world_(muts.front()->world())
17 , root_(make_node(nullptr)) {
18 for (auto mut : muts)
19 make_node(mut, root_);
20 populate();
21}
22
24 : world_(world)
25 , root_(make_node(nullptr)) {
26 world.for_each(false, [this](Def* mut) { make_node(mut, root_); });
27 populate();
28}
29
30void Nest::populate() {
31 std::queue<Node*> queue;
32
33 if (root()->mut())
34 queue.push(root_);
35 else
36 for (auto child : root_->children().nodes())
37 queue.push(child);
38
39 while (!queue.empty()) {
40 auto curr_node = fe::pop(queue);
41 for (auto op : curr_node->mut()->deps()) {
42 for (auto local_mut : op->local_muts()) {
43 if ((*this)[local_mut] || !contains(local_mut)) continue;
44
45 if (curr_node->level() < local_mut->free_vars().size()) {
46 for (auto node = curr_node;; node = node->inest_) {
47 if (auto var = node->mut()->has_var()) {
48 if (local_mut->free_vars().contains(var)) {
49 queue.push(make_node(local_mut, node));
50 break;
51 }
52 }
53 }
54 } else {
55 uint32_t max = 0;
56 auto inest = root_;
57 for (auto var : local_mut->free_vars()) {
58 if (auto node = (*this)[var->binder()]; node && node->level() > max) {
59 max = node->level();
60 inest = node;
61 }
62 }
63 queue.push(make_node(local_mut, inest));
64 }
65 }
66 }
67 }
68}
69
70Nest::Node* Nest::make_node(Def* mut, Node* inest) {
71 auto node = std::unique_ptr<Node>(new Node(*this, mut, inest)); // can't use make_unique - c'tor is private
72 auto res = node.get();
73 mut2node_.emplace(mut, std::move(node));
74 if (mut) {
75 if (auto var = mut->has_var()) vars_ = world().vars().insert(vars_, var);
76 }
77 return res;
78}
79
80void Nest::assign_postorder_numbers() const {
81 if (root()->postorder_number_) return;
82
83 auto number = 0;
84
85 std::function<void(const Nest::Node*)> visit = [&](const Nest::Node* node) {
86 if (node->postorder_number_) return; // already visited
87 node->postorder_number_ = 0; // mark in progress
88 for (auto op : node->mut()->deps()) {
89 for (auto mut : op->local_muts())
90 if (auto succ = node->nest()[mut]) visit(succ);
91 }
92 node->postorder_number_ = ++number;
93 };
94
95 if (root()->mut()) {
96 // not virtual root, visit
97 visit(root());
98 } else {
99 // virtual root, visit children
100 for (auto [_, node] : root()->children())
101 visit(node);
102 root()->postorder_number_ = ++number;
103 }
104}
105
106template<bool bootstrapping>
107const Nest::Node* Nest::lca(const Node* n, const Node* m) {
108 while (n != m) {
109 // Nest::lca is also used within with_dominance and should not call it recursively
110 if constexpr (!bootstrapping) {
111 n->calc_dominance();
112 m->calc_dominance();
113 }
114 if (n->postorder_number_ < m->postorder_number_)
115 n = n->idom_ ? n->idom_ : n->inest();
116 else if (m->postorder_number_ < n->postorder_number_)
117 m = m->idom_ ? m->idom_ : m->inest();
118 }
119
120 return n;
121}
122
123void Nest::calc_sibl_deps(Node* curr) const {
124 if (curr->mut()) {
125 for (auto op : curr->mut()->deps()) {
126 for (auto local_mut : op->local_muts()) {
127 if (auto local_node = const_cast<Nest&>(*this)[local_mut]) {
128 if (local_node == curr)
129 local_node->link(local_node);
130 else if (auto inest = local_node->inest()) {
131 if (auto curr_child = inest->curr_child) {
132 assert(inest->children().contains(curr_child->mut()));
133 curr_child->link(local_node);
134 }
135 }
136 }
137 }
138 }
139 }
140
141 for (auto child : curr->children().nodes()) {
142 curr->curr_child = child;
143 calc_sibl_deps(child);
144 curr->curr_child = nullptr;
145 }
146}
147
148void Nest::calc_SCCs(Node* curr) const {
149 curr->calc_SCCs();
150 for (auto [_, child] : curr->children()) {
151 child->loop_depth_ = child->is_recursive() ? curr->loop_depth() + 1 : curr->loop_depth();
152 calc_SCCs(child);
153 }
154}
155
156void Nest::Node::calc_SCCs() {
157 Stack stack;
158 for (int i = 0; auto& [_, node] : children())
159 if (node->idx_ == Unvisited) i = node->tarjan(i, this, stack);
160}
161
162uint32_t Nest::Node::tarjan(uint32_t i, Node* inest, Stack& stack) {
163 this->idx_ = this->low_ = i++;
164 this->on_stack_ = true;
165 stack.emplace(this);
166
167 for (auto dep : this->sibl_deps_.nodes_) {
168 if (dep->idx_ == Unvisited) i = dep->tarjan(i, inest, stack);
169 if (dep->on_stack_) this->low_ = std::min(this->low_, dep->low_);
170 }
171
172 if (this->idx_ == this->low_) {
173 inest_->topo_.emplace_front(std::make_unique<SCC>());
174 SCC* scc = inest_->topo_.front().get();
175 Node* node;
176 int num = 0;
177 do {
178 node = fe::pop(stack);
179 node->on_stack_ = false;
180 node->recursive_ = true;
181 node->low_ = this->idx_;
182 ++num;
183
184 scc->emplace(node);
185 auto [_, ins] = inest_->SCCs_.emplace(node, scc);
186 assert_unused(ins);
187 } while (node != this);
188
189 if (num == 1 && !this->sibl_deps().contains(this)) this->recursive_ = false;
190 }
191
192 return i;
193}
194
195/// Calculates dominance using Cooper-Harvey-Kennedy algorithm
196/// from Cooper et al, "A Simple, Fast Dominance Algorithm".
197/// https://www.clear.rice.edu/comp512/Lectures/Papers/TR06-33870-Dom.pdf
198const Nest::Node* Nest::Node::calc_dominance() const {
199 if (idom_ || is_root() || !inest()->mut()) return this;
200 nest().assign_postorder_numbers();
201
202 if (!inest()->mut()) idom_ = inest();
203
204 // Holds all siblings in reverse post-order coming from the parent
205 absl::flat_hash_set<const Node*> visited;
206 fe::Vector<const Node*> nodes;
207
208 // Initialize entry nodes directly referenced by the parent
209 for (auto op : inest()->mut()->deps()) {
210 for (auto local_mut : op->local_muts())
211 if (auto node = nest()[local_mut]; node && node->inest() == inest()) node->idom_ = inest();
212 }
213
214 std::function<void(const Node*)> visit = [&](const Node* node) {
215 if (visited.contains(node)) return; // already visited
216 visited.insert(node);
217 for (auto child : node->sibl_deps())
218 visit(child);
219 nodes.push_back(node);
220 };
221
222 // Traverse siblings in postorder
223 for (auto op : inest()->mut()->deps()) {
224 for (auto mut : op->local_muts())
225 if (auto node = nest()[mut]; node && node->inest() == inest()) visit(node);
226 }
227
228 // Actual dominance algorithm
229 for (bool todo = true; todo;) {
230 todo = false;
231 for (auto node : nodes | std::views::reverse) {
232 // skip entry nodes
233 if (node->idom_ == inest()) continue;
234
235 const Node* new_idom = nullptr;
236 for (auto user : node->sibl_deps<false>())
237 if (user->idom_) new_idom = new_idom ? Nest::lca<true>(new_idom, user) : user;
238 if (node->idom_ != new_idom) {
239 node->idom_ = new_idom;
240 todo = true;
241 }
242 }
243 }
244
245 return this;
246}
247
248template const Nest::Node* Nest::lca<true>(const Node*, const Node*);
249template const Nest::Node* Nest::lca<false>(const Node*, const Node*);
250
251} // namespace mim
Base class for all Defs.
Definition def.h:273
const Children & children() const
Definition nest.h:95
const Node * inest() const
Immediate nester/parent of this Node.
Definition nest.h:39
Builds a nesting tree for all mutables/binders.
Definition nest.h:31
auto nodes() const
Definition nest.h:235
World & world() const
Definition nest.h:223
static const Node * lca(const Node *n, const Node *m)
Least common ancestor of n and m.
Definition nest.cpp:107
auto muts() const
Definition nest.h:234
const Node * root() const
Definition nest.h:224
Nest(Def *root)
Definition nest.cpp:9
bool contains(const Def *def) const
Definition nest.h:226
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:40
void for_each(bool elide_empty, std::function< void(Def *)>, bool schedule=false)
Definition world.cpp:739
auto & vars()
Definition world.h:694
Definition ast.h:16
Node
Definition def.h:120
auto nodes() const
Definition nest.h:65