1use crate::analysis;
2use crate::traversal::{
3 Action, ConstructVisitor, Named, Order, ParseVal, PassOpt, VisResult,
4 Visitor,
5};
6use calyx_ir::{self as ir, rewriter, GetAttributes, LibrarySignatures, RRC};
7use calyx_utils::Error;
8use ir::Nothing;
9use itertools::Itertools;
10use std::collections::{HashMap, HashSet};
11use std::rc::Rc;
12
13pub struct ComponentInliner {
25 always_inline: bool,
27 new_fsms: bool,
30 control_map: HashMap<ir::Id, ir::Control>,
32 interface_rewrites: rewriter::PortRewriteMap,
34 inlined_cells: Vec<RRC<ir::Cell>>,
37}
38
39impl Named for ComponentInliner {
40 fn name() -> &'static str {
41 "inline"
42 }
43
44 fn description() -> &'static str {
45 "inline all component instances marked with @inline attribute"
46 }
47
48 fn opts() -> Vec<PassOpt> {
49 vec![
50 PassOpt::new(
51 "always",
52 "Attempt to inline all components into the main component",
53 ParseVal::Bool(false),
54 PassOpt::parse_bool,
55 ),
56 PassOpt::new(
57 "new-fsms",
58 "Instantiate new FSM for each inlined component",
59 ParseVal::Bool(false),
60 PassOpt::parse_bool,
61 ),
62 ]
63 }
64}
65
66impl ComponentInliner {
67 fn new(always_inline: bool, new_fsms: bool) -> Self {
70 ComponentInliner {
71 always_inline,
72 new_fsms,
73 control_map: HashMap::default(),
74 interface_rewrites: HashMap::default(),
75 inlined_cells: Vec::default(),
76 }
77 }
78}
79
80impl ConstructVisitor for ComponentInliner {
81 fn from(ctx: &ir::Context) -> calyx_utils::CalyxResult<Self>
82 where
83 Self: Sized,
84 {
85 let opts = Self::get_opts(ctx);
86 Ok(ComponentInliner::new(
87 opts[&"always"].bool(),
88 opts[&"new-fsms"].bool(),
89 ))
90 }
91
92 fn clear_data(&mut self) {
93 *self = ComponentInliner::new(self.always_inline, self.new_fsms);
94 }
95}
96
97impl ComponentInliner {
98 fn inline_cell(
100 builder: &mut ir::Builder,
101 cell_ref: &RRC<ir::Cell>,
102 ) -> (ir::Id, RRC<ir::Cell>) {
103 let cell = cell_ref.borrow();
104 let cn = cell.name();
105 let new_cell = match &cell.prototype {
106 ir::CellType::Primitive {
107 name,
108 param_binding,
109 ..
110 } => builder.add_primitive(
111 cn,
112 *name,
113 ¶m_binding.iter().map(|(_, v)| *v).collect_vec(),
114 ),
115 ir::CellType::Component { name } => {
116 builder.add_component(cn, *name, cell.get_signature())
117 }
118 ir::CellType::Constant { val, width } => {
119 builder.add_constant(*val, *width)
120 }
121 ir::CellType::ThisComponent => unreachable!(),
122 };
123 (cn, new_cell)
124 }
125
126 fn rewrite_assigns(
132 assigns: &mut [ir::Assignment<Nothing>],
133 port_rewrite: &ir::Rewriter,
134 new_group: Option<&RRC<ir::Group>>,
135 ) {
136 assigns.iter_mut().for_each(|assign| {
137 assign.for_each_port(|port| {
138 port_rewrite.get(port).or_else(|| {
139 if let Some(grp) = new_group {
140 if port.borrow().is_hole() {
141 return Some(grp.borrow().get(&port.borrow().name));
142 }
143 }
144 None
145 })
146 });
147 })
148 }
149
150 fn rewrite_assigns_static(
156 assigns: &mut [ir::Assignment<ir::StaticTiming>],
157 port_rewrite: &ir::Rewriter,
158 new_group: Option<&RRC<ir::StaticGroup>>,
159 ) {
160 assigns.iter_mut().for_each(|assign| {
161 assign.for_each_port(|port| {
162 port_rewrite.get(port).or_else(|| {
163 if let Some(grp) = new_group {
164 if port.borrow().is_hole() {
165 return Some(grp.borrow().get(&port.borrow().name));
166 }
167 }
168 None
169 })
170 });
171 })
172 }
173
174 fn rewrite_vec(&self, v: &mut [(ir::Id, RRC<ir::Port>)]) {
177 v.iter_mut().for_each(|(_, port)| {
178 let key = port.borrow().canonical();
179 if let Some(rewrite) = self.interface_rewrites.get(&key) {
180 *port = Rc::clone(rewrite);
181 }
182 })
183 }
184
185 fn rewrite_invoke_ports(&self, invoke: &mut ir::Invoke) {
187 self.rewrite_vec(&mut invoke.inputs);
188 self.rewrite_vec(&mut invoke.outputs);
189 }
190
191 fn inline_group(
194 builder: &mut ir::Builder,
195 port_rewrite: &ir::Rewriter,
196 gr: &RRC<ir::Group>,
197 ) -> (ir::Id, RRC<ir::Group>) {
198 let group = gr.borrow();
199 let new_group = builder.add_group(group.name());
200 new_group.borrow_mut().attributes = group.attributes.clone();
201
202 let mut asgns = group.assignments.clone();
204 Self::rewrite_assigns(&mut asgns, port_rewrite, Some(&new_group));
205 new_group.borrow_mut().assignments = asgns;
206 (group.name(), new_group)
207 }
208
209 fn inline_static_group(
212 builder: &mut ir::Builder,
213 port_rewrite: &ir::Rewriter,
214 gr: &RRC<ir::StaticGroup>,
215 ) -> (ir::Id, RRC<ir::StaticGroup>) {
216 let group = gr.borrow();
217 let new_group =
218 builder.add_static_group(group.name(), group.get_latency());
219 new_group.borrow_mut().attributes = group.attributes.clone();
220
221 let mut asgns = group.assignments.clone();
223 Self::rewrite_assigns_static(
224 &mut asgns,
225 port_rewrite,
226 Some(&new_group),
227 );
228 new_group.borrow_mut().assignments = asgns;
229 (group.name(), new_group)
230 }
231
232 fn inline_comb_group(
235 builder: &mut ir::Builder,
236 port_rewrite: &ir::Rewriter,
237 gr: &RRC<ir::CombGroup>,
238 ) -> (ir::Id, RRC<ir::CombGroup>) {
239 let group = gr.borrow();
240 let new_group = builder.add_comb_group(group.name());
241 new_group.borrow_mut().attributes = group.attributes.clone();
242
243 let mut asgns = group.assignments.clone();
245 Self::rewrite_assigns(&mut asgns, port_rewrite, None);
246 new_group.borrow_mut().assignments = asgns;
247 (group.name(), new_group)
248 }
249
250 fn inline_interface(
252 builder: &mut ir::Builder,
253 comp: &ir::Component,
254 name: ir::Id,
255 ) -> rewriter::PortRewriteMap {
256 comp.signature
258 .borrow()
259 .ports
260 .iter()
261 .map(|port_ref| {
262 let port = port_ref.borrow();
263 let wire_name = format!("{}_{}", name, port.name);
264 let wire_ref =
265 builder.add_primitive(wire_name, "std_wire", &[port.width]);
266 let wire = wire_ref.borrow();
267 let pn = match port.direction {
268 ir::Direction::Input => "in",
269 ir::Direction::Output => "out",
270 ir::Direction::Inout => unreachable!(),
271 };
272 (port.canonical(), wire.get(pn))
273 })
274 .collect()
275 }
276
277 fn inline_component(
284 builder: &mut ir::Builder,
285 mut cell_map: rewriter::RewriteMap<ir::Cell>,
286 comp: &ir::Component,
287 name: ir::Id,
288 ) -> (
289 ir::Control,
290 impl Iterator<Item = (ir::Canonical, RRC<ir::Port>)>,
291 ) {
292 cell_map.extend(comp.cells.iter().filter_map(|cell_ref| {
295 if !cell_ref.borrow().is_reference() {
296 Some(Self::inline_cell(builder, cell_ref))
297 } else {
298 None
299 }
300 }));
301
302 let interface_map = Self::inline_interface(builder, comp, name);
304 let mut rewrite = ir::Rewriter {
305 cell_map,
306 port_map: interface_map,
307 ..Default::default()
308 };
309
310 rewrite.group_map = comp
313 .get_groups()
314 .iter()
315 .map(|gr| Self::inline_group(builder, &rewrite, gr))
316 .collect();
317 rewrite.static_group_map = comp
318 .get_static_groups()
319 .iter()
320 .map(|gr| Self::inline_static_group(builder, &rewrite, gr))
321 .collect();
322 rewrite.comb_group_map = comp
323 .comb_groups
324 .iter()
325 .map(|gr| Self::inline_comb_group(builder, &rewrite, gr))
326 .collect();
327
328 let mut cont_assigns = comp.continuous_assignments.clone();
330 Self::rewrite_assigns(&mut cont_assigns, &rewrite, None);
331 builder
332 .component
333 .continuous_assignments
334 .extend(cont_assigns);
335
336 let mut con = ir::Cloner::control(&comp.control.borrow());
338 rewrite.rewrite_control(&mut con);
339
340 let rev_interface_map =
343 rewrite.port_map.into_iter().map(move |(cp, pr)| {
344 let ir::Canonical { port: p, .. } = cp;
345 let port = pr.borrow();
346 let np = match port.name.id.as_str() {
347 "in" => "out",
348 "out" => "in",
349 _ => unreachable!(),
350 };
351 (
352 ir::Canonical::new(name, p),
353 port.cell_parent().borrow().get(np),
354 )
355 });
356
357 (con, rev_interface_map)
358 }
359}
360
361impl Visitor for ComponentInliner {
362 fn iteration_order() -> Order {
364 Order::Post
365 }
366
367 fn start(
368 &mut self,
369 comp: &mut ir::Component,
370 sigs: &LibrarySignatures,
371 comps: &[ir::Component],
372 ) -> VisResult {
373 let (inline_cells, cells): (Vec<_>, Vec<_>) =
375 comp.cells.drain().partition(|cr| {
376 let cell = cr.borrow();
377 if self.always_inline {
380 cell.is_component()
381 } else {
382 cell.get_attribute(ir::BoolAttr::Inline).is_some()
383 }
384 });
385 comp.cells.append(cells.into_iter());
386
387 if inline_cells.is_empty() {
389 return Ok(Action::Stop);
390 }
391
392 let invoke_bindings: HashMap<ir::Id, _> =
395 analysis::ControlPorts::<true>::from(&*comp.control.borrow())
396 .get_all_bindings()
397 .into_iter()
398 .filter(|(instance, _)| {
399 inline_cells.iter().any(|c| c.borrow().name() == instance)
400 })
401 .collect();
402
403 for (instance, bindings) in &invoke_bindings {
405 if bindings.len() > 1 {
406 let bindings_str = bindings
407 .iter()
408 .map(|(cells, ports)| {
409 format!(
410 "[{}]({})",
411 cells
412 .iter()
413 .map(|(c, cell)| format!(
414 "{c}={}",
415 cell.borrow().name()
416 ))
417 .join(", "),
418 ports
419 .iter()
420 .map(|(p, port)| format!(
421 "{p}={}",
422 port.borrow().canonical()
423 ))
424 .join(", ")
425 )
426 })
427 .join("\n");
428 return Err(
429 Error::pass_assumption(
430 Self::name(),
431 format!(
432 "Instance `{}.{instance}` invoked with multiple parameters (currently unsupported):\n{bindings_str}",
433 comp.name,
434 )));
435 }
436 }
437
438 let comp_map = comps
440 .iter()
441 .map(|comp| (&comp.name, comp))
442 .collect::<HashMap<_, _>>();
443
444 let mut interface_rewrites: rewriter::PortRewriteMap = HashMap::new();
446 let mut inlined_cells = HashSet::new();
448 let mut builder = ir::Builder::new(comp, sigs);
449 for cell_ref in &inline_cells {
450 let cell = cell_ref.borrow();
451 if !cell.is_component() {
453 let msg = format!(
454 "Cannot inline `{}`. It is a instance of primitive: `{}`",
455 cell.name(),
456 cell.type_name()
457 .unwrap_or_else(|| ir::Id::from("constant"))
458 );
459
460 return Err(Error::pass_assumption(Self::name(), msg)
461 .with_pos(&cell.attributes));
462 }
463
464 let comp_name = cell.type_name().unwrap();
465 let cell_map =
466 if let Some(binding) = &invoke_bindings.get(&cell.name()) {
467 let (cell_binds, _) = &binding[0];
468 cell_binds.iter().map(|(k, v)| (*k, v.clone())).collect()
469 } else {
470 log::info!(
471 "no binding for `{}` which means instance is unused",
472 cell.name()
473 );
474 HashMap::new()
475 };
476 let (control, rewrites) = Self::inline_component(
477 &mut builder,
478 cell_map,
479 comp_map[&comp_name],
480 cell.name(),
481 );
482 interface_rewrites.extend(rewrites);
483 self.control_map.insert(cell.name(), control);
484 inlined_cells.insert(cell.name());
485 }
486
487 builder.component.for_each_assignment(|assign| {
492 assign.for_each_port(|pr| {
493 let port = &pr.borrow();
494 let np = interface_rewrites.get(&port.canonical());
495 if np.is_some() && (port.name == "go" || port.name == "done") {
496 panic!(
497 "Cannot inline instance. It is structurally structurally invoked: `{}`",
498 port.cell_parent().borrow().name(),
499 );
500 }
501 np.cloned()
502 });
503 });
504
505 builder.component.for_each_static_assignment(|assign| {
506 assign.for_each_port(|pr| {
507 let port = &pr.borrow();
508 let np = interface_rewrites.get(&port.canonical());
509 if np.is_some() && (port.name == "go" || port.name == "done") {
510 panic!(
511 "Cannot inline instance. It is structurally structurally invoked: `{}`",
512 port.cell_parent().borrow().name(),
513 );
514 }
515 np.cloned()
516 });
517 });
518
519 for (instance, mut bindings) in invoke_bindings {
521 let Some((_, binding)) = bindings.pop() else {
522 unreachable!("Instance binding is empty");
523 };
524 let mut assigns = binding
525 .into_iter()
526 .filter(|(_, pr)| {
527 let port = pr.borrow();
528 !port.attributes.has(ir::BoolAttr::Clk)
530 && !port.attributes.has(ir::BoolAttr::Reset)
531 })
532 .map(|(name, param)| {
533 let port = Rc::clone(
534 &interface_rewrites
535 [&ir::Canonical::new(instance, name)],
536 );
537 let name = param.borrow().canonical();
540 let new_param = interface_rewrites
541 .get(&name)
542 .map(Rc::clone)
543 .unwrap_or(param);
544 let dir = port.borrow().direction.clone();
545 match dir {
546 ir::Direction::Input => builder.build_assignment(
547 port,
548 new_param,
549 ir::Guard::True,
550 ),
551 ir::Direction::Output => builder.build_assignment(
552 new_param,
553 port,
554 ir::Guard::True,
555 ),
556 ir::Direction::Inout => unreachable!(),
557 }
558 })
559 .collect_vec();
560 builder
561 .component
562 .continuous_assignments
563 .append(&mut assigns);
564 }
565
566 self.interface_rewrites = interface_rewrites;
567 self.inlined_cells = inline_cells;
570
571 Ok(Action::Continue)
572 }
573
574 fn start_if(
575 &mut self,
576 s: &mut ir::If,
577 _comp: &mut ir::Component,
578 _sigs: &LibrarySignatures,
579 _comps: &[ir::Component],
580 ) -> VisResult {
581 let name = &s.port.borrow().canonical();
582 if let Some(new_port) = self.interface_rewrites.get(name) {
583 s.port = Rc::clone(new_port);
584 }
585 Ok(Action::Continue)
586 }
587
588 fn start_while(
589 &mut self,
590 s: &mut ir::While,
591 _comp: &mut ir::Component,
592 _sigs: &LibrarySignatures,
593 _comps: &[ir::Component],
594 ) -> VisResult {
595 let name = &s.port.borrow().canonical();
596 if let Some(new_port) = self.interface_rewrites.get(name) {
597 s.port = Rc::clone(new_port);
598 }
599 Ok(Action::Continue)
600 }
601
602 fn invoke(
603 &mut self,
604 s: &mut ir::Invoke,
605 _comp: &mut ir::Component,
606 _sigs: &LibrarySignatures,
607 _comps: &[ir::Component],
608 ) -> VisResult {
609 self.rewrite_invoke_ports(s);
612
613 let cell = s.comp.borrow();
616 if let Some(con) = self.control_map.get_mut(&cell.name()) {
617 if self.new_fsms {
618 con.get_mut_attributes().insert(ir::BoolAttr::NewFSM, 1);
619 }
620 Ok(Action::change(ir::Cloner::control(con)))
621 } else {
622 Ok(Action::Continue)
623 }
624 }
625}