calyx_opt/passes/
canonical.rs

1use crate::analysis;
2use crate::traversal::{Action, ConstructVisitor, Named, VisResult, Visitor};
3use calyx_ir::Guard;
4use calyx_ir::{self as ir, LibrarySignatures};
5
6/// Perform serval canonicalizations on the program.
7///
8/// ## Simplifying Guards
9/// For each group and continuous assignments, canonicalize guard
10/// statements that has constant 1 as either a source or a guard.
11///
12/// # Example
13/// ```
14/// a[done] = r1.done ? 1'd1 -> a[done] = r1.done
15/// ```
16///
17/// ## Dataflow Ordering of Assignments
18/// Uses [analysis::DataflowOrder] to sort all sets of assignments in the
19/// program into dataflow order.
20pub struct Canonicalize {
21    // A [analysis::DataflowOrder] used to reorder assignments into dataflow order.
22    order: analysis::DataflowOrder,
23}
24
25impl ConstructVisitor for Canonicalize {
26    fn from(ctx: &ir::Context) -> calyx_utils::CalyxResult<Self>
27    where
28        Self: Sized,
29    {
30        let order = analysis::DataflowOrder::new(ctx.lib.signatures())?;
31        Ok(Canonicalize { order })
32    }
33
34    fn clear_data(&mut self) {
35        // Data is shared between components
36    }
37}
38
39impl Named for Canonicalize {
40    fn name() -> &'static str {
41        "canonicalize"
42    }
43
44    fn description() -> &'static str {
45        "canonicalize the program"
46    }
47}
48
49impl Visitor for Canonicalize {
50    fn start(
51        &mut self,
52        comp: &mut ir::Component,
53        _ctx: &LibrarySignatures,
54        _comps: &[ir::Component],
55    ) -> VisResult {
56        comp.for_each_assignment(|assign| {
57            if let Guard::Port(p) = &(*assign.guard) {
58                // 1'd1 ? r1.done
59                if p.borrow().is_constant(1, 1) {
60                    assign.guard = Guard::True.into()
61                }
62            }
63        });
64        comp.for_each_static_assignment(|assign| {
65            if let Guard::Port(p) = &(*assign.guard) {
66                // 1'd1 ? r1.done
67                if p.borrow().is_constant(1, 1) {
68                    assign.guard = Guard::True.into()
69                }
70            }
71        });
72
73        for gr in comp.get_groups().iter() {
74            // Handles group[done] = a ? 1'd1 -> group[done] = a
75            let mut group = gr.borrow_mut();
76            let done_assign = group.done_cond_mut();
77            if let Guard::Port(p) = &(*done_assign.guard) {
78                if done_assign.src.borrow().is_constant(1, 1) {
79                    done_assign.src = p.clone(); //rc clone
80                    done_assign.guard = Guard::True.into();
81                }
82            }
83            // Deals with aassignment ordering
84            let assigns = std::mem::take(&mut group.assignments);
85            group.assignments = self.order.dataflow_sort(assigns)?;
86        }
87        for gr in comp.get_static_groups().iter() {
88            // Do *not* handle group[done] = a ? 1'd1. Keep it as is.
89            // Deals with aassignment orderin
90            let mut group = gr.borrow_mut();
91            let assigns = std::mem::take(&mut group.assignments);
92            group.assignments = self.order.dataflow_sort(assigns)?;
93        }
94        for cgr in comp.comb_groups.iter() {
95            let assigns = std::mem::take(&mut cgr.borrow_mut().assignments);
96            cgr.borrow_mut().assignments = self.order.dataflow_sort(assigns)?;
97        }
98        let cont_assigns = std::mem::take(&mut comp.continuous_assignments);
99        comp.continuous_assignments = self.order.dataflow_sort(cont_assigns)?;
100
101        Ok(Action::Stop)
102    }
103}