1#![warn(missing_docs)]
2
3extern crate proc_macro;
4
5use std::collections::{BTreeMap, BTreeSet};
6use std::fmt::Debug;
7use std::iter::FusedIterator;
8
9use itertools::Itertools;
10use proc_macro2::{Ident, Literal, Span, TokenStream};
11use quote::{ToTokens, format_ident, quote, quote_spanned};
12use serde::{Deserialize, Serialize};
13use slotmap::{Key, SecondaryMap, SlotMap, SparseSecondaryMap};
14use syn::spanned::Spanned;
15
16use super::graph_write::{Dot, GraphWrite, Mermaid};
17use super::ops::{
18 DelayType, FloType, OPERATORS, OperatorWriteOutput, WriteContextArgs, find_op_op_constraints,
19 null_write_iterator_fn,
20};
21use super::{
22 CONTEXT, Color, DiMulGraph, GRAPH, GraphEdgeId, GraphLoopId, GraphNode, GraphNodeId,
23 GraphSubgraphId, HANDOFF_NODE_STR, HandoffKind, MODULE_BOUNDARY_NODE_STR, OperatorInstance,
24 PortIndexValue, SINGLETON_SLOT_NODE_STR, Varname, change_spans, get_operator_generics,
25};
26use crate::diagnostic::{Diagnostic, Diagnostics, Level};
27use crate::pretty_span::{PrettyRowCol, PrettySpan};
28use crate::process_singletons;
29
30#[derive(Clone, Debug, Serialize, Deserialize)]
32pub struct ResolvedHandoffRef {
33 pub node_id: Option<GraphNodeId>,
35 pub is_mut: bool,
37 pub access_group: Option<u32>,
39}
40
41#[derive(Default, Debug, Serialize, Deserialize)]
51pub struct DfirGraph {
52 nodes: SlotMap<GraphNodeId, GraphNode>,
54
55 #[serde(skip)]
58 operator_instances: SecondaryMap<GraphNodeId, OperatorInstance>,
59 operator_tag: SecondaryMap<GraphNodeId, String>,
61 graph: DiMulGraph<GraphNodeId, GraphEdgeId>,
63 ports: SecondaryMap<GraphEdgeId, (PortIndexValue, PortIndexValue)>,
65
66 node_loops: SecondaryMap<GraphNodeId, GraphLoopId>,
68 loop_nodes: SlotMap<GraphLoopId, Vec<GraphNodeId>>,
70 loop_parent: SparseSecondaryMap<GraphLoopId, GraphLoopId>,
72 root_loops: Vec<GraphLoopId>,
74 loop_children: SecondaryMap<GraphLoopId, Vec<GraphLoopId>>,
76
77 node_subgraph: SecondaryMap<GraphNodeId, GraphSubgraphId>,
79
80 subgraph_nodes: SlotMap<GraphSubgraphId, Vec<GraphNodeId>>,
82 subgraph_toposort: Vec<GraphSubgraphId>,
84
85 node_handoff_references: SparseSecondaryMap<GraphNodeId, Vec<ResolvedHandoffRef>>,
87 node_varnames: SparseSecondaryMap<GraphNodeId, Varname>,
89
90 handoff_delay_type: SparseSecondaryMap<GraphNodeId, DelayType>,
94}
95
96impl DfirGraph {
98 pub fn new() -> Self {
100 Default::default()
101 }
102}
103
104impl DfirGraph {
106 pub fn node(&self, node_id: GraphNodeId) -> &GraphNode {
108 self.nodes.get(node_id).expect("Node not found.")
109 }
110
111 pub fn node_op_inst(&self, node_id: GraphNodeId) -> Option<&OperatorInstance> {
116 self.operator_instances.get(node_id)
117 }
118
119 pub fn node_varname(&self, node_id: GraphNodeId) -> Option<&Varname> {
121 self.node_varnames.get(node_id)
122 }
123
124 pub fn node_subgraph(&self, node_id: GraphNodeId) -> Option<GraphSubgraphId> {
126 self.node_subgraph.get(node_id).copied()
127 }
128
129 pub fn node_degree_in(&self, node_id: GraphNodeId) -> usize {
131 self.graph.degree_in(node_id)
132 }
133
134 pub fn node_degree_out(&self, node_id: GraphNodeId) -> usize {
136 self.graph.degree_out(node_id)
137 }
138
139 pub fn node_successors(
141 &self,
142 src: GraphNodeId,
143 ) -> impl '_
144 + DoubleEndedIterator<Item = (GraphEdgeId, GraphNodeId)>
145 + ExactSizeIterator
146 + FusedIterator
147 + Clone
148 + Debug {
149 self.graph.successors(src)
150 }
151
152 pub fn node_predecessors(
154 &self,
155 dst: GraphNodeId,
156 ) -> impl '_
157 + DoubleEndedIterator<Item = (GraphEdgeId, GraphNodeId)>
158 + ExactSizeIterator
159 + FusedIterator
160 + Clone
161 + Debug {
162 self.graph.predecessors(dst)
163 }
164
165 pub fn node_successor_edges(
167 &self,
168 src: GraphNodeId,
169 ) -> impl '_
170 + DoubleEndedIterator<Item = GraphEdgeId>
171 + ExactSizeIterator
172 + FusedIterator
173 + Clone
174 + Debug {
175 self.graph.successor_edges(src)
176 }
177
178 pub fn node_predecessor_edges(
180 &self,
181 dst: GraphNodeId,
182 ) -> impl '_
183 + DoubleEndedIterator<Item = GraphEdgeId>
184 + ExactSizeIterator
185 + FusedIterator
186 + Clone
187 + Debug {
188 self.graph.predecessor_edges(dst)
189 }
190
191 pub fn node_successor_nodes(
193 &self,
194 src: GraphNodeId,
195 ) -> impl '_
196 + DoubleEndedIterator<Item = GraphNodeId>
197 + ExactSizeIterator
198 + FusedIterator
199 + Clone
200 + Debug {
201 self.graph.successor_vertices(src)
202 }
203
204 pub fn node_predecessor_nodes(
206 &self,
207 dst: GraphNodeId,
208 ) -> impl '_
209 + DoubleEndedIterator<Item = GraphNodeId>
210 + ExactSizeIterator
211 + FusedIterator
212 + Clone
213 + Debug {
214 self.graph.predecessor_vertices(dst)
215 }
216
217 pub fn node_ids(&self) -> slotmap::basic::Keys<'_, GraphNodeId, GraphNode> {
219 self.nodes.keys()
220 }
221
222 pub fn nodes(&self) -> slotmap::basic::Iter<'_, GraphNodeId, GraphNode> {
224 self.nodes.iter()
225 }
226
227 pub fn insert_node(
229 &mut self,
230 node: GraphNode,
231 varname_opt: Option<Ident>,
232 loop_opt: Option<GraphLoopId>,
233 ) -> GraphNodeId {
234 let node_id = self.nodes.insert(node);
235 if let Some(varname) = varname_opt {
236 self.node_varnames.insert(node_id, Varname(varname));
237 }
238 if let Some(loop_id) = loop_opt {
239 self.node_loops.insert(node_id, loop_id);
240 self.loop_nodes[loop_id].push(node_id);
241 }
242 node_id
243 }
244
245 pub fn insert_node_op_inst(&mut self, node_id: GraphNodeId, op_inst: OperatorInstance) {
247 assert!(matches!(
248 self.nodes.get(node_id),
249 Some(GraphNode::Operator(_))
250 ));
251 let old_inst = self.operator_instances.insert(node_id, op_inst);
252 assert!(old_inst.is_none());
253 }
254
255 pub fn insert_node_op_insts_all(&mut self, diagnostics: &mut Diagnostics) {
257 let mut op_insts = Vec::new();
262 let mut handoff_nodes: Vec<(GraphNodeId, HandoffKind, Span)> = Vec::new();
264
265 for (node_id, node) in self.nodes() {
266 let GraphNode::Operator(operator) = node else {
267 continue;
268 };
269 if self.node_op_inst(node_id).is_some() {
270 continue;
271 };
272
273 let handoff_kind = match &*operator.name_string() {
275 "handoff" => Some(HandoffKind::Vec),
276 "singleton" => Some(HandoffKind::Singleton),
277 "optional" => Some(HandoffKind::Optional),
278 _ => None,
279 };
280 if let Some(kind) = handoff_kind {
281 if !operator.args.is_empty() {
282 diagnostics.push(Diagnostic::spanned(
283 operator.path.span(),
284 Level::Error,
285 format!("`{}` takes no arguments.", operator.name_string()),
286 ));
287 }
288 if operator.type_arguments().is_some() {
289 diagnostics.push(Diagnostic::spanned(
290 operator.path.span(),
291 Level::Error,
292 format!("`{}` takes no generic arguments.", operator.name_string()),
293 ));
294 }
295 handoff_nodes.push((node_id, kind, operator.path.span()));
296 continue;
297 }
298
299 let Some(op_constraints) = find_op_op_constraints(operator) else {
301 diagnostics.push(Diagnostic::spanned(
302 operator.path.span(),
303 Level::Error,
304 format!("Unknown operator `{}`", operator.name_string()),
305 ));
306 continue;
307 };
308
309 let (input_ports, output_ports) = {
311 let mut input_edges: Vec<(&PortIndexValue, GraphNodeId)> = self
312 .node_predecessors(node_id)
313 .map(|(edge_id, pred_id)| (self.edge_ports(edge_id).1, pred_id))
314 .collect();
315 input_edges.sort();
317 let input_ports: Vec<PortIndexValue> = input_edges
318 .into_iter()
319 .map(|(port, _pred)| port)
320 .cloned()
321 .collect();
322
323 let mut output_edges: Vec<(&PortIndexValue, GraphNodeId)> = self
325 .node_successors(node_id)
326 .map(|(edge_id, succ)| (self.edge_ports(edge_id).0, succ))
327 .collect();
328 output_edges.sort();
330 let output_ports: Vec<PortIndexValue> = output_edges
331 .into_iter()
332 .map(|(port, _succ)| port)
333 .cloned()
334 .collect();
335
336 (input_ports, output_ports)
337 };
338
339 let generics = get_operator_generics(diagnostics, operator);
341 {
343 let generics_span = generics
345 .generic_args
346 .as_ref()
347 .map(Spanned::span)
348 .unwrap_or_else(|| operator.path.span());
349
350 if !op_constraints
351 .persistence_args
352 .contains(&generics.persistence_args.len())
353 {
354 diagnostics.push(Diagnostic::spanned(
355 generics.persistence_args_span().unwrap_or(generics_span),
356 Level::Error,
357 format!(
358 "`{}` should have {} persistence lifetime arguments, actually has {}.",
359 op_constraints.name,
360 op_constraints.persistence_args.human_string(),
361 generics.persistence_args.len()
362 ),
363 ));
364 }
365 if !op_constraints.type_args.contains(&generics.type_args.len()) {
366 diagnostics.push(Diagnostic::spanned(
367 generics.type_args_span().unwrap_or(generics_span),
368 Level::Error,
369 format!(
370 "`{}` should have {} generic type arguments, actually has {}.",
371 op_constraints.name,
372 op_constraints.type_args.human_string(),
373 generics.type_args.len()
374 ),
375 ));
376 }
377 }
378
379 op_insts.push((
380 node_id,
381 OperatorInstance {
382 op_constraints,
383 input_ports,
384 output_ports,
385 singletons_referenced: operator.singletons_referenced.clone(),
386 generics,
387 arguments_pre: operator.args.clone(),
388 arguments_raw: operator.args_raw.clone(),
389 },
390 ));
391 }
392
393 for (node_id, op_inst) in op_insts {
394 self.insert_node_op_inst(node_id, op_inst);
395 }
396
397 for (node_id, kind, span) in handoff_nodes {
399 self.nodes[node_id] = GraphNode::Handoff {
400 kind,
401 src_span: span,
402 dst_span: span,
403 };
404 }
405 }
406
407 pub fn insert_intermediate_node(
419 &mut self,
420 edge_id: GraphEdgeId,
421 new_node: GraphNode,
422 ) -> (GraphNodeId, GraphEdgeId) {
423 let span = Some(new_node.span());
424
425 let op_inst_opt = 'oc: {
427 let GraphNode::Operator(operator) = &new_node else {
428 break 'oc None;
429 };
430 let Some(op_constraints) = find_op_op_constraints(operator) else {
431 break 'oc None;
432 };
433 let (input_port, output_port) = self.ports.get(edge_id).cloned().unwrap();
434
435 let mut dummy_diagnostics = Diagnostics::new();
436 let generics = get_operator_generics(&mut dummy_diagnostics, operator);
437 assert!(dummy_diagnostics.is_empty());
438
439 Some(OperatorInstance {
440 op_constraints,
441 input_ports: vec![input_port],
442 output_ports: vec![output_port],
443 singletons_referenced: operator.singletons_referenced.clone(),
444 generics,
445 arguments_pre: operator.args.clone(),
446 arguments_raw: operator.args_raw.clone(),
447 })
448 };
449
450 let node_id = self.nodes.insert(new_node);
452 if let Some(op_inst) = op_inst_opt {
454 self.operator_instances.insert(node_id, op_inst);
455 }
456 let (e0, e1) = self
458 .graph
459 .insert_intermediate_vertex(node_id, edge_id)
460 .unwrap();
461
462 let (src_idx, dst_idx) = self.ports.remove(edge_id).unwrap();
464 self.ports
465 .insert(e0, (src_idx, PortIndexValue::Elided(span)));
466 self.ports
467 .insert(e1, (PortIndexValue::Elided(span), dst_idx));
468
469 (node_id, e1)
470 }
471
472 pub fn remove_intermediate_node(&mut self, node_id: GraphNodeId) {
475 assert_eq!(
476 1,
477 self.node_degree_in(node_id),
478 "Removed intermediate node must have one predecessor"
479 );
480 assert_eq!(
481 1,
482 self.node_degree_out(node_id),
483 "Removed intermediate node must have one successor"
484 );
485 assert!(
486 self.node_subgraph.is_empty() && self.subgraph_nodes.is_empty(),
487 "Should not remove intermediate node after subgraph partitioning"
488 );
489
490 assert!(self.nodes.remove(node_id).is_some());
491 let (new_edge_id, (pred_edge_id, succ_edge_id)) =
492 self.graph.remove_intermediate_vertex(node_id).unwrap();
493 self.operator_instances.remove(node_id);
494 self.node_varnames.remove(node_id);
495
496 let (src_port, _) = self.ports.remove(pred_edge_id).unwrap();
497 let (_, dst_port) = self.ports.remove(succ_edge_id).unwrap();
498 self.ports.insert(new_edge_id, (src_port, dst_port));
499 }
500
501 pub(crate) fn node_color(&self, node_id: GraphNodeId) -> Option<Color> {
507 if matches!(self.node(node_id), GraphNode::Handoff { .. }) {
508 return Some(Color::Hoff);
509 }
510
511 if let GraphNode::Operator(op) = self.node(node_id)
513 && (op.name_string() == "resolve_futures_blocking"
514 || op.name_string() == "resolve_futures_blocking_ordered")
515 {
516 return Some(Color::Push);
517 }
518
519 let inn_degree = self.node_predecessor_nodes(node_id).len();
521 let out_degree = self.node_successor_nodes(node_id).len();
523
524 match (inn_degree, out_degree) {
525 (0, 0) => None, (0, 1) => Some(Color::Pull),
527 (1, 0) => Some(Color::Push),
528 (1, 1) => None, (_many, 0 | 1) => Some(Color::Pull),
530 (0 | 1, _many) => Some(Color::Push),
531 (_many, _to_many) => Some(Color::Comp),
532 }
533 }
534
535 pub fn set_operator_tag(&mut self, node_id: GraphNodeId, tag: String) {
537 self.operator_tag.insert(node_id, tag);
538 }
539}
540
541impl DfirGraph {
543 pub fn set_node_handoff_references(
546 &mut self,
547 node_id: GraphNodeId,
548 singletons_referenced: Vec<ResolvedHandoffRef>,
549 ) -> Option<Vec<ResolvedHandoffRef>> {
550 self.node_handoff_references
551 .insert(node_id, singletons_referenced)
552 }
553
554 pub fn node_handoff_references(&self, node_id: GraphNodeId) -> &[ResolvedHandoffRef] {
557 self.node_handoff_references
558 .get(node_id)
559 .map(std::ops::Deref::deref)
560 .unwrap_or_default()
561 }
562
563 pub fn node_handoff_reference_groups(&self) -> NodeHandoffReferenceGroups<'_> {
565 let mut handoff_references = NodeHandoffReferenceGroups::new();
566 for node_id in self.node_ids() {
567 if let GraphNode::Operator(operator) = self.node(node_id) {
568 let resolved = self.node_handoff_references(node_id);
569 for (resolved_ref, ref_token) in
570 resolved.iter().zip(operator.singletons_referenced.iter())
571 {
572 if let Some(target_nid) = resolved_ref.node_id {
573 handoff_references
574 .entry(target_nid)
575 .or_default()
576 .entry(resolved_ref.access_group)
577 .or_default()
578 .push((node_id, resolved_ref, ref_token.span()));
579 }
580 }
581 }
582 }
583 handoff_references
584 }
585}
586
587pub type NodeHandoffReferenceGroups<'a> =
590 BTreeMap<GraphNodeId, BTreeMap<Option<u32>, Vec<(GraphNodeId, &'a ResolvedHandoffRef, Span)>>>;
591
592impl DfirGraph {
594 pub fn merge_modules(&mut self) -> Result<(), Diagnostic> {
602 let mod_bound_nodes = self
603 .nodes()
604 .filter(|(_nid, node)| matches!(node, GraphNode::ModuleBoundary { .. }))
605 .map(|(nid, _node)| nid)
606 .collect::<Vec<_>>();
607
608 for mod_bound_node in mod_bound_nodes {
609 self.remove_module_boundary(mod_bound_node)?;
610 }
611
612 Ok(())
613 }
614
615 fn remove_module_boundary(&mut self, mod_bound_node: GraphNodeId) -> Result<(), Diagnostic> {
619 assert!(
620 self.node_subgraph.is_empty() && self.subgraph_nodes.is_empty(),
621 "Should not remove intermediate node after subgraph partitioning"
622 );
623
624 let mut mod_pred_ports = BTreeMap::new();
625 let mut mod_succ_ports = BTreeMap::new();
626
627 for mod_out_edge in self.node_predecessor_edges(mod_bound_node) {
628 let (pred_port, succ_port) = self.edge_ports(mod_out_edge);
629 mod_pred_ports.insert(succ_port.clone(), (mod_out_edge, pred_port.clone()));
630 }
631
632 for mod_inn_edge in self.node_successor_edges(mod_bound_node) {
633 let (pred_port, succ_port) = self.edge_ports(mod_inn_edge);
634 mod_succ_ports.insert(pred_port.clone(), (mod_inn_edge, succ_port.clone()));
635 }
636
637 if mod_pred_ports.keys().collect::<BTreeSet<_>>()
638 != mod_succ_ports.keys().collect::<BTreeSet<_>>()
639 {
640 let GraphNode::ModuleBoundary { input, import_expr } = self.node(mod_bound_node) else {
642 panic!();
643 };
644
645 if *input {
646 return Err(Diagnostic {
647 span: *import_expr,
648 level: Level::Error,
649 message: format!(
650 "The ports into the module did not match. input: {:?}, expected: {:?}",
651 mod_pred_ports.keys().map(|x| x.to_string()).join(", "),
652 mod_succ_ports.keys().map(|x| x.to_string()).join(", ")
653 ),
654 });
655 } else {
656 return Err(Diagnostic {
657 span: *import_expr,
658 level: Level::Error,
659 message: format!(
660 "The ports out of the module did not match. output: {:?}, expected: {:?}",
661 mod_succ_ports.keys().map(|x| x.to_string()).join(", "),
662 mod_pred_ports.keys().map(|x| x.to_string()).join(", "),
663 ),
664 });
665 }
666 }
667
668 for (port, (pred_edge, pred_port)) in mod_pred_ports {
669 let (succ_edge, succ_port) = mod_succ_ports.remove(&port).unwrap();
670
671 let (src, _) = self.edge(pred_edge);
672 let (_, dst) = self.edge(succ_edge);
673 self.remove_edge(pred_edge);
674 self.remove_edge(succ_edge);
675
676 let new_edge_id = self.graph.insert_edge(src, dst);
677 self.ports.insert(new_edge_id, (pred_port, succ_port));
678 }
679
680 self.graph.remove_vertex(mod_bound_node);
681 self.nodes.remove(mod_bound_node);
682
683 Ok(())
684 }
685}
686
687impl DfirGraph {
689 pub fn edge(&self, edge_id: GraphEdgeId) -> (GraphNodeId, GraphNodeId) {
691 let (src, dst) = self.graph.edge(edge_id).expect("Edge not found.");
692 (src, dst)
693 }
694
695 pub fn edge_ports(&self, edge_id: GraphEdgeId) -> (&PortIndexValue, &PortIndexValue) {
697 let (src_port, dst_port) = self.ports.get(edge_id).expect("Edge not found.");
698 (src_port, dst_port)
699 }
700
701 pub fn edge_ids(&self) -> slotmap::basic::Keys<'_, GraphEdgeId, (GraphNodeId, GraphNodeId)> {
703 self.graph.edge_ids()
704 }
705
706 pub fn edges(
708 &self,
709 ) -> impl '_
710 + ExactSizeIterator<Item = (GraphEdgeId, (GraphNodeId, GraphNodeId))>
711 + FusedIterator
712 + Clone
713 + Debug {
714 self.graph.edges()
715 }
716
717 pub fn insert_edge(
719 &mut self,
720 src: GraphNodeId,
721 src_port: PortIndexValue,
722 dst: GraphNodeId,
723 dst_port: PortIndexValue,
724 ) -> GraphEdgeId {
725 let edge_id = self.graph.insert_edge(src, dst);
726 self.ports.insert(edge_id, (src_port, dst_port));
727 edge_id
728 }
729
730 pub fn remove_edge(&mut self, edge: GraphEdgeId) {
732 let (_src, _dst) = self.graph.remove_edge(edge).unwrap();
733 let (_src_port, _dst_port) = self.ports.remove(edge).unwrap();
734 }
735}
736
737impl DfirGraph {
739 pub fn subgraph(&self, subgraph_id: GraphSubgraphId) -> &Vec<GraphNodeId> {
741 self.subgraph_nodes
742 .get(subgraph_id)
743 .expect("Subgraph not found.")
744 }
745
746 pub fn subgraph_ids(&self) -> slotmap::basic::Keys<'_, GraphSubgraphId, Vec<GraphNodeId>> {
748 self.subgraph_nodes.keys()
749 }
750
751 pub fn subgraph_toposort(&self) -> &[GraphSubgraphId] {
753 &self.subgraph_toposort
754 }
755
756 pub fn set_subgraph_toposort(&mut self, order: Vec<GraphSubgraphId>) {
758 self.subgraph_toposort = order;
759 }
760
761 pub fn subgraphs(&self) -> slotmap::basic::Iter<'_, GraphSubgraphId, Vec<GraphNodeId>> {
763 self.subgraph_nodes.iter()
764 }
765
766 pub fn insert_subgraph(
768 &mut self,
769 node_ids: Vec<GraphNodeId>,
770 ) -> Result<GraphSubgraphId, (GraphNodeId, GraphSubgraphId)> {
771 for &node_id in node_ids.iter() {
773 if let Some(&old_sg_id) = self.node_subgraph.get(node_id) {
774 return Err((node_id, old_sg_id));
775 }
776 }
777 let subgraph_id = self.subgraph_nodes.insert_with_key(|sg_id| {
778 for &node_id in node_ids.iter() {
779 self.node_subgraph.insert(node_id, sg_id);
780 }
781 node_ids
782 });
783
784 Ok(subgraph_id)
785 }
786
787 pub fn remove_from_subgraph(&mut self, node_id: GraphNodeId) -> bool {
789 if let Some(old_sg_id) = self.node_subgraph.remove(node_id) {
790 self.subgraph_nodes[old_sg_id].retain(|&other_node_id| other_node_id != node_id);
791 true
792 } else {
793 false
794 }
795 }
796
797 pub fn handoff_delay_type(&self, node_id: GraphNodeId) -> Option<DelayType> {
799 self.handoff_delay_type.get(node_id).copied()
800 }
801
802 pub fn set_handoff_delay_type(&mut self, node_id: GraphNodeId, delay_type: DelayType) {
804 self.handoff_delay_type.insert(node_id, delay_type);
805 }
806
807 fn find_pull_to_push_idx(&self, subgraph_nodes: &[GraphNodeId]) -> usize {
809 subgraph_nodes
810 .iter()
811 .position(|&node_id| {
812 self.node_color(node_id)
813 .is_some_and(|color| Color::Pull != color)
814 })
815 .unwrap_or(subgraph_nodes.len())
816 }
817}
818
819impl DfirGraph {
821 fn node_as_ident(&self, node_id: GraphNodeId, is_pred: bool) -> Ident {
823 let name = match &self.nodes[node_id] {
824 GraphNode::Operator(_) => format!("op_{:?}", node_id.data()),
825 GraphNode::Handoff {
826 kind: HandoffKind::Vec,
827 ..
828 } => format!(
829 "hoff_{:?}_{}",
830 node_id.data(),
831 if is_pred { "recv" } else { "send" }
832 ),
833 GraphNode::Handoff {
834 kind: HandoffKind::Singleton | HandoffKind::Optional,
835 ..
836 } => format!(
837 "singleton_{:?}_{}",
838 node_id.data(),
839 if is_pred { "recv" } else { "send" }
840 ),
841 GraphNode::ModuleBoundary { .. } => panic!(),
842 };
843 let span = match (is_pred, &self.nodes[node_id]) {
844 (_, GraphNode::Operator(operator)) => operator.span(),
845 (true, &GraphNode::Handoff { src_span, .. }) => src_span,
846 (false, &GraphNode::Handoff { dst_span, .. }) => dst_span,
847 (_, GraphNode::ModuleBoundary { .. }) => panic!(),
848 };
849 Ident::new(&name, span)
850 }
851
852 fn hoff_buf_ident(&self, hoff_id: GraphNodeId, span: Span) -> Ident {
854 Ident::new(&format!("hoff_{:?}_buf", hoff_id.data()), span)
855 }
856
857 fn hoff_back_ident(&self, hoff_id: GraphNodeId, span: Span) -> Ident {
859 Ident::new(&format!("hoff_{:?}_back", hoff_id.data()), span)
860 }
861
862 fn helper_resolve_singletons(&self, node_id: GraphNodeId, span: Span) -> Vec<TokenStream> {
871 self.node_handoff_references(node_id)
872 .iter()
873 .map(|resolved_ref| {
874 let ref_node_id = resolved_ref
876 .node_id
877 .expect("Expected singleton to be resolved but was not, this is a bug.");
878 let is_mut = resolved_ref.is_mut;
879 match self.node(ref_node_id) {
880 GraphNode::Handoff {
881 kind: HandoffKind::Singleton,
882 ..
883 } => {
884 let buf_ident = self.hoff_buf_ident(ref_node_id, span);
885 if is_mut {
886 quote_spanned! {span=> #buf_ident.as_mut().unwrap() }
887 } else {
888 quote_spanned! {span=> #buf_ident.as_ref().unwrap() }
889 }
890 }
891 GraphNode::Handoff {
892 kind: HandoffKind::Optional | HandoffKind::Vec,
893 ..
894 } => {
895 let buf_ident = self.hoff_buf_ident(ref_node_id, span);
896 if is_mut {
897 quote_spanned! {span=> &mut #buf_ident }
898 } else {
899 quote_spanned! {span=> &#buf_ident }
900 }
901 }
902 _ => {
903 unreachable!("Only handoff nodes should be reachable as handoff references")
904 }
905 }
906 })
907 .collect::<Vec<_>>()
908 }
909
910 fn helper_collect_subgraph_handoffs(
913 &self,
914 ) -> SecondaryMap<GraphSubgraphId, (Vec<GraphNodeId>, Vec<GraphNodeId>)> {
915 let mut subgraph_handoffs: SecondaryMap<
917 GraphSubgraphId,
918 (Vec<GraphNodeId>, Vec<GraphNodeId>),
919 > = self
920 .subgraph_nodes
921 .keys()
922 .map(|k| (k, Default::default()))
923 .collect();
924
925 for (hoff_id, hoff) in self.nodes() {
927 if !matches!(hoff, GraphNode::Handoff { .. }) {
928 continue;
929 }
930 for (_edge, succ_id) in self.node_successors(hoff_id) {
932 let succ_sg = self
933 .node_subgraph(succ_id)
934 .expect("bug: successor not in subgraph, may be a doubled/adjacent handoff");
935 subgraph_handoffs[succ_sg].0.push(hoff_id);
936 }
937 for (_edge, pred_id) in self.node_predecessors(hoff_id) {
939 let pred_sg = self
940 .node_subgraph(pred_id)
941 .expect("bug: predecessor not in subgraph, may be a doubled/adjacent handoff");
942 subgraph_handoffs[pred_sg].1.push(hoff_id);
943 }
944 }
945
946 subgraph_handoffs
947 }
948
949 fn helper_loop_output_handoffs(&self) -> SecondaryMap<GraphLoopId, Vec<GraphNodeId>> {
952 let mut loop_hoffs_out = SecondaryMap::<GraphLoopId, Vec<GraphNodeId>>::new();
953
954 for (hoff_id, hoff) in self.nodes() {
955 if !matches!(hoff, GraphNode::Handoff { .. }) {
956 continue;
957 }
958
959 let loop_pred = self
960 .node_predecessors(hoff_id)
961 .next()
962 .and_then(|(_, pred)| self.node_loop(pred));
963 let loop_succ = self
964 .node_successors(hoff_id)
965 .next()
966 .and_then(|(_, succ)| self.node_loop(succ));
967
968 if let Some(loop_pred) = loop_pred
969 && loop_succ == self.loop_parent(loop_pred)
970 {
971 loop_hoffs_out
973 .entry(loop_pred)
974 .expect("loop removed")
975 .or_default()
976 .push(hoff_id);
977 }
978 }
979
980 loop_hoffs_out
981 }
982
983 fn is_inside_loop(&self, node_loop: Option<GraphLoopId>, loop_id: GraphLoopId) -> bool {
985 let mut current = node_loop;
986 while let Some(l) = current {
987 if l == loop_id {
988 return true;
989 }
990 current = self.loop_parent(l);
991 }
992 false
993 }
994
995 fn emit_loop_gate(
1004 &self,
1005 loop_id: GraphLoopId,
1006 child_body: TokenStream,
1007 loop_input_handoffs: &SecondaryMap<GraphLoopId, Vec<GraphNodeId>>,
1008 back_edge_hoffs_and_lazyness: &SparseSecondaryMap<GraphNodeId, bool>,
1009 loop_swap_code: &std::collections::HashMap<GraphLoopId, Vec<TokenStream>>,
1010 output: &mut TokenStream,
1011 ) {
1012 let swap_code = loop_swap_code
1014 .get(&loop_id)
1015 .map(|v| v.as_slice())
1016 .unwrap_or(&[]);
1017
1018 let is_root_loop = self.loop_parent(loop_id).is_none();
1020
1021 let entry_handoffs = loop_input_handoffs.get(loop_id).expect("loop missing");
1023 let mut gate_checks: Vec<TokenStream> = entry_handoffs
1024 .iter()
1025 .filter(|&&hoff_id| {
1026 let is_lazy = self
1029 .node_successors(hoff_id)
1030 .next()
1031 .and_then(|(_, succ)| self.node_op_inst(succ))
1032 .is_some_and(|op_inst| {
1033 op_inst.op_constraints.flo_type == Some(FloType::WindowingLazy)
1034 });
1035 !is_lazy
1036 })
1037 .map(|&hoff_id| {
1038 let span = self.node(hoff_id).span();
1039 let buf_ident = self.hoff_buf_ident(hoff_id, span);
1040 if back_edge_hoffs_and_lazyness.contains_key(hoff_id) {
1041 let back_ident = self.hoff_back_ident(hoff_id, span);
1042 quote_spanned! {span=> !#back_ident.is_empty() }
1043 } else {
1044 quote_spanned! {span=> !#buf_ident.is_empty() }
1045 }
1046 })
1047 .collect();
1048
1049 if !is_root_loop {
1051 for (hoff_id, hoff) in self.nodes() {
1052 if !matches!(hoff, GraphNode::Handoff { .. }) {
1053 continue;
1054 }
1055 let Some(delay_type) = self.handoff_delay_type(hoff_id) else {
1056 continue;
1057 };
1058 if delay_type != DelayType::Loop {
1059 continue;
1060 }
1061 let hoff_loop = self
1063 .node_successors(hoff_id)
1064 .next()
1065 .and_then(|(_, succ)| self.node_subgraph(succ))
1066 .and_then(|sg| self.subgraph_loop(sg));
1067 if hoff_loop != Some(loop_id) {
1068 continue;
1069 }
1070 let span = self.node(hoff_id).span();
1071 let back_ident = self.hoff_back_ident(hoff_id, span);
1072 gate_checks.push(quote_spanned! {span=> !#back_ident.is_empty() });
1073 }
1074 }
1075
1076 if is_root_loop {
1079 for (hoff_id, hoff) in self.nodes() {
1080 if !matches!(hoff, GraphNode::Handoff { .. }) {
1081 continue;
1082 }
1083 let Some(delay_type) = self.handoff_delay_type(hoff_id) else {
1084 continue;
1085 };
1086 if delay_type != DelayType::Tick {
1087 continue;
1088 }
1089 let hoff_loop = self
1091 .node_successors(hoff_id)
1092 .next()
1093 .and_then(|(_, succ)| self.node_subgraph(succ))
1094 .and_then(|sg| self.subgraph_loop(sg));
1095 if hoff_loop != Some(loop_id) {
1096 continue;
1097 }
1098 let span = self.node(hoff_id).span();
1099 let back_ident = self.hoff_back_ident(hoff_id, span);
1100 gate_checks.push(quote_spanned! {span=> !#back_ident.is_empty() });
1101 }
1102 }
1103
1104 if gate_checks.is_empty() {
1105 output.extend(child_body);
1107 output.extend(quote! { #( #swap_code )* });
1108 } else if is_root_loop {
1109 output.extend(quote! {
1111 if false #( || #gate_checks )* {
1112 #child_body
1113 #( #swap_code )*
1114 }
1115 });
1116 } else {
1117 output.extend(quote! {
1119 while false #( || #gate_checks )* {
1120 #child_body
1121 #( #swap_code )*
1122 }
1123 });
1124 }
1125 }
1126
1127 fn helper_loop_input_handoffs(&self) -> SecondaryMap<GraphLoopId, Vec<GraphNodeId>> {
1129 let mut loop_hoffs_inn = SecondaryMap::<GraphLoopId, Vec<GraphNodeId>>::new();
1130
1131 for (hoff_id, hoff) in self.nodes() {
1133 if !matches!(hoff, GraphNode::Handoff { .. }) {
1134 continue;
1135 }
1136
1137 let loop_pred = self
1139 .node_predecessors(hoff_id)
1140 .next()
1141 .and_then(|(_, pred)| self.node_loop(pred));
1142 let loop_succ = self
1143 .node_successors(hoff_id)
1144 .next()
1145 .and_then(|(_, succ)| self.node_loop(succ));
1146
1147 if let Some(loop_succ) = loop_succ
1148 && loop_pred == self.loop_parent(loop_succ)
1149 {
1150 loop_hoffs_inn
1152 .entry(loop_succ)
1153 .expect("loop removed")
1154 .or_default()
1155 .push(hoff_id);
1156 }
1157 }
1158
1159 loop_hoffs_inn
1160 }
1161
1162 pub fn as_code(
1177 &self,
1178 root: &TokenStream,
1179 include_type_guards: bool,
1180 prefix: TokenStream,
1181 diagnostics: &mut Diagnostics,
1182 ) -> Result<TokenStream, Diagnostics> {
1183 self.as_code_with_options(root, include_type_guards, true, prefix, diagnostics)
1184 }
1185
1186 pub fn as_code_with_options(
1195 &self,
1196 root: &TokenStream,
1197 include_type_guards: bool,
1198 include_meta: bool,
1199 prefix: TokenStream,
1200 diagnostics: &mut Diagnostics,
1201 ) -> Result<TokenStream, Diagnostics> {
1202 let df = Ident::new(GRAPH, Span::call_site());
1203 let context = Ident::new(CONTEXT, Span::call_site());
1204 let bump_ident = Ident::new("__dfir_bump", Span::call_site());
1206
1207 let handoff_nodes = self
1209 .nodes
1210 .iter()
1211 .filter_map(|(node_id, node)| match node {
1212 &GraphNode::Handoff {
1213 kind,
1214 src_span,
1215 dst_span,
1216 } => Some((node_id, kind, (src_span, dst_span))),
1217 GraphNode::Operator(_) => None,
1218 GraphNode::ModuleBoundary { .. } => panic!(),
1219 })
1220 .collect::<Vec<_>>();
1221
1222 let back_edge_hoffs_and_lazyness = handoff_nodes
1226 .iter()
1227 .map(|&(node_id, _, _)| node_id)
1228 .filter_map(|node_id| {
1229 let delay_type = self.handoff_delay_type(node_id)?;
1230 Some((
1231 node_id,
1232 matches!(delay_type, DelayType::TickLazy | DelayType::LoopLazy),
1233 ))
1234 })
1235 .collect::<SparseSecondaryMap<_, _>>();
1236
1237 let back_buffer_idents_laziness = handoff_nodes
1239 .iter()
1240 .filter_map(|&(hoff_id, _kind, (src_span, dst_span))| {
1241 back_edge_hoffs_and_lazyness.get(hoff_id).map(|&is_lazy| {
1242 let span = src_span.join(dst_span).unwrap_or(src_span);
1243 let back_ident = self.hoff_back_ident(hoff_id, span);
1244 let buf_ident = self.hoff_buf_ident(hoff_id, span);
1245 (back_ident, buf_ident, is_lazy)
1246 })
1247 })
1248 .collect::<Vec<_>>();
1249
1250 let back_edge_swap_code = handoff_nodes
1257 .iter()
1258 .filter(|&&(node_id, _kind, _)| {
1259 self.handoff_delay_type(node_id)
1260 .is_some_and(|dt| matches!(dt, DelayType::Tick | DelayType::TickLazy))
1261 })
1262 .filter(|&&(hoff_id, _kind, _)| {
1263 let consumer_loop = self
1266 .node_successors(hoff_id)
1267 .next()
1268 .and_then(|(_, succ)| self.node_subgraph(succ))
1269 .and_then(|sg| self.subgraph_loop(sg));
1270 if let Some(loop_id) = consumer_loop {
1271 self.loop_parent(loop_id).is_some()
1273 } else {
1274 true
1276 }
1277 })
1278 .map(|&(hoff_id, _kind, _)| {
1279 let span = self.nodes[hoff_id].span();
1280 let buf_ident = self.hoff_buf_ident(hoff_id, span);
1281 let back_ident = self.hoff_back_ident(hoff_id, span);
1282 quote_spanned! {span=>
1283 ::std::mem::swap(&mut #buf_ident, &mut #back_ident);
1284 }
1285 })
1286 .collect::<Vec<_>>();
1287
1288 let mut loop_swap_code: std::collections::HashMap<GraphLoopId, Vec<TokenStream>> =
1292 std::collections::HashMap::new();
1293 for &(hoff_id, _kind, _) in handoff_nodes.iter() {
1294 let Some(delay_type) = self.handoff_delay_type(hoff_id) else {
1295 continue;
1296 };
1297 let loop_id = self
1299 .node_successors(hoff_id)
1300 .next()
1301 .and_then(|(_, succ)| self.node_subgraph(succ))
1302 .and_then(|sg| self.subgraph_loop(sg));
1303 let Some(loop_id) = loop_id else {
1304 continue;
1305 };
1306 let include = match delay_type {
1307 DelayType::Loop | DelayType::LoopLazy => true,
1308 DelayType::Tick | DelayType::TickLazy => {
1309 self.loop_parent(loop_id).is_none()
1311 }
1312 };
1313 if !include {
1314 continue;
1315 }
1316 let span = self.nodes[hoff_id].span();
1317 let buf_ident = self.hoff_buf_ident(hoff_id, span);
1318 let back_ident = self.hoff_back_ident(hoff_id, span);
1319 loop_swap_code
1320 .entry(loop_id)
1321 .or_default()
1322 .push(quote_spanned! {span=>
1323 ::std::mem::swap(&mut #buf_ident, &mut #back_ident);
1324 });
1325 }
1326
1327 let subgraph_handoffs = self.helper_collect_subgraph_handoffs();
1329
1330 let all_subgraphs: Vec<_> = self
1332 .subgraph_toposort()
1333 .iter()
1334 .map(|&sg_id| (sg_id, self.subgraph(sg_id)))
1335 .collect();
1336
1337 let mut op_prologue_code = Vec::new();
1341 let mut op_tick_end_code = Vec::new();
1342
1343 let mut loop_stack: Vec<(GraphLoopId, TokenStream)> = Vec::new();
1347 let mut current_output = TokenStream::new();
1348
1349 let loop_input_handoffs = self.helper_loop_input_handoffs();
1351 let loop_output_handoffs = self.helper_loop_output_handoffs();
1352
1353 {
1354 for &(subgraph_id, subgraph_nodes) in all_subgraphs.iter() {
1355 let sg_loop = self.subgraph_loop(subgraph_id);
1356
1357 while let Some(&(top_loop, _)) = loop_stack.last() {
1360 if sg_loop == Some(top_loop) || self.is_inside_loop(sg_loop, top_loop) {
1361 break;
1362 }
1363 let (closed_loop, child_body) = loop_stack.pop().unwrap();
1365 let target = if let Some((_, parent_body)) = loop_stack.last_mut() {
1366 parent_body
1367 } else {
1368 &mut current_output
1369 };
1370 self.emit_loop_gate(
1371 closed_loop,
1372 child_body,
1373 &loop_input_handoffs,
1374 &back_edge_hoffs_and_lazyness,
1375 &loop_swap_code,
1376 target,
1377 );
1378 }
1379
1380 if let Some(target_loop) = sg_loop
1382 && loop_stack.last().map(|&(l, _)| l) != Some(target_loop)
1383 {
1384 let mut path = Vec::new();
1386 let mut cur = Some(target_loop);
1387 while let Some(l) = cur {
1388 if loop_stack.last().map(|&(top, _)| top) == Some(l) {
1389 break;
1390 }
1391 path.push(l);
1392 cur = self.loop_parent(l);
1393 }
1394 for &loop_id in path.iter().rev() {
1397 if let Some(exit_hoffs) = loop_output_handoffs.get(loop_id) {
1399 let exit_hoff_decls = exit_hoffs.iter().map(|&hoff_id| {
1400 let span = self.nodes[hoff_id].span();
1401 let buf_ident = self.hoff_buf_ident(hoff_id, span);
1402 let GraphNode::Handoff { kind, .. } = self.node(hoff_id) else {
1403 panic!()
1404 };
1405 match kind {
1406 HandoffKind::Vec => quote_spanned! {span=>
1407 let mut #buf_ident = #root::bumpalo::collections::Vec::new_in(&#bump_ident);
1408 },
1409 HandoffKind::Singleton | HandoffKind::Optional => quote_spanned! {span=>
1410 let mut #buf_ident = ::std::option::Option::None;
1411 },
1412 }
1413 });
1414 let target = if let Some((_, body)) = loop_stack.last_mut() {
1415 body
1416 } else {
1417 &mut current_output
1418 };
1419 target.extend(quote! { #( #exit_hoff_decls )* });
1420 }
1421 loop_stack.push((loop_id, TokenStream::new()));
1422 }
1423 }
1424 let sg_metrics_ffi = subgraph_id.data().as_ffi();
1425 let (recv_hoffs, send_hoffs) = &subgraph_handoffs[subgraph_id];
1426
1427 let recv_port_idents: Vec<Ident> = recv_hoffs
1429 .iter()
1430 .map(|&hoff_id| self.node_as_ident(hoff_id, true))
1431 .collect();
1432 let send_port_idents: Vec<Ident> = send_hoffs
1433 .iter()
1434 .map(|&hoff_id| self.node_as_ident(hoff_id, false))
1435 .collect();
1436
1437 let recv_buf_idents: Vec<Ident> = recv_hoffs
1439 .iter()
1440 .map(|&hoff_id| self.hoff_buf_ident(hoff_id, self.nodes[hoff_id].span()))
1441 .collect();
1442 let send_buf_idents: Vec<Ident> = send_hoffs
1443 .iter()
1444 .map(|&hoff_id| self.hoff_buf_ident(hoff_id, self.nodes[hoff_id].span()))
1445 .collect();
1446
1447 let recv_kinds = recv_hoffs
1449 .iter()
1450 .map(|&hoff_id| {
1451 let GraphNode::Handoff { kind, .. } = self.node(hoff_id) else {
1452 panic!()
1453 };
1454 *kind
1455 })
1456 .collect::<Vec<_>>();
1457 let send_kinds = send_hoffs
1458 .iter()
1459 .map(|&hoff_id| {
1460 let GraphNode::Handoff { kind, .. } = self.node(hoff_id) else {
1461 panic!()
1462 };
1463 *kind
1464 })
1465 .collect::<Vec<_>>();
1466
1467 let recv_port_code: Vec<TokenStream> = recv_port_idents
1471 .iter()
1472 .zip(recv_buf_idents.iter())
1473 .zip(recv_kinds.iter())
1474 .zip(recv_hoffs.iter())
1475 .map(|(((port_ident, buf_ident), &kind), &hoff_id)| {
1476 let hoff_ffi = hoff_id.data().as_ffi();
1477 let work_done = Ident::new("__dfir_work_done", Span::call_site());
1481 let metrics = Ident::new("__dfir_metrics", Span::call_site());
1482
1483 let (len_expr, drain_expr) = match kind {
1485 HandoffKind::Singleton | HandoffKind::Optional => (
1486 quote! { if #buf_ident.is_some() { 1usize } else { 0usize } },
1487 quote! { #root::dfir_pipes::pull::iter(#buf_ident.take().into_iter()) },
1488 ),
1489 HandoffKind::Vec => {
1490 let drain_ident = if back_edge_hoffs_and_lazyness.contains_key(hoff_id) {
1494 &self.hoff_back_ident(hoff_id, buf_ident.span())
1495 } else {
1496 buf_ident
1497 };
1498 (
1499 quote! { #drain_ident.len() },
1500 quote! { #root::dfir_pipes::pull::iter(#drain_ident.drain(..)) },
1501 )
1502 }
1503 };
1504
1505 quote_spanned! {port_ident.span()=>
1506 {
1507 let hoff_len = #len_expr;
1508 if hoff_len > 0 {
1509 #work_done = true;
1510 }
1511 let hoff_metrics = &#metrics.handoffs[
1512 #root::slotmap::KeyData::from_ffi(#hoff_ffi).into()
1513 ];
1514 hoff_metrics.total_items_count.update(|x| x + hoff_len);
1515 hoff_metrics.curr_items_count.set(hoff_len);
1516 }
1517 let #port_ident = #drain_expr;
1518 }
1519 })
1520 .collect();
1521
1522 let send_port_code: Vec<TokenStream> = send_port_idents
1524 .iter()
1525 .zip(send_buf_idents.iter())
1526 .zip(send_kinds.iter())
1527 .map(|((port_ident, buf_ident), &kind)| {
1528 match kind {
1529 HandoffKind::Singleton => {
1530 quote_spanned! {port_ident.span()=>
1532 let #port_ident = #root::dfir_pipes::push::for_each(|__item| {
1533 if #buf_ident.replace(__item).is_some() {
1534 panic!("singleton() received more than one item");
1535 }
1536 });
1537 }
1538 }
1539 HandoffKind::Optional => {
1540 quote_spanned! {port_ident.span()=>
1542 let #port_ident = #root::dfir_pipes::push::for_each(|__item| {
1543 if #buf_ident.replace(__item).is_some() {
1544 panic!("optional() received more than one item");
1545 }
1546 });
1547 }
1548 }
1549 HandoffKind::Vec => {
1550 quote_spanned! {port_ident.span()=>
1551 let #port_ident = #root::dfir_pipes::push::for_each(|item| { #buf_ident.push(item); });
1553 }
1554 }
1555 }
1556 })
1557 .collect();
1558
1559 let loop_id = self.node_loop(subgraph_nodes[0]);
1561
1562 let mut subgraph_op_iter_code = Vec::new();
1563 let mut subgraph_op_iter_after_code = Vec::new();
1564 {
1565 let pull_to_push_idx = self.find_pull_to_push_idx(subgraph_nodes);
1566
1567 let (pull_half, push_half) = subgraph_nodes.split_at(pull_to_push_idx);
1568 let nodes_iter = pull_half.iter().chain(push_half.iter().rev());
1569
1570 for (idx, &node_id) in nodes_iter.enumerate() {
1571 let node = &self.nodes[node_id];
1572 assert!(
1573 matches!(node, GraphNode::Operator(_)),
1574 "Handoffs are not part of subgraphs."
1575 );
1576 let op_inst = &self.operator_instances[node_id];
1577
1578 let op_span = node.span();
1579 let op_name = op_inst.op_constraints.name;
1580 let root = change_spans(root.clone(), op_span);
1582 let op_constraints = OPERATORS
1583 .iter()
1584 .find(|op| op_name == op.name)
1585 .unwrap_or_else(|| panic!("Failed to find op: {}", op_name));
1586
1587 let ident = self.node_as_ident(node_id, false);
1588
1589 {
1590 let mut input_edges = self
1593 .graph
1594 .predecessor_edges(node_id)
1595 .map(|edge_id| (self.edge_ports(edge_id).1, edge_id))
1596 .collect::<Vec<_>>();
1597 input_edges.sort();
1599
1600 let inputs = input_edges
1601 .iter()
1602 .map(|&(_port, edge_id)| {
1603 let (pred, _) = self.edge(edge_id);
1604 self.node_as_ident(pred, true)
1605 })
1606 .collect::<Vec<_>>();
1607
1608 let mut output_edges = self
1610 .graph
1611 .successor_edges(node_id)
1612 .map(|edge_id| (&self.ports[edge_id].0, edge_id))
1613 .collect::<Vec<_>>();
1614 output_edges.sort();
1616
1617 let outputs = output_edges
1618 .iter()
1619 .map(|&(_port, edge_id)| {
1620 let (_, succ) = self.edge(edge_id);
1621 self.node_as_ident(succ, false)
1622 })
1623 .collect::<Vec<_>>();
1624
1625 let is_pull = idx < pull_to_push_idx;
1626
1627 let df_local = &Ident::new(GRAPH, op_span.resolved_at(df.span()));
1636 let context = &Ident::new(CONTEXT, op_span.resolved_at(context.span()));
1637
1638 let singletons_resolved =
1639 self.helper_resolve_singletons(node_id, op_span);
1640
1641 let arguments = &process_singletons::postprocess_singletons(
1642 op_inst.arguments_raw.clone(),
1643 singletons_resolved,
1644 );
1645
1646 let source_tag = 'a: {
1647 if let Some(tag) = self.operator_tag.get(node_id).cloned() {
1648 break 'a tag;
1649 }
1650
1651 if proc_macro::is_available() {
1652 let op_span = op_span.unwrap();
1653 break 'a format!(
1654 "loc_{}_{}_{}_{}_{}",
1655 crate::pretty_span::make_source_path_relative(
1656 &op_span.file()
1657 )
1658 .display()
1659 .to_string()
1660 .replace(|x: char| !x.is_ascii_alphanumeric(), "_"),
1661 op_span.start().line(),
1662 op_span.start().column(),
1663 op_span.end().line(),
1664 op_span.end().column(),
1665 );
1666 }
1667
1668 format!(
1669 "loc_nopath_{}_{}_{}_{}",
1670 op_span.start().line,
1671 op_span.start().column,
1672 op_span.end().line,
1673 op_span.end().column
1674 )
1675 };
1676
1677 let work_fn = format_ident!(
1678 "{}__{}__{}",
1679 ident,
1680 op_name,
1681 source_tag,
1682 span = op_span
1683 );
1684 let work_fn_async = format_ident!("{}__async", work_fn, span = op_span);
1685
1686 let context_args = WriteContextArgs {
1687 root: &root,
1688 df_ident: df_local,
1689 context,
1690 subgraph_id,
1691 node_id,
1692 loop_id,
1693 op_span,
1694 op_tag: self.operator_tag.get(node_id).cloned(),
1695 work_fn: &work_fn,
1696 work_fn_async: &work_fn_async,
1697 ident: &ident,
1698 is_pull,
1699 inputs: &inputs,
1700 outputs: &outputs,
1701 op_name,
1702 op_inst,
1703 arguments,
1704 };
1705
1706 let write_result =
1707 (op_constraints.write_fn)(&context_args, diagnostics);
1708 let OperatorWriteOutput {
1709 write_prologue,
1710 write_iterator,
1711 write_iterator_after,
1712 write_tick_end,
1713 } = write_result.unwrap_or_else(|()| {
1714 assert!(
1715 diagnostics.has_error(),
1716 "Operator `{}` returned `Err` but emitted no diagnostics, this is a bug.",
1717 op_name,
1718 );
1719 OperatorWriteOutput {
1720 write_iterator: null_write_iterator_fn(&context_args),
1721 ..Default::default()
1722 }
1723 });
1724
1725 op_prologue_code.push(syn::parse_quote! {
1726 #[allow(non_snake_case)]
1727 #[inline(always)]
1728 fn #work_fn<T>(thunk: impl ::std::ops::FnOnce() -> T) -> T {
1729 thunk()
1730 }
1731
1732 #[allow(non_snake_case)]
1733 #[inline(always)]
1734 async fn #work_fn_async<T>(
1735 thunk: impl ::std::future::Future<Output = T>,
1736 ) -> T {
1737 thunk.await
1738 }
1739 });
1740 op_prologue_code.push(write_prologue);
1741 op_tick_end_code.push(write_tick_end);
1742 subgraph_op_iter_code.push(write_iterator);
1743
1744 if include_type_guards {
1745 let type_guard = if is_pull {
1746 quote_spanned! {op_span=>
1747 let #ident = {
1748 #[allow(non_snake_case)]
1749 #[inline(always)]
1750 pub fn #work_fn<Item, Input>(input: Input)
1751 -> impl #root::dfir_pipes::pull::Pull<Item = Item, Meta = (), CanPend = Input::CanPend, CanEnd = Input::CanEnd>
1752 where
1753 Input: #root::dfir_pipes::pull::Pull<Item = Item, Meta = ()>,
1754 {
1755 #root::pin_project_lite::pin_project! {
1756 #[repr(transparent)]
1757 struct Pull<Item, Input: #root::dfir_pipes::pull::Pull<Item = Item>> {
1758 #[pin]
1759 inner: Input
1760 }
1761 }
1762
1763 impl<Item, Input> #root::dfir_pipes::pull::Pull for Pull<Item, Input>
1764 where
1765 Input: #root::dfir_pipes::pull::Pull<Item = Item>,
1766 {
1767 type Ctx<'ctx> = Input::Ctx<'ctx>;
1768
1769 type Item = Item;
1770 type Meta = Input::Meta;
1771 type CanPend = Input::CanPend;
1772 type CanEnd = Input::CanEnd;
1773
1774 #[inline(always)]
1775 fn pull(
1776 self: ::std::pin::Pin<&mut Self>,
1777 ctx: &mut Self::Ctx<'_>,
1778 ) -> #root::dfir_pipes::pull::PullStep<Self::Item, Self::Meta, Self::CanPend, Self::CanEnd> {
1779 #root::dfir_pipes::pull::Pull::pull(self.project().inner, ctx)
1780 }
1781
1782 #[inline(always)]
1783 fn size_hint(&self) -> (usize, Option<usize>) {
1784 #root::dfir_pipes::pull::Pull::size_hint(&self.inner)
1785 }
1786 }
1787
1788 Pull {
1789 inner: input
1790 }
1791 }
1792 #work_fn::<_, _>( #ident )
1793 };
1794 }
1795 } else {
1796 quote_spanned! {op_span=>
1797 let #ident = {
1798 #[allow(non_snake_case)]
1799 #[inline(always)]
1800 pub fn #work_fn<Item, Psh>(psh: Psh) -> impl #root::dfir_pipes::push::Push<Item, (), CanPend = Psh::CanPend>
1801 where
1802 Psh: #root::dfir_pipes::push::Push<Item, ()>
1803 {
1804 #root::pin_project_lite::pin_project! {
1805 #[repr(transparent)]
1806 struct PushGuard<Psh> {
1807 #[pin]
1808 inner: Psh,
1809 }
1810 }
1811
1812 impl<Item, Psh> #root::dfir_pipes::push::Push<Item, ()> for PushGuard<Psh>
1813 where
1814 Psh: #root::dfir_pipes::push::Push<Item, ()>,
1815 {
1816 type Ctx<'ctx> = Psh::Ctx<'ctx>;
1817
1818 type CanPend = Psh::CanPend;
1819
1820 #[inline(always)]
1821 fn poll_ready(
1822 self: ::std::pin::Pin<&mut Self>,
1823 ctx: &mut Self::Ctx<'_>,
1824 ) -> #root::dfir_pipes::push::PushStep<Self::CanPend> {
1825 #root::dfir_pipes::push::Push::poll_ready(self.project().inner, ctx)
1826 }
1827
1828 #[inline(always)]
1829 fn start_send(
1830 self: ::std::pin::Pin<&mut Self>,
1831 item: Item,
1832 meta: (),
1833 ) {
1834 #root::dfir_pipes::push::Push::start_send(self.project().inner, item, meta)
1835 }
1836
1837 #[inline(always)]
1838 fn poll_finalize(
1839 self: ::std::pin::Pin<&mut Self>,
1840 ctx: &mut Self::Ctx<'_>,
1841 ) -> #root::dfir_pipes::push::PushStep<Self::CanPend> {
1842 #root::dfir_pipes::push::Push::poll_finalize(self.project().inner, ctx)
1843 }
1844
1845 #[inline(always)]
1846 fn size_hint(
1847 self: ::std::pin::Pin<&mut Self>,
1848 hint: (usize, Option<usize>),
1849 ) {
1850 #root::dfir_pipes::push::Push::size_hint(self.project().inner, hint)
1851 }
1852 }
1853
1854 PushGuard {
1855 inner: psh
1856 }
1857 }
1858 #work_fn( #ident )
1859 };
1860 }
1861 };
1862 subgraph_op_iter_code.push(type_guard);
1863 }
1864 subgraph_op_iter_after_code.push(write_iterator_after);
1865 }
1866 }
1867
1868 {
1869 let pull_ident = if 0 < pull_to_push_idx {
1871 self.node_as_ident(subgraph_nodes[pull_to_push_idx - 1], false)
1872 } else {
1873 recv_port_idents[0].clone()
1875 };
1876
1877 #[rustfmt::skip]
1878 let push_ident = if let Some(&node_id) =
1879 subgraph_nodes.get(pull_to_push_idx)
1880 {
1881 self.node_as_ident(node_id, false)
1882 } else if 1 == send_port_idents.len() {
1883 send_port_idents[0].clone()
1885 } else {
1886 diagnostics.push(Diagnostic::spanned(
1887 pull_ident.span(),
1888 Level::Error,
1889 "Degenerate subgraph detected, is there a disconnected `null()` or other degenerate pipeline somewhere?",
1890 ));
1891 continue;
1892 };
1893
1894 let pivot_span = pull_ident
1896 .span()
1897 .join(push_ident.span())
1898 .unwrap_or_else(|| push_ident.span());
1899 let pivot_fn_ident = Ident::new(
1900 &format!("pivot_run_sg_{:?}", subgraph_id.data()),
1901 pivot_span,
1902 );
1903 let root = change_spans(root.clone(), pivot_span);
1904 subgraph_op_iter_code.push(quote_spanned! {pivot_span=>
1905 #[inline(always)]
1906 fn #pivot_fn_ident<Pul, Psh, Item>(pull: Pul, push: Psh)
1907 -> impl ::std::future::Future<Output = ()>
1908 where
1909 Pul: #root::dfir_pipes::pull::Pull<Item = Item>,
1910 Psh: #root::dfir_pipes::push::Push<Item, Pul::Meta>,
1911 {
1912 #root::dfir_pipes::pull::Pull::send_push(pull, push)
1913 }
1914 (#pivot_fn_ident)(#pull_ident, #push_ident).await;
1915 });
1916 }
1917 };
1918
1919 let sg_fut_ident = subgraph_id.as_ident(Span::call_site());
1923
1924 let send_metrics_code = send_hoffs
1926 .iter()
1927 .zip(send_buf_idents.iter())
1928 .zip(send_kinds.iter())
1929 .map(|((&hoff_id, buf_ident), &kind)| {
1930 let hoff_ffi = hoff_id.data().as_ffi();
1931 let len_expr = match kind {
1932 HandoffKind::Singleton | HandoffKind::Optional => {
1933 quote! { if #buf_ident.is_some() { 1 } else { 0 } }
1934 }
1935 HandoffKind::Vec => {
1936 quote! { #buf_ident.len() }
1937 }
1938 };
1939 quote! {
1940 __dfir_metrics.handoffs[
1941 #root::slotmap::KeyData::from_ffi(#hoff_ffi).into()
1942 ].curr_items_count.set(#len_expr);
1943 }
1944 })
1945 .collect::<Vec<_>>();
1946
1947 let send_hoff_make_code = send_buf_idents.iter()
1951 .zip(send_kinds.iter())
1952 .zip(send_hoffs.iter())
1953 .filter_map(|((buf_ident, &kind), &hoff_id)| {
1954 let span = buf_ident.span();
1955 if back_edge_hoffs_and_lazyness.contains_key(hoff_id) {
1956 Some(quote_spanned! {span=>
1959 #buf_ident.clear();
1960 })
1961 } else {
1962 let receiver_loop = self
1965 .node_successors(hoff_id)
1966 .next()
1967 .and_then(|(_, succ)| self.node_loop(succ));
1968 let is_exit = if let Some(sender_loop) = sg_loop {
1969 receiver_loop == self.loop_parent(sender_loop)
1970 } else {
1971 false
1972 };
1973 if is_exit {
1974 None
1976 } else {
1977 Some(match kind {
1978 HandoffKind::Vec => quote_spanned! {span=>
1979 let mut #buf_ident = #root::bumpalo::collections::Vec::new_in(&#bump_ident);
1980 },
1981 HandoffKind::Singleton | HandoffKind::Optional => quote_spanned! {span=>
1982 let mut #buf_ident = ::std::option::Option::None;
1983 },
1984 })
1985 }
1986 }
1987 })
1988 .collect::<Vec<_>>();
1989 let recv_hoff_drop_code = recv_buf_idents
1993 .iter()
1994 .zip(recv_hoffs.iter())
1995 .filter(|&(_, &hoff_id)| !back_edge_hoffs_and_lazyness.contains_key(hoff_id))
1996 .map(|(buf_ident, _)| {
1997 let span = buf_ident.span();
1998 quote_spanned! {span=>
1999 let _ = #buf_ident;
2000 }
2001 });
2002
2003 let sg_block = quote! {
2005 #( #send_hoff_make_code )*
2007
2008 let #sg_fut_ident = async {
2009 let #context = &#df;
2010 #( #recv_port_code )*
2011 #( #send_port_code )*
2012 #( #subgraph_op_iter_code )*
2013 #( #subgraph_op_iter_after_code )*
2014 };
2015 {
2016 let sg_metrics = &__dfir_metrics.subgraphs[
2018 #root::slotmap::KeyData::from_ffi(#sg_metrics_ffi).into()
2019 ];
2020 #root::scheduled::metrics::InstrumentSubgraph::new(
2021 #sg_fut_ident, sg_metrics
2022 ).await;
2023 sg_metrics.total_run_count.update(|x| x + 1);
2024
2025 #( #send_metrics_code )*
2027
2028 #( #recv_hoff_drop_code )*
2030 }
2031 };
2032 if let Some((_, body)) = loop_stack.last_mut() {
2033 body.extend(sg_block);
2034 } else {
2035 current_output.extend(sg_block);
2036 }
2037 }
2038 }
2039
2040 let gated_subgraph_code = {
2042 while let Some((closed_loop, child_body)) = loop_stack.pop() {
2043 let target = if let Some((_, parent_body)) = loop_stack.last_mut() {
2044 parent_body
2045 } else {
2046 &mut current_output
2047 };
2048 self.emit_loop_gate(
2049 closed_loop,
2050 child_body,
2051 &loop_input_handoffs,
2052 &back_edge_hoffs_and_lazyness,
2053 &loop_swap_code,
2054 target,
2055 );
2056 }
2057 current_output
2058 };
2059
2060 if diagnostics.has_error() {
2061 return Err(std::mem::take(diagnostics));
2062 }
2063 let _ = diagnostics; let (meta_graph_arg, diagnostics_arg) = if include_meta {
2066 let meta_graph_json = serde_json::to_string(&self).unwrap();
2067 let meta_graph_json = Literal::string(&meta_graph_json);
2068
2069 let serde_diagnostics: Vec<_> = diagnostics.iter().map(Diagnostic::to_serde).collect();
2070 let diagnostics_json = serde_json::to_string(&*serde_diagnostics).unwrap();
2071 let diagnostics_json = Literal::string(&diagnostics_json);
2072
2073 (
2074 quote! { Some(#meta_graph_json) },
2075 quote! { Some(#diagnostics_json) },
2076 )
2077 } else {
2078 (quote! { None }, quote! { None })
2079 };
2080
2081 let metrics_init_code = {
2083 let handoff_inits = handoff_nodes.iter().map(|&(node_id, _, _)| {
2084 let ffi = node_id.data().as_ffi();
2085 quote! {
2086 dfir_metrics.handoffs.insert(
2087 #root::slotmap::KeyData::from_ffi(#ffi).into(),
2088 ::std::default::Default::default(),
2089 );
2090 }
2091 });
2092 let subgraph_inits = all_subgraphs.iter().map(|&(sg_id, _)| {
2093 let ffi = sg_id.data().as_ffi();
2094 quote! {
2095 dfir_metrics.subgraphs.insert(
2096 #root::slotmap::KeyData::from_ffi(#ffi).into(),
2097 ::std::default::Default::default(),
2098 );
2099 }
2100 });
2101 handoff_inits.chain(subgraph_inits).collect::<Vec<_>>()
2102 };
2103
2104 let back_buffer_idents = back_buffer_idents_laziness
2106 .iter()
2107 .map(|(back_ident, _, _)| back_ident);
2108 let defer_tick_buf_idents = back_buffer_idents_laziness
2110 .iter()
2111 .map(|(_, buf_ident, _)| buf_ident);
2112 let non_lazy_schedule_idents: Vec<&Ident> = handoff_nodes
2117 .iter()
2118 .filter_map(|&(hoff_id, _, _)| {
2119 let delay_type = self.handoff_delay_type(hoff_id)?;
2120 if matches!(delay_type, DelayType::TickLazy | DelayType::LoopLazy) {
2122 return None;
2123 }
2124 let span = self.nodes[hoff_id].span();
2125 let expected_back_ident = self.hoff_back_ident(hoff_id, span);
2126 let entry = back_buffer_idents_laziness
2127 .iter()
2128 .find(|(back_ident, _, _)| *back_ident == expected_back_ident)?;
2129
2130 if delay_type == DelayType::Tick {
2132 let consumer_loop = self
2133 .node_successors(hoff_id)
2134 .next()
2135 .and_then(|(_, succ)| self.node_subgraph(succ))
2136 .and_then(|sg| self.subgraph_loop(sg));
2137 if consumer_loop.is_some_and(|lid| self.loop_parent(lid).is_none()) {
2138 return Some(&entry.0); }
2140 }
2141 Some(&entry.1) })
2143 .collect();
2144
2145 Ok(quote! {
2148 {
2149 #prefix
2150
2151 use #root::{var_expr, var_args};
2152
2153 let __dfir_wake_state = ::std::sync::Arc::new(
2154 #root::scheduled::context::WakeState::default()
2155 );
2156
2157 let __dfir_metrics = {
2158 let mut dfir_metrics = #root::scheduled::metrics::DfirMetrics::default();
2159 #( #metrics_init_code )*
2160 ::std::rc::Rc::new(dfir_metrics)
2161 };
2162
2163 #[allow(unused_mut)]
2164 let mut #df = #root::scheduled::context::Context::new(
2165 ::std::clone::Clone::clone(&__dfir_wake_state),
2166 __dfir_metrics,
2167 );
2168
2169 #( #op_prologue_code )*
2170
2171 #( let mut #back_buffer_idents = ::std::vec::Vec::new(); )*
2175 #( let mut #defer_tick_buf_idents = ::std::vec::Vec::new(); )*
2176
2177 let mut #bump_ident = #root::bumpalo::Bump::new();
2179
2180 let mut __dfir_work_done = true;
2185 #[allow(unused_qualifications, unused_mut, unused_variables, clippy::await_holding_refcell_ref, clippy::deref_addrof)]
2186 let __dfir_inline_tick = async move |#df: &mut #root::scheduled::context::Context| {
2187 #bump_ident.reset();
2189
2190 {
2191 let __dfir_metrics = #df.metrics();
2192
2193 #gated_subgraph_code
2194
2195 if false #( || !#non_lazy_schedule_idents.is_empty() )* {
2198 #df.schedule_subgraph(true);
2199 }
2200
2201 #( #back_edge_swap_code )*
2204 }
2205
2206 #( #op_tick_end_code )*
2208
2209 #df.__end_tick();
2210
2211 ::std::mem::take(&mut __dfir_work_done)
2212 };
2213 #root::scheduled::context::Dfir::new(
2214 __dfir_inline_tick,
2215 #df,
2216 #meta_graph_arg,
2217 #diagnostics_arg,
2218 )
2219 }
2220 })
2221 }
2222
2223 pub fn node_color_map(&self) -> SparseSecondaryMap<GraphNodeId, Color> {
2226 let mut node_color_map: SparseSecondaryMap<GraphNodeId, Color> = self
2227 .node_ids()
2228 .filter_map(|node_id| {
2229 let op_color = self.node_color(node_id)?;
2230 Some((node_id, op_color))
2231 })
2232 .collect();
2233
2234 for sg_nodes in self.subgraph_nodes.values() {
2236 let pull_to_push_idx = self.find_pull_to_push_idx(sg_nodes);
2237
2238 for (idx, node_id) in sg_nodes.iter().copied().enumerate() {
2239 let is_pull = idx < pull_to_push_idx;
2240 node_color_map.insert(node_id, if is_pull { Color::Pull } else { Color::Push });
2241 }
2242 }
2243
2244 node_color_map
2245 }
2246
2247 pub fn to_mermaid(&self, write_config: &WriteConfig) -> String {
2249 let mut output = String::new();
2250 self.write_mermaid(&mut output, write_config).unwrap();
2251 output
2252 }
2253
2254 pub fn write_mermaid(
2256 &self,
2257 output: impl std::fmt::Write,
2258 write_config: &WriteConfig,
2259 ) -> std::fmt::Result {
2260 let mut graph_write = Mermaid::new(output);
2261 self.write_graph(&mut graph_write, write_config)
2262 }
2263
2264 pub fn to_dot(&self, write_config: &WriteConfig) -> String {
2266 let mut output = String::new();
2267 let mut graph_write = Dot::new(&mut output);
2268 self.write_graph(&mut graph_write, write_config).unwrap();
2269 output
2270 }
2271
2272 pub fn write_dot(
2274 &self,
2275 output: impl std::fmt::Write,
2276 write_config: &WriteConfig,
2277 ) -> std::fmt::Result {
2278 let mut graph_write = Dot::new(output);
2279 self.write_graph(&mut graph_write, write_config)
2280 }
2281
2282 pub(crate) fn write_graph<W>(
2284 &self,
2285 mut graph_write: W,
2286 write_config: &WriteConfig,
2287 ) -> Result<(), W::Err>
2288 where
2289 W: GraphWrite,
2290 {
2291 fn helper_edge_label(
2292 src_port: &PortIndexValue,
2293 dst_port: &PortIndexValue,
2294 ) -> Option<String> {
2295 let src_label = match src_port {
2296 PortIndexValue::Path(path) => Some(path.to_token_stream().to_string()),
2297 PortIndexValue::Int(index) => Some(index.value.to_string()),
2298 _ => None,
2299 };
2300 let dst_label = match dst_port {
2301 PortIndexValue::Path(path) => Some(path.to_token_stream().to_string()),
2302 PortIndexValue::Int(index) => Some(index.value.to_string()),
2303 _ => None,
2304 };
2305 let label = match (src_label, dst_label) {
2306 (Some(l1), Some(l2)) => Some(format!("{}\n{}", l1, l2)),
2307 (Some(l1), None) => Some(l1),
2308 (None, Some(l2)) => Some(l2),
2309 (None, None) => None,
2310 };
2311 label
2312 }
2313
2314 let node_color_map = self.node_color_map();
2316
2317 graph_write.write_prologue()?;
2319
2320 let mut skipped_handoffs = BTreeSet::new();
2322 for (node_id, node) in self.nodes() {
2323 if matches!(node, GraphNode::Handoff { .. }) && write_config.no_handoffs {
2324 skipped_handoffs.insert(node_id);
2325 continue;
2326 }
2327 graph_write.write_node_definition(
2328 node_id,
2329 &if write_config.op_short_text {
2330 node.to_name_string()
2331 } else if write_config.op_text_no_imports {
2332 let full_text = node.to_pretty_string();
2334 let mut output = String::new();
2335 for sentence in full_text.split('\n') {
2336 if sentence.trim().starts_with("use") {
2337 continue;
2338 }
2339 output.push('\n');
2340 output.push_str(sentence);
2341 }
2342 output.into()
2343 } else {
2344 node.to_pretty_string()
2345 },
2346 if write_config.no_pull_push {
2347 None
2348 } else {
2349 node_color_map.get(node_id).copied()
2350 },
2351 )?;
2352 }
2353
2354 for (edge_id, (src_id, mut dst_id)) in self.edges() {
2356 if skipped_handoffs.contains(&src_id) {
2358 continue;
2359 }
2360
2361 let (src_port, mut dst_port) = self.edge_ports(edge_id);
2362 if skipped_handoffs.contains(&dst_id) {
2363 let mut handoff_succs = self.node_successors(dst_id);
2367 if handoff_succs.len() == 0 {
2368 continue;
2369 }
2370 let (succ_edge, succ_node) = handoff_succs.next().unwrap();
2371 dst_id = succ_node;
2372 dst_port = self.edge_ports(succ_edge).1;
2373 }
2374
2375 let label = helper_edge_label(src_port, dst_port);
2376 let delay_type = self
2377 .node_op_inst(dst_id)
2378 .and_then(|op_inst| (op_inst.op_constraints.input_delaytype_fn)(dst_port));
2379 graph_write.write_edge(src_id, dst_id, delay_type, label.as_deref(), false)?;
2380 }
2381
2382 if !write_config.no_references {
2384 for dst_id in self.node_ids() {
2385 for src_ref_id in self
2386 .node_handoff_references(dst_id)
2387 .iter()
2388 .filter_map(|r| r.node_id)
2389 {
2390 let resolved_src = if skipped_handoffs.contains(&src_ref_id) {
2393 self.node_predecessor_nodes(src_ref_id).next()
2394 } else {
2395 Some(src_ref_id)
2396 };
2397 let Some(resolved_src) = resolved_src else {
2398 continue;
2399 };
2400 let label = None;
2401 graph_write.write_edge(resolved_src, dst_id, None, label, true)?;
2402 }
2403 }
2404 }
2405
2406 let loop_subgraphs = self.subgraph_ids().map(|sg_id| {
2414 let loop_id = if write_config.no_loops {
2415 None
2416 } else {
2417 self.subgraph_loop(sg_id)
2418 };
2419 (loop_id, sg_id)
2420 });
2421 let loop_subgraphs = into_group_map(loop_subgraphs);
2422 for (loop_id, subgraph_ids) in loop_subgraphs {
2423 if let Some(loop_id) = loop_id {
2424 graph_write.write_loop_start(loop_id)?;
2425 }
2426
2427 let subgraph_varnames_nodes = subgraph_ids.into_iter().flat_map(|sg_id| {
2429 self.subgraph(sg_id).iter().copied().map(move |node_id| {
2430 let opt_sg_id = if write_config.no_subgraphs {
2431 None
2432 } else {
2433 Some(sg_id)
2434 };
2435 (opt_sg_id, (self.node_varname(node_id), node_id))
2436 })
2437 });
2438 let subgraph_varnames_nodes = into_group_map(subgraph_varnames_nodes);
2439 for (sg_id, varnames) in subgraph_varnames_nodes {
2440 if let Some(sg_id) = sg_id {
2441 graph_write.write_subgraph_start(sg_id)?;
2442 }
2443
2444 let varname_nodes = varnames.into_iter().map(|(varname, node)| {
2446 let varname = if write_config.no_varnames {
2447 None
2448 } else {
2449 varname
2450 };
2451 (varname, node)
2452 });
2453 let varname_nodes = into_group_map(varname_nodes);
2454 for (varname, node_ids) in varname_nodes {
2455 if let Some(varname) = varname {
2456 graph_write.write_varname_start(&varname.0.to_string(), sg_id)?;
2457 }
2458
2459 for node_id in node_ids {
2461 graph_write.write_node(node_id)?;
2462 }
2463
2464 if varname.is_some() {
2465 graph_write.write_varname_end()?;
2466 }
2467 }
2468
2469 if sg_id.is_some() {
2470 graph_write.write_subgraph_end()?;
2471 }
2472 }
2473
2474 if loop_id.is_some() {
2475 graph_write.write_loop_end()?;
2476 }
2477 }
2478
2479 graph_write.write_epilogue()?;
2481
2482 Ok(())
2483 }
2484
2485 pub fn surface_syntax_string(&self) -> String {
2487 let mut string = String::new();
2488 self.write_surface_syntax(&mut string).unwrap();
2489 string
2490 }
2491
2492 pub fn write_surface_syntax(&self, write: &mut impl std::fmt::Write) -> std::fmt::Result {
2494 for (key, node) in self.nodes.iter() {
2495 match node {
2496 GraphNode::Operator(op) => {
2497 writeln!(write, "_{:?} = {};", key.data(), op.to_token_stream())?;
2498 }
2499 GraphNode::Handoff {
2500 kind: HandoffKind::Vec,
2501 ..
2502 } => {
2503 writeln!(write, "_{:?} = handoff();", key.data())?;
2504 }
2505 GraphNode::Handoff {
2506 kind: HandoffKind::Singleton,
2507 ..
2508 } => {
2509 writeln!(write, "_{:?} = singleton();", key.data())?;
2510 }
2511 GraphNode::Handoff {
2512 kind: HandoffKind::Optional,
2513 ..
2514 } => {
2515 writeln!(write, "_{:?} = optional();", key.data())?;
2516 }
2517 GraphNode::ModuleBoundary { .. } => panic!(),
2518 }
2519 }
2520 writeln!(write)?;
2521 for (e, (src_key, dst_key)) in self.graph.edges() {
2522 let (src_port, dst_port) = self.edge_ports(e);
2523 let src_port_str = if src_port.is_specified() {
2524 format!("[{}]", src_port)
2525 } else {
2526 String::new()
2527 };
2528 let dst_port_str = if dst_port.is_specified() {
2529 format!("[{}]", dst_port)
2530 } else {
2531 String::new()
2532 };
2533 writeln!(
2534 write,
2535 "_{:?}{} -> {}_{:?};",
2536 src_key.data(),
2537 src_port_str,
2538 dst_port_str,
2539 dst_key.data()
2540 )?;
2541 }
2542 Ok(())
2543 }
2544
2545 pub fn mermaid_string_flat(&self) -> String {
2547 let mut string = String::new();
2548 self.write_mermaid_flat(&mut string).unwrap();
2549 string
2550 }
2551
2552 pub fn write_mermaid_flat(&self, write: &mut impl std::fmt::Write) -> std::fmt::Result {
2554 writeln!(write, "flowchart TB")?;
2555 for (key, node) in self.nodes.iter() {
2556 match node {
2557 GraphNode::Operator(operator) => writeln!(
2558 write,
2559 " %% {span}\n {id:?}[\"{row_col} <tt>{code}</tt>\"]",
2560 span = PrettySpan(node.span()),
2561 id = key.data(),
2562 row_col = PrettyRowCol(node.span()),
2563 code = operator
2564 .to_token_stream()
2565 .to_string()
2566 .replace('&', "&")
2567 .replace('<', "<")
2568 .replace('>', ">")
2569 .replace('"', """)
2570 .replace('\n', "<br>"),
2571 ),
2572 GraphNode::Handoff {
2573 kind: HandoffKind::Vec,
2574 ..
2575 } => {
2576 writeln!(write, r#" {:?}{{"{}"}}"#, key.data(), HANDOFF_NODE_STR)
2577 }
2578 GraphNode::Handoff {
2579 kind: HandoffKind::Singleton | HandoffKind::Optional,
2580 ..
2581 } => {
2582 writeln!(
2583 write,
2584 r#" {:?}{{"{}"}}"#,
2585 key.data(),
2586 SINGLETON_SLOT_NODE_STR
2587 )
2588 }
2589 GraphNode::ModuleBoundary { .. } => {
2590 writeln!(
2591 write,
2592 r#" {:?}{{"{}"}}"#,
2593 key.data(),
2594 MODULE_BOUNDARY_NODE_STR
2595 )
2596 }
2597 }?;
2598 }
2599 writeln!(write)?;
2600 for (_e, (src_key, dst_key)) in self.graph.edges() {
2601 writeln!(write, " {:?}-->{:?}", src_key.data(), dst_key.data())?;
2602 }
2603 Ok(())
2604 }
2605}
2606
2607impl DfirGraph {
2609 pub fn loop_ids(&self) -> slotmap::basic::Keys<'_, GraphLoopId, Vec<GraphNodeId>> {
2611 self.loop_nodes.keys()
2612 }
2613
2614 pub fn loops(&self) -> slotmap::basic::Iter<'_, GraphLoopId, Vec<GraphNodeId>> {
2616 self.loop_nodes.iter()
2617 }
2618
2619 pub fn insert_loop(&mut self, parent_loop: Option<GraphLoopId>) -> GraphLoopId {
2621 let loop_id = self.loop_nodes.insert(Vec::new());
2622 self.loop_children.insert(loop_id, Vec::new());
2623 if let Some(parent_loop) = parent_loop {
2624 self.loop_parent.insert(loop_id, parent_loop);
2625 self.loop_children
2626 .get_mut(parent_loop)
2627 .unwrap()
2628 .push(loop_id);
2629 } else {
2630 self.root_loops.push(loop_id);
2631 }
2632 loop_id
2633 }
2634
2635 pub fn node_loop(&self, node_id: GraphNodeId) -> Option<GraphLoopId> {
2637 self.node_loops.get(node_id).copied()
2638 }
2639
2640 pub fn subgraph_loop(&self, subgraph_id: GraphSubgraphId) -> Option<GraphLoopId> {
2642 let &node_id = self.subgraph(subgraph_id).first().unwrap();
2643 let out = self.node_loop(node_id);
2644 debug_assert!(
2645 self.subgraph(subgraph_id)
2646 .iter()
2647 .all(|&node_id| self.node_loop(node_id) == out),
2648 "Subgraph nodes should all have the same loop context."
2649 );
2650 out
2651 }
2652
2653 pub fn loop_parent(&self, loop_id: GraphLoopId) -> Option<GraphLoopId> {
2655 self.loop_parent.get(loop_id).copied()
2656 }
2657
2658 pub fn loop_children(&self, loop_id: GraphLoopId) -> &Vec<GraphLoopId> {
2660 self.loop_children.get(loop_id).unwrap()
2661 }
2662
2663 pub fn root_loops(&self) -> &[GraphLoopId] {
2665 &self.root_loops
2666 }
2667}
2668
2669#[derive(Clone, Debug, Default)]
2671#[cfg_attr(feature = "clap-derive", derive(clap::Args))]
2672pub struct WriteConfig {
2673 #[cfg_attr(feature = "clap-derive", arg(long))]
2675 pub no_subgraphs: bool,
2676 #[cfg_attr(feature = "clap-derive", arg(long))]
2678 pub no_varnames: bool,
2679 #[cfg_attr(feature = "clap-derive", arg(long))]
2681 pub no_pull_push: bool,
2682 #[cfg_attr(feature = "clap-derive", arg(long))]
2684 pub no_handoffs: bool,
2685 #[cfg_attr(feature = "clap-derive", arg(long))]
2687 pub no_references: bool,
2688 #[cfg_attr(feature = "clap-derive", arg(long))]
2690 pub no_loops: bool,
2691
2692 #[cfg_attr(feature = "clap-derive", arg(long))]
2694 pub op_short_text: bool,
2695 #[cfg_attr(feature = "clap-derive", arg(long))]
2697 pub op_text_no_imports: bool,
2698}
2699
2700#[derive(Copy, Clone, Debug)]
2702#[cfg_attr(feature = "clap-derive", derive(clap::Parser, clap::ValueEnum))]
2703pub enum WriteGraphType {
2704 Mermaid,
2706 Dot,
2708}
2709
2710fn into_group_map<K, V>(iter: impl IntoIterator<Item = (K, V)>) -> BTreeMap<K, Vec<V>>
2712where
2713 K: Ord,
2714{
2715 let mut out: BTreeMap<_, Vec<_>> = BTreeMap::new();
2716 for (k, v) in iter {
2717 out.entry(k).or_default().push(v);
2718 }
2719 out
2720}