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