1use super::{
2 Assignment, Attributes, BackendConf, Builder, Cell, CellType, Component,
3 Context, Control, Direction, GetAttributes, Guard, Id, Invoke,
4 LibrarySignatures, Port, PortDef, RESERVED_NAMES, RRC, StaticControl,
5 StaticInvoke, Transition,
6};
7use crate::{Nothing, PortComp, StaticTiming};
8use calyx_frontend::{BoolAttr, NumAttr, Workspace, ast};
9use calyx_utils::{CalyxResult, Error, GPosIdx, WithPos};
10use itertools::Itertools;
11
12use std::collections::{HashMap, HashSet};
13use std::num::NonZeroU64;
14use std::rc::Rc;
15
16#[derive(Default)]
19struct SigCtx {
20 comp_sigs: HashMap<Id, (Vec<PortDef<u64>>, Option<NonZeroU64>)>,
22
23 lib: LibrarySignatures,
25}
26
27#[derive(Debug, Copy, Clone)]
28pub struct AstConversionConfig {
29 pub extend_signatures: bool,
30}
31
32impl Default for AstConversionConfig {
33 fn default() -> Self {
34 Self {
35 extend_signatures: true,
36 }
37 }
38}
39
40fn get_comp_latency(
44 sig_ctx: &SigCtx,
45 cell: RRC<Cell>,
46 attrs: &Attributes,
47) -> CalyxResult<Option<NonZeroU64>> {
48 let comp_name = cell
49 .borrow()
50 .type_name()
51 .unwrap_or_else(|| unreachable!("invoked component without a name"));
52 if let Some(prim) = sig_ctx.lib.find_primitive(comp_name) {
53 match prim.latency {
54 Some(val) => Ok(Some(val)),
55 None => {
56 let prim_sig = &prim.signature;
57 let interval_value = prim_sig
60 .iter()
61 .find(|port_def| port_def.attributes.has(NumAttr::Go))
62 .and_then(|go_port| {
63 go_port.attributes.get(NumAttr::Interval)
64 });
65 match interval_value {
67 Some(lat) => Ok(NonZeroU64::new(lat)),
68 None => Ok(None),
69 }
70 }
71 }
72 } else if let Some((comp_sig, latency)) = sig_ctx.comp_sigs.get(&comp_name)
73 {
74 match latency {
75 Some(val) => Ok(Some(*val)),
76 None => {
77 let interval_value = comp_sig
80 .iter()
81 .find(|port_def| port_def.attributes.has(NumAttr::Go))
82 .and_then(|go_port| {
83 go_port.attributes.get(NumAttr::Interval)
84 });
85 match interval_value {
87 Some(lat) => Ok(NonZeroU64::new(lat)),
88 None => Ok(None),
89 }
90 }
91 }
92 } else {
93 Err(
94 Error::undefined(comp_name, "primitive or component".to_string())
95 .with_pos(attrs),
96 )
97 }
98}
99
100fn get_static_latency(
103 sig_ctx: &SigCtx,
104 cell: RRC<Cell>,
105 attrs: &Attributes,
106) -> CalyxResult<Option<NonZeroU64>> {
107 let comp_name = cell
108 .borrow()
109 .type_name()
110 .unwrap_or_else(|| unreachable!("invoked component without a name"));
111 if let Some(prim) = sig_ctx.lib.find_primitive(comp_name) {
112 Ok(prim.latency)
113 } else if let Some((_, latency)) = sig_ctx.comp_sigs.get(&comp_name) {
114 Ok(*latency)
115 } else {
116 Err(
117 Error::undefined(comp_name, "primitive or component".to_string())
118 .with_pos(attrs),
119 )
120 }
121}
122
123fn check_valid_port(
126 cell: RRC<Cell>,
127 port_name: &Id,
128 attrs: &Attributes,
129 sig_ctx: &SigCtx,
130) -> CalyxResult<()> {
131 let cell_name = cell.borrow().name();
132 let comp_name = cell
133 .borrow()
134 .type_name()
135 .unwrap_or_else(|| unreachable!("invoked component without a name"));
136 let sig_ports: HashSet<_> =
137 if let Some(prim) = sig_ctx.lib.find_primitive(comp_name) {
138 prim.signature
139 .iter()
140 .map(|port_def| port_def.name())
141 .collect()
142 } else if let Some((comp_sigs, _)) = sig_ctx.comp_sigs.get(&comp_name) {
143 comp_sigs.iter().map(|port_def| port_def.name()).collect()
144 } else {
145 return Err(Error::undefined(
146 comp_name,
147 "primitive or component".to_string(),
148 )
149 .with_pos(attrs));
150 };
151 if !sig_ports.contains(port_name) {
152 return Err(Error::malformed_structure(format!(
153 "cell `{cell_name}` (which is an instance of {comp_name}) does not have port named `{port_name}`"
154 ))
155 .with_pos(attrs));
156 };
157 Ok(())
158}
159
160fn check_signature(pds: &[PortDef<u64>]) -> CalyxResult<()> {
162 let mut ports: HashSet<Id> = HashSet::new();
163 for pd in pds {
164 let name = pd.name();
165 match &pd.direction {
167 Direction::Input => {
168 if !ports.contains(&name) {
169 ports.insert(name);
170 } else {
171 return Err(Error::already_bound(name, "port".to_string()));
172 }
173 }
174 Direction::Output => {
175 if !ports.contains(&name) {
176 ports.insert(name);
177 } else {
178 return Err(Error::already_bound(name, "port".to_string()));
179 }
180 }
181 Direction::Inout => {
182 panic!("Components shouldn't have inout ports.")
183 }
184 }
185 }
186 Ok(())
187}
188
189pub fn ast_to_ir(
191 mut workspace: Workspace,
192 config: AstConversionConfig,
193) -> CalyxResult<Context> {
194 let prims = workspace.lib.signatures().collect_vec();
195 let mut all_names: HashSet<&Id> =
196 HashSet::with_capacity(workspace.components.len() + prims.len());
197
198 let prim_names = prims.iter().map(|p| (&p.name, p.attributes.copy_span()));
199
200 let comp_names = workspace
201 .components
202 .iter()
203 .map(|comp| (&comp.name, comp.attributes.copy_span()));
204
205 for (bound, span) in prim_names.chain(comp_names) {
206 if all_names.contains(bound) {
207 return Err(Error::already_bound(
208 *bound,
209 "component or primitive".to_string(),
210 )
211 .with_pos(&span));
212 }
213 all_names.insert(bound);
214 }
215
216 let mut sig_ctx = SigCtx {
218 lib: workspace.lib,
219 ..Default::default()
220 };
221
222 for comp in workspace
224 .declarations
225 .iter_mut()
226 .chain(workspace.components.iter_mut())
227 {
228 let sig = &mut comp.signature;
229 check_signature(&*sig)?;
230 if !comp.attributes.has(BoolAttr::NoInterface)
232 && !comp.is_comb
233 && config.extend_signatures
234 {
235 Component::extend_signature(sig);
236 }
237 sig_ctx
238 .comp_sigs
239 .insert(comp.name, (sig.clone(), comp.latency));
240 }
241
242 let comps: Vec<Component> = workspace
244 .components
245 .into_iter()
246 .map(|comp| build_component(comp, &mut sig_ctx, config))
247 .collect::<Result<_, _>>()?;
248
249 let entrypoint = comps
251 .iter()
252 .find(|c| c.attributes.has(BoolAttr::TopLevel))
253 .or_else(|| comps.iter().find(|c| c.name == "main"))
254 .map(|c| c.name)
255 .ok_or_else(|| Error::misc("No entry point for the program. Program needs to be either mark a component with the \"toplevel\" attribute or define a component named `main`".to_string()))?;
256
257 Ok(Context {
258 components: comps,
259 lib: sig_ctx.lib,
260 bc: BackendConf::default(),
261 entrypoint,
262 extra_opts: vec![],
263 metadata: workspace.metadata,
264 source_info_table: workspace.source_info_table,
265 })
266}
267
268fn validate_component(
269 comp: &ast::ComponentDef,
270 sig_ctx: &SigCtx,
271) -> CalyxResult<()> {
272 let mut cells: HashMap<Id, GPosIdx> = HashMap::new();
273 let mut groups: HashMap<Id, GPosIdx> = HashMap::new();
274
275 for cell in &comp.cells {
276 let attrs = &cell.attributes;
277 if let Some(pos) = cells.get(&cell.name) {
278 let prev =
279 pos.into_option().map(|s| s.format("Previous definition"));
280 return Err(Error::already_bound(cell.name, "cell".to_string())
281 .with_pos(attrs)
282 .with_post_msg(prev));
283 }
284 cells.insert(cell.name, cell.attributes.copy_span());
285
286 let proto_name = cell.prototype.name;
287
288 if sig_ctx.lib.find_primitive(proto_name).is_none()
289 && !sig_ctx.comp_sigs.contains_key(&proto_name)
290 {
291 return Err(Error::undefined(
292 proto_name,
293 "primitive or component".to_string(),
294 )
295 .with_pos(attrs));
296 }
297 }
298
299 for group in &comp.groups {
300 let name = &group.name;
301 let attrs = &group.attributes;
302 if let Some(pos) = groups.get(name) {
303 let prev =
304 pos.into_option().map(|s| s.format("Previous definition"));
305 return Err(Error::already_bound(*name, "group".to_string())
306 .with_pos(attrs)
307 .with_post_msg(prev));
308 }
309 if let Some(pos) = cells.get(name) {
310 let prev =
311 pos.into_option().map(|s| s.format("Previous definition"));
312 return Err(Error::already_bound(*name, "cell".to_string())
313 .with_pos(attrs)
314 .with_post_msg(prev));
315 }
316 if let Some(pos) = cells.get(name) {
317 let prev =
318 pos.into_option().map(|s| s.format("Previous definition"));
319 return Err(Error::already_bound(*name, "cell".to_string())
320 .with_pos(attrs)
321 .with_post_msg(prev));
322 }
323 groups.insert(*name, group.attributes.copy_span());
324 }
325
326 Ok(())
327}
328
329fn build_component(
331 comp: ast::ComponentDef,
332 sig_ctx: &mut SigCtx,
333 config: AstConversionConfig,
334) -> CalyxResult<Component> {
335 validate_component(&comp, sig_ctx)?;
337
338 let mut ir_component = Component::new(
339 comp.name,
340 comp.signature,
341 !comp.attributes.has(BoolAttr::NoInterface)
342 && !comp.is_comb
343 && config.extend_signatures,
344 comp.is_comb,
345 comp.latency,
348 );
349
350 let mut builder =
351 Builder::new(&mut ir_component, &sig_ctx.lib).not_generated();
352
353 comp.cells
356 .into_iter()
357 .try_for_each(|cell| add_cell(cell, sig_ctx, &mut builder))?;
358
359 comp.groups
360 .into_iter()
361 .try_for_each(|g| add_group(g, &mut builder))?;
362
363 comp.static_groups
364 .into_iter()
365 .try_for_each(|g| add_static_group(g, &mut builder))?;
366
367 comp.fsms
368 .into_iter()
369 .try_for_each(|f| add_fsm(f, &mut builder))?;
370
371 let continuous_assignments =
372 build_assignments(comp.continuous_assignments, &mut builder)?;
373 builder.component.continuous_assignments = continuous_assignments;
374
375 let control =
377 super::rrc(build_control(comp.control, sig_ctx, &mut builder)?);
378
379 builder.component.control = control;
380
381 ir_component.attributes = comp.attributes;
382
383 ir_component
386 .add_names(RESERVED_NAMES.iter().map(|s| Id::from(*s)).collect());
387
388 Ok(ir_component)
389}
390
391fn add_cell(
394 cell: ast::Cell,
395 sig_ctx: &SigCtx,
396 builder: &mut Builder,
397) -> CalyxResult<()> {
398 let proto_name = cell.prototype.name;
399
400 let res = if sig_ctx.lib.find_primitive(proto_name).is_some() {
401 let c = builder
402 .try_add_primitive(cell.name, proto_name, &cell.prototype.params)
403 .map_err(|e| e.with_pos(&cell.attributes))?;
404 c.borrow_mut().set_reference(cell.reference);
405 c
406 } else {
407 let name = builder.component.generate_name(cell.name);
410 let sig = &sig_ctx.comp_sigs[&proto_name].0;
411 let typ = CellType::Component { name: proto_name };
412 let reference = cell.reference;
413 let cell = Builder::cell_from_signature(name, typ, sig.clone());
415 cell.borrow_mut().set_reference(reference);
416 builder.component.cells.add(Rc::clone(&cell));
417 cell
418 };
419
420 res.borrow_mut().attributes = cell.attributes;
422
423 Ok(())
424}
425
426fn add_group(group: ast::Group, builder: &mut Builder) -> CalyxResult<()> {
431 if group.is_comb {
432 let ir_group = builder.add_comb_group(group.name);
433 let assigns = build_assignments(group.wires, builder)?;
434
435 ir_group.borrow_mut().attributes = group.attributes;
436 ir_group.borrow_mut().assignments = assigns;
437 } else {
438 let ir_group = builder.add_group(group.name);
439 let assigns = build_assignments(group.wires, builder)?;
440
441 ir_group.borrow_mut().attributes = group.attributes;
442 ir_group.borrow_mut().assignments = assigns;
443 };
444
445 Ok(())
446}
447
448fn add_static_group(
451 group: ast::StaticGroup,
452 builder: &mut Builder,
453) -> CalyxResult<()> {
454 if group.latency.get() == 0 {
455 return Err(Error::malformed_structure(
456 "static group with 0 latency".to_string(),
457 ));
458 }
459 let ir_group = builder.add_static_group(group.name, group.latency.get());
460 let assigns = build_static_assignments(group.wires, builder)?;
461
462 ir_group.borrow_mut().attributes = group.attributes;
463 ir_group.borrow_mut().assignments = assigns;
464
465 Ok(())
466}
467
468fn add_fsm(fsm: ast::Fsm, builder: &mut Builder) -> CalyxResult<()> {
473 let ir_fsm = builder.add_fsm(fsm.name);
474 let (state_assignments, state_transition): (
475 Vec<Vec<Assignment<Nothing>>>,
476 Vec<Transition>,
477 ) = fsm
478 .fsm_states
479 .into_iter()
480 .map(|rule| -> CalyxResult<_> {
481 Ok((
482 build_assignments(rule.assignments, builder)?,
483 build_transition(rule.transition, builder)?,
484 ))
485 })
486 .collect::<CalyxResult<Vec<_>>>()?
487 .into_iter()
488 .unzip();
489
490 {
491 let mut ir_fsm = ir_fsm.borrow_mut();
492 ir_fsm.attributes = fsm.attributes;
493 ir_fsm.assignments = state_assignments;
494 ir_fsm.transitions = state_transition;
495 }
496
497 Ok(())
498}
499
500fn get_port_ref(port: ast::Port, comp: &Component) -> CalyxResult<RRC<Port>> {
504 match port {
505 ast::Port::Comp { component, port } => comp
506 .find_cell(component)
507 .ok_or_else(|| Error::undefined(component, "cell".to_string()))?
508 .borrow()
509 .find(port)
510 .ok_or_else(|| {
511 Error::undefined(
512 Id::new(format!("{component}.{port}")),
513 "port".to_string(),
514 )
515 }),
516 ast::Port::This { port } => {
517 comp.signature.borrow().find(&port).ok_or_else(|| {
518 Error::undefined(port, "component port".to_string())
519 })
520 }
521 ast::Port::Hole {
522 struct_elem,
523 name: port,
524 } => {
525 if let Some(f) = comp.find_fsm(struct_elem) {
526 return f.borrow().find(port).ok_or_else(|| {
527 Error::undefined(
528 Id::new(format!("{}.{}", f.borrow().name(), port)),
529 "hole".to_string(),
530 )
531 });
532 } else if let Some(g) = comp.find_group(struct_elem) {
533 return g.borrow().find(port).ok_or_else(|| {
534 Error::undefined(
535 Id::new(format!("{}.{}", g.borrow().name(), port)),
536 "hole".to_string(),
537 )
538 });
539 } else {
540 return comp
541 .find_static_group(struct_elem)
542 .ok_or_else(|| {
543 Error::undefined(struct_elem, "group".to_string())
544 })?
545 .borrow()
546 .find(port)
547 .ok_or_else(|| Error::undefined(port, "hole".to_string()));
548 }
549 }
550 }
551}
552
553fn atom_to_port(
558 atom: ast::Atom,
559 builder: &mut Builder,
560) -> CalyxResult<RRC<Port>> {
561 match atom {
562 ast::Atom::Num(n) => {
563 let port = builder.add_constant(n.val, n.width).borrow().get("out");
564 Ok(Rc::clone(&port))
565 }
566 ast::Atom::Port(p) => get_port_ref(p, builder.component),
567 }
568}
569
570fn ensure_direction(pr: RRC<Port>, dir: Direction) -> CalyxResult<RRC<Port>> {
572 let port_dir = pr.borrow().direction.clone();
573 match (dir, port_dir) {
574 (Direction::Input, Direction::Output) => {
575 let name = pr.borrow().canonical();
576 Err(Error::malformed_structure(format!(
577 "Port `{name}` occurs in write position but is an output port",
578 )))
579 }
580 (Direction::Output, Direction::Input) => {
581 let name = pr.borrow().canonical();
582 Err(Error::malformed_structure(format!(
583 "Port `{name}` occurs in write position but is an output port",
584 )))
585 }
586 _ => Ok(pr),
587 }
588}
589
590fn build_assignment(
593 wire: ast::Wire,
594 builder: &mut Builder,
595) -> CalyxResult<Assignment<Nothing>> {
596 let src_port: RRC<Port> = ensure_direction(
597 atom_to_port(wire.src.expr, builder)?,
598 Direction::Output,
599 )?;
600 let dst_port: RRC<Port> = ensure_direction(
601 get_port_ref(wire.dest, builder.component)?,
602 Direction::Input,
603 )?;
604 if src_port.borrow().width != dst_port.borrow().width {
605 let msg = format!(
606 "Mismatched port widths. Source has size {} while destination requires {}.",
607 src_port.borrow().width,
608 dst_port.borrow().width,
609 );
610 return Err(Error::malformed_structure(msg).with_pos(&wire.attributes));
611 }
612 let guard = match wire.src.guard {
613 Some(g) => build_guard(g, builder)?,
614 None => Guard::True,
615 };
616
617 let mut assign = builder.build_assignment(dst_port, src_port, guard);
618 assign.attributes = wire.attributes;
619 Ok(assign)
620}
621
622fn build_static_assignment(
625 wire: ast::StaticWire,
626 builder: &mut Builder,
627) -> CalyxResult<Assignment<StaticTiming>> {
628 let src_port: RRC<Port> = ensure_direction(
629 atom_to_port(wire.src.expr, builder)?,
630 Direction::Output,
631 )?;
632 let dst_port: RRC<Port> = ensure_direction(
633 get_port_ref(wire.dest, builder.component)?,
634 Direction::Input,
635 )?;
636 if src_port.borrow().width != dst_port.borrow().width {
637 let msg = format!(
638 "Mismatched port widths. Source has size {} while destination requires {}.",
639 src_port.borrow().width,
640 dst_port.borrow().width,
641 );
642 return Err(Error::malformed_structure(msg).with_pos(&wire.attributes));
643 }
644 let guard = match wire.src.guard {
645 Some(g) => build_static_guard(g, builder)?,
646 None => Guard::True,
647 };
648
649 let mut assign = builder.build_assignment(dst_port, src_port, guard);
650 assign.attributes = wire.attributes;
651 Ok(assign)
652}
653
654fn build_assignments(
655 assigns: Vec<ast::Wire>,
656 builder: &mut Builder,
657) -> CalyxResult<Vec<Assignment<Nothing>>> {
658 assigns
659 .into_iter()
660 .map(|w| {
661 let attrs = w.attributes.clone();
662 build_assignment(w, builder).map_err(|err| err.with_pos(&attrs))
663 })
664 .collect::<CalyxResult<Vec<_>>>()
665}
666
667fn build_static_assignments(
668 assigns: Vec<ast::StaticWire>,
669 builder: &mut Builder,
670) -> CalyxResult<Vec<Assignment<StaticTiming>>> {
671 assigns
672 .into_iter()
673 .map(|w| {
674 let attrs = w.attributes.clone();
675 build_static_assignment(w, builder)
676 .map_err(|err| err.with_pos(&attrs))
677 })
678 .collect::<CalyxResult<Vec<_>>>()
679}
680
681fn build_transition(
684 transition: ast::Transition,
685 builder: &mut Builder,
686) -> CalyxResult<Transition> {
687 match transition {
688 ast::Transition::Unconditional(state_idx) => {
689 Ok(Transition::Unconditional(state_idx))
690 }
691 ast::Transition::Conditional(conditions) => {
692 let conds = conditions
693 .into_iter()
694 .map(|(condition, state_idx)| {
695 let guard = match condition {
696 Some(g) => build_guard(g, builder)?,
697 None => Guard::True,
698 };
699 Ok((guard, state_idx))
700 })
701 .collect::<CalyxResult<Vec<(Guard<Nothing>, u64)>>>()?;
702
703 Ok(Transition::Conditional(conds))
704 }
705 }
706}
707
708fn build_guard(
710 guard: ast::GuardExpr,
711 bd: &mut Builder,
712) -> CalyxResult<Guard<Nothing>> {
713 use ast::GuardExpr as GE;
714
715 let into_box_guard = |g: Box<GE>, bd: &mut Builder| -> CalyxResult<_> {
716 Ok(Box::new(build_guard(*g, bd)?))
717 };
718
719 Ok(match guard {
720 GE::Atom(atom) => Guard::port(ensure_direction(
721 atom_to_port(atom, bd)?,
722 Direction::Output,
723 )?),
724 GE::Or(l, r) => Guard::or(build_guard(*l, bd)?, build_guard(*r, bd)?),
725 GE::And(l, r) => Guard::and(build_guard(*l, bd)?, build_guard(*r, bd)?),
726 GE::Not(g) => Guard::Not(into_box_guard(g, bd)?),
727 GE::CompOp((op, l, r)) => {
728 let nl = ensure_direction(atom_to_port(l, bd)?, Direction::Output)?;
729 let nr = ensure_direction(atom_to_port(r, bd)?, Direction::Output)?;
730 let nop = match op {
731 ast::GuardComp::Eq => PortComp::Eq,
732 ast::GuardComp::Neq => PortComp::Neq,
733 ast::GuardComp::Gt => PortComp::Gt,
734 ast::GuardComp::Lt => PortComp::Lt,
735 ast::GuardComp::Geq => PortComp::Geq,
736 ast::GuardComp::Leq => PortComp::Leq,
737 };
738 Guard::CompOp(nop, nl, nr)
739 }
740 })
741}
742
743fn build_static_guard(
745 guard: ast::StaticGuardExpr,
746 bd: &mut Builder,
747) -> CalyxResult<Guard<StaticTiming>> {
748 use ast::StaticGuardExpr as SGE;
749
750 let into_box_guard = |g: Box<SGE>, bd: &mut Builder| -> CalyxResult<_> {
751 Ok(Box::new(build_static_guard(*g, bd)?))
752 };
753
754 Ok(match guard {
755 SGE::StaticInfo(interval) => Guard::Info(StaticTiming::new(interval)),
756 SGE::Atom(atom) => Guard::port(ensure_direction(
757 atom_to_port(atom, bd)?,
758 Direction::Output,
759 )?),
760 SGE::Or(l, r) => {
761 Guard::or(build_static_guard(*l, bd)?, build_static_guard(*r, bd)?)
762 }
763 SGE::And(l, r) => {
764 Guard::and(build_static_guard(*l, bd)?, build_static_guard(*r, bd)?)
765 }
766 SGE::Not(g) => Guard::Not(into_box_guard(g, bd)?),
767 SGE::CompOp((op, l, r)) => {
768 let nl = ensure_direction(atom_to_port(l, bd)?, Direction::Output)?;
769 let nr = ensure_direction(atom_to_port(r, bd)?, Direction::Output)?;
770 let nop = match op {
771 ast::GuardComp::Eq => PortComp::Eq,
772 ast::GuardComp::Neq => PortComp::Neq,
773 ast::GuardComp::Gt => PortComp::Gt,
774 ast::GuardComp::Lt => PortComp::Lt,
775 ast::GuardComp::Geq => PortComp::Geq,
776 ast::GuardComp::Leq => PortComp::Leq,
777 };
778 Guard::CompOp(nop, nl, nr)
779 }
780 })
781}
782
783fn assert_latencies_eq(
786 given_latency: Option<NonZeroU64>,
787 inferred_latency: u64,
788) {
789 if let Some(v) = given_latency {
790 assert_eq!(
791 v.get(),
792 inferred_latency,
793 "inferred latency: {inferred_latency}, given latency: {v}"
794 )
795 };
796}
797
798fn build_static_seq(
800 stmts: Vec<ast::Control>,
801 attributes: Attributes,
802 latency: Option<NonZeroU64>,
803 builder: &mut Builder,
804 sig_ctx: &SigCtx,
805) -> CalyxResult<StaticControl> {
806 let ir_stmts = stmts
807 .into_iter()
808 .map(|c| build_static_control(c, sig_ctx, builder))
809 .collect::<CalyxResult<Vec<_>>>()?;
810 let inferred_latency =
811 ir_stmts.iter().fold(0, |acc, s| acc + (s.get_latency()));
812 assert_latencies_eq(latency, inferred_latency);
813 let mut s = StaticControl::seq(ir_stmts, inferred_latency);
814 *s.get_mut_attributes() = attributes;
815 Ok(s)
816}
817
818fn build_static_par(
819 stmts: Vec<ast::Control>,
820 attributes: Attributes,
821 latency: Option<NonZeroU64>,
822 builder: &mut Builder,
823 sig_ctx: &SigCtx,
824) -> CalyxResult<StaticControl> {
825 let ir_stmts = stmts
826 .into_iter()
827 .map(|c| build_static_control(c, sig_ctx, builder))
828 .collect::<CalyxResult<Vec<_>>>()?;
829 let inferred_latency = match ir_stmts.iter().max_by_key(|s| s.get_latency())
830 {
831 Some(s) => s.get_latency(),
832 None => {
833 return Err(Error::malformed_control(
834 "empty par block".to_string(),
835 ));
836 }
837 };
838 assert_latencies_eq(latency, inferred_latency);
839 let mut p = StaticControl::par(ir_stmts, inferred_latency);
840 *p.get_mut_attributes() = attributes;
841 Ok(p)
842}
843
844fn build_static_if(
845 port: ast::Port,
846 tbranch: ast::Control,
847 fbranch: ast::Control,
848 attributes: Attributes,
849 latency: Option<NonZeroU64>,
850 builder: &mut Builder,
851 sig_ctx: &SigCtx,
852) -> CalyxResult<StaticControl> {
853 let ir_tbranch = build_static_control(tbranch, sig_ctx, builder)?;
854 let ir_fbranch = build_static_control(fbranch, sig_ctx, builder)?;
855 let inferred_latency =
856 std::cmp::max(ir_tbranch.get_latency(), ir_fbranch.get_latency());
857 assert_latencies_eq(latency, inferred_latency);
858 let mut con = StaticControl::static_if(
859 ensure_direction(
860 get_port_ref(port, builder.component)?,
861 Direction::Output,
862 )?,
863 Box::new(ir_tbranch),
864 Box::new(ir_fbranch),
865 inferred_latency,
866 );
867 *con.get_mut_attributes() = attributes;
868 Ok(con)
869}
870
871fn build_static_repeat(
872 num_repeats: u64,
873 body: ast::Control,
874 builder: &mut Builder,
875 attributes: Attributes,
876 sig_ctx: &SigCtx,
877) -> CalyxResult<StaticControl> {
878 let body = build_static_control(body, sig_ctx, builder)?;
879 let total_latency = body.get_latency() * num_repeats;
880 let mut scon =
881 StaticControl::repeat(num_repeats, total_latency, Box::new(body));
882 *scon.get_mut_attributes() = attributes;
883 Ok(scon)
884}
885
886fn build_static_control(
888 control: ast::Control,
889 sig_ctx: &SigCtx,
890 builder: &mut Builder,
891) -> CalyxResult<StaticControl> {
892 let sc = match control {
893 ast::Control::Enable {
894 comp: group,
895 attributes,
896 } => {
897 if builder.component.find_group(group).is_some() {
898 return Err(Error::malformed_control(
900 "found dynamic group in static context".to_string(),
901 )
902 .with_pos(&attributes));
903 };
904 let mut en = StaticControl::from(Rc::clone(
905 &builder.component.find_static_group(group).ok_or_else(
906 || {
907 Error::undefined(group, "group".to_string())
908 .with_pos(&attributes)
909 },
910 )?,
911 ));
912 *en.get_mut_attributes() = attributes;
913 en
914 }
915 ast::Control::StaticInvoke {
916 comp,
917 inputs,
918 outputs,
919 attributes,
920 ref_cells,
921 latency,
922 comb_group,
923 } => {
924 let cell = Rc::clone(
925 &builder.component.find_cell(comp).ok_or_else(|| {
926 Error::undefined(comp, "cell".to_string())
927 .with_pos(&attributes)
928 })?,
929 );
930 let comp_name = cell.borrow().type_name().unwrap_or_else(|| {
931 unreachable!("invoked component without a name")
932 });
933 let c_latency =
934 get_comp_latency(sig_ctx, Rc::clone(&cell), &attributes)?;
935 let unwrapped_latency = if let Some(v) = c_latency {
936 v
937 } else {
938 return Err(Error::malformed_control(format!(
939 "component {comp_name} is statically invoked, but is neither static nor does it have @interval attribute its @go port"
940 ))
941 .with_pos(&attributes));
942 };
943 assert_latencies_eq(latency, unwrapped_latency.into());
944
945 let inputs = inputs
946 .into_iter()
947 .map(|(id, port)| {
948 check_valid_port(
950 Rc::clone(&cell),
951 &id,
952 &attributes,
953 sig_ctx,
954 )?;
955 atom_to_port(port, builder)
956 .and_then(|pr| ensure_direction(pr, Direction::Output))
957 .map(|p| (id, p))
958 })
959 .collect::<Result<_, _>>()?;
960 let outputs = outputs
961 .into_iter()
962 .map(|(id, port)| {
963 check_valid_port(
965 Rc::clone(&cell),
966 &id,
967 &attributes,
968 sig_ctx,
969 )?;
970 atom_to_port(port, builder)
971 .and_then(|pr| ensure_direction(pr, Direction::Input))
972 .map(|p| (id, p))
973 })
974 .collect::<Result<_, _>>()?;
975 let mut inv = StaticInvoke {
976 comp: cell,
977 inputs,
978 outputs,
979 attributes: Attributes::default(),
980 ref_cells: Vec::new(),
981 latency: unwrapped_latency.into(),
982 comb_group: None,
983 };
984 if !ref_cells.is_empty() {
985 let mut ext_cell_tuples = Vec::new();
986 for (outcell, incell) in ref_cells {
987 let ext_cell_ref =
988 builder.component.find_cell(incell).ok_or_else(
989 || Error::undefined(incell, "cell".to_string()),
990 )?;
991 ext_cell_tuples.push((outcell, ext_cell_ref));
992 }
993 inv.ref_cells = ext_cell_tuples;
994 }
995 if let Some(cg) = comb_group {
996 let cg_ref =
997 builder.component.find_comb_group(cg).ok_or_else(|| {
998 Error::undefined(cg, "combinational group".to_string())
999 .with_pos(&inv.attributes)
1000 })?;
1001 inv.comb_group = Some(cg_ref);
1002 }
1003 let mut con = StaticControl::Invoke(inv);
1004 *con.get_mut_attributes() = attributes;
1005 return Ok(con);
1006 }
1007 ast::Control::StaticSeq {
1008 stmts,
1009 attributes,
1010 latency,
1011 } => {
1012 return build_static_seq(
1013 stmts, attributes, latency, builder, sig_ctx,
1014 );
1015 }
1016 ast::Control::StaticPar {
1017 stmts,
1018 attributes,
1019 latency,
1020 } => {
1021 return build_static_par(
1022 stmts, attributes, latency, builder, sig_ctx,
1023 );
1024 }
1025 ast::Control::StaticIf {
1026 port,
1027 tbranch,
1028 fbranch,
1029 attributes,
1030 latency,
1031 } => {
1032 return build_static_if(
1033 port, *tbranch, *fbranch, attributes, latency, builder, sig_ctx,
1034 );
1035 }
1036 ast::Control::StaticRepeat {
1037 attributes,
1038 num_repeats,
1039 body,
1040 } => {
1041 return build_static_repeat(
1042 num_repeats,
1043 *body,
1044 builder,
1045 attributes,
1046 sig_ctx,
1047 );
1048 }
1049 ast::Control::Par { .. }
1050 | ast::Control::If { .. }
1051 | ast::Control::While { .. }
1052 | ast::Control::Seq { .. }
1053 | ast::Control::Repeat { .. }
1054 | ast::Control::Invoke { .. } => {
1055 return Err(Error::malformed_control(
1056 "found dynamic control in static context".to_string(),
1057 )
1058 .with_pos(control.get_attributes()));
1059 }
1060 ast::Control::Empty { attributes } => {
1061 let mut emp = StaticControl::empty();
1062 *emp.get_mut_attributes() = attributes;
1063 emp
1064 }
1065 };
1066 Ok(sc)
1067}
1068
1069fn build_control(
1071 control: ast::Control,
1072 sig_ctx: &SigCtx,
1073 builder: &mut Builder,
1074) -> CalyxResult<Control> {
1075 let c = match control {
1076 ast::Control::Enable {
1077 comp: component,
1078 attributes,
1079 } => {
1080 if let Some(f) = builder.component.find_fsm(component) {
1081 let mut en = Control::fsm_enable(Rc::clone(&f));
1082 *en.get_mut_attributes() = attributes;
1083 en
1084 } else if let Some(g) = builder.component.find_group(component) {
1085 let mut en = Control::enable(Rc::clone(&g));
1086 *en.get_mut_attributes() = attributes;
1087 en
1088 } else {
1089 let mut en = Control::Static(StaticControl::from(Rc::clone(
1090 &builder
1091 .component
1092 .find_static_group(component)
1093 .ok_or_else(|| {
1094 Error::undefined(
1095 component,
1096 "group or fsm".to_string(),
1097 )
1098 .with_pos(&attributes)
1099 })?,
1100 )));
1101 *en.get_mut_attributes() = attributes;
1102 en
1103 }
1104 }
1105 ast::Control::StaticInvoke {
1106 comp,
1107 inputs,
1108 outputs,
1109 attributes,
1110 ref_cells,
1111 latency,
1112 comb_group,
1113 } => {
1114 let cell = Rc::clone(
1115 &builder.component.find_cell(comp).ok_or_else(|| {
1116 Error::undefined(comp, "cell".to_string())
1117 .with_pos(&attributes)
1118 })?,
1119 );
1120 let comp_name = cell.borrow().type_name().unwrap_or_else(|| {
1121 unreachable!("invoked component without a name")
1122 });
1123 let c_latency =
1124 get_comp_latency(sig_ctx, Rc::clone(&cell), &attributes)?;
1125 let unwrapped_latency = if let Some(v) = c_latency {
1126 v
1127 } else {
1128 return Err(Error::malformed_control(format!(
1129 "component {comp_name} is statically invoked, but is neither static nor does it have @interval attribute its @go port"
1130 ))
1131 .with_pos(&attributes));
1132 };
1133 assert_latencies_eq(latency, unwrapped_latency.into());
1134
1135 let inputs = inputs
1136 .into_iter()
1137 .map(|(id, port)| {
1138 check_valid_port(
1140 Rc::clone(&cell),
1141 &id,
1142 &attributes,
1143 sig_ctx,
1144 )?;
1145 atom_to_port(port, builder)
1146 .and_then(|pr| ensure_direction(pr, Direction::Output))
1147 .map(|p| (id, p))
1148 })
1149 .collect::<Result<_, _>>()?;
1150 let outputs = outputs
1151 .into_iter()
1152 .map(|(id, port)| {
1153 check_valid_port(
1155 Rc::clone(&cell),
1156 &id,
1157 &attributes,
1158 sig_ctx,
1159 )?;
1160 atom_to_port(port, builder)
1161 .and_then(|pr| ensure_direction(pr, Direction::Input))
1162 .map(|p| (id, p))
1163 })
1164 .collect::<Result<_, _>>()?;
1165 let mut inv = StaticInvoke {
1166 comp: cell,
1167 inputs,
1168 outputs,
1169 attributes: Attributes::default(),
1170 ref_cells: Vec::new(),
1171 latency: unwrapped_latency.into(),
1172 comb_group: None,
1173 };
1174 if !ref_cells.is_empty() {
1175 let mut ext_cell_tuples = Vec::new();
1176 for (outcell, incell) in ref_cells {
1177 let ext_cell_ref =
1178 builder.component.find_cell(incell).ok_or_else(
1179 || Error::undefined(incell, "cell".to_string()),
1180 )?;
1181 ext_cell_tuples.push((outcell, ext_cell_ref));
1182 }
1183 inv.ref_cells = ext_cell_tuples;
1184 }
1185 if let Some(cg) = comb_group {
1186 let cg_ref =
1187 builder.component.find_comb_group(cg).ok_or_else(|| {
1188 Error::undefined(cg, "combinational group".to_string())
1189 .with_pos(&inv.attributes)
1190 })?;
1191 inv.comb_group = Some(cg_ref);
1192 }
1193 let mut con = StaticControl::Invoke(inv);
1194 *con.get_mut_attributes() = attributes;
1195 Control::Static(con)
1196 }
1197 ast::Control::Invoke {
1198 comp: component,
1199 inputs,
1200 outputs,
1201 attributes,
1202 comb_group,
1203 ref_cells,
1204 } => {
1205 let cell = Rc::clone(
1206 &builder.component.find_cell(component).ok_or_else(|| {
1207 Error::undefined(component, "cell".to_string())
1208 .with_pos(&attributes)
1209 })?,
1210 );
1211
1212 let comp_name = cell.borrow().type_name().unwrap_or_else(|| {
1213 unreachable!("invoked component without a name")
1214 });
1215
1216 if get_static_latency(sig_ctx, Rc::clone(&cell), &attributes)?
1218 .is_some()
1219 {
1220 return Err(Error::malformed_control(format!(
1221 "static component {comp_name} is dynamically invoked"
1222 ))
1223 .with_pos(&attributes));
1224 }
1225
1226 let inputs = inputs
1227 .into_iter()
1228 .map(|(id, port)| {
1229 check_valid_port(
1231 Rc::clone(&cell),
1232 &id,
1233 &attributes,
1234 sig_ctx,
1235 )?;
1236 atom_to_port(port, builder)
1237 .and_then(|pr| ensure_direction(pr, Direction::Output))
1238 .map(|p| (id, p))
1239 })
1240 .collect::<Result<_, _>>()?;
1241 let outputs = outputs
1242 .into_iter()
1243 .map(|(id, port)| {
1244 check_valid_port(
1246 Rc::clone(&cell),
1247 &id,
1248 &attributes,
1249 sig_ctx,
1250 )?;
1251 atom_to_port(port, builder)
1252 .and_then(|pr| ensure_direction(pr, Direction::Input))
1253 .map(|p| (id, p))
1254 })
1255 .collect::<Result<_, _>>()?;
1256 let mut inv = Invoke {
1257 comp: cell,
1258 inputs,
1259 outputs,
1260 attributes,
1261 comb_group: None,
1262 ref_cells: Vec::new(),
1263 };
1264 if let Some(cg) = comb_group {
1265 let cg_ref =
1266 builder.component.find_comb_group(cg).ok_or_else(|| {
1267 Error::undefined(cg, "combinational group".to_string())
1268 .with_pos(&inv.attributes)
1269 })?;
1270 inv.comb_group = Some(cg_ref);
1271 }
1272 if !ref_cells.is_empty() {
1273 let mut ext_cell_tuples = Vec::new();
1274 for (outcell, incell) in ref_cells {
1275 let ext_cell_ref =
1276 builder.component.find_cell(incell).ok_or_else(
1277 || Error::undefined(incell, "cell".to_string()),
1278 )?;
1279 ext_cell_tuples.push((outcell, ext_cell_ref));
1280 }
1281 inv.ref_cells = ext_cell_tuples;
1282 }
1283 Control::Invoke(inv)
1284 }
1285 ast::Control::Seq { stmts, attributes } => {
1286 let mut s = Control::seq(
1287 stmts
1288 .into_iter()
1289 .map(|c| build_control(c, sig_ctx, builder))
1290 .collect::<CalyxResult<Vec<_>>>()?,
1291 );
1292 *s.get_mut_attributes() = attributes;
1293 s
1294 }
1295 ast::Control::StaticSeq {
1296 stmts,
1297 attributes,
1298 latency,
1299 } => {
1300 let s =
1301 build_static_seq(stmts, attributes, latency, builder, sig_ctx);
1302 Control::Static(s?)
1303 }
1304 ast::Control::StaticPar {
1305 stmts,
1306 attributes,
1307 latency,
1308 } => {
1309 let s =
1310 build_static_par(stmts, attributes, latency, builder, sig_ctx);
1311 Control::Static(s?)
1312 }
1313 ast::Control::StaticIf {
1314 port,
1315 tbranch,
1316 fbranch,
1317 attributes,
1318 latency,
1319 } => {
1320 let s = build_static_if(
1321 port, *tbranch, *fbranch, attributes, latency, builder, sig_ctx,
1322 );
1323 Control::Static(s?)
1324 }
1325 ast::Control::StaticRepeat {
1326 attributes,
1327 num_repeats,
1328 body,
1329 } => {
1330 let s = build_static_repeat(
1331 num_repeats,
1332 *body,
1333 builder,
1334 attributes,
1335 sig_ctx,
1336 );
1337 Control::Static(s?)
1338 }
1339 ast::Control::Par { stmts, attributes } => {
1340 let mut p = Control::par(
1341 stmts
1342 .into_iter()
1343 .map(|c| build_control(c, sig_ctx, builder))
1344 .collect::<CalyxResult<Vec<_>>>()?,
1345 );
1346 *p.get_mut_attributes() = attributes;
1347 p
1348 }
1349 ast::Control::If {
1350 port,
1351 cond: maybe_cond,
1352 tbranch,
1353 fbranch,
1354 attributes,
1355 } => {
1356 let group = maybe_cond
1357 .map(|cond| {
1358 builder.component.find_comb_group(cond).ok_or_else(|| {
1359 Error::undefined(
1360 cond,
1361 "combinational group".to_string(),
1362 )
1363 .with_pos(&attributes)
1364 })
1365 })
1366 .transpose()?;
1367 let mut con = Control::if_(
1368 ensure_direction(
1369 get_port_ref(port, builder.component)?,
1370 Direction::Output,
1371 )?,
1372 group,
1373 Box::new(build_control(*tbranch, sig_ctx, builder)?),
1374 Box::new(build_control(*fbranch, sig_ctx, builder)?),
1375 );
1376 *con.get_mut_attributes() = attributes;
1377 con
1378 }
1379 ast::Control::While {
1380 port,
1381 cond: maybe_cond,
1382 body,
1383 attributes,
1384 } => {
1385 let group = maybe_cond
1386 .map(|cond| {
1387 builder.component.find_comb_group(cond).ok_or_else(|| {
1388 Error::undefined(
1389 cond,
1390 "combinational group".to_string(),
1391 )
1392 .with_pos(&attributes)
1393 })
1394 })
1395 .transpose()?;
1396 let mut con = Control::while_(
1397 ensure_direction(
1398 get_port_ref(port, builder.component)?,
1399 Direction::Output,
1400 )?,
1401 group,
1402 Box::new(build_control(*body, sig_ctx, builder)?),
1403 );
1404 *con.get_mut_attributes() = attributes;
1405 con
1406 }
1407 ast::Control::Repeat {
1408 num_repeats,
1409 body,
1410 attributes,
1411 } => {
1412 let mut con = Control::repeat(
1413 num_repeats,
1414 Box::new(build_control(*body, sig_ctx, builder)?),
1415 );
1416 *con.get_mut_attributes() = attributes;
1417 con
1418 }
1419 ast::Control::Empty { attributes } => {
1420 let mut emp = Control::empty();
1421 *emp.get_mut_attributes() = attributes;
1422 emp
1423 }
1424 };
1425 Ok(c)
1426}