calyx_opt/passes/
default_assigns.rs

1use crate::analysis::AssignmentAnalysis;
2use crate::traversal::{Action, ConstructVisitor, Named, VisResult, Visitor};
3use calyx_ir::{self as ir, LibrarySignatures};
4use calyx_utils::{CalyxResult, Error};
5use itertools::Itertools;
6use std::collections::HashMap;
7
8/// Adds default assignments to all non-`@data` ports of an instance.
9pub struct DefaultAssigns {
10    /// Mapping from component to data ports
11    data_ports: HashMap<ir::Id, Vec<ir::Id>>,
12}
13
14impl Named for DefaultAssigns {
15    fn name() -> &'static str {
16        "default-assigns"
17    }
18
19    fn description() -> &'static str {
20        "adds default assignments to all non-`@data` ports of an instance."
21    }
22}
23
24impl ConstructVisitor for DefaultAssigns {
25    fn from(ctx: &ir::Context) -> CalyxResult<Self>
26    where
27        Self: Sized,
28    {
29        let data_ports = ctx
30            .lib
31            .signatures()
32            .map(|sig| {
33                let ports = sig.signature.iter().filter_map(|p| {
34                    if p.direction == ir::Direction::Input
35                        && !p.attributes.has(ir::BoolAttr::Data)
36                        && !p.attributes.has(ir::BoolAttr::Clk)
37                        && !p.attributes.has(ir::BoolAttr::Reset)
38                    {
39                        Some(p.name())
40                    } else {
41                        None
42                    }
43                });
44                (sig.name, ports.collect())
45            })
46            .collect();
47        Ok(Self { data_ports })
48    }
49
50    fn clear_data(&mut self) {
51        /* shared across components */
52    }
53}
54
55impl Visitor for DefaultAssigns {
56    fn start(
57        &mut self,
58        comp: &mut ir::Component,
59        sigs: &LibrarySignatures,
60        _comps: &[ir::Component],
61    ) -> VisResult {
62        if !comp.is_structural() {
63            return Err(Error::pass_assumption(
64                Self::name(),
65                format!("component {} is not purely structural", comp.name),
66            ));
67        }
68
69        // We only need to consider write set of the continuous assignments
70        // and the register that controls an FSM
71        let writes = comp
72            .continuous_assignments
73            .iter()
74            .analysis()
75            .writes()
76            .group_by_cell();
77
78        let mut assigns = Vec::new();
79
80        let mt = vec![];
81        let cells = comp.cells.iter().cloned().collect_vec();
82        let mut builder = ir::Builder::new(comp, sigs);
83
84        for cr in &cells {
85            let cell = cr.borrow();
86            let Some(typ) = cell.type_name() else {
87                continue;
88            };
89            let Some(required) = self.data_ports.get(&typ) else {
90                continue;
91            };
92
93            // For all the assignments not in the write set, add a default assignment
94            // if the assignment does not write to an FSM-controlling register
95            let mut cell_writes: Vec<ir::RRC<ir::Port>> = writes
96                .get(&cell.name())
97                .unwrap_or(&mt)
98                .iter()
99                .map(ir::RRC::clone)
100                .collect();
101
102            if cell.attributes.has(ir::BoolAttr::FSMControl) {
103                cell_writes
104                    .extend(cell.ports().into_iter().map(ir::RRC::clone));
105            }
106
107            let cell_writes = cell_writes
108                .into_iter()
109                .map(|p| {
110                    let p = p.borrow();
111                    p.name
112                })
113                .collect_vec();
114
115            assigns.extend(
116                required.iter().filter(|p| (!cell_writes.contains(p))).map(
117                    |name| {
118                        let port = cell.get(name);
119                        let zero = builder.add_constant(0, port.borrow().width);
120                        let assign: ir::Assignment<ir::Nothing> = builder
121                            .build_assignment(
122                                cell.get(name),
123                                zero.borrow().get("out"),
124                                ir::Guard::True,
125                            );
126                        log::info!(
127                            "Adding {}",
128                            ir::Printer::assignment_to_str(&assign)
129                        );
130                        assign
131                    },
132                ),
133            );
134        }
135
136        comp.continuous_assignments.extend(assigns);
137
138        // Purely structural pass
139        Ok(Action::Stop)
140    }
141}