1use std::{
2 cmp,
3 collections::{BTreeMap, BTreeSet, HashMap},
4};
5
6use crate::traversal::{
7 Action, ConstructVisitor, Named, ParseVal, PassOpt, VisResult, Visitor,
8};
9use calyx_frontend::SetAttr;
10use calyx_ir::{self as ir, Nothing};
11use calyx_utils::{CalyxResult, Id, OutputFile};
12use serde::Serialize;
13
14const UNIQUE_GROUP_SUFFIX: &str = "UG";
22
23pub struct UniquefyEnables {
24 path_descriptor_json: Option<OutputFile>,
25 path_descriptor_infos: BTreeMap<String, PathDescriptorInfo>,
26 par_thread_json: Option<OutputFile>,
27 par_thread_info: BTreeMap<String, BTreeMap<String, u32>>,
28 invoke_cell_to_counter: HashMap<Id, i64>,
31}
32
33impl Named for UniquefyEnables {
34 fn name() -> &'static str {
35 "uniquefy-enables"
36 }
37
38 fn description() -> &'static str {
39 "Make all control (dynamic and static) enables unique."
40 }
41
42 fn opts() -> Vec<crate::traversal::PassOpt> {
43 vec![
44 PassOpt::new(
45 "path-descriptor-json",
46 "Write the path descriptor of each enable and par to a JSON file",
47 ParseVal::OutStream(OutputFile::Null),
48 PassOpt::parse_outstream,
49 ),
50 PassOpt::new(
51 "par-thread-json",
52 "Write an assigned thread ID of each enable to a JSON file",
53 ParseVal::OutStream(OutputFile::Null),
54 PassOpt::parse_outstream,
55 ),
56 ]
57 }
58}
59
60#[derive(Serialize)]
62struct PathDescriptorInfo {
63 pub enables: BTreeMap<String, String>,
65 pub control_pos: BTreeMap<String, BTreeSet<u32>>,
69}
70
71impl ConstructVisitor for UniquefyEnables {
72 fn from(ctx: &ir::Context) -> CalyxResult<Self>
73 where
74 Self: Sized + Named,
75 {
76 let opts = Self::get_opts(ctx);
77 Ok(UniquefyEnables {
78 path_descriptor_json: opts[&"path-descriptor-json"]
79 .not_null_outstream(),
80 path_descriptor_infos: BTreeMap::new(),
81 par_thread_json: opts[&"par-thread-json"].not_null_outstream(),
82 par_thread_info: BTreeMap::new(),
83 invoke_cell_to_counter: HashMap::new(),
84 })
85 }
86
87 fn clear_data(&mut self) {
88 self.invoke_cell_to_counter = HashMap::new();
89 }
90}
91
92fn assign_par_threads_static(
93 control: &ir::StaticControl,
94 start_idx: u32,
95 next_idx: u32,
96 enable_to_track: &mut BTreeMap<String, u32>,
97) -> u32 {
98 match control {
99 ir::StaticControl::Repeat(ir::StaticRepeat { body, .. }) => {
100 assign_par_threads_static(
101 body,
102 start_idx,
103 next_idx,
104 enable_to_track,
105 )
106 }
107 ir::StaticControl::Enable(ir::StaticEnable { group, .. }) => {
108 let group_name = group.borrow().name().to_string();
109 enable_to_track.insert(group_name, start_idx);
110 start_idx + 1
111 }
112 ir::StaticControl::Par(ir::StaticPar { stmts, .. }) => {
113 let mut idx = next_idx;
114 for stmt in stmts {
115 idx = assign_par_threads_static(
116 stmt,
117 idx,
118 idx + 1,
119 enable_to_track,
120 );
121 }
122 idx
123 }
124 ir::StaticControl::Seq(ir::StaticSeq { stmts, .. }) => {
125 let mut new_next_idx = next_idx;
126 for stmt in stmts {
127 let potential_new_idx = assign_par_threads_static(
128 stmt,
129 start_idx,
130 new_next_idx,
131 enable_to_track,
132 );
133 new_next_idx = cmp::max(new_next_idx, potential_new_idx)
134 }
135 new_next_idx
136 }
137 ir::StaticControl::If(ir::StaticIf {
138 tbranch, fbranch, ..
139 }) => {
140 let false_next_idx = assign_par_threads_static(
141 tbranch,
142 start_idx,
143 next_idx,
144 enable_to_track,
145 );
146 assign_par_threads_static(
147 fbranch,
148 start_idx,
149 false_next_idx,
150 enable_to_track,
151 )
152 }
153 ir::StaticControl::Invoke(ir::StaticInvoke { comb_group, .. }) => {
154 let c_group_name =
155 comb_group.as_ref().unwrap().borrow().name().to_string();
156 enable_to_track.insert(c_group_name, start_idx);
157 start_idx + 1
158 }
159 _ => next_idx,
160 }
161}
162
163fn assign_par_threads(
164 control: &ir::Control,
165 start_idx: u32,
166 next_idx: u32,
167 enable_to_track: &mut BTreeMap<String, u32>,
168) -> u32 {
169 match control {
170 ir::Control::Seq(ir::Seq { stmts, .. }) => {
171 let mut new_next_idx = next_idx;
172 for stmt in stmts {
173 let potential_new_idx = assign_par_threads(
174 stmt,
175 start_idx,
176 new_next_idx,
177 enable_to_track,
178 );
179 new_next_idx = cmp::max(new_next_idx, potential_new_idx)
180 }
181 new_next_idx
182 }
183 ir::Control::Enable(enable) => {
184 let group_name = enable.group.borrow().name().to_string();
185 enable_to_track.insert(group_name, start_idx);
186 start_idx + 1
187 }
188 ir::Control::Par(ir::Par { stmts, .. }) => {
189 let mut idx = next_idx;
190 for stmt in stmts {
191 idx = assign_par_threads(stmt, idx, idx + 1, enable_to_track);
192 }
193 idx
194 }
195 ir::Control::If(ir::If {
196 tbranch,
197 fbranch,
198 cond,
199 ..
200 }) => {
201 let true_next_idx = if let Some(comb_group) = cond {
202 enable_to_track
203 .insert(comb_group.borrow().name().to_string(), start_idx);
204 start_idx + 1
205 } else {
206 start_idx
207 };
208 let false_next_idx = assign_par_threads(
209 tbranch,
210 true_next_idx,
211 next_idx,
212 enable_to_track,
213 );
214 assign_par_threads(
215 fbranch,
216 start_idx,
217 false_next_idx,
218 enable_to_track,
219 )
220 }
221 ir::Control::While(ir::While { body, cond, .. }) => {
222 let body_start_idx = if let Some(comb_group) = cond {
223 enable_to_track
224 .insert(comb_group.borrow().name().to_string(), start_idx);
225 start_idx + 1
226 } else {
227 start_idx
228 };
229 assign_par_threads(body, body_start_idx, next_idx, enable_to_track)
230 }
231 ir::Control::Repeat(ir::Repeat { body, .. }) => {
232 assign_par_threads(body, start_idx, next_idx, enable_to_track)
233 }
234 ir::Control::Static(static_control) => assign_par_threads_static(
235 static_control,
236 start_idx,
237 next_idx,
238 enable_to_track,
239 ),
240 ir::Control::Invoke(ir::Invoke { comb_group, .. }) => {
241 let c_group_name =
242 comb_group.as_ref().unwrap().borrow().name().to_string();
243 enable_to_track.insert(c_group_name, start_idx);
244 start_idx + 1
245 }
246 _ => next_idx,
247 }
248}
249
250fn compute_path_descriptors_static(
251 control: &ir::StaticControl,
252 current_id: String,
253 path_descriptor_info: &mut PathDescriptorInfo,
254 parent_is_component: bool,
255) {
256 match control {
257 ir::StaticControl::Repeat(ir::StaticRepeat {
258 body,
259 attributes,
260 ..
261 }) => {
262 let repeat_id = format!("{current_id}-");
263 let body_id = format!("{repeat_id}b");
264 compute_path_descriptors_static(
265 body,
266 body_id,
267 path_descriptor_info,
268 false,
269 );
270 let new_pos_set = retrieve_pos_set(attributes);
271 path_descriptor_info
272 .control_pos
273 .insert(repeat_id, new_pos_set);
274 }
275 ir::StaticControl::Enable(ir::StaticEnable { group, .. }) => {
276 let group_id = if parent_is_component {
277 format!("{current_id}0")
279 } else {
280 current_id
281 };
282 let group_name = group.borrow().name();
283 path_descriptor_info
284 .enables
285 .insert(group_name.to_string(), group_id);
286 }
287 ir::StaticControl::Invoke(ir::StaticInvoke { comb_group, .. }) => {
288 let invoke_id = if parent_is_component {
290 format!("{current_id}0")
291 } else {
292 current_id
293 };
294 let comb_group_name = comb_group.as_ref().unwrap().borrow().name();
295 path_descriptor_info
296 .enables
297 .insert(comb_group_name.to_string(), invoke_id);
298 }
299 ir::StaticControl::Par(ir::StaticPar {
300 stmts, attributes, ..
301 }) => {
302 let par_id = format!("{current_id}-");
303 for (acc, stmt) in stmts.iter().enumerate() {
304 let stmt_id = format!("{par_id}{acc}");
305 compute_path_descriptors_static(
306 stmt,
307 stmt_id,
308 path_descriptor_info,
309 false,
310 );
311 }
312 let new_pos_set: BTreeSet<u32> = retrieve_pos_set(attributes);
313 path_descriptor_info.control_pos.insert(par_id, new_pos_set);
314 }
315 ir::StaticControl::Seq(ir::StaticSeq {
316 stmts, attributes, ..
317 }) => {
318 let seq_id = format!("{current_id}-");
319 for (acc, stmt) in stmts.iter().enumerate() {
320 let stmt_id = format!("{seq_id}{acc}");
321 compute_path_descriptors_static(
322 stmt,
323 stmt_id,
324 path_descriptor_info,
325 false,
326 );
327 }
328 let new_pos_set: BTreeSet<u32> = retrieve_pos_set(attributes);
329 path_descriptor_info.control_pos.insert(seq_id, new_pos_set);
330 }
331 ir::StaticControl::If(ir::StaticIf {
332 tbranch,
333 fbranch,
334 attributes,
335 ..
336 }) => {
337 let if_id = format!("{current_id}-");
338 let true_id = format!("{if_id}t");
340 compute_path_descriptors_static(
341 tbranch,
342 true_id,
343 path_descriptor_info,
344 false,
345 );
346 let false_id = format!("{if_id}f");
348 compute_path_descriptors_static(
349 fbranch,
350 false_id,
351 path_descriptor_info,
352 false,
353 );
354 path_descriptor_info
355 .control_pos
356 .insert(if_id, retrieve_pos_set(attributes));
357 }
358 ir::StaticControl::Empty(_empty) => (),
359 }
360}
361
362fn compute_path_descriptors(
363 control: &ir::Control,
364 current_id: String,
365 path_descriptor_info: &mut PathDescriptorInfo,
366 parent_is_component: bool,
367) {
368 match control {
369 ir::Control::Seq(ir::Seq {
370 stmts, attributes, ..
371 }) => {
372 let seq_id = format!("{current_id}-");
373 for (acc, stmt) in stmts.iter().enumerate() {
374 let stmt_id = format!("{current_id}-{acc}");
375 compute_path_descriptors(
376 stmt,
377 stmt_id,
378 path_descriptor_info,
379 false,
380 );
381 }
382 let new_pos_set = retrieve_pos_set(attributes);
383 path_descriptor_info.control_pos.insert(seq_id, new_pos_set);
384 }
385 ir::Control::Par(ir::Par {
386 stmts, attributes, ..
387 }) => {
388 let par_id = format!("{current_id}-");
389 for (acc, stmt) in stmts.iter().enumerate() {
390 let stmt_id = format!("{par_id}{acc}");
391 compute_path_descriptors(
392 stmt,
393 stmt_id,
394 path_descriptor_info,
395 false,
396 );
397 }
398 let new_pos_set = retrieve_pos_set(attributes);
400 path_descriptor_info.control_pos.insert(par_id, new_pos_set);
401 }
402 ir::Control::If(ir::If {
403 tbranch,
404 fbranch,
405 attributes,
406 cond,
407 ..
408 }) => {
409 let if_id = format!("{current_id}-");
410 if let Some(comb_group) = cond {
412 let comb_id = format!("{if_id}c");
413 path_descriptor_info
414 .enables
415 .insert(comb_group.borrow().name().to_string(), comb_id);
416 }
417
418 let true_id = format!("{if_id}t");
420 compute_path_descriptors(
421 tbranch,
422 true_id,
423 path_descriptor_info,
424 false,
425 );
426 let false_id = format!("{if_id}f");
428 compute_path_descriptors(
429 fbranch,
430 false_id,
431 path_descriptor_info,
432 false,
433 );
434 let new_pos_set = retrieve_pos_set(attributes);
436 path_descriptor_info.control_pos.insert(if_id, new_pos_set);
437 }
438 ir::Control::While(ir::While {
439 body,
440 attributes,
441 cond,
442 ..
443 }) => {
444 let while_id = format!("{current_id}-");
445 let body_id = format!("{while_id}b");
446 if let Some(comb_group) = cond {
450 let comb_id = format!("{while_id}c");
451 path_descriptor_info
452 .enables
453 .insert(comb_group.borrow().name().to_string(), comb_id);
454 }
455 compute_path_descriptors(
456 body,
457 body_id,
458 path_descriptor_info,
459 false,
460 );
461 let new_pos_set = retrieve_pos_set(attributes);
463 path_descriptor_info
464 .control_pos
465 .insert(while_id, new_pos_set);
466 }
467 ir::Control::Enable(ir::Enable { group, .. }) => {
468 let group_id = if parent_is_component {
469 format!("{current_id}0")
471 } else {
472 current_id
473 };
474 let group_name = group.borrow().name();
475 path_descriptor_info
476 .enables
477 .insert(group_name.to_string(), group_id);
478 }
479 ir::Control::Invoke(ir::Invoke { comb_group, .. }) => {
480 let invoke_id = if parent_is_component {
481 format!("{current_id}0")
483 } else {
484 current_id
485 };
486 let comb_group_name =
487 comb_group.as_ref().unwrap().borrow().name().to_string();
488 path_descriptor_info
489 .enables
490 .insert(comb_group_name, invoke_id);
491 }
492 ir::Control::Repeat(ir::Repeat {
493 body, attributes, ..
494 }) => {
495 let repeat_id = format!("{current_id}-");
496 let body_id = format!("{repeat_id}b");
497 compute_path_descriptors(
498 body,
499 body_id,
500 path_descriptor_info,
501 false,
502 );
503 let new_pos_set = retrieve_pos_set(attributes);
505 path_descriptor_info
506 .control_pos
507 .insert(repeat_id, new_pos_set);
508 }
509 ir::Control::Static(static_control) => {
510 compute_path_descriptors_static(
511 static_control,
512 current_id,
513 path_descriptor_info,
514 parent_is_component,
515 );
516 }
517 ir::Control::Empty(_) => (),
518 ir::Control::FSMEnable(_) => todo!(),
519 }
520}
521
522fn retrieve_pos_set(attributes: &calyx_ir::Attributes) -> BTreeSet<u32> {
524 let new_pos_set: BTreeSet<u32> =
525 if let Some(pos_set) = attributes.get_set(SetAttr::Pos) {
526 pos_set.iter().copied().collect()
527 } else {
528 BTreeSet::new()
529 };
530 new_pos_set
531}
532
533fn create_unique_comb_group(
536 cond: &Option<std::rc::Rc<std::cell::RefCell<calyx_ir::CombGroup>>>,
537 comp: &mut calyx_ir::Component,
538 sigs: &calyx_ir::LibrarySignatures,
539) -> Option<std::rc::Rc<std::cell::RefCell<calyx_ir::CombGroup>>> {
540 if let Some(comb_group) = cond {
541 let unique_comb_group_name: String =
542 format!("{}{}", comb_group.borrow().name(), UNIQUE_GROUP_SUFFIX);
543 let mut builder = ir::Builder::new(comp, sigs);
544 let unique_comb_group = builder.add_comb_group(unique_comb_group_name);
545 unique_comb_group.borrow_mut().assignments =
546 comb_group.borrow().assignments.clone();
547 unique_comb_group.borrow_mut().attributes =
548 comb_group.borrow().attributes.clone();
549 Some(unique_comb_group)
550 } else {
551 None
552 }
553}
554
555impl Visitor for UniquefyEnables {
556 fn finish_while(
557 &mut self,
558 s: &mut calyx_ir::While,
559 comp: &mut calyx_ir::Component,
560 sigs: &calyx_ir::LibrarySignatures,
561 _comps: &[calyx_ir::Component],
562 ) -> VisResult {
563 s.cond = create_unique_comb_group(&s.cond, comp, sigs);
565 Ok(Action::Continue)
566 }
567
568 fn finish_if(
569 &mut self,
570 s: &mut calyx_ir::If,
571 comp: &mut calyx_ir::Component,
572 sigs: &calyx_ir::LibrarySignatures,
573 _comps: &[calyx_ir::Component],
574 ) -> VisResult {
575 s.cond = create_unique_comb_group(&s.cond, comp, sigs);
577 Ok(Action::Continue)
578 }
579
580 fn enable(
581 &mut self,
582 s: &mut calyx_ir::Enable,
583 comp: &mut calyx_ir::Component,
584 sigs: &calyx_ir::LibrarySignatures,
585 _comps: &[calyx_ir::Component],
586 ) -> VisResult {
587 let group_name = s.group.borrow().name();
589 let unique_group_name: String =
591 format!("{group_name}{UNIQUE_GROUP_SUFFIX}");
592 let mut builder = ir::Builder::new(comp, sigs);
594 let unique_group = builder.add_group(unique_group_name);
595 let mut unique_group_assignments: Vec<calyx_ir::Assignment<Nothing>> =
596 Vec::new();
597 for asgn in s.group.borrow().assignments.iter() {
598 if asgn.dst.borrow().get_parent_name() == group_name
599 && asgn.dst.borrow().name == "done"
600 {
601 let new_done_asgn = builder.build_assignment(
603 unique_group.borrow().get("done"),
604 asgn.src.clone(),
605 *asgn.guard.clone(),
606 );
607 unique_group_assignments.push(new_done_asgn);
608 } else {
609 unique_group_assignments.push(asgn.clone());
610 }
611 }
612 unique_group
613 .borrow_mut()
614 .assignments
615 .append(&mut unique_group_assignments);
616 unique_group.borrow_mut().attributes =
618 s.group.borrow().attributes.clone();
619 Ok(Action::Change(Box::new(ir::Control::enable(unique_group))))
620 }
621
622 fn static_enable(
623 &mut self,
624 s: &mut calyx_ir::StaticEnable,
625 comp: &mut calyx_ir::Component,
626 sigs: &calyx_ir::LibrarySignatures,
627 _comps: &[calyx_ir::Component],
628 ) -> VisResult {
629 let group_name = s.group.borrow().name();
631 let unique_group_name = format!("{group_name}{UNIQUE_GROUP_SUFFIX}");
632 let mut builder = ir::Builder::new(comp, sigs);
634 let unique_group = builder.add_static_group(
635 unique_group_name,
636 s.group.borrow().get_latency(),
637 );
638 unique_group.borrow_mut().assignments =
641 s.group.borrow().assignments.clone();
642 unique_group.borrow_mut().attributes =
644 s.group.borrow().attributes.clone();
645 Ok(Action::Change(Box::new(ir::Control::static_enable(
646 unique_group,
647 ))))
648 }
649
650 fn invoke(
651 &mut self,
652 s: &mut calyx_ir::Invoke,
653 comp: &mut calyx_ir::Component,
654 sigs: &calyx_ir::LibrarySignatures,
655 _comps: &[calyx_ir::Component],
656 ) -> VisResult {
657 let mut builder = ir::Builder::new(comp, sigs);
659 let invoked_cell_name = s.comp.borrow().name();
660 let invoke_id = if let Some(comb_group_ref) = &s.comb_group {
661 Id::new(format!(
662 "{}_{}",
663 invoked_cell_name,
664 comb_group_ref.borrow().name()
665 ))
666 } else {
667 invoked_cell_name
668 };
669 let internal_counter =
671 self.invoke_cell_to_counter.get(&invoke_id).unwrap_or(&0);
672 let comb_cell_prefix = format!(
673 "invoke_{invoke_id}{internal_counter}{UNIQUE_GROUP_SUFFIX}"
674 );
675 let new_comb_group = builder.add_comb_group(comb_cell_prefix);
676 self.invoke_cell_to_counter
678 .insert(invoked_cell_name, *internal_counter + 1);
679 if let Some(comb_group_ref) = &s.comb_group {
680 for asgn in &comb_group_ref.borrow().assignments {
682 new_comb_group.borrow_mut().assignments.push(asgn.clone());
683 }
684 }
685 s.comb_group = Some(new_comb_group);
686 Ok(Action::Continue)
687 }
688
689 fn static_invoke(
690 &mut self,
691 s: &mut calyx_ir::StaticInvoke,
692 comp: &mut calyx_ir::Component,
693 sigs: &calyx_ir::LibrarySignatures,
694 _comps: &[calyx_ir::Component],
695 ) -> VisResult {
696 let mut builder = ir::Builder::new(comp, sigs);
698 let invoked_cell_name = s.comp.borrow().name();
699 let internal_counter = self
701 .invoke_cell_to_counter
702 .get(&invoked_cell_name)
703 .unwrap_or(&0);
704 let comb_cell_prefix = format!(
705 "invoke_{invoked_cell_name}{internal_counter}{UNIQUE_GROUP_SUFFIX}"
706 );
707 self.invoke_cell_to_counter
709 .insert(invoked_cell_name, *internal_counter + 1);
710 let new_comb_group = builder.add_comb_group(comb_cell_prefix);
711 if let Some(comb_group_ref) = &s.comb_group {
712 for asgn in &comb_group_ref.borrow().assignments {
714 new_comb_group.borrow_mut().assignments.push(asgn.clone());
715 }
716 }
717 s.comb_group = Some(new_comb_group);
718 Ok(Action::Continue)
719 }
720
721 fn finish(
722 &mut self,
723 comp: &mut calyx_ir::Component,
724 _sigs: &calyx_ir::LibrarySignatures,
725 _comps: &[calyx_ir::Component],
726 ) -> VisResult {
727 let control = comp.control.borrow();
729 let mut path_descriptor_info = PathDescriptorInfo {
730 enables: BTreeMap::new(),
731 control_pos: BTreeMap::new(),
732 };
733 compute_path_descriptors(
734 &control,
735 format!("{}.", comp.name),
736 &mut path_descriptor_info,
737 true,
738 );
739 self.path_descriptor_infos
740 .insert(comp.name.to_string(), path_descriptor_info);
741 let mut enable_to_track: BTreeMap<String, u32> = BTreeMap::new();
743 assign_par_threads(&control, 0, 1, &mut enable_to_track);
744 self.par_thread_info
745 .insert(comp.name.to_string(), enable_to_track);
746 Ok(Action::Continue)
747 }
748
749 fn finish_context(&mut self, _ctx: &mut calyx_ir::Context) -> VisResult {
750 if let Some(json_out_file) = &mut self.path_descriptor_json {
752 let _ = serde_json::to_writer_pretty(
753 json_out_file.get_write(),
754 &self.path_descriptor_infos,
755 );
756 }
757 if let Some(json_out_file) = &mut self.par_thread_json {
759 let _ = serde_json::to_writer_pretty(
760 json_out_file.get_write(),
761 &self.par_thread_info,
762 );
763 }
764 Ok(Action::Continue)
765 }
766}