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