logicaffeine_compile/codegen_mx/
mod.rs1use crate::codegen_ana::{try_region, AnalogArtifact, AnalogTarget, RegionLane};
20use logicaffeine_language::ast::stmt::{Expr, Stmt};
21
22#[derive(Debug, Clone)]
24pub struct RegionBinding {
25 pub fn_name: String,
26 pub lane: RegionLane,
27 pub artifact: AnalogArtifact,
28}
29
30#[derive(Debug, Clone, PartialEq)]
33pub struct RegionRefusal {
34 pub fn_name: String,
35 pub reason: String,
36}
37
38#[derive(Debug, Clone)]
41pub struct MixedPartition {
42 pub bindings: Vec<RegionBinding>,
43 pub refusals: Vec<RegionRefusal>,
44}
45
46impl MixedPartition {
47 pub fn all_fns(&self) -> Vec<&str> {
49 let mut v: Vec<(&str, bool)> = self
50 .bindings
51 .iter()
52 .map(|b| (b.fn_name.as_str(), true))
53 .chain(self.refusals.iter().map(|r| (r.fn_name.as_str(), false)))
54 .collect();
55 v.sort_by_key(|(n, _)| n.to_string());
56 v.into_iter().map(|(n, _)| n).collect()
57 }
58}
59
60pub fn partition(source: &str, target: &AnalogTarget) -> MixedPartition {
64 let fn_names: Vec<String> =
65 crate::ui_bridge::with_parsed_program(source, |parsed, interner| match parsed {
66 Ok((stmts, _types, _pol)) => stmts
67 .iter()
68 .filter_map(|s| match s {
69 Stmt::FunctionDef { name, .. } => Some(interner.resolve(*name).to_string()),
70 _ => None,
71 })
72 .collect(),
73 Err(_) => Vec::new(),
74 });
75 let mut bindings = Vec::new();
76 let mut refusals = Vec::new();
77 for fn_name in fn_names {
78 match try_region(source, &fn_name, target) {
79 Ok((lane, artifact)) => bindings.push(RegionBinding {
80 fn_name,
81 lane,
82 artifact,
83 }),
84 Err(e) => refusals.push(RegionRefusal {
85 fn_name,
86 reason: e.to_string(),
87 }),
88 }
89 }
90 MixedPartition { bindings, refusals }
91}
92
93#[derive(Debug, Clone)]
95pub struct StackBinding {
96 pub binding: RegionBinding,
97 pub placement: crate::codegen_ana::stack_place::StackPlacement,
98 pub thermal: crate::codegen_ana::thermal::ThermalMap,
101}
102
103#[derive(Debug, Clone)]
108pub struct StackPartition {
109 pub bindings: Vec<StackBinding>,
110 pub refusals: Vec<RegionRefusal>,
111}
112
113pub fn partition_on_stack(
117 source: &str,
118 stack: &crate::codegen_ana::stack::StackTarget,
119 hop_cost_pitches: f64,
120) -> StackPartition {
121 use crate::codegen_ana::stack_place::place_stack;
122 use crate::codegen_ana::thermal::{check_cooling, thermal_default_opts, thermal_map};
123
124 let Some((_, target)) = stack.analog_tier() else {
125 return StackPartition {
126 bindings: Vec::new(),
127 refusals: vec![RegionRefusal {
128 fn_name: "*".to_string(),
129 reason: "stack has no analog tier".to_string(),
130 }],
131 };
132 };
133 let flat = partition(source, &target.clone());
134 let mut bindings = Vec::new();
135 let mut refusals = flat.refusals;
136 for b in flat.bindings {
137 let placed = match place_stack(&b.artifact.schedule.quantized, stack, hop_cost_pitches) {
138 Ok(p) => p,
139 Err(e) => {
140 refusals.push(RegionRefusal {
141 fn_name: b.fn_name,
142 reason: e.to_string(),
143 });
144 continue;
145 }
146 };
147 let map = match thermal_map(
148 stack,
149 &b.artifact.schedule.quantized,
150 &placed,
151 &thermal_default_opts(),
152 ) {
153 Ok(m) => m,
154 Err(e) => {
155 refusals.push(RegionRefusal {
156 fn_name: b.fn_name,
157 reason: e.to_string(),
158 });
159 continue;
160 }
161 };
162 if let Err(e) = check_cooling(&map, stack) {
163 refusals.push(RegionRefusal {
164 fn_name: b.fn_name,
165 reason: e.to_string(),
166 });
167 continue;
168 }
169 bindings.push(StackBinding {
170 binding: b,
171 placement: placed,
172 thermal: map,
173 });
174 }
175 StackPartition { bindings, refusals }
176}
177
178#[derive(Clone, Copy, Debug, PartialEq, Eq)]
180pub enum Fabric {
181 Dataflow,
183 Dynamics,
185 Relaxation,
187 Digital,
189}
190
191pub fn fabrics(source: &str, target: &AnalogTarget) -> Vec<(String, Fabric)> {
196 fn expr_calls(e: &Expr<'_>, needle: &str, interner: &crate::intern::Interner) -> bool {
197 match e {
198 Expr::Call { function, args } => {
199 interner.resolve(*function) == needle
200 || args.iter().any(|a| expr_calls(a, needle, interner))
201 }
202 Expr::BinaryOp { left, right, .. } => {
203 expr_calls(left, needle, interner) || expr_calls(right, needle, interner)
204 }
205 _ => false,
206 }
207 }
208 fn body_calls(stmts: &[Stmt<'_>], needle: &str, interner: &crate::intern::Interner) -> bool {
209 stmts.iter().any(|s| match s {
210 Stmt::Let { value, .. } | Stmt::Set { value, .. } => {
211 expr_calls(value, needle, interner)
212 }
213 Stmt::Return { value: Some(v) } => expr_calls(v, needle, interner),
214 Stmt::If {
215 then_block,
216 else_block,
217 ..
218 } => {
219 body_calls(then_block, needle, interner)
220 || else_block
221 .map(|e| body_calls(e, needle, interner))
222 .unwrap_or(false)
223 }
224 Stmt::While { body, .. } | Stmt::Repeat { body, .. } => {
225 body_calls(body, needle, interner)
226 }
227 _ => false,
228 })
229 }
230 let names_and_shapes: Vec<(String, bool, bool)> =
231 crate::ui_bridge::with_parsed_program(source, |parsed, interner| match parsed {
232 Ok((stmts, _t, _p)) => stmts
233 .iter()
234 .filter_map(|s| match s {
235 Stmt::FunctionDef { name, body, .. } => Some((
236 interner.resolve(*name).to_string(),
237 body_calls(body, "solveLinear", interner),
238 body_calls(body, "minimizeQuadratic", interner),
239 )),
240 _ => None,
241 })
242 .collect(),
243 Err(_) => Vec::new(),
244 });
245 names_and_shapes
246 .into_iter()
247 .map(|(name, solves, minimizes)| {
248 let fabric = if minimizes {
249 Fabric::Relaxation
250 } else if solves {
251 Fabric::Dynamics
252 } else if try_region(source, &name, target).is_ok() {
253 Fabric::Dataflow
254 } else {
255 Fabric::Digital
256 };
257 (name, fabric)
258 })
259 .collect()
260}