polydat_core/compile/
lattice.rs1use crate::kernel::PolydatProgram;
19
20pub struct ConeEntry {
22 pub label: String,
24 pub members: Vec<String>,
26 pub boundary_in: usize,
28 pub boundary_out: usize,
30}
31
32pub struct ResidueEntry {
34 pub name: String,
36 pub p3_classifiable: bool,
39 pub p2_capable: bool,
43}
44
45pub struct LatticeReport {
47 pub cones: Vec<ConeEntry>,
49 pub fused_nodes: usize,
51 pub residue: Vec<ResidueEntry>,
53 pub p2_headroom: usize,
57 pub p3_unfused: usize,
61}
62
63#[cfg(feature = "jit")]
64fn p3_classifiable(node: &dyn crate::ast::PolydatNode) -> bool {
65 !matches!(
66 crate::compile::jit::classify_node(node),
67 crate::compile::jit::JitOp::Fallback
68 )
69}
70
71#[cfg(not(feature = "jit"))]
72fn p3_classifiable(_node: &dyn crate::ast::PolydatNode) -> bool {
73 false
74}
75
76pub fn lattice_report(program: &PolydatProgram) -> LatticeReport {
78 let mut cones = Vec::new();
79 let mut residue = Vec::new();
80 let mut fused_nodes = 0;
81 for i in 0..program.node_count() {
82 let node = program.node_ref(i);
83 if let Some(sub) = node.fusion_subgraph() {
84 let members: Vec<String> = sub.members.iter().map(|m| m.meta().name.clone()).collect();
85 fused_nodes += members.len();
86 cones.push(ConeEntry {
87 label: node.meta().name.clone(),
88 members,
89 boundary_in: node.meta().wire_inputs().len(),
90 boundary_out: node.meta().outs.len(),
91 });
92 } else {
93 let wire_types = program.node_wire_types(i);
94 residue.push(ResidueEntry {
95 name: node.meta().name.clone(),
96 p3_classifiable: p3_classifiable(node),
97 p2_capable: crate::compile::node_tier(node, &wire_types)
100 != crate::ast::CompileLevel::Phase1,
101 });
102 }
103 }
104 let p2_headroom = residue
105 .iter()
106 .filter(|r| r.p2_capable && !r.p3_classifiable)
107 .count();
108 let p3_unfused = residue.iter().filter(|r| r.p3_classifiable).count();
109 LatticeReport {
110 cones,
111 fused_nodes,
112 residue,
113 p2_headroom,
114 p3_unfused,
115 }
116}
117
118impl std::fmt::Display for LatticeReport {
119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120 writeln!(
121 f,
122 "lattice: {} cone{} ({} node{} fused), {} interpreter node{}",
123 self.cones.len(),
124 if self.cones.len() == 1 { "" } else { "s" },
125 self.fused_nodes,
126 if self.fused_nodes == 1 { "" } else { "s" },
127 self.residue.len(),
128 if self.residue.len() == 1 { "" } else { "s" },
129 )?;
130 for cone in &self.cones {
131 writeln!(
132 f,
133 " cone {} — {} member{}, boundary {}→{}",
134 cone.label,
135 cone.members.len(),
136 if cone.members.len() == 1 { "" } else { "s" },
137 cone.boundary_in,
138 cone.boundary_out,
139 )?;
140 }
141 for r in &self.residue {
142 let tier = match (r.p3_classifiable, r.p2_capable) {
143 (true, _) => "p3-classifiable, unfused",
144 (false, true) => "p2-capable (headroom)",
145 (false, false) => "p1-only",
146 };
147 writeln!(f, " interp {:30} [{tier}]", r.name)?;
148 }
149 if self.p2_headroom > 0 {
150 writeln!(
151 f,
152 " headroom: {} node{} p2-capable without p3 — candidates \
153 for P2-at-cone-boundaries (SRD-105)",
154 self.p2_headroom,
155 if self.p2_headroom == 1 { "" } else { "s" },
156 )?;
157 }
158 Ok(())
159 }
160}