calyx_opt/passes/
comb_prop.rs1use crate::traversal::{
2 Action, ConstructVisitor, Named, ParseVal, PassOpt, VisResult, Visitor,
3};
4use calyx_ir::{self as ir, RRC};
5use itertools::Itertools;
6use std::rc::Rc;
7
8#[derive(Default, Clone)]
11struct WireRewriter {
12 rewrites: ir::rewriter::PortRewriteMap,
13}
14
15impl WireRewriter {
16 pub fn insert_src_rewrite(
23 &mut self,
24 wire: RRC<ir::Cell>,
25 src: RRC<ir::Port>,
26 ) {
27 let wire_out = wire.borrow().get("out");
28 log::debug!(
29 "src rewrite: {} -> {}",
30 wire_out.borrow().canonical(),
31 src.borrow().canonical(),
32 );
33 let old = self.insert(wire_out, Rc::clone(&src));
34 assert!(
35 old.is_none(),
36 "Attempting to add multiple sources to a wire"
37 );
38 }
39
40 pub fn insert_dst_rewrite(
47 &mut self,
48 wire: RRC<ir::Cell>,
49 dst: RRC<ir::Port>,
50 ) {
51 let wire_in = wire.borrow().get("in");
52 log::debug!(
53 "dst rewrite: {} -> {}",
54 wire_in.borrow().canonical(),
55 dst.borrow().canonical(),
56 );
57 let old_v = self.insert(Rc::clone(&wire_in), dst);
58
59 if old_v.is_some() {
67 self.remove(wire_in);
68 }
69
70 }
72
73 fn insert(
76 &mut self,
77 from: RRC<ir::Port>,
78 to: RRC<ir::Port>,
79 ) -> Option<RRC<ir::Port>> {
80 let from_idx = from.borrow().canonical();
81 let old = self.rewrites.insert(from_idx, to);
82 if log::log_enabled!(log::Level::Debug) {
83 if let Some(ref old) = old {
84 log::debug!(
85 "Previous rewrite: {} -> {}",
86 from.borrow().canonical(),
87 old.borrow().canonical()
88 );
89 }
90 }
91 old
92 }
93
94 pub fn remove(&mut self, from: RRC<ir::Port>) {
96 log::debug!("Removing rewrite for `{}'", from.borrow().canonical());
97 let from_idx = from.borrow().canonical();
98 self.rewrites.remove(&from_idx);
99 }
100
101 fn make_consistent(self) -> Self {
103 let rewrites = self
105 .rewrites
106 .iter()
107 .map(|(from, to)| {
108 let to_idx = to.borrow().canonical();
109 let mut final_to = self.rewrites.get(&to_idx);
110 while let Some(new_to) = final_to {
111 if let Some(new_new_to) =
112 self.rewrites.get(&new_to.borrow().canonical())
113 {
114 final_to = Some(new_new_to);
115 } else {
116 break;
117 }
118 }
119 (from.clone(), Rc::clone(final_to.unwrap_or(to)))
120 })
121 .collect();
122 Self { rewrites }
123 }
124}
125
126impl From<WireRewriter> for ir::rewriter::PortRewriteMap {
127 fn from(v: WireRewriter) -> Self {
128 v.make_consistent().rewrites
129 }
130}
131
132impl std::fmt::Debug for WireRewriter {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 for (ir::Canonical { cell, port }, port_ref) in &self.rewrites {
135 writeln!(
136 f,
137 "{}.{} -> {}",
138 cell.id,
139 port.id,
140 ir::Printer::port_to_str(&port_ref.borrow())
141 )?
142 }
143 Ok(())
144 }
145}
146
147pub struct CombProp {
192 no_eliminate: bool,
196}
197
198impl ConstructVisitor for CombProp {
199 fn from(ctx: &ir::Context) -> calyx_utils::CalyxResult<Self>
200 where
201 Self: Sized,
202 {
203 let opts = Self::get_opts(ctx);
204 Ok(CombProp {
205 no_eliminate: opts[&"no-eliminate"].bool(),
206 })
207 }
208
209 fn clear_data(&mut self) {
210 }
212}
213
214impl Named for CombProp {
215 fn name() -> &'static str {
216 "comb-prop"
217 }
218
219 fn description() -> &'static str {
220 "propagate unconditional continuous assignments"
221 }
222
223 fn opts() -> Vec<PassOpt> {
224 vec![PassOpt::new(
225 "no-eliminate",
226 "mark dead assignments with @dead instead of removing them",
227 ParseVal::Bool(false),
228 PassOpt::parse_bool,
229 )]
230 }
231}
232
233impl CombProp {
234 #[inline]
236 fn remove_predicate<T>(
237 rewritten: &[RRC<ir::Port>],
238 assign: &ir::Assignment<T>,
239 ) -> bool
240 where
241 T: Clone + Eq + ToString,
242 {
243 let out = rewritten.iter().any(|v| Rc::ptr_eq(v, &assign.dst));
244 if log::log_enabled!(log::Level::Debug) && out {
245 log::debug!("Removing: {}", ir::Printer::assignment_to_str(assign));
246 }
247 out
248 }
249
250 fn remove_rewritten(
252 &self,
253 rewritten: &[RRC<ir::Port>],
254 comp: &mut ir::Component,
255 ) {
256 log::debug!(
257 "Rewritten: {}",
258 rewritten
259 .iter()
260 .map(|p| format!("{}", p.borrow().canonical()))
261 .collect::<Vec<_>>()
262 .join(", ")
263 );
264 if self.no_eliminate {
266 for assign in &mut comp.continuous_assignments {
268 if Self::remove_predicate(rewritten, assign) {
269 assign.attributes.insert(ir::InternalAttr::DEAD, 1)
270 }
271 }
272 } else {
273 comp.continuous_assignments.retain_mut(|assign| {
274 !Self::remove_predicate(rewritten, assign)
275 });
276 }
277 }
278
279 fn parent_is_wire(parent: &ir::PortParent) -> bool {
280 match parent {
281 ir::PortParent::Cell(cell_wref) => {
282 let cr = cell_wref.upgrade();
283 let cell = cr.borrow();
284 cell.is_primitive(Some("std_wire"))
285 }
286 ir::PortParent::Group(_) => false,
287 ir::PortParent::StaticGroup(_) => false,
288 }
289 }
290
291 fn disable_rewrite<T>(
292 assign: &mut ir::Assignment<T>,
293 rewrites: &mut WireRewriter,
294 ) {
295 if assign.guard.is_true() {
296 return;
297 }
298 assign.for_each_port(|pr| {
299 let p = pr.borrow();
300 if p.direction == ir::Direction::Output
301 && Self::parent_is_wire(&p.parent)
302 {
303 let cell = p.cell_parent();
304 rewrites.remove(cell.borrow().get("in"));
305 }
306 None
308 });
309 }
310}
311
312impl Visitor for CombProp {
313 fn start(
314 &mut self,
315 comp: &mut ir::Component,
316 _sigs: &ir::LibrarySignatures,
317 _comps: &[ir::Component],
318 ) -> VisResult {
319 let mut rewrites = WireRewriter::default();
320
321 for assign in &mut comp.continuous_assignments {
322 if !assign.guard.is_true() {
324 continue;
325 }
326
327 let dst = assign.dst.borrow();
328 if Self::parent_is_wire(&dst.parent) {
329 rewrites.insert_src_rewrite(
330 dst.cell_parent(),
331 Rc::clone(&assign.src),
332 );
333 }
334
335 let src = assign.src.borrow();
336 if Self::parent_is_wire(&src.parent) {
337 rewrites.insert_dst_rewrite(
338 src.cell_parent(),
339 Rc::clone(&assign.dst),
340 );
341 }
342 }
343
344 comp.for_each_assignment(|assign| {
348 Self::disable_rewrite(assign, &mut rewrites)
349 });
350 comp.for_each_static_assignment(|assign| {
351 Self::disable_rewrite(assign, &mut rewrites)
352 });
353
354 let rewrites: ir::rewriter::PortRewriteMap = rewrites.into();
357 let rewritten = rewrites.values().cloned().collect_vec();
358 self.remove_rewritten(&rewritten, comp);
359
360 comp.for_each_assignment(|assign| {
361 if !assign.attributes.has(ir::InternalAttr::DEAD) {
362 assign.for_each_port(|port| {
363 rewrites.get(&port.borrow().canonical()).cloned()
364 })
365 }
366 });
367 comp.for_each_static_assignment(|assign| {
368 if !assign.attributes.has(ir::InternalAttr::DEAD) {
369 assign.for_each_port(|port| {
370 rewrites.get(&port.borrow().canonical()).cloned()
371 })
372 }
373 });
374
375 let rewriter = ir::Rewriter {
376 port_map: rewrites,
377 ..Default::default()
378 };
379 rewriter.rewrite_control(&mut comp.control.borrow_mut());
380
381 Ok(Action::Stop)
382 }
383}