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