Jlm
Loading...
Searching...
No Matches
traverser.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2010 2011 2012 2014 2015 Helge Bahmann <hcb@chaoticmind.net>
3 * Copyright 2013 2014 2015 Nico Reißmann <nico.reissmann@gmail.com>
4 * See COPYING for terms of redistribution.
5 */
6
7#include <jlm/rvsdg/graph.hpp>
9
10/*
11 How traversers operate:
12
13 Each node needs a number of "activations" depending on edges
14 connected to the node (is inputs or outputs, dependent on direction
15 of traversal).
16
17 Each edge coming from the region boundaries will provide one activation,
18 and each edge outgoing from a node that has been visited already will
19 provide one activation. So effectively, both bottom up and top down
20 traversers are just a little bit of beancounting edges and activations.
21 This is done in such a way that each node and each edge is visited exactly
22 once. Thus, the overall time complexity of either traversal is O(#N + #E).
23*/
24namespace jlm::rvsdg
25{
26
27namespace detail
28{
29
30template<typename Traverser>
32
33template<typename Traverser>
38
39template<typename Traverser>
40void
45
46template<typename Traverser>
47void
49{
50 traverser_.onNodeDestroy(node);
51}
52
53template<typename Traverser>
54void
56{
57 traverser_.onInputCreate(input);
58}
59
60template<typename Traverser>
61void
63{
64 traverser_.onInputChange(input, oldOrigin, newOrigin);
65}
66
67template<typename Traverser>
68void
70{
71 traverser_.onInputDestroy(input);
72}
73
74template<bool IsConst>
76
77template<bool IsConst>
79 : observer_(*region, *this),
80 nodeIdCutoff_(region->getNextNodeId())
81{
82 for (auto & node : region->TopNodes())
83 {
84 tracker_.checkNodeActivation(&node, node.ninputs());
85 }
86
87 for (auto argument : region->Arguments())
88 {
89 for (const auto & user : argument->Users())
90 {
91 if (auto node = TryGetOwnerNode<Node>(user))
92 {
93 tracker_.incActivationCount(node, node->ninputs());
94 }
95 }
96 }
97}
98
99template<bool IsConst>
100bool
102{
103 if (auto pred = TryGetOwnerNode<Node>(output))
104 {
105 return tracker_.isNodeVisited(pred);
106 }
107
108 // The output is a region argument, always considered activated
109 return true;
110}
111
112template<bool IsConst>
113void
115{
116 tracker_.checkMarkNodeVisitedIfFrontier(&node);
117 for (const auto & output : node.Outputs())
118 {
119 for (const auto & user : output.Users())
120 {
121 if (auto next = TryGetOwnerNode<Node>(user))
122 {
123 tracker_.incActivationCount(next, next->ninputs());
124 }
125 }
126 }
127}
128
129template<bool IsConst>
132{
133 while (true)
134 {
135 const auto node = tracker_.peek();
136 if (!node)
137 return nullptr;
138
139 markAsVisited(*node);
140
141 // Skip nodes that were created after traversal started
142 if (node->GetNodeId() >= nodeIdCutoff_)
143 continue;
144
145 return node;
146 }
147}
148
149template<bool IsConst>
150void
152{
153 if (node->ninputs() == 0)
155 tracker_.checkNodeActivation(node, node->ninputs());
156 }
157 else
158 {
159 for (const auto & input : node->Inputs())
161 if (isOutputActivated(*input.origin()))
162 {
163 tracker_.incActivationCount(node, node->ninputs());
164 }
165 }
167
168 // If node would end up on frontier (because all predecessors
169 // have been visited), mark it as visited instead (we do not
170 // want to revisit newly created nodes during topdown traversal).
171 tracker_.checkMarkNodeVisitedIfFrontier(node);
172}
173
174template<bool IsConst>
175void
177{
178 // The node is already dead, so removing it can never add anything to the frontier
179 tracker_.removeNode(node);
180}
181
182template<bool IsConst>
183void
185{
186 const auto node = TryGetOwnerNode<Node>(*input);
187 if (!node)
188 return;
189
190 if (isOutputActivated(*input->origin()))
191 {
192 tracker_.incActivationCount(node, node->ninputs());
193 }
194 else
195 {
196 tracker_.checkNodeDeactivation(node, node->ninputs());
197 }
198}
199
200template<bool IsConst>
201void
203 Input * input,
206{
207 const auto node = TryGetOwnerNode<Node>(*input);
208 if (!node)
209 return;
210
211 int change = 0;
212 if (isOutputActivated(*newOrigin))
213 {
214 change += 1;
215 }
216 if (isOutputActivated(*oldOrigin))
217 {
218 change -= 1;
219 }
220
221 if (change == 1)
222 {
223 tracker_.incActivationCount(node, node->ninputs());
224 }
225 else if (change == -1)
226 {
227 tracker_.decActivationCount(node, node->ninputs());
228 }
229}
230
231template<bool IsConst>
232void
234{
235 const auto node = TryGetOwnerNode<Node>(*input);
236 if (!node)
237 return;
238
239 if (isOutputActivated(*input->origin()))
240 {
241 // Removing an activated input can never cause de-activation of the node,
242 // so we can use 0 as the deactivation threshold
243 tracker_.decActivationCount(node, 0);
244 }
245 else
246 {
247 // The callback is called before the input is actually removed, so use the new threshold
248 tracker_.checkNodeActivation(node, node->ninputs() - 1);
249 }
250}
251
252template<bool IsConst>
254
255template<bool IsConst>
257 : observer_(*region, *this)
258{
259 for (auto & node : region->BottomNodes())
260 {
261 tracker_.checkNodeActivation(&node, node.numSuccessors());
262 }
263
264 for (auto result : region->Results())
265 {
266 if (auto node = TryGetOwnerNode<Node>(*result->origin()))
267 {
268 tracker_.incActivationCount(node, node->numSuccessors());
269 }
270 }
271}
272
273template<bool IsConst>
274bool
276{
277 if (auto node = TryGetOwnerNode<Node>(input))
278 {
279 return tracker_.isNodeVisited(node);
280 }
281
282 // The output is a region result, always considered activated
283 return true;
284}
285
286template<bool IsConst>
287void
289{
290 tracker_.checkMarkNodeVisitedIfFrontier(&node);
291 for (const auto & input : node.Inputs())
292 {
293 if (auto predecessor = TryGetOwnerNode<Node>(*input.origin()))
294 {
295 tracker_.incActivationCount(predecessor, predecessor->numSuccessors());
296 }
297 }
298}
299
300template<bool IsConst>
303{
304 auto node = tracker_.peek();
305 if (!node)
306 return nullptr;
307
308 markAsVisited(*node);
309 return node;
310}
311
312template<bool IsConst>
313void
315{
316 // The new node should never be visited, so activate all its predecessors immediately
317 markAsVisited(*node);
318}
319
320template<bool IsConst>
321void
323{
324 // In case this is the last node a predecessor is waiting for,
325 // make sure it gets activated before removal.
326 if (!tracker_.isNodeVisited(node))
327 markAsVisited(*node);
328
329 for (const auto & input : node->Inputs())
330 {
331 if (auto pred = TryGetOwnerNode<Node>(*input.origin()))
332 {
333 // Set the threshold to 0 here: The predecessor node is
334 // still connected, so its successor count is not correct.
335 // However, if the predecessor is on the frontier, it will
336 // remain on the frontier after this removal.
337 tracker_.decActivationCount(pred, 0);
338 }
339 }
340}
341
342template<bool IsConst>
343void
345{
346 const auto node = TryGetOwnerNode<Node>(*input->origin());
347 if (!node)
348 return;
349
350 if (isInputActivated(*input))
351 {
352 tracker_.incActivationCount(node, node->numSuccessors());
353 }
354 else
355 {
356 tracker_.checkNodeDeactivation(node, node->numSuccessors());
357 }
358}
359
360template<bool IsConst>
361void
363 Input * input,
366{
367 const auto inputActive = isInputActivated(*input);
368
370 {
371 if (inputActive)
372 {
373 tracker_.decActivationCount(oldNode, oldNode->numSuccessors());
374 }
375 else
376 {
377 // The oldNode might have just lost its final non-active user
378 tracker_.checkNodeActivation(oldNode, oldNode->numSuccessors());
379 }
380 }
381
383 {
384 if (inputActive)
385 {
386 tracker_.incActivationCount(newNode, newNode->numSuccessors());
387 }
388 else
389 {
390 tracker_.checkNodeDeactivation(newNode, newNode->numSuccessors());
391 }
392 }
393}
394
395template<bool IsConst>
396void
398{
399 const auto pred = TryGetOwnerNode<Node>(*input->origin());
400 if (!pred)
401 return;
402
403 if (isInputActivated(*input))
404 {
405 // Removing an activated user can never cause de-activation of pred.
406 // The threshold for de-activation can therefore be set to 0.
407 tracker_.decActivationCount(pred, 0);
408 }
409 else
410 {
411 tracker_.checkNodeActivation(pred, pred->numSuccessors() - 1);
412 }
413}
414
415template<typename NodeType>
416bool
418{
419 auto i = states_.find(node);
420 return i == states_.end() ? false : i->second.state == TraversalNodeState::behind;
421}
422
423template<typename NodeType>
424void
426{
427 auto i = states_.emplace(node, State{ TraversalNodeState::ahead }).first;
428 if (i->second.activationCount >= threshold && i->second.state == TraversalNodeState::ahead)
429 {
430 frontier_.push_back(node);
431 i->second.pos = std::prev(frontier_.end());
432 i->second.state = TraversalNodeState::frontier;
433 }
434}
435
436template<typename NodeType>
437void
439{
440 auto i = states_.emplace(node, State{ TraversalNodeState::ahead }).first;
441 if (i->second.activationCount < threshold && i->second.state == TraversalNodeState::frontier)
442 {
443 frontier_.erase(i->second.pos);
444 i->second.pos = frontier_.end();
445 i->second.state = TraversalNodeState::ahead;
446 }
447}
448
449template<typename NodeType>
450void
452{
453 auto i = states_.emplace(node, State{ TraversalNodeState::ahead }).first;
454 if (i->second.state == TraversalNodeState::frontier)
455 {
456 frontier_.erase(i->second.pos);
457 i->second.pos = frontier_.end();
458 i->second.state = TraversalNodeState::behind;
459 }
460}
461
462template<typename NodeType>
463void
465{
466 auto i = states_.emplace(node, State{ TraversalNodeState::ahead }).first;
467 i->second.activationCount += 1;
468 checkNodeActivation(node, threshold);
469}
470
471template<typename NodeType>
472void
474{
475 auto i = states_.emplace(node, State{ TraversalNodeState::ahead }).first;
476 i->second.activationCount -= 1;
477 checkNodeDeactivation(node, threshold);
478}
479
480template<typename NodeType>
481void
483{
484 if (const auto it = states_.find(node); it != states_.end())
485 {
486 if (it->second.state == TraversalNodeState::frontier)
487 frontier_.erase(it->second.pos);
488 states_.erase(it);
489 }
490}
491
492template<typename NodeType>
493NodeType *
495{
496 return frontier_.empty() ? nullptr : frontier_.front();
497}
498
499// Explicit instantiation of all versions
500template class TopDownTraverserGeneric<false>;
501template class TopDownTraverserGeneric<true>;
503template class BottomUpTraverserGeneric<true>;
504
505}
506
507}
Output * origin() const noexcept
Definition node.hpp:58
Proxy object to observe changes to a region.
Definition region.hpp:886
virtual void onNodeCreate(Node *node)=0
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
std::conditional_t< IsConst, const Node, Node > NodeType
bool isInputActivated(const Input &input) const
std::conditional_t< IsConst, const Region, Region > RegionType
void onInputChange(Input *input, Output *oldOrigin, Output *newOrigin)
void onNodeDestroy(Node *node) override
Definition traverser.cpp:48
ForwardingObserver(const Region &region, Traverser &traverser)
Definition traverser.cpp:34
void onInputCreate(Input *input) override
Definition traverser.cpp:55
void onInputChange(Input *input, Output *oldOrigin, Output *newOrigin) override
Definition traverser.cpp:62
void onNodeCreate(Node *node) override
Definition traverser.cpp:41
void onInputDestroy(Input *input) override
Definition traverser.cpp:69
bool isOutputActivated(const Output &output) const
void onInputChange(Input *in, Output *old_origin, Output *new_origin)
std::conditional_t< IsConst, const Node, Node > NodeType
std::conditional_t< IsConst, const Region, Region > RegionType
void checkMarkNodeVisitedIfFrontier(NodeType *node)
Marks a node visited if it is currently ready for visiting.
bool isNodeVisited(NodeType *node) const
Determines whether node has been visited already.
void decActivationCount(NodeType *node, std::size_t threshold)
Decrements activation count; removes from frontier if threshold is no longer met.
void incActivationCount(NodeType *node, std::size_t threshold)
Increments activation count; adds to frontier if threshold is met.
void checkNodeActivation(NodeType *node, std::size_t threshold)
Checks activation count whether node is ready for visiting.
void checkNodeDeactivation(NodeType *node, std::size_t threshold)
Checks activation count whether node is no longer ready for visiting.
void removeNode(NodeType *node)
Removes any state associated with the given node.
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872