wasmtime/compile/
call_graph.rs

1//! Construction of the call-graph, for the purposes of inlining.
2//!
3//! These call graphs are not necessarily complete or accurate, and Wasmtime's
4//! soundness does not rely on those properties. First off, we do not attempt to
5//! understand indirect calls, which at their worst must force any call analysis
6//! give up and say "the callee could be absolutely any function". More
7//! interestingly, these call graphs are only used for scheduling bottom-up
8//! inlining, so the worst that inaccurate information can do is cause us to
9//! miss inlining opportunities or lose potential parallelism in our
10//! schedule. For best results, however, every direct call that is potentially
11//! inlinable should be reported when constructing these call graphs.
12
13use super::*;
14use core::{
15    fmt::{self, Debug},
16    ops::Range,
17};
18use wasmtime_environ::{EntityRef, SecondaryMap};
19
20/// A call graph reified into a densely packed and quickly accessible
21/// representation.
22///
23/// In a call graph, nodes are functions, and an edge `f --> g` means that the
24/// function `f` calls the function `g`.
25pub struct CallGraph<Node>
26where
27    Node: EntityRef + Debug,
28{
29    /// A map from each node to the subslice of `self.edge_elems` that are its
30    /// edges.
31    edges: SecondaryMap<Node, Range<u32>>,
32
33    /// Densely packed edge elements for `self.edges`.
34    edge_elems: Vec<Node>,
35}
36
37impl<Node> Debug for CallGraph<Node>
38where
39    Node: EntityRef + Debug,
40{
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        struct Edges<'a, Node: EntityRef + Debug>(&'a CallGraph<Node>);
43
44        impl<'a, Node: EntityRef + Debug> Debug for Edges<'a, Node> {
45            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46                f.debug_map()
47                    .entries(self.0.nodes().map(|n| (n, self.0.edges(n))))
48                    .finish()
49            }
50        }
51
52        f.debug_struct("CallGraph")
53            .field("edges", &Edges(self))
54            .finish()
55    }
56}
57
58impl<Node> CallGraph<Node>
59where
60    Node: EntityRef + Debug,
61{
62    /// Construct a new call graph.
63    ///
64    /// `funcs` should be an iterator over all function nodes in this call
65    /// graph's translation unit.
66    ///
67    /// The `get_calls` function should yield (by pushing onto the given `Vec`)
68    /// all of the callee function nodes that the given caller function node
69    /// calls.
70    pub fn new(
71        funcs: impl IntoIterator<Item = Node>,
72        mut get_calls: impl FnMut(Node, &mut Vec<Node>) -> Result<()>,
73    ) -> Result<Self> {
74        let funcs = funcs.into_iter();
75
76        let (min, max) = funcs.size_hint();
77        let capacity = max.unwrap_or_else(|| 2 * min);
78        let mut edges = SecondaryMap::with_capacity(capacity);
79        let mut edge_elems = vec![];
80
81        let mut calls = vec![];
82        for caller in funcs {
83            debug_assert!(calls.is_empty());
84            get_calls(caller, &mut calls)?;
85
86            debug_assert_eq!(edges[caller], Range::default());
87            edges[caller] = extend_with_range(&mut edge_elems, calls.drain(..));
88        }
89
90        Ok(CallGraph { edges, edge_elems })
91    }
92
93    /// Get the function nodes in this call graph.
94    pub fn nodes(&self) -> impl ExactSizeIterator<Item = Node> {
95        self.edges.keys()
96    }
97
98    /// Get the callee function nodes that the given caller function node calls.
99    pub fn edges(&self, node: Node) -> &[Node] {
100        let Range { start, end } = self.edges[node].clone();
101        let start = usize::try_from(start).unwrap();
102        let end = usize::try_from(end).unwrap();
103        &self.edge_elems[start..end]
104    }
105}