calyx_opt/passes/
dead_cell_removal.rs1use crate::traversal::{Action, Named, VisResult, Visitor};
2use calyx_ir::{self as ir};
3use std::collections::HashSet;
4use std::iter;
5
6const LOOP_THRESHOLD: u64 = 5;
8
9#[derive(Default)]
11pub struct DeadCellRemoval {
12 all_reads: HashSet<ir::Id>,
14}
15
16impl Named for DeadCellRemoval {
17 fn name() -> &'static str {
18 "dead-cell-removal"
19 }
20
21 fn description() -> &'static str {
22 "removes cells that are never used inside a component"
23 }
24}
25
26impl DeadCellRemoval {
27 fn retain_write<T: Clone + Eq + ToString>(
30 &self,
31 wire_reads: &HashSet<ir::Id>,
32 asgn: &ir::Assignment<T>,
33 ) -> bool {
34 let dst = asgn.dst.borrow();
35 if dst.is_hole() {
36 true
37 } else {
38 let parent = &dst.get_parent_name();
39 let out =
40 self.all_reads.contains(parent) || wire_reads.contains(parent);
41 if !out {
42 log::debug!(
43 "`{}' because `{}' is unused",
44 ir::Printer::assignment_to_str(asgn),
45 parent
46 )
47 }
48 out
49 }
50 }
51
52 fn visit_invoke(
53 &mut self,
54 comp: &ir::RRC<ir::Cell>,
55 inputs: &[(ir::Id, ir::RRC<ir::Port>)],
56 outputs: &[(ir::Id, ir::RRC<ir::Port>)],
57 ref_cells: &[(ir::Id, ir::RRC<ir::Cell>)],
58 ) {
59 let cells = inputs
60 .iter()
61 .map(|(_, p)| p)
62 .chain(outputs.iter().map(|(_, p)| p))
63 .map(|p| p.borrow().get_parent_name())
64 .chain(iter::once(comp.borrow().name()))
65 .chain(ref_cells.iter().map(|(_, c)| c.borrow().name()));
66 self.all_reads.extend(cells);
67 }
68}
69
70impl Visitor for DeadCellRemoval {
71 fn start_if(
72 &mut self,
73 s: &mut ir::If,
74 _comp: &mut ir::Component,
75 _sigs: &ir::LibrarySignatures,
76 _comps: &[ir::Component],
77 ) -> VisResult {
78 self.all_reads.insert(s.port.borrow().get_parent_name());
79 Ok(Action::Continue)
80 }
81
82 fn start_static_if(
83 &mut self,
84 s: &mut ir::StaticIf,
85 _comp: &mut ir::Component,
86 _sigs: &ir::LibrarySignatures,
87 _comps: &[ir::Component],
88 ) -> VisResult {
89 self.all_reads.insert(s.port.borrow().get_parent_name());
90 Ok(Action::Continue)
91 }
92
93 fn start_while(
94 &mut self,
95 s: &mut ir::While,
96 _comp: &mut ir::Component,
97 _sigs: &ir::LibrarySignatures,
98 _comps: &[ir::Component],
99 ) -> VisResult {
100 self.all_reads.insert(s.port.borrow().get_parent_name());
101 Ok(Action::Continue)
102 }
103
104 fn invoke(
105 &mut self,
106 s: &mut ir::Invoke,
107 _comp: &mut ir::Component,
108 _sigs: &ir::LibrarySignatures,
109 _comps: &[ir::Component],
110 ) -> VisResult {
111 self.visit_invoke(&s.comp, &s.inputs, &s.outputs, &s.ref_cells);
112 Ok(Action::Continue)
113 }
114
115 fn static_invoke(
116 &mut self,
117 s: &mut ir::StaticInvoke,
118 _comp: &mut ir::Component,
119 _sigs: &ir::LibrarySignatures,
120 _comps: &[ir::Component],
121 ) -> VisResult {
122 self.visit_invoke(&s.comp, &s.inputs, &s.outputs, &s.ref_cells);
123 Ok(Action::Continue)
124 }
125
126 fn finish(
127 &mut self,
128 comp: &mut ir::Component,
129 _sigs: &ir::LibrarySignatures,
130 _comps: &[ir::Component],
131 ) -> VisResult {
132 self.all_reads.extend(
134 comp.cells
135 .iter()
136 .filter(|c| {
137 let cell = c.borrow();
138 cell.attributes.get(ir::BoolAttr::External).is_some()
139 || cell.is_reference()
140 })
141 .map(|c| c.borrow().name()),
142 );
143 self.all_reads.insert(comp.signature.borrow().name());
145
146 let mut count = 0;
148 loop {
149 let mut wire_reads = HashSet::new();
150 comp.for_each_assignment(|assign| {
151 assign.for_each_port(|port| {
152 let port = port.borrow();
153 if port.direction == ir::Direction::Output {
154 wire_reads.insert(port.get_parent_name());
155 }
156 None
157 });
158 });
159 comp.for_each_static_assignment(|assign| {
160 assign.for_each_port(|port| {
161 let port = port.borrow();
162 if port.direction == ir::Direction::Output {
163 wire_reads.insert(port.get_parent_name());
164 }
165 None
166 });
167 });
168
169 for gr in comp.get_groups().iter() {
171 gr.borrow_mut()
172 .assignments
173 .retain(|asgn| self.retain_write(&wire_reads, asgn))
174 }
175 for gr in comp.get_static_groups().iter() {
177 gr.borrow_mut()
178 .assignments
179 .retain(|asgn| self.retain_write(&wire_reads, asgn))
180 }
181 for cgr in comp.comb_groups.iter() {
182 cgr.borrow_mut()
183 .assignments
184 .retain(|asgn| self.retain_write(&wire_reads, asgn))
185 }
186 comp.continuous_assignments
187 .retain(|asgn| self.retain_write(&wire_reads, asgn));
188
189 let removed = comp.cells.retain(|c| {
191 let cell = c.borrow();
192 self.all_reads.contains(&cell.name())
193 || wire_reads.contains(&cell.name())
194 });
195
196 if removed == 0 {
197 break;
198 }
199
200 count += 1;
201 }
202
203 if count >= LOOP_THRESHOLD {
204 log::warn!("{} looped {count} times", Self::name());
205 }
206
207 Ok(Action::Stop)
208 }
209}