MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
nest.h
Go to the documentation of this file.
1#pragma once
2
3#include <memory>
4#include <optional>
5#include <ranges>
6#include <stack>
7
8#include <absl/container/btree_map.h>
9#include <absl/container/btree_set.h>
10
11#include "mim/def.h"
12
13namespace mim {
14
15namespace detail {
16/// Like GIDLt, but the *nesting virtual root* is represented as a `nullptr` Def%, which must sort first.
17struct NullSafeDefGIDLt {
18 constexpr bool operator()(const Def* a, const Def* b) const noexcept {
19 return (a ? a->gid() : 0) < (b ? b->gid() : 0);
20 }
21};
22} // namespace detail
23
24/// Builds a nesting tree for all mutables/binders.
25///
26/// @note This type should typically be constructed as `const`,
27/// because some member functions used during lookup have private non-`const` overloads.
28/// ```
29/// const auto nest = Nest(lam);
30/// ```
31class Nest {
32public:
33 class Node {
34 public:
35 /// @name Getters
36 ///@{
37 std::string name() const { return mut() ? mut()->unique_name() : std::string("virtual"); }
38 const Nest& nest() const { return nest_; }
39 const Node* inest() const { return inest_; } ///< Immediate nester/parent of this Node.
40 /// [Immediate Dominator](https://en.wikipedia.org/wiki/Dominator_(graph_theory)) for children in connected
41 /// components. This is used to transform first order programs into structured form in the
42 /// [sflow](mim::plug::sflow) plugin and for late code placement in [Nest::lca].
43 auto idom() const { return calc_dominance()->idom_; }
44 bool is_root() const { return inest_ == nullptr; }
45 /// The *mutable* capsulated in this Node or `nullptr`, if it's a *virtual root* comprising several Node%s.
46 Def* mut() const {
47 assert(mut_ || is_root());
48 return mut_;
49 }
50 uint32_t level() const { return level_; }
51 uint32_t loop_depth() const { return sccs().loop_depth_; }
52 /// Stable id for deterministic hashing/ordering: the underlying mut's gid, or `0` for the (unique) virtual
53 /// root.
54 uint32_t gid() const { return mut_ ? mut_->gid() : 0; }
55 ///@}
56
57 /// @name Children
58 ///@{
59 struct Children {
60 ///@name Get children as muts and/or nodes.
61 ///@{
62 // clang-format off
63 auto mut2node() const { return mut2node_ | std::views::transform([](auto p) { return std::pair{p.first, const_cast<const Node*>(p.second)}; }); }
64 auto muts() const { return mut2node_ | std::views::keys; }
65 auto nodes() const { return mut2node_ | std::views::transform([](auto p) { return const_cast<const Node*>(p.second); }); }
66 // clang-format on
67 size_t num() const { return mut2node_.size(); } ///< Number of children.
68 ///@}
69
70 /// @name Lookup
71 ///@{
72 const Node* operator[](Def* mut) const { return const_cast<Children*>(this)->operator[](mut); }
73 bool contains(Def* mut) const { return mut2node_.contains(mut); } ///< is @p mut a child?
74 ///@}
75
76 /// @name Iterators
77 ///@{
78 auto begin() const { return mut2node_.cbegin(); }
79 auto end() const { return mut2node_.cend(); }
80 ///@}
81
82 private:
83 const auto& mut2node() { return mut2node_; }
84 auto nodes() { return mut2node_ | std::views::values; }
85 auto muts() { return mut2node_ | std::views::keys; }
86 auto begin() { return mut2node_.begin(); }
87 auto end() { return mut2node_.end(); }
88 Node* operator[](Def* mut) { return fe::lookup(mut2node_, mut); }
89
90 absl::btree_map<Def*, Node*, GIDLt<Def*>> mut2node_;
91
92 friend class Nest;
93 };
94
95 const Children& children() const { return children_; }
96 Children& children() { return children_; }
97 ///@}
98
99 template<bool Forward>
100 struct SiblDeps {
101 /// @name Get sibling dependencies
102 ///@{
103 auto nodes() const {
104 return nodes_ | std::views::transform([](Node* n) { return const_cast<const Node*>(n); });
105 }
106 ///@}
107
108 /// @name Getters
109 ///@{
110 size_t num() const { return nodes().size(); }
111 bool contains(const Node* n) const { return nodes_.contains(const_cast<Node*>(n)); }
112 ///@}
113
114 /// @name Iterators
115 ///@{
116 auto begin() const { return nodes_.cbegin(); }
117 auto end() const { return nodes_.cend(); }
118 ///@}
119
120 private:
121 const auto& nodes() { return nodes_; }
122 auto begin() { return nodes_.begin(); }
123 auto end() { return nodes_.end(); }
124
125 absl::btree_set<Node*, GIDLt<Node*>> nodes_;
126
127 friend class Nest;
128 };
129
130 /// @name Sibling Dependencies
131 /// These are the dependencies across children():
132 /// A child `n` depends() on `m`, if a subtree of `n` uses `m`.
133 ///@{
134 template<bool Forward = true>
135 auto& sibl_deps() {
136 nest().calc_sibl_deps();
137 if constexpr (Forward)
138 return sibl_deps_;
139 else
140 return sibl_rev_deps_;
141 }
142
143 template<bool Forward = true>
144 const auto& sibl_deps() const {
145 return const_cast<Node*>(this)->sibl_deps<Forward>();
146 }
147 ///@}
148
149 /// Strongly Connected Component.
150 using SCC = absl::btree_set<const Node*, GIDLt<const Node*>>;
151 /// @name SCCs
152 /// [SCCs](https://en.wikipedia.org/wiki/Strongly_connected_component) for all children dependencies.
153 /// @note The Nest::root() cannot be is_mutually_recursive() by definition.
154 /// If you have a set of mutually recursive Def%s as "root", include them all by using a *virtual root*.
155 ///@{
156 const auto& SCCs() { return sccs().SCCs_; }
157 const auto& topo() const { return sccs().topo_; } ///< Topological sorting of all SCCs.
158 bool is_recursive() const { return sccs().recursive_; }
159 bool is_mutually_recursive() const { return is_recursive() && inest_ && inest_->SCCs_[this]->size() > 1; }
160 bool is_directly_recursive() const { return is_recursive() && (!inest_ || inest_->SCCs_[this]->size() == 1); }
161 ///@}
162
163 private:
164 Node(const Nest& nest, Def* mut, Node* inest)
165 : nest_(nest)
166 , mut_(mut)
167 , inest_(inest)
168 , level_(inest ? inest->level() + 1 : 0) {
169 if (inest) inest->children_.mut2node_.emplace(mut, this);
170 }
171
172 const Node& sccs() const { return nest().calc_SCCs(), *this; }
173
174 void link(Node* other) { this->sibl_deps_.nodes_.emplace(other), other->sibl_rev_deps_.nodes_.emplace(this); }
175 void dot(fe::Tab, std::ostream&) const;
176
177 /// SCCs
178 using Stack = std::stack<Node*>;
179 void calc_SCCs();
180 uint32_t tarjan(uint32_t, Node*, Stack&);
181
182 /// Dominance
183 const Node* calc_dominance() const;
184
185 const Nest& nest_;
186 Def* mut_;
187 Node* inest_;
188 uint32_t level_;
189 uint32_t loop_depth_ : 31 = 0;
190 bool recursive_ : 1 = false;
191 SiblDeps<true> sibl_deps_;
192 SiblDeps<false> sibl_rev_deps_;
193 Children children_;
194 std::deque<std::unique_ptr<SCC>> topo_;
195 absl::btree_map<const Node*, const SCC*, GIDLt<const Node*>> SCCs_;
196 mutable const Node* idom_ = nullptr;
197 // Nodes higher up in dominator tree within same sibling layer have higher postorder numbers.
198 // This property is used to efficiently find the correct node for late code placement via [Nest::lca].
199 mutable std::optional<size_t> postorder_number_ = std::nullopt;
200
201 // implementaiton details
202 static constexpr uint32_t Unvisited = uint32_t(-1);
203 uint32_t idx_ = Unvisited;
204 uint32_t low_ : 31 = 0;
205 bool on_stack_ : 1 = false;
206 Node* curr_child = nullptr;
207
208 friend class Nest;
209 };
210
211 /// @name Constructors
212 ///@{
213 Nest(Def* root);
214 Nest(fe::View<Def*> muts); ///< Constructs a *virtual root* with @p muts as children.
215 Nest(World&); ///< *Virtual root* with all World::externals as children.
216 Nest(const Nest&) = delete;
217 Nest(Nest&&) = delete;
218 Nest& operator=(Nest) = delete;
219 ///@}
220
221 /// @name Getters
222 ///@{
223 World& world() const { return world_; }
224 const Node* root() const { return root_; }
225 Vars vars() const { return vars_; } ///< All Var%s occurring in this Nest.
226 bool contains(const Def* def) const { return def->has_free_vars_in(vars()); }
227 bool is_recursive() const { return calc_SCCs().root()->is_recursive(); }
228 ///@}
229
230 /// @name Nodes
231 ///@{
232 size_t num_nodes() const { return mut2node_.size(); }
233 // clang-format off
234 auto muts() const { return mut2node_ | std::views::keys; }
235 auto nodes() const { return mut2node_ | std::views::transform([](const auto& p) { return (const Node*)p.second.get(); }); }
236 // clang-format on
237
238 const Node* operator[](Def* mut) const {
239 if (auto i = mut2node_.find(mut); i != mut2node_.end()) return i->second.get();
240 return nullptr;
241 }
242 ///@}
243
244 /// @name Iterators
245 ///@{
246 auto begin() const { return mut2node_.cbegin(); }
247 auto end() const { return mut2node_.cend(); }
248 ///@}
249
250 template<bool bootstrapping = false>
251 static const Node* lca(const Node* n, const Node* m); ///< Least common ancestor of @p n and @p m.
252
253 /// @name dot
254 /// GraphViz output.
255 ///@{
256 void dot(std::ostream& os) const;
257 void dot(const char* file = nullptr) const;
258 void dot(std::string s) const { dot(s.c_str()); }
259 ///@}
260
261private:
262 auto begin() { return mut2node_.begin(); }
263 auto end() { return mut2node_.end(); }
264
265 void populate();
266 Node* make_node(Def*, Node* inest = nullptr);
267 void calc_sibl_deps(Node*) const;
268 void calc_SCCs(Node*) const;
269 void assign_postorder_numbers() const;
270 Node* operator[](Def* mut) { return const_cast<Node*>(std::as_const(*this)[mut]); }
271
272 void calc_sibl_deps() const {
273 if (!siblings_) {
274 siblings_ = true;
275 calc_sibl_deps(root_);
276 }
277 }
278
279 const Nest& calc_SCCs() const {
280 if (!sccs_) {
281 sccs_ = true;
282 calc_SCCs(root_);
283 }
284 return *this;
285 }
286
287 World& world_;
288 absl::btree_map<Def*, std::unique_ptr<Node>, detail::NullSafeDefGIDLt> mut2node_;
289 Vars vars_;
290 Node* root_;
291 mutable bool siblings_ = false;
292 mutable bool sccs_ = false;
293};
294
295} // namespace mim
Base class for all Defs.
Definition def.h:273
std::string unique_name() const
name + "_" + Def::gid
Definition def.cpp:616
bool has_free_vars_in(Vars) const
Same as vars.has_intersection(free_vars()).
Definition def.cpp:281
std::string name() const
Definition nest.h:37
const auto & SCCs()
Definition nest.h:156
auto & sibl_deps()
Definition nest.h:135
const auto & sibl_deps() const
Definition nest.h:144
uint32_t level() const
Definition nest.h:50
Def * mut() const
The mutable capsulated in this Node or nullptr, if it's a virtual root comprising several Nodes.
Definition nest.h:46
friend class Nest
Definition nest.h:208
bool is_recursive() const
Definition nest.h:158
const Children & children() const
Definition nest.h:95
bool is_root() const
Definition nest.h:44
auto idom() const
Immediate Dominator for children in connected components.
Definition nest.h:43
const auto & topo() const
Topological sorting of all SCCs.
Definition nest.h:157
const Nest & nest() const
Definition nest.h:38
bool is_directly_recursive() const
Definition nest.h:160
bool is_mutually_recursive() const
Definition nest.h:159
Children & children()
Definition nest.h:96
absl::btree_set< const Node *, GIDLt< const Node * > > SCC
Strongly Connected Component.
Definition nest.h:150
uint32_t loop_depth() const
Definition nest.h:51
const Node * inest() const
Immediate nester/parent of this Node.
Definition nest.h:39
uint32_t gid() const
Stable id for deterministic hashing/ordering: the underlying mut's gid, or 0 for the (unique) virtual...
Definition nest.h:54
Nest(Nest &&)=delete
void dot(std::ostream &os) const
Definition dot.cpp:247
bool is_recursive() const
Definition nest.h:227
const Node * operator[](Def *mut) const
Definition nest.h:238
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
Nest & operator=(Nest)=delete
void dot(std::string s) const
Definition nest.h:258
auto muts() const
Definition nest.h:234
const Node * root() const
Definition nest.h:224
auto end() const
Definition nest.h:247
Nest(Def *root)
Definition nest.cpp:9
Vars vars() const
All Vars occurring in this Nest.
Definition nest.h:225
auto begin() const
Definition nest.h:246
Nest(const Nest &)=delete
size_t num_nodes() const
Definition nest.h:232
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
Definition ast.h:16
fe::Patricia< const Var, DefKey >::Set Vars
Definition def.h:112
Node
Definition def.h:120
auto muts() const
Definition nest.h:64
auto begin() const
Definition nest.h:78
size_t num() const
Number of children.
Definition nest.h:67
bool contains(Def *mut) const
is mut a child?
Definition nest.h:73
friend class Nest
Definition nest.h:92
auto nodes() const
Definition nest.h:65
auto end() const
Definition nest.h:79
auto mut2node() const
Definition nest.h:63
const Node * operator[](Def *mut) const
Definition nest.h:72
auto end() const
Definition nest.h:117
auto begin() const
Definition nest.h:116
auto nodes() const
Definition nest.h:103
friend class Nest
Definition nest.h:127
size_t num() const
Definition nest.h:110
bool contains(const Node *n) const
Definition nest.h:111