calyx_opt/passes/
constant_port_prop.rs

1use crate::traversal::{Action, Named, VisResult, Visitor};
2use calyx_ir::{self as ir, LibrarySignatures};
3use std::collections::{HashMap, HashSet};
4
5/// Replaces ports with constants when we can infer the value of the port.
6///
7/// Currently, the pass deals with a single case: If a combinational group
8/// is *only* associated with invoke(s) of a single cell, then uses of the
9/// cell's `go` and `done` ports in the comb group can be replaced with
10/// 1 and 0 respectively.
11///
12/// # Example
13/// ```no_run
14/// wires {
15///     comb group comb_group {
16///       wire.in = !invoked_cell.done ? 1'd1;
17///     }
18/// }
19/// control {
20///     invoke invoked_cell[]()() with comb_group;
21/// }
22/// ```
23/// In `comb_group` above, the use of `invoked_cell.done` is unnecessary, since
24/// `comb_group` is only active during the invocation of `invoked_cell`. So,
25/// the pass replaces the use of `invoked_cell.done` with zero.
26#[derive(Debug, Default)]
27pub struct ConstantPortProp {
28    /// name of comb group -> name of cell being invoked
29    comb_groups_to_modify: HashMap<ir::Id, ir::Id>,
30    /// comb groups used in while/if blocks, or used in multiple invokes
31    /// used to filter out comb groups that we *shouldn't* modify
32    /// NOTE: This needs to be in a separate set because we may process whiles/ifs before invokes.
33    comb_groups_used_elsewhere: HashSet<ir::Id>,
34}
35
36impl Named for ConstantPortProp {
37    fn name() -> &'static str {
38        "constant-port-prop"
39    }
40
41    fn description() -> &'static str {
42        "propagates constants when port values can be inferred"
43    }
44}
45
46impl Visitor for ConstantPortProp {
47    fn invoke(
48        &mut self,
49        s: &mut calyx_ir::Invoke,
50        _comp: &mut calyx_ir::Component,
51        _sigs: &LibrarySignatures,
52        _comps: &[calyx_ir::Component],
53    ) -> VisResult {
54        if let Some(cg) = &s.comb_group {
55            let cg_name = cg.borrow().name();
56            let cell_name = s.comp.borrow().name();
57            if let Some(registered_cell_name) =
58                self.comb_groups_to_modify.get(&cg_name)
59            {
60                if *registered_cell_name != cell_name {
61                    // there is a different invoke that is using the same comb group
62                    self.comb_groups_used_elsewhere.insert(cg_name);
63                }
64            } else {
65                // no invokes have used this comb group so far
66                self.comb_groups_to_modify.insert(cg_name, cell_name);
67            }
68        }
69        Ok(Action::Continue)
70    }
71
72    fn start_while(
73        &mut self,
74        s: &mut calyx_ir::While,
75        _comp: &mut calyx_ir::Component,
76        _sigs: &LibrarySignatures,
77        _comps: &[calyx_ir::Component],
78    ) -> VisResult {
79        if let Some(comb_group) = &s.cond {
80            self.comb_groups_used_elsewhere
81                .insert(comb_group.borrow().name());
82        }
83        Ok(Action::Continue)
84    }
85
86    fn start_if(
87        &mut self,
88        s: &mut calyx_ir::If,
89        _comp: &mut calyx_ir::Component,
90        _sigs: &LibrarySignatures,
91        _comps: &[calyx_ir::Component],
92    ) -> VisResult {
93        if let Some(comb_group) = &s.cond {
94            self.comb_groups_used_elsewhere
95                .insert(comb_group.borrow().name());
96        }
97        Ok(Action::Continue)
98    }
99
100    fn finish(
101        &mut self,
102        comp: &mut calyx_ir::Component,
103        sigs: &LibrarySignatures,
104        _comps: &[calyx_ir::Component],
105    ) -> VisResult {
106        let mut builder = ir::Builder::new(comp, sigs);
107        let one = builder.add_constant(1, 1);
108        let zero = builder.add_constant(0, 1);
109        // modify assignments of comb groups that aren't used in while/ifs and in multiple invokes
110        for comb_group_ref in comp.comb_groups.iter().filter(|item| {
111            !self
112                .comb_groups_used_elsewhere
113                .contains(&item.borrow().name())
114        }) {
115            // for comb_group_ref in comp.comb_groups.iter() {
116            let mut comb_group = comb_group_ref.borrow_mut();
117            let comb_group_name = comb_group.name();
118            if let Some(cell_name) =
119                self.comb_groups_to_modify.get(&comb_group_name)
120            {
121                let mut modified_asgns =
122                    std::mem::take(&mut comb_group.assignments);
123                for asgn in modified_asgns.iter_mut() {
124                    asgn.for_each_port(|port_ref| {
125                        let mut res = None;
126                        let port = port_ref.borrow();
127                        if let ir::PortParent::Cell(cell_wref) = &port.parent {
128                            if cell_wref.upgrade().borrow().name() == cell_name
129                                && port.name == "done"
130                            {
131                                // replace cell.done with 0
132                                res = Some(zero.borrow().get("out"));
133                            } else if cell_wref.upgrade().borrow().name()
134                                == cell_name
135                                && port.name == "go"
136                            {
137                                // replace cell.done with 1
138                                res = Some(one.borrow().get("out"));
139                            }
140                        }
141                        res
142                    });
143                }
144                comb_group.assignments = modified_asgns;
145            }
146        }
147
148        Ok(Action::Continue)
149    }
150}