calyx_opt/traversal/
post_order.rs

1use calyx_ir::{self as ir, CellType};
2use calyx_utils::CalyxResult;
3use petgraph::algo;
4use petgraph::graph::{DiGraph, NodeIndex};
5use std::collections::HashMap;
6
7/// The order in which the components are traversed.
8#[derive(Default, PartialEq, Eq)]
9pub enum Order {
10    /// Use an arbitrary order.
11    #[default]
12    No,
13    /// Traverse components in pre-order.
14    Pre,
15    /// Traverse components in post-order.
16    Post,
17}
18
19/// Define traversal order of components: pre-order, post-order, or none.
20///
21/// ## No order
22/// Iterates over the components in any order
23///
24/// ## Post-order
25/// If a component `B` creates a cell of type `A` then component `A` is
26/// guaranteed to be visited before `B`.
27/// This is done by finding a topological order over a graph where `A` will
28/// have a directed edge to `B`.
29///
30/// Instead of constructing a new vector of components in a topological order,
31/// the implementation builds an `order` vector which contains indices into the
32/// original component vector.
33/// This way, we can return the components in the input order once we're done
34/// with the post order traversal.
35///
36/// ## Pre-order
37/// Reverse of post-order
38///
39/// ## Example
40/// ```rust
41/// let comps: Vec<ir::Component>;
42/// // Construct a post order.
43/// let post = PostOrder::new(comps, Order::Post);
44/// // Apply a mutable update to components.
45/// let upd: FnMut(&mut ir::Component) -> CalyxResult<()>;
46/// post.apply_update(upd);
47/// // Recover the components in original order.
48/// let new_comps = post.take();
49/// ```
50pub struct CompTraversal {
51    /// A topological ordering of the components.
52    order: Vec<NodeIndex>,
53    /// Vector of components in the original ordering.
54    comps: Vec<ir::Component>,
55}
56
57impl CompTraversal {
58    /// Returns a new instance the PostOrder iterator given a Vector of components.
59    ///
60    /// # Panics
61    /// Panics if there is no post-order traversal of the vectors possible.
62    pub fn new(comps: Vec<ir::Component>, order: Order) -> Self {
63        // If the order is not specified, return the components in the original order.
64        if order == Order::No {
65            return Self {
66                order: (0..comps.len()).map(NodeIndex::new).collect(),
67                comps,
68            };
69        }
70        let mut graph: DiGraph<usize, ()> = DiGraph::new();
71        // Reverse mapping from index to comps.
72        let rev_map: HashMap<ir::Id, NodeIndex> = comps
73            .iter()
74            .enumerate()
75            .map(|(idx, c)| (c.name, graph.add_node(idx)))
76            .collect::<HashMap<_, _>>();
77
78        // Construct a graph.
79        for comp in &comps {
80            for cell in comp.cells.iter() {
81                if let CellType::Component { name, .. } =
82                    &cell.borrow().prototype
83                {
84                    graph.add_edge(rev_map[name], rev_map[&comp.name], ());
85                }
86            }
87        }
88
89        // Build a topologically sorted ordering of the graph.
90        let mut topo = algo::toposort(&graph, None)
91            .expect("There is a cycle in definition of component cells");
92
93        // Reverse the order if a pre-order traversal is requested
94        if order == Order::Pre {
95            topo.reverse();
96        }
97        Self { order: topo, comps }
98    }
99
100    /// Traverses components in post-order and applies `upd`.
101    pub fn apply_update<F>(&mut self, mut upd: F) -> CalyxResult<()>
102    where
103        F: FnMut(&mut ir::Component, &Vec<ir::Component>) -> CalyxResult<()>,
104    {
105        for idx in self.order.iter() {
106            let mut comp = self.comps.remove(idx.index());
107            upd(&mut comp, &self.comps)?;
108            self.comps.insert(idx.index(), comp)
109        }
110
111        Ok(())
112    }
113
114    /// Returns the underlying component vector in original order.
115    pub fn take(self) -> Vec<ir::Component> {
116        self.comps
117    }
118}