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 <map>
4#include <memory>
5#include <optional>
6#include <ranges>
7#include <stack>
8
9#include "mim/def.h"
10
11namespace mim {
12
13namespace detail {
14/// Like GIDLt, but the *nesting virtual root* is represented as a `nullptr` Def%, which must sort first.
15struct NullSafeDefGIDLt {
16 constexpr bool operator()(const Def* a, const Def* b) const noexcept {
17 return (a ? a->gid() : 0) < (b ? b->gid() : 0);
18 }
19};
20} // namespace detail
21
22/// Builds a nesting tree for all mutables/binders.
23/// The constructor also computes sibling dependencies, SCCs, loop depths, and dominance.
24/// A Nest is immutable afterwards.
25class Nest {
26public:
27 class Node {
28 public:
29 /// @name Getters
30 ///@{
31 std::string name() const { return mut() ? mut()->unique_name() : std::string("virtual"); }
32 const Nest& nest() const { return nest_; }
33 const Node* inest() const { return inest_; } ///< Immediate nester/parent of this Node.
34 /// [Immediate Dominator](https://en.wikipedia.org/wiki/Dominator_(graph_theory)) for children in connected
35 /// components; used for late code placement in [Nest::lca].
36 const Node* idom() const { return idom_; }
37 bool is_root() const { return inest_ == nullptr; }
38 /// The *mutable* capsulated in this Node or `nullptr`, if it's a *virtual root* comprising several Node%s.
39 Def* mut() const {
40 assert(mut_ || is_root());
41 return mut_;
42 }
43 uint32_t level() const { return level_; }
44 uint32_t loop_depth() const { return loop_depth_; }
45 /// Stable id for deterministic hashing/ordering: the underlying mut's gid, or `0` for the (unique) virtual
46 /// root.
47 uint32_t gid() const { return mut_ ? mut_->gid() : 0; }
48 ///@}
49
50 /// @name Children
51 ///@{
52 struct Children {
53 ///@name Get children as muts and/or nodes.
54 ///@{
55 // clang-format off
56 auto muts() const { return mut2node_ | std::views::keys; }
57 auto nodes() const { return mut2node_ | std::views::transform([](auto p) { return const_cast<const Node*>(p.second); }); }
58 // clang-format on
59 size_t num() const { return mut2node_.size(); } ///< Number of children.
60 ///@}
61
62 /// @name Lookup
63 ///@{
64 const Node* operator[](Def* mut) const { return fe::lookup(mut2node_, mut); }
65 bool contains(Def* mut) const { return mut2node_.contains(mut); } ///< is @p mut a child?
66 ///@}
67
68 /// @name Iterators
69 ///@{
70 auto begin() const { return mut2node_.cbegin(); }
71 auto end() const { return mut2node_.cend(); }
72 ///@}
73
74 private:
75 auto mut_nodes() { return mut2node_ | std::views::values; }
76
77 MutMap<Node*> mut2node_;
78
79 friend class Nest;
80 };
81
82 const Children& children() const { return children_; }
83 ///@}
84
85 struct SiblDeps {
86 /// @name Get sibling dependencies
87 ///@{
88 auto nodes() const {
89 return nodes_ | std::views::transform([](Node* n) { return const_cast<const Node*>(n); });
90 }
91 ///@}
92
93 /// @name Getters
94 ///@{
95 size_t num() const { return nodes_.size(); }
96 bool contains(const Node* n) const { return nodes_.contains(const_cast<Node*>(n)); }
97 ///@}
98
99 /// @name Iterators
100 ///@{
101 auto begin() const { return nodes_.cbegin(); }
102 auto end() const { return nodes_.cend(); }
103 ///@}
104
105 private:
106 GIDSet<Node*> nodes_;
107
108 friend class Nest;
109 };
110
111 /// @name Sibling Dependencies
112 /// These are the dependencies across children():
113 /// A child `n` depends() on `m`, if a subtree of `n` uses `m`.
114 ///@{
115 template<bool Forward = true>
116 const SiblDeps& sibl_deps() const {
117 if constexpr (Forward)
118 return sibl_deps_;
119 else
120 return sibl_rev_deps_;
121 }
122 ///@}
123
124 /// Strongly Connected Component.
126 /// @name SCCs
127 /// [SCCs](https://en.wikipedia.org/wiki/Strongly_connected_component) for all children dependencies.
128 /// @note The Nest::root() cannot be is_mutually_recursive() by definition.
129 /// If you have a set of mutually recursive Def%s as "root", include them all by using a *virtual root*.
130 ///@{
131 bool is_recursive() const { return recursive_; }
132 bool is_mutually_recursive() const { return is_recursive() && !is_root() && scc().size() > 1; }
133 bool is_directly_recursive() const { return is_recursive() && (is_root() || scc().size() == 1); }
134 /// The SCC among its siblings this Node belongs to; the Nest::root() has none.
135 const SCC& scc() const {
136 assert(!is_root());
137 return *scc_;
138 }
139 ///@}
140
141 private:
142 Node(const Nest& nest, Def* mut, Node* inest)
143 : nest_(nest)
144 , mut_(mut)
145 , inest_(inest)
146 , level_(inest ? inest->level() + 1 : 0) {
147 if (inest) inest->children_.mut2node_.emplace(mut, this);
148 }
149
150 void link(Node* other) { this->sibl_deps_.nodes_.emplace(other), other->sibl_rev_deps_.nodes_.emplace(this); }
151 void dot(fe::Tab, std::ostream&) const;
152
153 using Stack = std::stack<Node*>;
154 uint32_t tarjan(uint32_t, Stack&);
155
156 const Nest& nest_;
157 Def* mut_;
158 Node* inest_;
159 uint32_t level_;
160 uint32_t loop_depth_ : 31 = 0;
161 bool recursive_ : 1 = false;
162 SiblDeps sibl_deps_;
163 SiblDeps sibl_rev_deps_;
164 Children children_;
165 std::deque<std::unique_ptr<SCC>> topo_;
166 const SCC* scc_ = nullptr;
167 const Node* idom_ = nullptr;
168 // Nodes higher up in dominator tree within same sibling layer have higher postorder numbers.
169 // This property is used to efficiently find the correct node for late code placement via [Nest::lca].
170 std::optional<size_t> postorder_number_ = std::nullopt;
171
172 // Tarjan's bookkeeping.
173 static constexpr uint32_t Unvisited = uint32_t(-1);
174 uint32_t idx_ = Unvisited;
175 uint32_t low_ : 31 = 0;
176 bool on_stack_ : 1 = false;
177
178 friend class Nest;
179 };
180
181 /// @name Constructors
182 ///@{
183 Nest(Def* root);
184 /// *Virtual root* with @p muts as children; @p transitive also adds the closed mutables they reach.
185 Nest(World&, fe::View<Def*> muts, bool transitive = false);
186 Nest(World&); ///< *Virtual root* with all World::externals as children.
187 Nest(const Nest&) = delete;
188 Nest(Nest&&) = delete;
189 Nest& operator=(Nest) = delete;
190 ///@}
191
192 /// @name Getters
193 ///@{
194 World& world() const { return world_; }
195 const Node* root() const { return root_; }
196 Vars vars() const { return vars_; } ///< All Var%s occurring in this Nest.
197 bool contains(const Def* def) const { return def->has_free_vars_in(vars()); }
198 bool is_recursive() const { return root_->is_recursive(); }
199 ///@}
200
201 /// @name Nodes
202 ///@{
203 // clang-format off
204 auto muts() const { return mut2node_ | std::views::keys; }
205 auto nodes() const { return mut2node_ | std::views::transform([](const auto& p) { return (const Node*)p.second.get(); }); }
206 // clang-format on
207
208 const Node* operator[](Def* mut) const { return node(mut); }
209 /// The innermost Node binding a free Var of @p def, or the root() if there is none.
210 /// For a mutable in this Nest, that is its Node::inest.
211 const Node* inest(const Def* def) const { return innermost(def); }
212 ///@}
213
214 /// @name Iterators
215 ///@{
216 auto begin() const { return mut2node_.cbegin(); }
217 auto end() const { return mut2node_.cend(); }
218 ///@}
219
220 static const Node* lca(const Node* n, const Node* m); ///< Least common ancestor of @p n and @p m.
221
222 /// @name dot
223 /// GraphViz output.
224 ///@{
225 void dot(std::ostream& os, DotConfig cfg = {}) const;
226 void dot(const char* file = nullptr, DotConfig cfg = {}) const;
227 void dot(std::string s, DotConfig cfg = {}) const { dot(s.c_str(), cfg); }
228 ///@}
229
230private:
231 Node* node(Def* mut) const {
232 if (auto i = mut2node_.find(mut); i != mut2node_.end()) return i->second.get();
233 return nullptr;
234 }
235
236 Node* innermost(const Def*) const;
237 Node* innermost(Node*, Vars) const;
238 void analyze();
239 void populate(bool transitive);
240 Node* make_node(Def*, Node* inest = nullptr);
241 void calc_sibl_deps(Node*, fe::Vector<Node*>& path);
242 void calc_SCCs(Node*);
243 void number(Node*, size_t&);
244 void calc_dominance(Node*);
245 void sibl_post_order(Node*, ankerl::unordered_dense::set<const Node*>&, fe::Vector<Node*>&);
246
247 World& world_;
248 std::map<Def*, std::unique_ptr<Node>, detail::NullSafeDefGIDLt> mut2node_;
249 Vars vars_;
250 Node* root_;
251};
252
253} // namespace mim
Base class for all Defs.
Definition def.h:289
std::string unique_name() const
name + "_" + Def::gid
Definition def.cpp:634
bool has_free_vars_in(Vars) const
Same as vars.has_intersection(free_vars()).
Definition def.cpp:285
std::string name() const
Definition nest.h:31
const Node * idom() const
Immediate Dominator for children in connected components; used for late code placement in [Nest::lca]...
Definition nest.h:36
const SiblDeps & sibl_deps() const
Definition nest.h:116
GIDSet< const Node * > SCC
Strongly Connected Component.
Definition nest.h:125
uint32_t level() const
Definition nest.h:43
Def * mut() const
The mutable capsulated in this Node or nullptr, if it's a virtual root comprising several Nodes.
Definition nest.h:39
friend class Nest
Definition nest.h:178
bool is_recursive() const
Definition nest.h:131
const Children & children() const
Definition nest.h:82
bool is_root() const
Definition nest.h:37
const SCC & scc() const
The SCC among its siblings this Node belongs to; the Nest::root() has none.
Definition nest.h:135
const Nest & nest() const
Definition nest.h:32
bool is_directly_recursive() const
Definition nest.h:133
bool is_mutually_recursive() const
Definition nest.h:132
uint32_t loop_depth() const
Definition nest.h:44
const Node * inest() const
Immediate nester/parent of this Node.
Definition nest.h:33
uint32_t gid() const
Stable id for deterministic hashing/ordering: the underlying mut's gid, or 0 for the (unique) virtual...
Definition nest.h:47
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
Nest(Nest &&)=delete
void dot(std::ostream &os, DotConfig cfg={}) const
Definition dot.cpp:281
bool is_recursive() const
Definition nest.h:198
void dot(std::string s, DotConfig cfg={}) const
Definition nest.h:227
const Node * operator[](Def *mut) const
Definition nest.h:208
auto nodes() const
Definition nest.h:205
World & world() const
Definition nest.h:194
Nest & operator=(Nest)=delete
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
auto end() const
Definition nest.h:217
Nest(Def *root)
Definition nest.cpp:9
Vars vars() const
All Vars occurring in this Nest.
Definition nest.h:196
auto begin() const
Definition nest.h:216
Nest(const Nest &)=delete
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
Definition ast.h:16
ankerl::unordered_dense::set< K, GIDHash< K > > GIDSet
Definition gid.h:23
fe::Patricia< const Var, DefKey >::Set Vars
Definition def.h:115
Node
Definition def.h:123
GIDMap< Def *, To > MutMap
Definition def.h:105
Options for Def::dot, World::dot, and Nest::dot.
Definition def.h:241
auto muts() const
Definition nest.h:56
auto begin() const
Definition nest.h:70
size_t num() const
Number of children.
Definition nest.h:59
bool contains(Def *mut) const
is mut a child?
Definition nest.h:65
friend class Nest
Definition nest.h:79
auto nodes() const
Definition nest.h:57
auto end() const
Definition nest.h:71
const Node * operator[](Def *mut) const
Definition nest.h:64
auto end() const
Definition nest.h:102
auto begin() const
Definition nest.h:101
size_t num() const
Definition nest.h:95
friend class Nest
Definition nest.h:108
auto nodes() const
Definition nest.h:88
bool contains(const Node *n) const
Definition nest.h:96