wasmtime/compile/scc.rs
1//! Strongly-connected components.
2//!
3//! This module implements [Tarjan's algorithm] for finding strongly-connected
4//! components.
5//!
6//! This algorithm takes `O(V+E)` time and uses `O(V+E)` space.
7//!
8//! Tarjan's algorithm is usually presented as a recursive algorithm, but we do
9//! not trust the input and cannot recurse over it for fear of blowing the
10//! stack. Therefore, this implementation is iterative.
11//!
12//! [Tarjan's algorithm]: https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
13
14use super::*;
15use std::{
16 collections::BTreeSet,
17 fmt::{self, Debug},
18 hash::Hash,
19 ops::Range,
20};
21use wasmtime_environ::{
22 EntityRef, EntitySet, PrimaryMap, SecondaryMap, packed_option::PackedOption,
23};
24
25/// A strongly-connected component.
26#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct Scc(u32);
28wasmtime_environ::entity_impl!(Scc);
29
30/// The set of strongly-connected components for a graph of `Node`s.
31pub struct StronglyConnectedComponents<Node>
32where
33 Node: EntityRef,
34{
35 /// A map from a component to the range of `self.component_nodes` that make
36 /// up that component's nodes.
37 components: PrimaryMap<Scc, Range<u32>>,
38
39 /// The data storage for the values of `self.components`.
40 component_nodes: Vec<Node>,
41}
42
43impl<Node> Debug for StronglyConnectedComponents<Node>
44where
45 Node: EntityRef + Debug,
46{
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 struct Map<'a, Node: EntityRef + Debug>(&'a StronglyConnectedComponents<Node>);
49
50 impl<'a, Node> Debug for Map<'a, Node>
51 where
52 Node: EntityRef + Debug,
53 {
54 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> fmt::Result {
55 f.debug_map().entries(self.0.iter()).finish()
56 }
57 }
58
59 f.debug_struct("StronglyConnectedComponents")
60 .field("components", &Map(self))
61 .finish()
62 }
63}
64
65impl<Node> StronglyConnectedComponents<Node>
66where
67 Node: EntityRef + std::fmt::Debug,
68{
69 /// Find the strongly-connected for the given graph.
70 pub fn new<I, F, S>(nodes: I, successors: F) -> Self
71 where
72 I: IntoIterator<Item = Node>,
73 F: Fn(Node) -> S,
74 S: Iterator<Item = Node>,
75 {
76 let nodes = nodes.into_iter();
77
78 // The resulting components and their nodes.
79 let mut component_nodes = vec![];
80 let mut components = PrimaryMap::<Scc, Range<u32>>::new();
81
82 // The DFS index counter.
83 let mut index = NonMaxU32::default();
84
85 // The DFS index and the earliest on-stack node reachable from each
86 // node.
87 let (min, max) = nodes.size_hint();
88 let capacity = max.unwrap_or_else(|| 2 * min);
89 let mut indices = SecondaryMap::<Node, Option<NonMaxU32>>::with_capacity(capacity);
90 let mut lowlinks = SecondaryMap::<Node, Option<NonMaxU32>>::with_capacity(capacity);
91
92 // The stack of nodes we are currently finding an SCC for. Not the same
93 // as the DFS stack: we only pop from this stack once we find the root
94 // of an SCC.
95 let mut stack = vec![];
96 let mut on_stack = EntitySet::<Node>::new();
97
98 let mut dfs = Dfs::new(nodes);
99 while let Some(event) = dfs.next(
100 &successors,
101 // We have seen the node before if we have assigned it a DFS index.
102 |node| indices[node].is_some(),
103 ) {
104 match event {
105 DfsEvent::Pre(node) => {
106 debug_assert!(indices[node].is_none());
107 debug_assert!(lowlinks[node].is_none());
108
109 // Assign an index to this node.
110 indices[node] = Some(index);
111
112 // Its current lowlink is itself. This will get updated to
113 // be accurate as we visit the node's successors.
114 lowlinks[node] = Some(index);
115
116 // Increment the DFS counter.
117 index = NonMaxU32::new(index.get() + 1).unwrap();
118
119 // Push the node onto the SCC stack.
120 stack.push(node);
121 let is_newly_on_stack = on_stack.insert(node);
122 debug_assert!(is_newly_on_stack);
123 }
124
125 DfsEvent::AfterEdge(node, succ) => {
126 debug_assert!(indices[node].is_some());
127 debug_assert!(lowlinks[node].is_some());
128 debug_assert!(lowlinks[node] <= indices[node]);
129 debug_assert!(indices[succ].is_some());
130 debug_assert!(lowlinks[succ].is_some());
131 debug_assert!(lowlinks[succ] <= indices[succ]);
132
133 // If the successor is still on the SCC stack, then it is
134 // part of the same SCC as this node, so propagate its
135 // lowlink to this node's lowlink.
136 if on_stack.contains(succ) {
137 lowlinks[node] = Some(std::cmp::min(
138 lowlinks[node].unwrap(),
139 lowlinks[succ].unwrap(),
140 ));
141 }
142 }
143
144 DfsEvent::Post(node) => {
145 debug_assert!(indices[node].is_some());
146 debug_assert!(lowlinks[node].is_some());
147
148 // If this node's index is the same as its lowlink, then it
149 // is the root of a component. Pop this component's elements
150 // from the SCC stack and push them into our result data
151 // structures.
152 if indices[node] == lowlinks[node] {
153 let mut done = false;
154 components.push(extend_with_range(
155 &mut component_nodes,
156 std::iter::from_fn(|| {
157 if done {
158 return None;
159 }
160 let v = stack.pop().unwrap();
161 let was_on_stack = on_stack.remove(v);
162 debug_assert!(was_on_stack);
163 if v == node {
164 done = true;
165 }
166 Some(v)
167 }),
168 ));
169 }
170 }
171 }
172 }
173
174 Self {
175 components,
176 component_nodes,
177 }
178 }
179
180 /// Get the number of components.
181 pub fn len(&self) -> usize {
182 self.components.len()
183 }
184
185 fn node_range(&self, range: Range<u32>) -> &[Node] {
186 let start = usize::try_from(range.start).unwrap();
187 let end = usize::try_from(range.end).unwrap();
188 &self.component_nodes[start..end]
189 }
190
191 /// Iterate over each strongly-connnected component and the `Node`s that are
192 /// members of it.
193 ///
194 /// Iteration happens in reverse-topological order (successors are visited
195 /// before predecessors in the resulting SCC DAG).
196 pub fn iter(&self) -> impl ExactSizeIterator<Item = (Scc, &[Node])> + '_ {
197 self.components
198 .iter()
199 .map(|(component, range)| (component, self.node_range(range.clone())))
200 }
201
202 /// Iterate over each strongly-connected component.
203 ///
204 /// Iteration happens in reverse-topological order (successors are visited
205 /// before predecessors in the resulting SCC DAG).
206 pub fn keys(&self) -> impl ExactSizeIterator<Item = Scc> {
207 self.components.keys()
208 }
209
210 /// Iterate over the `Node`s that make up each strongly-connected component.
211 ///
212 /// Iteration happens in reverse-topological order (successors are visited
213 /// before predecessors in the resulting SCC DAG).
214 #[cfg(test)]
215 pub fn values(&self) -> impl ExactSizeIterator<Item = &[Node]> + '_ {
216 self.components
217 .values()
218 .map(|range| self.node_range(range.clone()))
219 }
220
221 /// Get the `Node`s that make up the given strongly-connected component.
222 pub fn nodes(&self, component: Scc) -> &[Node] {
223 let range = self.components[component].clone();
224 self.node_range(range)
225 }
226
227 /// Get this set of strongly-connected components' "evaporation".
228 ///
229 /// Given an input graph `G`, we can construct a new graph where the new
230 /// graph's nodes are the strongly-connected components of `G` and there is
231 /// an edge `scc(a) --> scc(b)` for every edge `a --> b` in the input graph
232 /// `G` where `scc(a) != scc(b)` and `scc` is the function from a node to
233 /// its strongly-connected component. This new graph is known as the
234 /// "condensation" of `G`.
235 ///
236 /// In the following diagram, the solid lines are the input graph and the
237 /// dotted lines show its condensation:
238 ///
239 /// ```ignore
240 /// ...............
241 /// : :
242 /// : ,-----. :
243 /// : | | :
244 /// : V | :
245 /// : +---+ | :
246 /// : | a |---' :
247 /// : +---+ :
248 /// : | :
249 /// :....|........:
250 /// | :
251 /// | :
252 /// | :
253 /// | V
254 /// .....|.............. ....................
255 /// : | : : :
256 /// : V :......>: :
257 /// : +---+ +---+ : : +---+ +---+ :
258 /// : | b |<-->| c |------------>| d |<-->| e | :
259 /// : +---+ +---+ : : +---+ +---+ :
260 /// : | : : | :
261 /// :....|.............: :.............|....:
262 /// | : : |
263 /// | : : |
264 /// | : : |
265 /// | V : |
266 /// .....|........................ : |
267 /// : | : : |
268 /// : | ,----------------. : : |
269 /// : | | | :<....: |
270 /// : V | V : |
271 /// : +---+ +---+ +---+ : |
272 /// : | f |<---| g |<---| h |<-------------'
273 /// : +---+ +---+ +---+ :
274 /// : :
275 /// :............................:
276 /// ```
277 ///
278 /// Note that a graph's condensation is always acyclic, because the
279 /// strongly-conneted components encapsulate and hide any cycles from the
280 /// input graph.
281 ///
282 /// I am not aware of an existing name for the reverse (or transpose)
283 /// condensation, where each of the condensation's edges `a --> b` are
284 /// flipped into `b --> a`, so, at cfallin's brilliant suggestion, I have
285 /// decided to call it an "evaporation".
286 ///
287 /// In the context of a call graph, the condensation allows us to derive a
288 /// partial dependency ordering for bottom-up inlining:
289 ///
290 /// * An edge `scc({a,b,...}) --> scc({c,d,...})` means that the functions
291 /// `{c,d,...}` should be processed for inlining before functions
292 /// `{a,b,...}`, since some functions in `{a,b,...}` call some functions
293 /// in `{c,d,...}` and we might want to inline these calls but only after
294 /// `{c,d,...}` have already had their calls inlined.
295 ///
296 /// * Functions within an SCC are unordered (and we probably don't want to
297 /// inline between them at all, or only want to do so in a very limited
298 /// manner, since their members are mutually recursive).
299 ///
300 /// A call graph's evaporation, by flipping edges from pointing to an SCC's
301 /// dependencies to pointing to its dependers, allows us to answer the
302 /// question "given that I've finished processing calls in `scc({e,f,...})`
303 /// for inlining, which other SCCs are now potentially ready for
304 /// processing?".
305 pub fn evaporation<F, S>(&self, successors: F) -> Evaporation
306 where
307 F: Fn(Node) -> S,
308 S: Iterator<Item = Node>,
309 {
310 log::trace!("Creating the evaporation of {self:#?}");
311
312 let node_to_component: SecondaryMap<Node, PackedOption<Scc>> = self
313 .iter()
314 .flat_map(|(c, nodes)| {
315 nodes
316 .iter()
317 .copied()
318 .map(move |node| (node, PackedOption::from(Some(c))))
319 })
320 .collect();
321
322 // Create a set of all reverse dependencies. This set contains an entry
323 // `(to, from)` for all `from --> to` edges in the SCC's condensation.
324 //
325 // Note that we use a `BTreeSet` so that the results are ordered, all
326 // edges `a --> *` are contiguous for each component `a`, and we can
327 // therefore pack them densely in the resulting `Evaporation`.
328 let mut reverse_edge_set = BTreeSet::<(Scc, Scc)>::new();
329 for (from_component, nodes) in self.iter() {
330 for node in nodes {
331 for succ in successors(*node) {
332 let to_component = node_to_component[succ].unwrap();
333 if to_component == from_component {
334 continue;
335 }
336 reverse_edge_set.insert((to_component, from_component));
337 }
338 }
339 }
340
341 // Convert the `reverse_edge_set` into our densely-packed
342 // representation.
343 let mut reverse_edges =
344 SecondaryMap::<Scc, Range<u32>>::with_capacity(self.components.len());
345 let mut reverse_edge_elems = Vec::with_capacity(reverse_edge_set.len());
346 for (to_node, from_node) in reverse_edge_set {
347 let range = extend_with_range(&mut reverse_edge_elems, Some(from_node));
348
349 if reverse_edges[to_node] == Range::default() {
350 reverse_edges[to_node] = range;
351 } else {
352 debug_assert_eq!(reverse_edges[to_node].end, range.start);
353 reverse_edges[to_node].end = range.end;
354 }
355 }
356
357 let result = Evaporation {
358 reverse_edges,
359 reverse_edge_elems,
360 };
361 log::trace!(" -> {result:#?}");
362 result
363 }
364}
365
366/// The "evaporation" of a graph and its strongly-connected components.
367///
368/// Created by `StronglyConnectedComponents::evaporation`; see that method's
369/// documentation for more details.
370pub struct Evaporation {
371 reverse_edges: SecondaryMap<Scc, Range<u32>>,
372 reverse_edge_elems: Vec<Scc>,
373}
374
375impl Debug for Evaporation {
376 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377 struct Map<'a>(&'a Evaporation);
378
379 impl<'a> Debug for Map<'a> {
380 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
381 f.debug_map()
382 .entries(
383 self.0
384 .reverse_edges
385 .keys()
386 .map(|k| (k, self.0.reverse_edges(k))),
387 )
388 .finish()
389 }
390 }
391
392 f.debug_struct("Evaporation")
393 .field("reverse_edges", &Map(self))
394 .finish()
395 }
396}
397
398impl Evaporation {
399 /// Get the reverse dependencies of the given strongly-connected component.
400 ///
401 /// The result is all of the SCCs whose members have edges in the original
402 /// graph to one or more of this SCC's members.
403 pub fn reverse_edges(&self, component: Scc) -> &[Scc] {
404 let Range { start, end } = self.reverse_edges[component].clone();
405 let start = usize::try_from(start).unwrap();
406 let end = usize::try_from(end).unwrap();
407 &self.reverse_edge_elems[start..end]
408 }
409}
410
411/// An iterative depth-first traversal.
412struct Dfs<Node> {
413 stack: Vec<DfsEvent<Node>>,
414}
415
416impl<Node> Dfs<Node> {
417 fn new(roots: impl IntoIterator<Item = Node>) -> Self {
418 Self {
419 stack: roots.into_iter().map(|v| DfsEvent::Pre(v)).collect(),
420 }
421 }
422}
423
424#[derive(Clone, Copy, Debug, PartialEq, Eq)]
425enum DfsEvent<Node> {
426 /// The first time seeing this node.
427 Pre(Node),
428
429 /// After having just visited the given edge.
430 AfterEdge(Node, Node),
431
432 /// Finished visiting this node and all of its successors.
433 Post(Node),
434}
435
436impl<Node> Dfs<Node>
437where
438 Node: Copy + std::fmt::Debug,
439{
440 /// Pump the traversal, yielding the next `DfsEvent`.
441 fn next<S>(
442 &mut self,
443 successors: impl Fn(Node) -> S,
444 seen: impl Fn(Node) -> bool,
445 ) -> Option<DfsEvent<Node>>
446 where
447 S: Iterator<Item = Node>,
448 {
449 loop {
450 let event = self.stack.pop()?;
451
452 if let DfsEvent::Pre(node) = event {
453 if seen(node) {
454 continue;
455 }
456
457 let successors = successors(node);
458
459 let (min, max) = successors.size_hint();
460 let estimated_successors_len = max.unwrap_or_else(|| 2 * min);
461 self.stack.reserve(
462 // We push an after-edge and pre event for each successor.
463 2 * estimated_successors_len
464 // And we push one post event for this node.
465 + 1,
466 );
467
468 self.stack.push(DfsEvent::Post(node));
469 for succ in successors {
470 self.stack.push(DfsEvent::AfterEdge(node, succ));
471 if !seen(succ) {
472 self.stack.push(DfsEvent::Pre(succ));
473 }
474 }
475 }
476
477 return Some(event);
478 }
479 }
480}
481
482mod non_max {
483 use std::num::NonZeroU32;
484
485 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
486 pub struct NonMaxU32(NonZeroU32);
487
488 impl Default for NonMaxU32 {
489 fn default() -> Self {
490 Self::new(0).unwrap()
491 }
492 }
493
494 impl core::fmt::Debug for NonMaxU32 {
495 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
496 f.debug_tuple("NonMaxU32").field(&self.get()).finish()
497 }
498 }
499
500 impl NonMaxU32 {
501 pub fn new(x: u32) -> Option<Self> {
502 if x == u32::MAX {
503 None
504 } else {
505 // Safety: We know that `x+1` is non-zero because it will not
506 // overflow because `x` is not `u32::MAX`.
507 Some(Self(unsafe { NonZeroU32::new_unchecked(x + 1) }))
508 }
509 }
510
511 pub fn get(&self) -> u32 {
512 self.0.get() - 1
513 }
514 }
515}
516use non_max::*;
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521
522 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
523 pub struct Node(u32);
524 wasmtime_environ::entity_impl!(Node);
525
526 #[derive(Debug)]
527 struct Graph {
528 edges: SecondaryMap<Node, Vec<Node>>,
529 }
530
531 impl Default for Graph {
532 fn default() -> Self {
533 let _ = env_logger::try_init();
534 Self {
535 edges: Default::default(),
536 }
537 }
538 }
539
540 impl Graph {
541 fn define(&mut self, node: u32, edges: impl IntoIterator<Item = u32>) -> &mut Self {
542 assert!(self.edges[Node::from_u32(node)].is_empty());
543 self.edges[Node::from_u32(node)].extend(edges.into_iter().map(|v| Node::from_u32(v)));
544 self
545 }
546
547 fn edges(&self, node: Node) -> impl Iterator<Item = Node> {
548 self.edges[node].iter().copied()
549 }
550 }
551
552 fn components(graph: &Graph) -> Vec<Vec<u32>> {
553 let components = StronglyConnectedComponents::new(graph.edges.keys(), |v| graph.edges(v));
554 components
555 .values()
556 .map(|vs| vs.iter().map(|v| v.as_u32()).collect::<Vec<_>>())
557 .collect()
558 }
559
560 #[test]
561 fn test_empty() {
562 let graph = Graph::default();
563 assert!(components(&graph).is_empty());
564 }
565
566 #[test]
567 fn test_single_node() {
568 // +---+
569 // | 0 |
570 // +---+
571 let mut graph = Graph::default();
572 graph.define(0, []);
573
574 assert_eq!(components(&graph), vec![vec![0]]);
575 }
576
577 #[test]
578 fn test_single_node_cycle() {
579 // ,---.
580 // | |
581 // V |
582 // +---+ |
583 // | 0 |-'
584 // +---+
585 let mut graph = Graph::default();
586 graph.define(0, [0]);
587
588 assert_eq!(components(&graph), vec![vec![0]]);
589 }
590
591 #[test]
592 fn test_disconnected_nodes() {
593 // +---+ +---+
594 // | 0 | | 1 |
595 // +---+ +---+
596 let mut graph = Graph::default();
597 graph.define(0, []);
598 graph.define(1, []);
599 assert_eq!(components(&graph), vec![vec![1], vec![0]]);
600 }
601
602 #[test]
603 fn test_chained_nodes() {
604 // +---+ +---+ +---+ +---+
605 // | 0 |<--| 1 |<--| 2 |<--| 3 |
606 // +---+ +---+ +---+ +---+
607 let mut graph = Graph::default();
608 graph.define(0, []);
609 graph.define(1, [0]);
610 graph.define(2, [1]);
611 graph.define(3, [2]);
612 assert_eq!(components(&graph), vec![vec![0], vec![1], vec![2], vec![3]]);
613 }
614
615 #[test]
616 fn test_simple_multi_node_cycle() {
617 // ,-----------------------.
618 // | |
619 // | V
620 // +---+ +---+ +---+ +---+
621 // | 0 |<--| 1 |<--| 2 |<--| 3 |
622 // +---+ +---+ +---+ +---+
623 let mut graph = Graph::default();
624 graph.define(0, [3]);
625 graph.define(1, [0]);
626 graph.define(2, [1]);
627 graph.define(3, [2]);
628 assert_eq!(components(&graph), vec![vec![0, 1, 2, 3]]);
629 }
630
631 #[test]
632 fn test_complicated_multi_node_cycle() {
633 // ,---------------.
634 // | |
635 // | V
636 // +---+ +---+ +---+ +---+ +---+
637 // | 0 |<--| 1 |<--| 2 |<--| 3 |<--| 4 |
638 // +---+ +---+ +---+ +---+ +---+
639 // | ^
640 // | |
641 // `---------------'
642 let mut graph = Graph::default();
643 graph.define(0, [3]);
644 graph.define(1, [0]);
645 graph.define(2, [1, 4]);
646 graph.define(3, [2]);
647 graph.define(4, [3]);
648 assert_eq!(components(&graph), vec![vec![0, 1, 2, 3, 4]]);
649 }
650
651 #[test]
652 fn test_disconnected_cycles() {
653 // +---+ +---+
654 // | 0 | | 1 |
655 // +---+ +---+
656 // ^ ^
657 // | |
658 // V V
659 // +---+ +---+
660 // | 2 | | 3 |
661 // +---+ +---+
662 let mut graph = Graph::default();
663 graph.define(0, [2]);
664 graph.define(1, [3]);
665 graph.define(2, [0]);
666 graph.define(3, [1]);
667 assert_eq!(components(&graph), vec![vec![1, 3], vec![0, 2]]);
668 }
669
670 #[test]
671 fn test_chain_of_cycles() {
672 // ,-----.
673 // | |
674 // V |
675 // +---+ |
676 // | 0 |---'
677 // +---+
678 // |
679 // V
680 // +---+ +---+
681 // | 1 |<-->| 2 |
682 // +---+ +---+
683 // |
684 // | ,----------------.
685 // | | |
686 // V | V
687 // +---+ +---+ +---+
688 // | 3 |<---| 4 |<---| 5 |
689 // +---+ +---+ +---+
690 let mut graph = Graph::default();
691 graph.define(0, [0, 1]);
692 graph.define(1, [2, 3]);
693 graph.define(2, [1]);
694 graph.define(3, [5]);
695 graph.define(4, [3]);
696 graph.define(5, [4]);
697 assert_eq!(components(&graph), vec![vec![3, 4, 5], vec![1, 2], vec![0]]);
698 }
699
700 #[test]
701 fn test_multiple_edges_to_same_component() {
702 // +---+ +---+
703 // | 0 | | 1 |
704 // +---+ +---+
705 // ^ ^
706 // | |
707 // V V
708 // +---+ +---+
709 // | 2 | | 3 |
710 // +---+ +---+
711 // | |
712 // `------. ,------'
713 // | |
714 // V V
715 // +---+
716 // | 4 |
717 // +---+
718 // ^
719 // |
720 // V
721 // +---+
722 // | 5 |
723 // +---+
724 let mut graph = Graph::default();
725 graph.define(0, [2]);
726 graph.define(1, [3]);
727 graph.define(2, [0, 4]);
728 graph.define(3, [1, 4]);
729 graph.define(4, [5]);
730 graph.define(5, [4]);
731 assert_eq!(components(&graph), vec![vec![4, 5], vec![1, 3], vec![0, 2]]);
732 }
733
734 #[test]
735 fn test_duplicate_edges() {
736 // +---+ +---+
737 // | 0 | | 1 |
738 // +---+ +---+
739 // ^ ^
740 // | |
741 // V V
742 // +---+ +---+
743 // | 2 |---------->| 3 |
744 // +---+ +---+
745 // | ^
746 // `---------------'
747 let mut graph = Graph::default();
748 graph.define(0, [2]);
749 graph.define(1, [3]);
750 graph.define(2, [0, 3, 3]);
751 graph.define(3, [1]);
752 assert_eq!(components(&graph), vec![vec![1, 3], vec![0, 2]]);
753 }
754}