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