calyx_opt/passes/
hole_inliner.rs1use crate::traversal::{Action, Named, VisResult, Visitor};
2use crate::{analysis::GraphAnalysis, passes::TopDownCompileControl};
3use calyx_ir::{self as ir, structure, LibrarySignatures, RRC};
4use calyx_utils::Error;
5use ir::Nothing;
6use std::{collections::HashMap, rc::Rc};
7
8#[derive(Default)]
9pub struct HoleInliner;
16
17impl Named for HoleInliner {
18 fn name() -> &'static str {
19 "hole-inliner"
20 }
21
22 fn description() -> &'static str {
23 "inlines holes"
24 }
25}
26
27type Store = HashMap<ir::Canonical, (RRC<ir::Port>, ir::Guard<ir::Nothing>)>;
28
29fn fixed_point(graph: &GraphAnalysis, map: &mut Store) {
53 let mut worklist = Vec::new();
55
56 let has_holes = |guard: &ir::Guard<Nothing>| {
58 guard
59 .all_ports()
60 .iter()
61 .map(|p| p.borrow().is_hole())
62 .any(|e| e)
63 };
64
65 for (key, (_, guard)) in map.iter() {
67 if !has_holes(guard) {
68 worklist.push(key.clone())
69 }
70 }
71
72 while !worklist.is_empty() {
73 let hole_key = worklist.pop().unwrap_or_else(|| unreachable!());
74 let (hole, new_guard) = map[&hole_key].clone();
75
76 for read in graph
78 .reads_from(&hole.borrow())
79 .filter(|p| p.borrow().is_hole())
80 {
81 let key = read.borrow().canonical();
83 map.entry(read.borrow().canonical())
84 .and_modify(|(_, guard)| {
85 guard.for_each(&mut |port| {
86 if port.borrow().canonical() == hole_key {
87 Some(new_guard.clone())
88 } else {
89 None
90 }
91 })
92 });
93 if !has_holes(&map[&key].1) {
95 worklist.push(key)
96 }
97 }
98 }
99}
100
101impl Visitor for HoleInliner {
102 fn start(
103 &mut self,
104 comp: &mut ir::Component,
105 sigs: &LibrarySignatures,
106 _comps: &[ir::Component],
107 ) -> VisResult {
108 let top_level = match &*comp.control.borrow() {
110 ir::Control::Empty(_) => return Ok(Action::Stop),
111 ir::Control::Enable(en) => Rc::clone(&en.group),
112 _ => return Err(Error::malformed_control(format!(
113 "{}: Control shoudl be a single enable. Try running `{}` before inlining.",
114 Self::name(),
115 TopDownCompileControl::name()))
116 )
117 };
118
119 let this_comp = Rc::clone(&comp.signature);
120 let mut builder = ir::Builder::new(comp, sigs);
121
122 let mut asgns = vec![
124 builder.build_assignment(
125 top_level.borrow().get("go"),
126 this_comp.borrow().get_unique_with_attr(ir::NumAttr::Go)?,
127 ir::Guard::True,
128 ),
129 builder.build_assignment(
130 this_comp.borrow().get_unique_with_attr(ir::NumAttr::Done)?,
131 top_level.borrow().get("done"),
132 ir::Guard::True,
133 ),
134 ];
135 builder.component.continuous_assignments.append(&mut asgns);
136
137 let analysis = GraphAnalysis::from(&*builder.component);
139 let subgraph = analysis
140 .edge_induced_subgraph(|src, dst| src.is_hole() || dst.is_hole());
141
142 if subgraph.has_cycles() {
144 return Err(Error::malformed_structure(
146 "Cyclic hole definition.".to_string(),
147 ));
148 }
149
150 let mut map: Store = HashMap::new();
152 let mut assignments = vec![];
153 for group in builder.component.get_groups().iter() {
154 let mut group = group.borrow_mut();
156 assignments.append(&mut group.assignments.drain(..).collect());
157 }
158
159 assert!(
160 builder.component.get_static_groups().is_empty(),
161 "should have removed static groups when inlining holes"
162 );
163
164 assignments.append(
166 &mut builder.component.continuous_assignments.drain(..).collect(),
167 );
168
169 for asgn in &mut assignments {
170 let dst = asgn.dst.borrow();
172 if dst.is_hole() {
173 map.entry(dst.canonical())
174 .and_modify(|(_, val)| {
175 *val = val.clone().or(asgn
177 .guard
178 .clone()
179 .and(ir::Guard::port(Rc::clone(&asgn.src))));
180 })
181 .or_insert((
182 Rc::clone(&asgn.dst),
183 asgn.guard
184 .clone()
185 .and(ir::Guard::port(Rc::clone(&asgn.src))),
186 ));
187 }
188 }
189
190 fixed_point(&subgraph, &mut map);
192
193 assignments.retain(|asgn| !asgn.dst.borrow().is_hole());
195
196 structure!(
199 builder;
200 let signal_on = constant(1, 1);
201 );
202 assignments.iter_mut().for_each(|asgn| {
203 if asgn.src.borrow().is_hole() {
204 let and_guard = ir::Guard::port(Rc::clone(&asgn.src));
205 *asgn.guard &= and_guard;
206 asgn.src = signal_on.borrow().get("out");
207 }
208 });
209
210 for asgn in &mut assignments {
212 asgn.guard.for_each(&mut |port| {
213 if port.borrow().is_hole() {
214 Some(map[&port.borrow().canonical()].1.clone())
215 } else {
216 None
217 }
218 })
219 }
220 comp.continuous_assignments = assignments;
221
222 comp.get_groups_mut().clear();
224 comp.get_static_groups_mut().clear();
225
226 Ok(Action::change(ir::Control::empty()))
228 }
229}