1use core::panic;
2use std::collections::{HashMap, HashSet};
3
4use crate::traversal::{Action, ConstructVisitor, Named, VisResult, Visitor};
5use calyx_ir::{self as ir, BoolAttr, Guard, Id, Nothing, NumAttr};
6use calyx_utils::CalyxResult;
7
8pub struct ProfilerInstrumentation {}
11
12type CallsFromGroupMap<T> = HashMap<Id, Vec<(Id, ir::Guard<T>)>>;
15
16impl Named for ProfilerInstrumentation {
17 fn name() -> &'static str {
18 "profiler-instrumentation"
19 }
20
21 fn description() -> &'static str {
22 "Add instrumentation for profiling"
23 }
24
25 fn opts() -> Vec<crate::traversal::PassOpt> {
26 vec![]
27 }
28}
29
30impl ConstructVisitor for ProfilerInstrumentation {
31 fn from(_ctx: &ir::Context) -> CalyxResult<Self>
32 where
33 Self: Sized + Named,
34 {
35 Ok(ProfilerInstrumentation {})
36 }
37
38 fn clear_data(&mut self) {}
39}
40
41fn group(comp: &mut ir::Component, sigs: &ir::LibrarySignatures) {
43 let mut structural_enable_map: CallsFromGroupMap<Nothing> = HashMap::new();
45 let mut cell_invoke_map: CallsFromGroupMap<Nothing> = HashMap::new();
47 let mut primitive_invoke_map: CallsFromGroupMap<Nothing> = HashMap::new();
49 let group_names = comp
50 .groups
51 .iter()
52 .map(|group| group.borrow().name())
53 .collect::<Vec<_>>();
54
55 for group_ref in comp.groups.iter() {
57 let group = &group_ref.borrow();
58 let mut comb_primitives_covered = HashSet::new();
60 let mut primitive_vec: Vec<(Id, ir::Guard<Nothing>)> = Vec::new();
61 for assignment_ref in group.assignments.iter() {
62 let dst_borrow = assignment_ref.dst.borrow();
63 if let ir::PortParent::Group(parent_group_ref) = &dst_borrow.parent
64 {
65 if dst_borrow.name == "go" {
66 let invoked_group_name =
68 parent_group_ref.upgrade().borrow().name();
69 let guard = *(assignment_ref.guard.clone());
70 match structural_enable_map.get_mut(&invoked_group_name) {
71 Some(vec_ref) => vec_ref.push((group.name(), guard)),
72 None => {
73 structural_enable_map.insert(
74 invoked_group_name,
75 vec![(group.name(), guard)],
76 );
77 }
78 }
79 }
80 }
81 if let ir::PortParent::Cell(cell_ref) = &dst_borrow.parent {
82 match cell_ref.upgrade().borrow().prototype.clone() {
83 calyx_ir::CellType::Primitive {
84 name: _,
85 param_binding: _,
86 is_comb,
87 latency: _,
88 } => {
89 let cell_name = cell_ref.upgrade().borrow().name();
90 if is_comb {
91 if comb_primitives_covered.insert(cell_name) {
93 primitive_vec.push((cell_name, Guard::True));
94 }
95 } else if dst_borrow.has_attribute(NumAttr::Go) {
96 let guard = Guard::and(
98 *(assignment_ref.guard.clone()),
99 Guard::port(ir::rrc(
100 assignment_ref.src.borrow().clone(),
101 )),
102 );
103 primitive_vec.push((cell_name, guard));
104 }
105 }
106 calyx_ir::CellType::Component { name: _ } => {
107 if dst_borrow.has_attribute(NumAttr::Go) {
108 let cell_name = cell_ref.upgrade().borrow().name();
109 let guard = *(assignment_ref.guard.clone());
110 match cell_invoke_map.get_mut(&group.name()) {
111 Some(vec_ref) => {
112 vec_ref.push((cell_name, guard));
113 }
114 None => {
115 cell_invoke_map.insert(
116 group.name(),
117 vec![(cell_name, guard)],
118 );
119 }
120 }
121 }
122 }
123 _ => (),
124 }
125 }
126 }
127 primitive_invoke_map.insert(group_ref.borrow().name(), primitive_vec);
128 }
129
130 let group_name_assign_and_cell = create_assignments(
132 comp,
133 sigs,
134 &group_names,
135 Some(structural_enable_map),
136 Some(cell_invoke_map),
137 Some(primitive_invoke_map),
138 );
139
140 for group in comp.groups.iter() {
142 for (group_name, asgn, cell) in group_name_assign_and_cell.iter() {
143 if group.borrow().name() == group_name {
144 group.borrow_mut().assignments.push(asgn.clone());
145 comp.cells.add(cell.to_owned());
146 }
147 }
148 }
149}
150
151fn combinational_group(comp: &mut ir::Component, sigs: &ir::LibrarySignatures) {
153 let mut cell_invoke_map: CallsFromGroupMap<Nothing> = HashMap::new();
157 let mut primitive_invoke_map: CallsFromGroupMap<Nothing> = HashMap::new();
159
160 let group_names = comp
161 .comb_groups
162 .iter()
163 .map(|group| group.borrow().name())
164 .collect::<Vec<_>>();
165
166 for group_ref in comp.comb_groups.iter() {
167 let group = &group_ref.borrow();
168 let mut comb_primitives_covered = HashSet::new();
169 let mut comb_cells_covered = HashSet::new();
170
171 for assignment_ref in group.assignments.iter() {
172 let dst_borrow = assignment_ref.dst.borrow();
173 if let ir::PortParent::Cell(cell_ref) = &dst_borrow.parent {
174 match cell_ref.upgrade().borrow().prototype.clone() {
175 calyx_ir::CellType::Primitive {
176 name: _,
177 param_binding: _,
178 is_comb,
179 latency: _,
180 } => {
181 let cell_name = cell_ref.upgrade().borrow().name();
182 if is_comb {
183 if comb_primitives_covered.insert(cell_name) {
185 match primitive_invoke_map
186 .get_mut(&group.name())
187 {
188 Some(vec_ref) => {
189 vec_ref.push((cell_name, Guard::True));
190 }
191 None => {
192 primitive_invoke_map.insert(
193 group.name(),
194 vec![(cell_name, Guard::True)],
195 );
196 }
197 }
198 }
199 } else if dst_borrow.has_attribute(NumAttr::Go) {
200 panic!(
201 "Non-combinational primitive {} invoked inside of combinational group {}!",
202 dst_borrow.canonical(),
203 group.name()
204 )
205 }
206 }
207 calyx_ir::CellType::Component { name: _ } => {
208 let cell_name = cell_ref.upgrade().borrow().name();
209 if dst_borrow.name == "go" {
210 panic!(
211 "Non-combinational cell {} invoked inside of combinational group {}!",
212 cell_name,
213 group.name()
214 );
215 } else if comb_cells_covered.insert(cell_name) {
216 let guard = *(assignment_ref.guard.clone());
217 match cell_invoke_map.get_mut(&group.name()) {
218 Some(vec_ref) => {
219 vec_ref.push((cell_name, guard));
220 }
221 None => {
222 cell_invoke_map.insert(
223 group.name(),
224 vec![(cell_name, guard)],
225 );
226 }
227 }
228 }
229 }
230 _ => (),
231 }
232 }
233 }
234 }
235
236 let group_name_asgn_and_cell = create_assignments(
237 comp,
238 sigs,
239 &group_names,
240 None, Some(cell_invoke_map),
242 Some(primitive_invoke_map),
243 );
244
245 for comb_group in comp.comb_groups.iter() {
247 for (comb_group_name, asgn, cell) in group_name_asgn_and_cell.iter() {
248 if comb_group.borrow().name() == comb_group_name {
249 comb_group.borrow_mut().assignments.push(asgn.clone());
250 comp.cells.add(cell.to_owned());
251 }
252 }
253 }
254}
255
256fn static_group(comp: &mut ir::Component, sigs: &ir::LibrarySignatures) {
258 let group_names = comp
259 .static_groups
260 .iter()
261 .map(|group| group.borrow().name())
262 .collect::<Vec<_>>();
263
264 let mut structural_enable_map: CallsFromGroupMap<ir::StaticTiming> =
266 HashMap::new();
267 let mut cell_invoke_map: CallsFromGroupMap<ir::StaticTiming> =
269 HashMap::new();
270 let mut primitive_invoke_map: CallsFromGroupMap<ir::StaticTiming> =
272 HashMap::new();
273
274 for group_ref in comp.static_groups.iter() {
275 let group = &group_ref.borrow();
276 let mut comb_primitives_covered = HashSet::new();
278 let mut primitive_vec: Vec<(Id, ir::Guard<ir::StaticTiming>)> =
279 Vec::new();
280 for assignment_ref in group.assignments.iter() {
281 let dst_borrow = assignment_ref.dst.borrow();
282 if let ir::PortParent::Group(parent_group_ref) = &dst_borrow.parent
283 {
284 if dst_borrow.name == "go" {
285 let invoked_group_name =
287 parent_group_ref.upgrade().borrow().name();
288 let guard = *(assignment_ref.guard).clone();
289 structural_enable_map
290 .entry(invoked_group_name)
291 .or_default()
292 .push((group.name(), guard));
293 }
294 }
295 if let ir::PortParent::Cell(cell_ref) = &dst_borrow.parent {
296 match cell_ref.upgrade().borrow().prototype.clone() {
297 calyx_ir::CellType::Primitive { is_comb, .. } => {
298 let cell_name = cell_ref.upgrade().borrow().name();
299 if is_comb {
300 if comb_primitives_covered.insert(cell_name) {
302 primitive_vec.push((cell_name, Guard::True));
303 }
304 } else if dst_borrow.has_attribute(NumAttr::Go) {
305 let guard = Guard::and(
307 *(assignment_ref.guard).clone(),
308 Guard::port(ir::rrc(
309 assignment_ref.src.borrow().clone(),
310 )),
311 );
312 primitive_vec.push((cell_name, guard));
313 }
314 }
315 calyx_ir::CellType::Component { name: _ } => {
316 if dst_borrow.has_attribute(NumAttr::Go) {
317 let cell_name = cell_ref.upgrade().borrow().name();
318 let guard = *(assignment_ref.guard.clone());
319 cell_invoke_map
320 .entry(group.name())
321 .or_default()
322 .push((cell_name, guard));
323 }
324 }
325 _ => (),
326 }
327 }
328 }
329 primitive_invoke_map.insert(group_ref.borrow().name(), primitive_vec);
330 }
331
332 let group_name_assign_and_cell = create_assignments(
333 comp,
334 sigs,
335 &group_names,
336 Some(structural_enable_map),
337 Some(cell_invoke_map),
338 Some(primitive_invoke_map),
339 );
340
341 for static_group in comp.static_groups.iter() {
343 for (static_group_name, asgn, cell) in group_name_assign_and_cell.iter()
344 {
345 if static_group.borrow().name() == static_group_name {
346 static_group.borrow_mut().assignments.push(asgn.clone());
347 comp.cells.add(cell.to_owned());
348 }
349 }
350 }
351}
352
353fn create_assignments<T: Clone>(
359 comp: &mut ir::Component,
360 sigs: &ir::LibrarySignatures,
361 group_names: &[Id],
362 structural_enable_map_opt: Option<CallsFromGroupMap<T>>,
363 cell_invoke_map_opt: Option<CallsFromGroupMap<T>>,
364 primitive_invoke_map_opt: Option<CallsFromGroupMap<T>>,
365) -> Vec<(
366 Id,
367 calyx_ir::Assignment<T>,
368 std::rc::Rc<std::cell::RefCell<calyx_ir::Cell>>,
369)> {
370 let delimiter = "___";
371 let comp_name = comp.name;
372 let mut builder = ir::Builder::new(comp, sigs);
374 let one = builder.add_constant(1, 1);
375
376 let mut group_name_assign_and_cell = Vec::new();
379
380 for group_name in group_names.iter() {
382 let name = format!("{group_name}{delimiter}{comp_name}_group_probe");
384 let probe_cell = builder.add_primitive(name, "std_wire", &[1]);
385 let probe_asgn: ir::Assignment<T> = builder.build_assignment(
386 probe_cell.borrow().get("in"),
387 one.borrow().get("out"),
388 Guard::True,
389 );
390 probe_cell.borrow_mut().add_attribute(BoolAttr::Control, 1);
392 probe_cell
393 .borrow_mut()
394 .add_attribute(BoolAttr::Protected, 1);
395 group_name_assign_and_cell.push((*group_name, probe_asgn, probe_cell));
396 }
397
398 if let Some(sem) = structural_enable_map_opt {
399 for (invoked_group_name, parent_groups) in sem.iter() {
401 for (parent_group, guard) in parent_groups.iter() {
402 let probe_cell_name = format!(
403 "{invoked_group_name}{delimiter}{parent_group}{delimiter}{comp_name}_se_probe"
404 );
405 let probe_cell =
406 builder.add_primitive(probe_cell_name, "std_wire", &[1]);
407 probe_cell.borrow_mut().add_attribute(BoolAttr::Control, 1);
408 probe_cell
409 .borrow_mut()
410 .add_attribute(BoolAttr::Protected, 1);
411 let probe_asgn: ir::Assignment<T> = builder.build_assignment(
412 probe_cell.borrow().get("in"),
413 one.borrow().get("out"),
414 guard.clone(),
415 );
416 group_name_assign_and_cell.push((
417 *parent_group,
418 probe_asgn,
419 probe_cell,
420 ));
421 }
422 }
423 }
424
425 if let Some(cell_invoke_map) = cell_invoke_map_opt {
426 for (invoker_group, invoked_cells) in cell_invoke_map.iter() {
428 for (invoked_cell, guard) in invoked_cells {
429 let probe_cell_name = format!(
430 "{invoked_cell}{delimiter}{invoker_group}{delimiter}{comp_name}_cell_probe"
431 );
432 let probe_cell =
433 builder.add_primitive(probe_cell_name, "std_wire", &[1]);
434 probe_cell.borrow_mut().add_attribute(BoolAttr::Control, 1);
435 probe_cell
436 .borrow_mut()
437 .add_attribute(BoolAttr::Protected, 1);
438 let probe_asgn: ir::Assignment<T> = builder.build_assignment(
440 probe_cell.borrow().get("in"),
441 one.borrow().get("out"),
442 guard.clone(),
443 );
444 group_name_assign_and_cell.push((
445 *invoker_group,
446 probe_asgn,
447 probe_cell,
448 ));
449 }
450 }
451 }
452
453 if let Some(primitive_invoke_map) = primitive_invoke_map_opt {
454 for (group, primitive_invs) in primitive_invoke_map.iter() {
456 for (primitive_cell_name, guard) in primitive_invs.iter() {
457 let probe_cell_name = format!(
458 "{primitive_cell_name}{delimiter}{group}{delimiter}{comp_name}_primitive_probe"
459 );
460 let probe_cell =
461 builder.add_primitive(probe_cell_name, "std_wire", &[1]);
462 probe_cell.borrow_mut().add_attribute(BoolAttr::Control, 1);
463 probe_cell
464 .borrow_mut()
465 .add_attribute(BoolAttr::Protected, 1);
466 let probe_asgn: ir::Assignment<T> = builder.build_assignment(
467 probe_cell.borrow().get("in"),
468 one.borrow().get("out"),
469 guard.clone(),
470 );
471 group_name_assign_and_cell
472 .push((*group, probe_asgn, probe_cell));
473 }
474 }
475 }
476
477 group_name_assign_and_cell
478}
479
480fn continuous_assignments(
484 comp: &mut ir::Component,
485 sigs: &ir::LibrarySignatures,
486) {
487 let mut cell_invoke_vec: Vec<(Id, ir::Guard<Nothing>)> = Vec::new();
489 let mut primitive_invoke_vec: Vec<(Id, ir::Guard<Nothing>)> = Vec::new();
491
492 let mut comb_primitives_covered = HashSet::new();
494 let mut comb_cells_covered = HashSet::new();
495 for assignment_ref in comp.continuous_assignments.iter() {
496 let dst_borrow = assignment_ref.dst.borrow();
497 let guard = *(assignment_ref.guard).clone();
498 if let ir::PortParent::Cell(cell_ref) = &dst_borrow.parent {
499 match cell_ref.upgrade().borrow().prototype.clone() {
500 calyx_ir::CellType::Primitive { .. } => {
501 let cell_name = cell_ref.upgrade().borrow().name();
502 if comb_primitives_covered.insert(cell_name) {
504 primitive_invoke_vec.push((cell_name, guard));
505 }
506 }
507 calyx_ir::CellType::Component { .. } => {
508 let cell_name = cell_ref.upgrade().borrow().name();
509 if comb_cells_covered.insert(cell_name) {
510 cell_invoke_vec.push((cell_name, guard));
511 }
512 }
513 _ => (),
514 }
515 }
516 }
517
518 let delimiter = "___";
520 let comp_name = comp.name;
521 let mut builder = ir::Builder::new(comp, sigs);
522 let one = builder.add_constant(1, 1);
523 let mut assign_and_cell = Vec::new();
524 for (primitive_cell_name, guard) in primitive_invoke_vec.iter() {
525 let probe_cell_name = format!(
526 "{primitive_cell_name}{delimiter}{comp_name}_contprimitive_probe"
527 );
528 let probe_cell =
529 builder.add_primitive(probe_cell_name, "std_wire", &[1]);
530 probe_cell.borrow_mut().add_attribute(BoolAttr::Control, 1);
531 probe_cell
532 .borrow_mut()
533 .add_attribute(BoolAttr::Protected, 1);
534 let probe_asgn: ir::Assignment<Nothing> = builder.build_assignment(
535 probe_cell.borrow().get("in"),
536 one.borrow().get("out"),
537 guard.clone(),
538 );
539 assign_and_cell.push((probe_asgn, probe_cell));
540 }
541 for (cell_name, guard) in cell_invoke_vec.iter() {
543 let probe_cell_name =
544 format!("{cell_name}{delimiter}{comp_name}_contcell_probe");
545 let probe_cell =
546 builder.add_primitive(probe_cell_name, "std_wire", &[1]);
547 probe_cell.borrow_mut().add_attribute(BoolAttr::Control, 1);
548 probe_cell
549 .borrow_mut()
550 .add_attribute(BoolAttr::Protected, 1);
551 let probe_asgn: ir::Assignment<Nothing> = builder.build_assignment(
552 probe_cell.borrow().get("in"),
553 one.borrow().get("out"),
554 guard.clone(),
555 );
556 assign_and_cell.push((probe_asgn, probe_cell));
557 }
558
559 for (asgn, cell) in assign_and_cell.iter() {
561 comp.continuous_assignments.push(asgn.clone());
562 comp.cells.add(cell.to_owned());
563 }
564}
565
566impl Visitor for ProfilerInstrumentation {
567 fn start(
568 &mut self,
569 comp: &mut ir::Component,
570 sigs: &ir::LibrarySignatures,
571 _comps: &[ir::Component],
572 ) -> VisResult {
573 group(comp, sigs);
574 combinational_group(comp, sigs);
575 static_group(comp, sigs);
576 continuous_assignments(comp, sigs);
577 Ok(Action::Continue)
578 }
579}