1use std::collections::HashMap;
16
17use crate::ast::SlotShape;
18use crate::ast::{PolydatNode, PortType};
19use crate::compile::closures::{
20 CompiledKernelPull, CompiledKernelPush, CompiledKernelPushPull, CompiledKernelRaw,
21};
22use crate::compile::select::{self, ProvMode};
23use crate::kernel::{PolydatKernel, PolydatProgram, WireSource};
24use crate::library::convert::{F64ToString, U64ToF64, U64ToString};
25use crate::library::json::JsonToStr;
26
27#[derive(Debug, Clone)]
30pub enum WireRef {
31 Input(String),
33 Node(String, usize),
35}
36
37impl WireRef {
38 pub fn node(name: impl Into<String>) -> Self {
40 WireRef::Node(name.into(), 0)
41 }
42
43 pub fn node_port(name: impl Into<String>, port: usize) -> Self {
45 WireRef::Node(name.into(), port)
46 }
47
48 pub fn input(name: impl Into<String>) -> Self {
50 WireRef::Input(name.into())
51 }
52}
53
54struct PendingNode {
55 name: String,
56 node: Box<dyn PolydatNode>,
57 inputs: Vec<WireRef>,
58}
59
60#[derive(Debug)]
62pub enum AssemblyError {
63 UnknownWire(String),
65 TypeMismatch {
67 from_node: String,
69 from_port: usize,
71 from_type: PortType,
73 to_node: String,
75 to_port: usize,
77 to_type: PortType,
79 },
80 DuplicateNode(String),
82 CycleDetected,
84 ArityMismatch {
86 node_name: String,
88 expected: usize,
90 got: usize,
92 },
93 Other(String),
95}
96
97impl std::fmt::Display for AssemblyError {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 match self {
100 AssemblyError::UnknownWire(name) => {
101 write!(f, "unknown wire: '{name}'\n\n")?;
102 writeln!(f, " No node output or coordinate named '{name}' exists.")?;
103 write!(
104 f,
105 " Check spelling, or add a node that produces this output."
106 )
107 }
108 AssemblyError::TypeMismatch {
109 from_node,
110 from_port,
111 from_type,
112 to_node,
113 to_port,
114 to_type,
115 } => {
116 writeln!(
117 f,
118 "type mismatch: cannot connect {from_type} output to {to_type} input"
119 )?;
120 writeln!(f)?;
121 writeln!(
122 f,
123 " {from_node} [{from_port}] ──({from_type})──▶ {to_node} [{to_port}] expects {to_type}"
124 )?;
125 writeln!(f)?;
126 let suggestion = match (from_type, to_type) {
128 (PortType::U64, PortType::Str) => {
129 Some("This should auto-convert. If you see this, file a bug.")
130 }
131 (PortType::F64, PortType::Str) => {
132 Some("This should auto-convert. If you see this, file a bug.")
133 }
134 (PortType::U64, PortType::F64) => {
135 Some("This should auto-convert. If you see this, file a bug.")
136 }
137 (PortType::U64, PortType::Bytes) => {
138 Some("Add u64_to_bytes() between them to convert.")
139 }
140 (PortType::Str, PortType::Bytes) => {
141 Some("String cannot be directly used as bytes.")
142 }
143 (PortType::U64, PortType::Json) => {
144 Some("Add to_json() between them to wrap as JSON.")
145 }
146 (PortType::Str, PortType::Json) => {
147 Some("Add str_to_json() to parse the string as JSON.")
148 }
149 (PortType::Bytes, PortType::Str) => {
150 Some("Add to_hex() or to_base64() to convert bytes to string.")
151 }
152 (PortType::Bytes, PortType::U64) => {
153 Some("Bytes cannot be directly converted to u64.")
154 }
155 _ => None,
156 };
157 if let Some(hint) = suggestion {
158 write!(f, " Hint: {hint}")?;
159 }
160 Ok(())
161 }
162 AssemblyError::DuplicateNode(name) => {
163 write!(f, "duplicate node name: '{name}'\n\n")?;
164 write!(f, " Two nodes cannot share the same name.")
165 }
166 AssemblyError::CycleDetected => {
167 write!(f, "cycle detected in DAG\n\n")?;
168 writeln!(
169 f,
170 " The graph contains a loop. Polydat graphs must be acyclic"
171 )?;
172 write!(f, " (data flows in one direction only).")
173 }
174 AssemblyError::ArityMismatch {
175 node_name,
176 expected,
177 got,
178 } => {
179 write!(f, "wrong number of inputs for '{node_name}'\n\n")?;
180 writeln!(f, " Expected {expected} input(s), but got {got}.")?;
181 if *got < *expected {
182 write!(f, " Connect more wires to this node's input ports.")
183 } else {
184 write!(f, " Disconnect extra wires from this node.")
185 }
186 }
187 AssemblyError::Other(msg) => write!(f, "{msg}"),
188 }
189 }
190}
191
192impl std::error::Error for AssemblyError {}
193
194pub(crate) struct ResolvedDag {
196 pub(crate) nodes: Vec<Box<dyn PolydatNode>>,
198 pub(crate) wiring: Vec<Vec<WireSource>>,
200 pub(crate) input_defs: Vec<crate::kernel::InputDef>,
202 pub(crate) coord_count: usize,
204 pub(crate) output_map: HashMap<String, (usize, usize)>,
206 pub(crate) output_order: Vec<String>,
208 pub(crate) source: String,
210 pub(crate) context: String,
212 pub(crate) output_modifiers: HashMap<String, crate::dsl::ast::BindingModifier>,
214 pub(crate) const_outputs: std::collections::HashSet<String>,
216 pub(crate) cursor_schemas: Vec<crate::iteration::source::SourceSchema>,
218}
219
220impl ResolvedDag {
221 fn input_names(&self) -> Vec<String> {
223 self.input_defs[..self.coord_count]
224 .iter()
225 .map(|d| d.name.clone())
226 .collect()
227 }
228}
229
230struct SlotLayout {
236 input_starts: Vec<usize>,
238 coord_slots: usize,
240 port_offsets: Vec<Vec<usize>>,
242 total_slots: usize,
244}
245
246fn slot_layout(resolved: &ResolvedDag) -> SlotLayout {
247 let mut input_starts = Vec::with_capacity(resolved.coord_count);
248 let mut next = 0usize;
249 for d in &resolved.input_defs {
250 input_starts.push(next);
251 next += d.port_type.slot_width();
252 }
253 let coord_slots = next;
254 let mut port_offsets: Vec<Vec<usize>> = Vec::with_capacity(resolved.nodes.len());
255 for node in &resolved.nodes {
256 let mut po = Vec::with_capacity(node.meta().outs.len());
257 for out in &node.meta().outs {
258 po.push(next);
259 next += out.typ.slot_width();
260 }
261 port_offsets.push(po);
262 }
263 SlotLayout {
264 input_starts,
265 coord_slots,
266 port_offsets,
267 total_slots: next,
268 }
269}
270
271fn node_step_op(
276 node: &dyn crate::ast::PolydatNode,
277 wire_types: &[PortType],
278) -> Option<(
279 crate::compile::closures::StepOp,
280 Vec<crate::ast::ScratchElem>,
281)> {
282 let meta = node.meta();
286 if (meta.name == "identity" || meta.name.starts_with("__port_")) && meta.outs.len() == 1 {
287 return Some(match meta.outs[0].typ.slot_color() {
288 crate::ast::SlotColor::Ref2 => {
289 let kit = ref_copy_kit(meta.outs[0].typ)?;
290 (crate::compile::closures::StepOp::Slot(kit.op), kit.scratch)
291 }
292 _ => (crate::compile::closures::StepOp::Copy, Vec::new()),
293 });
294 }
295 if let Some(op) = node.compiled_u64() {
296 return Some((crate::compile::closures::StepOp::U64(op), Vec::new()));
297 }
298 node.compiled_slot(wire_types)
299 .map(|kit| (crate::compile::closures::StepOp::Slot(kit.op), kit.scratch))
300}
301
302pub(crate) fn scratch_pairs(
313 name: &str,
314 ref_starts: &[usize],
315 scratch: &[crate::ast::ScratchElem],
316 base: usize,
317) -> Vec<(usize, usize)> {
318 use crate::ast::ScratchElem;
319 let publishing: Vec<usize> = scratch
320 .iter()
321 .enumerate()
322 .filter(|(_, e)| {
323 !matches!(
324 e,
325 ScratchElem::Slots | ScratchElem::Kernels | ScratchElem::State
326 )
327 })
328 .map(|(k, _)| base + k)
329 .collect();
330 assert!(
331 publishing.len() <= ref_starts.len(),
332 "slot-op step '{name}' declares {} publishing scratch entries for {} Ref output ports",
333 publishing.len(),
334 ref_starts.len()
335 );
336 ref_starts.iter().copied().zip(publishing).collect()
337}
338
339pub(crate) fn ref_copy_kit(ty: PortType) -> Option<crate::ast::CompiledSlotKit> {
345 use crate::ast::ScratchBuf;
346 let elem = ty.scratch_elem()?;
347 Some(crate::ast::CompiledSlotKit {
348 scratch: vec![elem],
349 op: Box::new(
350 move |inputs: &[u64], outputs: &mut [u64], scratch: &mut [ScratchBuf]| {
351 let (p, n) = (inputs[0] as usize, inputs[1] as usize);
352 macro_rules! copy_into {
353 ($v:expr, $t:ty) => {{
354 $v.clear();
355 $v.extend_from_slice(unsafe {
359 std::slice::from_raw_parts(p as *const $t, n)
360 });
361 }};
362 }
363 match &mut scratch[0] {
364 ScratchBuf::Str(v) | ScratchBuf::Bytes(v) => copy_into!(v, u8),
365 ScratchBuf::F32(v) => copy_into!(v, f32),
366 ScratchBuf::F64(v) => copy_into!(v, f64),
367 ScratchBuf::F16(v) => copy_into!(v, half::f16),
368 ScratchBuf::I8(v) => copy_into!(v, i8),
369 ScratchBuf::I16(v) => copy_into!(v, i16),
370 ScratchBuf::I32(v) => copy_into!(v, i32),
371 ScratchBuf::I64(v) => copy_into!(v, i64),
372 ScratchBuf::Value(v) => {
373 v.clear();
374 if n > 0 {
375 v.push(unsafe { (*(p as *const crate::ast::Value)).clone() });
377 }
378 }
379 ScratchBuf::Slots(_) | ScratchBuf::Kernels(_) | ScratchBuf::State(_) => {
380 unreachable!("a copy owns only a value entry")
381 }
382 }
383 let (ptr, len) = scratch[0].ptr_len();
384 outputs[0] = ptr;
385 outputs[1] = len;
386 },
387 ),
388 })
389}
390
391pub(crate) fn identity_op(node: &dyn crate::ast::PolydatNode) -> Option<crate::ast::CompiledU64Op> {
397 let meta = node.meta();
398 if meta.name != "identity" || meta.outs.len() != 1 {
399 return None;
400 }
401 if meta.outs[0].typ.slot_color() == crate::ast::SlotColor::Ref2 {
402 return None;
403 }
404 Some(Box::new(|inputs: &[u64], outputs: &mut [u64]| {
405 outputs.copy_from_slice(inputs)
406 }))
407}
408
409impl SlotLayout {
410 fn input_slots(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
413 let mut slots = Vec::new();
414 for source in &resolved.wiring[node_idx] {
415 let (start, w) = match source {
416 WireSource::Input(c) => (
417 self.input_starts.get(*c).copied().unwrap_or(*c),
418 resolved
419 .input_defs
420 .get(*c)
421 .map(|d| d.port_type.slot_width())
422 .unwrap_or(1),
423 ),
424 WireSource::NodeOutput(u, p) => (
425 self.port_offsets[*u][*p],
426 resolved.nodes[*u].meta().outs[*p].typ.slot_width(),
427 ),
428 };
429 slots.extend(start..start + w);
430 }
431 slots
432 }
433
434 fn output_slots(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
436 let mut slots = Vec::new();
437 for (p, out) in resolved.nodes[node_idx].meta().outs.iter().enumerate() {
438 let start = self.port_offsets[node_idx][p];
439 slots.extend(start..start + out.typ.slot_width());
440 }
441 slots
442 }
443
444 fn named_outputs(&self, resolved: &ResolvedDag) -> HashMap<String, usize> {
446 resolved
447 .output_map
448 .iter()
449 .map(|(name, (n, p))| (name.clone(), self.port_offsets[*n][*p]))
450 .collect()
451 }
452
453 fn ref_slot_mask(&self, resolved: &ResolvedDag) -> Vec<bool> {
459 use crate::ast::SlotColor;
460 let mut mask = vec![false; self.total_slots];
461 let mut mark = |start: usize, color: SlotColor| match color {
462 SlotColor::Ref2 => {
463 mask[start] = true;
464 mask[start + 1] = true;
465 }
466 SlotColor::Imm1 | SlotColor::Imm2 => {}
467 };
468 for (i, d) in resolved.input_defs.iter().enumerate() {
469 mark(self.input_starts[i], d.port_type.slot_color());
470 }
471 for (n, node) in resolved.nodes.iter().enumerate() {
472 for (p, out) in node.meta().outs.iter().enumerate() {
473 mark(self.port_offsets[n][p], out.typ.slot_color());
474 }
475 }
476 mask
477 }
478
479 fn ref_output_starts(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
483 resolved.nodes[node_idx]
484 .meta()
485 .outs
486 .iter()
487 .enumerate()
488 .filter(|(_, out)| out.typ.slot_color() == crate::ast::SlotColor::Ref2)
489 .map(|(p, _)| self.port_offsets[node_idx][p])
490 .collect()
491 }
492
493 fn expand_dependents(&self, resolved: &ResolvedDag, deps: &[Vec<usize>]) -> Vec<Vec<usize>> {
499 let mut out = Vec::with_capacity(self.coord_slots);
500 for (i, d) in resolved.input_defs.iter().enumerate() {
501 for _ in 0..d.port_type.slot_width() {
502 out.push(deps.get(i).cloned().unwrap_or_default());
503 }
504 }
505 out
506 }
507}
508
509pub struct PolydatAssembler {
511 input_defs: Vec<crate::kernel::InputDef>,
513 coord_count: usize,
515 nodes: Vec<PendingNode>,
516 output_order: Vec<String>,
518 outputs: HashMap<String, WireRef>,
519 source: String,
521 context: String,
523 output_modifiers: HashMap<String, crate::dsl::ast::BindingModifier>,
525 const_outputs: std::collections::HashSet<String>,
528 pub(crate) strict_values: bool,
533 pub(crate) strict_types: bool,
541 pub(crate) strict: bool,
546 pub(crate) jit_mode: Option<crate::compile::cone::JitMode>,
551 cursor_schemas: Vec<crate::iteration::source::SourceSchema>,
555}
556
557type P2Layout = (
561 usize,
562 usize,
563 Vec<crate::compile::closures::P2Step>,
564 HashMap<String, usize>,
565 Vec<bool>,
566 crate::compile::closures::P2Extras,
567);
568
569#[cfg(feature = "jit")]
575type JitLayout = (
576 usize,
577 usize,
578 Vec<(crate::compile::jit::JitOp, Vec<usize>, Vec<usize>)>,
579 HashMap<String, usize>,
580 crate::compile::jit::ScratchPlan,
581 Vec<usize>,
582);
583
584impl PolydatAssembler {
585 pub fn new(input_names: Vec<String>) -> Self {
587 let coord_count = input_names.len();
588 let input_defs: Vec<crate::kernel::InputDef> = input_names
589 .into_iter()
590 .map(|name| crate::kernel::InputDef {
591 name,
592 default: crate::ast::Value::U64(0),
593 port_type: crate::ast::PortType::U64,
594 kind: crate::kernel::InputKind::Coordinate,
595 })
596 .collect();
597 Self {
598 input_defs,
599 coord_count,
600 nodes: Vec::new(),
601 output_order: Vec::new(),
602 outputs: HashMap::new(),
603 source: String::new(),
604 context: "(assembler)".into(),
605 output_modifiers: HashMap::new(),
606 const_outputs: std::collections::HashSet::new(),
607 strict_values: false,
608 strict_types: false,
609 strict: false,
610 jit_mode: None,
611 cursor_schemas: Vec::new(),
612 }
613 }
614
615 pub fn set_cursor_schemas(&mut self, schemas: Vec<crate::iteration::source::SourceSchema>) {
620 self.cursor_schemas = schemas;
621 }
622
623 pub fn cursor_schemas(&self) -> &[crate::iteration::source::SourceSchema] {
625 &self.cursor_schemas
626 }
627
628 pub fn set_strict_wires(&mut self, strict_types: bool, strict_values: bool) {
632 self.strict_types = strict_types;
633 self.strict_values = strict_values;
634 }
635
636 pub fn set_strict(&mut self, strict: bool) {
642 self.strict = strict;
643 }
644
645 pub fn set_jit_mode(&mut self, mode: crate::compile::cone::JitMode) {
648 self.jit_mode = Some(mode);
649 }
650
651 pub fn set_context(&mut self, source: &str, context: &str) {
654 self.source = source.to_string();
655 self.context = context.to_string();
656 }
657
658 pub fn add_node(
660 &mut self,
661 name: impl Into<String>,
662 node: Box<dyn PolydatNode>,
663 inputs: Vec<WireRef>,
664 ) -> &mut Self {
665 self.nodes.push(PendingNode {
666 name: name.into(),
667 node,
668 inputs,
669 });
670 self
671 }
672
673 pub fn set_output_modifier(&mut self, name: &str, modifier: crate::dsl::ast::BindingModifier) {
675 if modifier != crate::dsl::ast::BindingModifier::NONE {
676 self.output_modifiers.insert(name.to_string(), modifier);
677 }
678 }
679
680 pub fn mark_const_output(&mut self, name: &str) {
685 self.const_outputs.insert(name.to_string());
686 }
687
688 pub fn add_output(&mut self, name: impl Into<String>, wire: WireRef) -> &mut Self {
690 let name = name.into();
691 if !self.outputs.contains_key(&name) {
692 self.output_order.push(name.clone());
693 }
694 self.outputs.insert(name, wire);
695 self
696 }
697
698 pub fn add_input(
708 &mut self,
709 name: impl Into<String>,
710 default: crate::ast::Value,
711 port_type: crate::ast::PortType,
712 kind: crate::kernel::InputKind,
713 ) -> &mut Self {
714 self.input_defs.push(crate::kernel::InputDef {
715 name: name.into(),
716 default,
717 port_type,
718 kind,
719 });
720 self
721 }
722
723 pub fn set_input_type(&mut self, name: &str, port_type: crate::ast::PortType) {
728 if let Some(d) = self.input_defs.iter_mut().find(|d| d.name == name) {
729 d.port_type = port_type;
730 }
731 }
732
733 pub fn input_names(&self) -> Vec<&str> {
735 self.input_defs.iter().map(|d| d.name.as_str()).collect()
736 }
737
738 pub fn node_output_type(&self, name: &str) -> Option<crate::ast::PortType> {
743 self.nodes
744 .iter()
745 .find(|n| n.name == name)
746 .and_then(|n| n.node.meta().outs.first())
747 .map(|p| p.typ)
748 }
749
750 pub fn output_names(&self) -> Vec<&str> {
752 self.outputs.keys().map(|s| s.as_str()).collect()
753 }
754
755 pub fn output_type(&self, name: &str) -> Option<PortType> {
759 self.nodes
760 .iter()
761 .find(|pn| pn.name == name)
762 .and_then(|pn| pn.node.meta().outs.first())
763 .map(|port| port.typ)
764 }
765
766 pub fn input_type(&self, name: &str) -> Option<PortType> {
768 self.input_defs
769 .iter()
770 .find(|d| d.name == name)
771 .map(|d| d.port_type)
772 }
773
774 pub fn wire_type(&self, wire: &WireRef) -> Option<PortType> {
779 match wire {
780 WireRef::Input(name) => self.input_type(name),
781 WireRef::Node(name, port_idx) => self
782 .nodes
783 .iter()
784 .find(|pn| &pn.name == name)
785 .and_then(|pn| pn.node.meta().outs.get(*port_idx))
786 .map(|p| p.typ),
787 }
788 }
789
790 pub fn compile(self) -> Result<PolydatKernel, AssemblyError> {
792 self.compile_with_log(None)
793 }
794
795 pub fn compile_with_log(
797 self,
798 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
799 ) -> Result<PolydatKernel, AssemblyError> {
800 let jit_mode = self.jit_mode.unwrap_or_default();
801 let strict = self.strict;
802 let mut resolved = self.resolve_with_log(log.as_deref_mut())?;
803 crate::compile::cone::extract_jit_cones(&mut resolved, jit_mode);
804 let _coord_names = resolved.input_names();
805 let modifiers = resolved.output_modifiers.clone();
806 let cursors = std::mem::take(&mut resolved.cursor_schemas);
807 let mut kernel = PolydatKernel::new_with_inputs(
808 resolved.nodes,
809 resolved.wiring,
810 resolved.input_defs,
811 resolved.coord_count,
812 resolved.output_map,
813 resolved.output_order,
814 resolved.const_outputs,
815 modifiers,
816 &resolved.source,
817 &resolved.context,
818 log,
819 strict,
820 )
821 .map_err(AssemblyError::Other)?;
822 if !cursors.is_empty() {
823 kernel.set_cursor_schemas(cursors);
824 }
825 kernel.set_cone_mode(jit_mode);
826 Ok(kernel)
827 }
828
829 fn refuse_strict(resolved: &ResolvedDag) -> Result<(), AssemblyError> {
833 let classes = PolydatProgram::classify_lifecycle(
834 &resolved.nodes,
835 &resolved.wiring,
836 &resolved.input_defs,
837 &resolved.output_map,
838 &resolved.output_modifiers,
839 );
840 let is_init: Vec<bool> = classes
841 .lifecycle
842 .iter()
843 .map(|lc| *lc == crate::kernel::EvalLifecycle::CompileConst)
844 .collect();
845 match PolydatProgram::strict_violation(
846 &resolved.nodes,
847 &resolved.wiring,
848 &is_init,
849 &resolved.output_map,
850 &resolved.output_modifiers,
851 ) {
852 Some(violation) => Err(AssemblyError::Other(violation)),
853 None => Ok(()),
854 }
855 }
856
857 pub fn try_compile(self) -> Result<CompiledKernelPushPull, Box<PolydatKernel>> {
864 let resolved = self.resolve().expect("assembly validation failed");
865 let coord_names = resolved.input_names();
866 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
867 match Self::build_p2_layout(&resolved) {
868 Ok(r) => r,
869 Err(_) => {
871 return Err(Box::new(PolydatKernel::new(
872 resolved.nodes,
873 resolved.wiring,
874 coord_names,
875 resolved.output_map,
876 &resolved.source,
877 &resolved.context,
878 )));
879 }
880 };
881 let dependents = slot_layout(&resolved).expand_dependents(
882 &resolved,
883 &PolydatProgram::compute_dependents(
884 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
885 resolved.input_defs.len(),
886 ),
887 );
888 Ok(CompiledKernelPushPull::new(
889 coord_count,
890 total_slots,
891 steps,
892 output_map,
893 dependents,
894 ref_slots,
895 extras,
896 ))
897 }
898
899 pub fn try_compile_raw(self) -> Result<CompiledKernelRaw, Box<PolydatKernel>> {
901 let resolved = match self.resolve() {
902 Ok(r) => r,
903 Err(_) => {
904 return Err(Box::new(PolydatKernel::new(
905 vec![],
906 vec![],
907 vec![],
908 HashMap::new(),
909 "",
910 "(fallback)",
911 )));
912 }
913 };
914 let coord_names = resolved.input_names();
915 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
916 match Self::build_p2_layout(&resolved) {
917 Ok(r) => r,
918 Err(_) => {
919 return Err(Box::new(PolydatKernel::new(
920 resolved.nodes,
921 resolved.wiring,
922 coord_names,
923 resolved.output_map,
924 &resolved.source,
925 &resolved.context,
926 )));
927 }
928 };
929 Ok(CompiledKernelRaw::new(
930 coord_count,
931 total_slots,
932 steps,
933 output_map,
934 ref_slots,
935 extras,
936 ))
937 }
938
939 pub fn try_compile_push(self) -> Result<CompiledKernelPush, Box<PolydatKernel>> {
941 let resolved = match self.resolve() {
942 Ok(r) => r,
943 Err(_) => {
944 return Err(Box::new(PolydatKernel::new(
945 vec![],
946 vec![],
947 vec![],
948 HashMap::new(),
949 "",
950 "(fallback)",
951 )));
952 }
953 };
954 let coord_names = resolved.input_names();
955 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
956 match Self::build_p2_layout(&resolved) {
957 Ok(r) => r,
958 Err(_) => {
959 return Err(Box::new(PolydatKernel::new(
960 resolved.nodes,
961 resolved.wiring,
962 coord_names,
963 resolved.output_map,
964 &resolved.source,
965 &resolved.context,
966 )));
967 }
968 };
969 let dependents = slot_layout(&resolved).expand_dependents(
970 &resolved,
971 &PolydatProgram::compute_dependents(
972 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
973 resolved.input_defs.len(),
974 ),
975 );
976 Ok(CompiledKernelPush::new(
977 coord_count,
978 total_slots,
979 steps,
980 output_map,
981 dependents,
982 ref_slots,
983 extras,
984 ))
985 }
986
987 pub fn try_compile_pull(self) -> Result<CompiledKernelPull, Box<PolydatKernel>> {
989 let resolved = match self.resolve() {
990 Ok(r) => r,
991 Err(_) => {
992 return Err(Box::new(PolydatKernel::new(
993 vec![],
994 vec![],
995 vec![],
996 HashMap::new(),
997 "",
998 "(fallback)",
999 )));
1000 }
1001 };
1002 let coord_names = resolved.input_names();
1003 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
1004 match Self::build_p2_layout(&resolved) {
1005 Ok(r) => r,
1006 Err(_) => {
1007 return Err(Box::new(PolydatKernel::new(
1008 resolved.nodes,
1009 resolved.wiring,
1010 coord_names,
1011 resolved.output_map,
1012 &resolved.source,
1013 &resolved.context,
1014 )));
1015 }
1016 };
1017 let dependents = slot_layout(&resolved).expand_dependents(
1018 &resolved,
1019 &PolydatProgram::compute_dependents(
1020 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
1021 resolved.input_defs.len(),
1022 ),
1023 );
1024 Ok(CompiledKernelPull::new(
1025 coord_count,
1026 total_slots,
1027 steps,
1028 output_map,
1029 &dependents,
1030 ref_slots,
1031 extras,
1032 ))
1033 }
1034
1035 fn build_p2_layout(resolved: &ResolvedDag) -> Result<P2Layout, String> {
1040 let layout = slot_layout(resolved);
1041
1042 let mut compiled_ops = Vec::with_capacity(resolved.nodes.len());
1043 let mut extras = crate::compile::closures::P2Extras::default();
1044 for (node_idx, node) in resolved.nodes.iter().enumerate() {
1045 compiled_ops.push(
1046 node_step_op(node.as_ref(), &wire_types_of(resolved, node_idx)).ok_or_else(
1047 || {
1048 format!(
1049 "node '{}' has no compiled form (docs/design/engine_parity.md)",
1050 node.meta().name
1051 )
1052 },
1053 )?,
1054 );
1055 }
1056 extras.externs = crate::compile::externs::Externs::new(
1057 &resolved.input_defs,
1058 resolved.coord_count,
1059 &layout.input_starts,
1060 &resolved.cursor_schemas,
1061 &shared_outputs_of(resolved),
1062 )?;
1063 extras.externs.set_output_names(&resolved.output_order);
1064 extras.output_types = resolved
1065 .output_map
1066 .iter()
1067 .map(|(name, (n, p))| (name.clone(), resolved.nodes[*n].meta().outs[*p].typ))
1068 .collect();
1069
1070 let classes = PolydatProgram::classify_lifecycle(
1074 &resolved.nodes,
1075 &resolved.wiring,
1076 &resolved.input_defs,
1077 &resolved.output_map,
1078 &resolved.output_modifiers,
1079 );
1080 let inventory = PolydatProgram::compute_node_inventory(&resolved.nodes, &resolved.wiring);
1081 let per_input = PolydatProgram::compute_dependents(
1082 &inventory.input_provenance,
1083 resolved.input_defs.len(),
1084 );
1085 extras.input_dependents = layout.expand_dependents(resolved, &per_input);
1086 extras.attribution = std::sync::Arc::new(Self::attribution_of(resolved));
1087
1088 let mut steps = Vec::with_capacity(resolved.nodes.len());
1089 for (node_idx, (op, scratch)) in compiled_ops.into_iter().enumerate() {
1090 steps.push(crate::compile::closures::P2Step {
1091 name: resolved.nodes[node_idx].meta().name.clone(),
1092 op,
1093 input_slots: layout.input_slots(resolved, node_idx),
1094 output_slots: layout.output_slots(resolved, node_idx),
1095 ref_output_starts: layout.ref_output_starts(resolved, node_idx),
1096 scratch,
1097 accepts_none: resolved.nodes[node_idx].accepts_none_inputs(),
1098 volatile: classes.nondeterministic[node_idx],
1099 constant: classes.lifecycle[node_idx] == crate::kernel::EvalLifecycle::CompileConst,
1100 side: matches!(
1101 resolved.nodes[node_idx].purity(),
1102 crate::ast::Purity::SideChannel { .. }
1103 ),
1104 });
1105 }
1106 let output_map = layout.named_outputs(resolved);
1107 let ref_slots = layout.ref_slot_mask(resolved);
1108
1109 Ok((
1110 layout.coord_slots,
1111 layout.total_slots,
1112 steps,
1113 output_map,
1114 ref_slots,
1115 extras,
1116 ))
1117 }
1118
1119 #[cfg(feature = "jit")]
1121 pub(crate) fn build_jit_layout(resolved: &ResolvedDag) -> Result<JitLayout, String> {
1122 let layout = slot_layout(resolved);
1123
1124 let mut scratch = crate::compile::jit::ScratchPlan::default();
1128 let mut jit_steps = Vec::new();
1129 for (node_idx, node) in resolved.nodes.iter().enumerate() {
1130 let mut jit_op = crate::compile::jit::classify_node_typed(
1131 node.as_ref(),
1132 &wire_types_of(resolved, node_idx),
1133 );
1134 if matches!(jit_op, crate::compile::jit::JitOp::Fallback) {
1135 return Err(format!(
1136 "node '{}' has no native form and no kit; pure native code cannot run it",
1137 node.meta().name
1138 ));
1139 }
1140 let base = scratch.elems.len();
1141 jit_op.place_scratch(base);
1142 let elems = jit_op.scratch_elems().to_vec();
1143 scratch.refs.extend(scratch_pairs(
1144 &node.meta().name,
1145 &layout.ref_output_starts(resolved, node_idx),
1146 &elems,
1147 base,
1148 ));
1149 scratch.elems.extend(elems);
1150 jit_steps.push((
1151 jit_op,
1152 layout.input_slots(resolved, node_idx),
1153 layout.output_slots(resolved, node_idx),
1154 ));
1155 }
1156
1157 let output_map = layout.named_outputs(resolved);
1158 let classes = PolydatProgram::classify_lifecycle(
1162 &resolved.nodes,
1163 &resolved.wiring,
1164 &resolved.input_defs,
1165 &resolved.output_map,
1166 &resolved.output_modifiers,
1167 );
1168 let volatile: Vec<usize> = (0..resolved.nodes.len())
1169 .filter(|&i| classes.nondeterministic[i])
1170 .collect();
1171 Ok((
1172 layout.coord_slots,
1173 layout.total_slots,
1174 jit_steps,
1175 output_map,
1176 scratch,
1177 volatile,
1178 ))
1179 }
1180
1181 #[cfg(feature = "jit")]
1184 fn jit_slot_info(resolved: &ResolvedDag) -> (Vec<bool>, HashMap<String, PortType>) {
1185 let layout = slot_layout(resolved);
1186 let guard = layout.ref_slot_mask(resolved);
1187 let types = resolved
1188 .output_map
1189 .iter()
1190 .map(|(name, (n, p))| (name.clone(), resolved.nodes[*n].meta().outs[*p].typ))
1191 .collect();
1192 (guard, types)
1193 }
1194
1195 #[cfg(feature = "jit")]
1200 pub fn try_compile_jit(self) -> Result<crate::compile::hybrid::HybridKernelPushPull, String> {
1201 self.compile_hybrid()
1202 }
1203
1204 #[cfg(feature = "jit")]
1206 pub fn try_compile_jit_raw(self) -> Result<crate::compile::hybrid::HybridKernelRaw, String> {
1207 Ok(self.compile_hybrid()?.into_raw())
1208 }
1209
1210 #[cfg(feature = "jit")]
1214 pub fn try_compile_jit_push(
1215 self,
1216 ) -> Result<crate::compile::hybrid::HybridKernelPushPull, String> {
1217 self.compile_hybrid()
1218 }
1219
1220 #[cfg(feature = "jit")]
1222 pub fn try_compile_jit_pull(self) -> Result<crate::compile::hybrid::HybridKernelPull, String> {
1223 Ok(self.compile_hybrid()?.into_pull())
1224 }
1225
1226 #[doc(hidden)]
1230 #[cfg(feature = "jit")]
1231 pub fn try_compile_pure_jit(self) -> Result<crate::compile::jit::JitKernelPushPull, String> {
1232 let resolved = self.resolve().map_err(|e| format!("{e}"))?;
1233 Self::jit_push_pull_from(resolved)
1234 }
1235
1236 #[cfg(feature = "jit")]
1237 fn jit_push_pull_from(
1238 resolved: ResolvedDag,
1239 ) -> Result<crate::compile::jit::JitKernelPushPull, String> {
1240 let _coord_names = resolved.input_names();
1241 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1242 Self::build_jit_layout(&resolved)?;
1243 let (guard, types) = Self::jit_slot_info(&resolved);
1244 let deps = slot_layout(&resolved).expand_dependents(
1245 &resolved,
1246 &PolydatProgram::compute_dependents(
1247 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
1248 resolved.input_defs.len(),
1249 ),
1250 );
1251 let externs = Self::externs_of(&resolved)?;
1252 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1253 let mut k = crate::compile::jit::compile_jit_push_pull(
1254 coord_count,
1255 total_slots,
1256 jit_steps,
1257 output_map,
1258 resolved.nodes,
1259 deps,
1260 externs,
1261 scratch,
1262 volatile,
1263 )?;
1264 k.set_slot_info(guard, types);
1265 k.set_attribution(attribution);
1266 Ok(k)
1267 }
1268
1269 fn externs_of(resolved: &ResolvedDag) -> Result<crate::compile::externs::Externs, String> {
1272 let layout = slot_layout(resolved);
1273 let mut externs = crate::compile::externs::Externs::new(
1274 &resolved.input_defs,
1275 resolved.coord_count,
1276 &layout.input_starts,
1277 &resolved.cursor_schemas,
1278 &shared_outputs_of(resolved),
1279 )?;
1280 externs.set_output_names(&resolved.output_order);
1281 Ok(externs)
1282 }
1283
1284 #[doc(hidden)]
1286 #[cfg(feature = "jit")]
1287 pub fn try_compile_pure_jit_raw(self) -> Result<crate::compile::jit::JitKernelRaw, String> {
1288 let resolved = self.resolve().map_err(|e| format!("{e}"))?;
1289 Self::jit_raw_from(resolved)
1290 }
1291
1292 pub(crate) fn attribution_of(resolved: &ResolvedDag) -> crate::compile::Attribution {
1297 let layout = slot_layout(resolved);
1298 let sites = resolved
1299 .nodes
1300 .iter()
1301 .enumerate()
1302 .map(|(node_idx, node)| {
1303 let mut outputs: Vec<String> = resolved
1304 .output_map
1305 .iter()
1306 .filter(|(_, (n, _))| *n == node_idx)
1307 .map(|(name, _)| name.clone())
1308 .collect();
1309 outputs.sort();
1310 let inputs = resolved.wiring[node_idx]
1311 .iter()
1312 .map(|source| match source {
1313 WireSource::Input(c) => (
1314 layout.input_starts.get(*c).copied().unwrap_or(*c),
1315 resolved
1316 .input_defs
1317 .get(*c)
1318 .map(|d| d.port_type)
1319 .unwrap_or(PortType::U64),
1320 ),
1321 WireSource::NodeOutput(u, p) => (
1322 layout.port_offsets[*u][*p],
1323 resolved.nodes[*u].meta().outs[*p].typ,
1324 ),
1325 })
1326 .collect();
1327 crate::compile::NodeSite {
1328 name: node.meta().name.to_string(),
1329 outputs,
1330 inputs,
1331 }
1332 })
1333 .collect();
1334 crate::compile::Attribution {
1335 sites,
1336 context: resolved.context.clone(),
1337 }
1338 }
1339
1340 #[cfg(feature = "jit")]
1341 fn jit_raw_from(resolved: ResolvedDag) -> Result<crate::compile::jit::JitKernelRaw, String> {
1342 let _coord_names = resolved.input_names();
1343 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1344 Self::build_jit_layout(&resolved)?;
1345 let (guard, types) = Self::jit_slot_info(&resolved);
1346 let externs = Self::externs_of(&resolved)?;
1347 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1348 let mut k = crate::compile::jit::compile_jit_raw_with(
1349 coord_count,
1350 total_slots,
1351 jit_steps,
1352 output_map,
1353 resolved.nodes,
1354 externs,
1355 scratch,
1356 volatile,
1357 )?;
1358 k.set_slot_info(guard, types);
1359 k.set_attribution(attribution);
1360 Ok(k)
1361 }
1362
1363 #[cfg(feature = "jit")]
1369 pub fn try_compile_tier1_simd_ordinal(
1370 self,
1371 driving_input: &str,
1372 output: &str,
1373 ) -> Result<
1374 crate::compile::simd_tier1::Tier1SimdExecutor,
1375 crate::compile::simd_tier1::Tier1SimdError,
1376 > {
1377 let resolved = self.resolve().map_err(|error| {
1378 crate::compile::simd_tier1::Tier1SimdError::VectorGraphBuild(error.to_string())
1379 })?;
1380 crate::compile::simd_tier1::compile_tier1_ordinal(resolved, driving_input, output)
1381 }
1382
1383 #[doc(hidden)]
1385 #[cfg(feature = "jit")]
1386 pub fn try_compile_pure_jit_push(self) -> Result<crate::compile::jit::JitKernelPush, String> {
1387 let resolved = self.resolve().map_err(|e| format!("{e}"))?;
1388 Self::jit_push_from(resolved)
1389 }
1390
1391 #[cfg(feature = "jit")]
1392 fn jit_push_from(resolved: ResolvedDag) -> Result<crate::compile::jit::JitKernelPush, String> {
1393 let _coord_names = resolved.input_names();
1394 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1395 Self::build_jit_layout(&resolved)?;
1396 let deps = slot_layout(&resolved).expand_dependents(
1397 &resolved,
1398 &PolydatProgram::compute_dependents(
1399 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
1400 resolved.input_defs.len(),
1401 ),
1402 );
1403 let (guard, types) = Self::jit_slot_info(&resolved);
1404 let externs = Self::externs_of(&resolved)?;
1405 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1406 let mut k = crate::compile::jit::compile_jit_push(
1407 coord_count,
1408 total_slots,
1409 jit_steps,
1410 output_map,
1411 resolved.nodes,
1412 deps,
1413 externs,
1414 scratch,
1415 volatile,
1416 )?;
1417 k.set_slot_info(guard, types);
1418 k.set_attribution(attribution);
1419 Ok(k)
1420 }
1421
1422 #[doc(hidden)]
1424 #[cfg(feature = "jit")]
1425 pub fn try_compile_pure_jit_pull(self) -> Result<crate::compile::jit::JitKernelPull, String> {
1426 let resolved = self.resolve().map_err(|e| format!("{e}"))?;
1427 Self::jit_pull_from(resolved)
1428 }
1429
1430 #[cfg(feature = "jit")]
1431 fn jit_pull_from(resolved: ResolvedDag) -> Result<crate::compile::jit::JitKernelPull, String> {
1432 let _coord_names = resolved.input_names();
1433 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1434 Self::build_jit_layout(&resolved)?;
1435 let deps = slot_layout(&resolved).expand_dependents(
1436 &resolved,
1437 &PolydatProgram::compute_dependents(
1438 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
1439 resolved.input_defs.len(),
1440 ),
1441 );
1442 let (guard, types) = Self::jit_slot_info(&resolved);
1443 let externs = Self::externs_of(&resolved)?;
1444 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1445 let mut k = crate::compile::jit::compile_jit_pull(
1446 coord_count,
1447 total_slots,
1448 jit_steps,
1449 output_map,
1450 resolved.nodes,
1451 &deps,
1452 externs,
1453 scratch,
1454 volatile,
1455 )?;
1456 k.set_slot_info(guard, types);
1457 k.set_attribution(attribution);
1458 Ok(k)
1459 }
1460
1461 #[doc(hidden)]
1466 pub fn compile_hybrid(self) -> Result<crate::compile::hybrid::HybridKernel, String> {
1467 let resolved = self.resolve().map_err(|e| format!("{e}"))?;
1468 Self::hybrid_from(resolved)
1469 }
1470
1471 fn hybrid_from(resolved: ResolvedDag) -> Result<crate::compile::hybrid::HybridKernel, String> {
1472 let _coord_names = resolved.input_names();
1473 let layout = slot_layout(&resolved);
1474
1475 let output_map = layout.named_outputs(&resolved);
1476 let input_widths: Vec<usize> = resolved
1477 .input_defs
1478 .iter()
1479 .map(|d| d.port_type.slot_width())
1480 .collect();
1481
1482 let ref_slots = layout.ref_slot_mask(&resolved);
1483 let input_types: Vec<PortType> = resolved.input_defs.iter().map(|d| d.port_type).collect();
1484 let externs = Self::externs_of(&resolved)?;
1485 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1486 let classes = PolydatProgram::classify_lifecycle(
1489 &resolved.nodes,
1490 &resolved.wiring,
1491 &resolved.input_defs,
1492 &resolved.output_map,
1493 &resolved.output_modifiers,
1494 );
1495 let constant: Vec<bool> = classes
1496 .lifecycle
1497 .iter()
1498 .map(|lc| *lc == crate::kernel::EvalLifecycle::CompileConst)
1499 .collect();
1500 let mut kernel = crate::compile::hybrid::build_hybrid(
1501 &resolved.nodes,
1502 &resolved.wiring,
1503 layout.coord_slots,
1504 layout.total_slots,
1505 &layout.port_offsets,
1506 &layout.input_starts,
1507 &input_widths,
1508 output_map,
1509 ref_slots,
1510 &input_types,
1511 externs,
1512 constant,
1513 classes.nondeterministic,
1514 attribution,
1515 )?;
1516 kernel.retain_nodes(resolved.nodes);
1517 Ok(kernel)
1518 }
1519
1520 fn resolve(self) -> Result<ResolvedDag, AssemblyError> {
1522 self.resolve_with_log(None)
1523 }
1524
1525 fn resolve_with_log(
1526 self,
1527 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
1528 ) -> Result<ResolvedDag, AssemblyError> {
1529 if let Some(log) = log.as_deref_mut() {
1535 let cursor_slot = |name: &str| {
1536 self.cursor_schemas
1537 .iter()
1538 .any(|s| name.starts_with(&format!("{}__cursor", s.name)))
1539 };
1540 for def in &self.input_defs {
1541 if matches!(
1542 def.kind,
1543 crate::kernel::InputKind::ExternalWrite
1544 | crate::kernel::InputKind::IterationExtern
1545 ) && def.default == crate::ast::Value::None
1546 && !cursor_slot(&def.name)
1547 {
1548 log.push(crate::dsl::events::CompileEvent::ExternWithoutDefault {
1549 name: def.name.clone(),
1550 port_type: def.port_type.to_string(),
1551 });
1552 }
1553 }
1554 }
1555 let mut name_to_idx: HashMap<String, usize> = HashMap::new();
1557 for (i, pn) in self.nodes.iter().enumerate() {
1558 if name_to_idx.contains_key(&pn.name) {
1559 return Err(AssemblyError::DuplicateNode(pn.name.clone()));
1560 }
1561 name_to_idx.insert(pn.name.clone(), i);
1562 }
1563
1564 let input_to_idx: HashMap<String, usize> = self
1566 .input_defs
1567 .iter()
1568 .enumerate()
1569 .map(|(i, d)| (d.name.clone(), i))
1570 .collect();
1571
1572 for pn in &self.nodes {
1574 let expected = pn.node.meta().wire_inputs().len();
1575 let got = pn.inputs.len();
1576 if expected != got {
1577 return Err(AssemblyError::ArityMismatch {
1578 node_name: pn.name.clone(),
1579 expected,
1580 got,
1581 });
1582 }
1583 }
1584
1585 let mut all_nodes: Vec<PendingNode> = Vec::new();
1586 let mut all_name_to_idx: HashMap<String, usize> = HashMap::new();
1587 let mut adapter_count = 0usize;
1588 let mut assertion_count = 0usize;
1589 let strict_values = self.strict_values;
1590 let strict_types = self.strict_types;
1591 let strict = self.strict;
1592
1593 for pn in self.nodes {
1594 let idx = all_nodes.len();
1595 all_name_to_idx.insert(pn.name.clone(), idx);
1596 all_nodes.push(pn);
1597 }
1598
1599 let mut resolved_wiring: Vec<Vec<WireSource>> = Vec::new();
1600
1601 for node_idx in 0..all_nodes.len() {
1602 let mut node_wiring = Vec::new();
1603
1604 for (port_idx, wire_ref) in all_nodes[node_idx].inputs.clone().iter().enumerate() {
1605 let expected_type = all_nodes[node_idx].node.meta().wire_inputs()[port_idx].typ;
1606
1607 let (source, source_type) = match wire_ref {
1608 WireRef::Input(name) => {
1609 let input_idx = input_to_idx
1610 .get(name)
1611 .ok_or_else(|| AssemblyError::UnknownWire(name.clone()))?;
1612 let source_type = self.input_defs[*input_idx].port_type;
1613 (WireSource::Input(*input_idx), source_type)
1614 }
1615 WireRef::Node(name, out_port) => {
1616 let src_idx = all_name_to_idx
1617 .get(name)
1618 .ok_or_else(|| AssemblyError::UnknownWire(name.clone()))?;
1619 let src_type = all_nodes[*src_idx].node.meta().outs[*out_port].typ;
1620 (WireSource::NodeOutput(*src_idx, *out_port), src_type)
1621 }
1622 };
1623
1624 let node_name_for_typing = &all_nodes[node_idx].node.meta().name;
1656 let skip_type_check =
1657 UNTYPED_VARIADIC_NODES.contains(&node_name_for_typing.as_str());
1658
1659 if skip_type_check || source_type == expected_type {
1660 node_wiring.push(source);
1661 } else if let Some(adapter) = auto_adapter(source_type, expected_type) {
1662 if strict {
1663 return Err(AssemblyError::Other(format!(
1664 "strict mode: implicit type coercion {source_type} → {expected_type} \
1665 into '{}'. Use an explicit conversion function (e.g., u64_to_f64, \
1666 f64_to_u64).",
1667 all_nodes[node_idx].name
1668 )));
1669 }
1670 let adapter_name = format!("__adapt_{adapter_count}");
1671 adapter_count += 1;
1672 let adapter_idx = all_nodes.len();
1673
1674 if let Some(ref mut log) = log {
1675 let from_name = match wire_ref {
1676 WireRef::Input(n) => n.clone(),
1677 WireRef::Node(n, _) => n.clone(),
1678 };
1679 log.push(crate::dsl::events::CompileEvent::TypeAdapterInserted {
1680 from_node: from_name,
1681 to_node: all_nodes[node_idx].name.clone(),
1682 adapter: format!("{source_type:?}→{expected_type:?}"),
1683 });
1684 }
1685
1686 all_name_to_idx.insert(adapter_name.clone(), adapter_idx);
1687
1688 let adapter_wiring = vec![source];
1689 while resolved_wiring.len() <= adapter_idx {
1690 resolved_wiring.push(Vec::new());
1691 }
1692 resolved_wiring[adapter_idx] = adapter_wiring;
1693
1694 all_nodes.push(PendingNode {
1695 name: adapter_name,
1696 node: adapter,
1697 inputs: vec![],
1698 });
1699
1700 node_wiring.push(WireSource::NodeOutput(adapter_idx, 0));
1701 } else {
1702 let from_name = match wire_ref {
1703 WireRef::Input(n) => n.clone(),
1704 WireRef::Node(n, _) => n.clone(),
1705 };
1706 return Err(AssemblyError::TypeMismatch {
1707 from_node: from_name,
1708 from_port: match wire_ref {
1709 WireRef::Input(_) => 0,
1710 WireRef::Node(_, p) => *p,
1711 },
1712 from_type: source_type,
1713 to_node: all_nodes[node_idx].name.clone(),
1714 to_port: port_idx,
1715 to_type: expected_type,
1716 });
1717 }
1718
1719 let sink_port = &all_nodes[node_idx].node.meta().wire_inputs()[port_idx];
1732 if let Some(constraint) = sink_port.constraint {
1733 let last_source = node_wiring.last().expect("wire just pushed").clone();
1734 if strict_values
1735 && !value_constraint_proven(&all_nodes, &last_source, &constraint)
1736 {
1737 let assert_name = format!("__assert_v_{assertion_count}");
1738 assertion_count += 1;
1739 let assert_idx = all_nodes.len();
1740
1741 if let Some(ref mut log) = log {
1742 let from_name = match wire_ref {
1743 WireRef::Input(n) => n.clone(),
1744 WireRef::Node(n, _) => n.clone(),
1745 };
1746 log.push(crate::dsl::events::CompileEvent::AssertionInserted {
1747 from_node: from_name,
1748 to_node: all_nodes[node_idx].name.clone(),
1749 kind: format!("{:?} value-assert {:?}", expected_type, constraint),
1750 });
1751 }
1752
1753 all_name_to_idx.insert(assert_name.clone(), assert_idx);
1754 let assert_wiring = vec![last_source];
1755 while resolved_wiring.len() <= assert_idx {
1756 resolved_wiring.push(Vec::new());
1757 }
1758 resolved_wiring[assert_idx] = assert_wiring;
1759
1760 all_nodes.push(PendingNode {
1761 name: assert_name,
1762 node: crate::library::assertions::assert_value_node(
1763 expected_type,
1764 constraint,
1765 ),
1766 inputs: vec![],
1767 });
1768
1769 *node_wiring.last_mut().unwrap() = WireSource::NodeOutput(assert_idx, 0);
1772 } else if let Some(ref mut log) = log {
1773 let from_name = match wire_ref {
1774 WireRef::Input(n) => n.clone(),
1775 WireRef::Node(n, _) => n.clone(),
1776 };
1777 log.push(crate::dsl::events::CompileEvent::AssertionSkipped {
1778 from_node: from_name,
1779 to_node: all_nodes[node_idx].name.clone(),
1780 reason: assertion_skip_reason(
1781 strict_values,
1782 &all_nodes,
1783 &last_source,
1784 &constraint,
1785 ),
1786 });
1787 }
1788 } else if strict_types && source_type != expected_type {
1789 }
1797 }
1798
1799 while resolved_wiring.len() <= node_idx {
1800 resolved_wiring.push(Vec::new());
1801 }
1802 resolved_wiring[node_idx] = node_wiring;
1803 }
1804
1805 while resolved_wiring.len() < all_nodes.len() {
1806 resolved_wiring.push(Vec::new());
1807 }
1808
1809 {
1814 let rules = crate::compile::fusion::default_rules();
1815 if !rules.is_empty() {
1816 let mut output_nodes: Vec<usize> = Vec::new();
1819 for wire_ref in self.outputs.values() {
1820 if let WireRef::Node(node_name, _) = wire_ref
1821 && let Some(&idx) = all_name_to_idx.get(node_name)
1822 {
1823 output_nodes.push(idx);
1824 }
1825 }
1826
1827 let mut opt_nodes: Vec<Option<Box<dyn PolydatNode>>> =
1829 all_nodes.into_iter().map(|pn| Some(pn.node)).collect();
1830
1831 let fused_count = crate::compile::fusion::apply_fusions(
1832 &mut opt_nodes,
1833 &mut resolved_wiring,
1834 &mut all_name_to_idx,
1835 &rules,
1836 &output_nodes,
1837 );
1838 if fused_count > 0
1839 && let Some(ref mut log) = log
1840 {
1841 log.push(crate::dsl::events::CompileEvent::FusionApplied {
1842 pattern: "subgraph".into(),
1843 nodes_replaced: fused_count,
1844 });
1845 }
1846
1847 all_nodes = opt_nodes
1850 .into_iter()
1851 .enumerate()
1852 .map(|(i, opt)| PendingNode {
1853 name: all_name_to_idx
1854 .iter()
1855 .find(|&(_, &idx)| idx == i)
1856 .map(|(n, _)| n.clone())
1857 .unwrap_or_else(|| format!("__removed_{i}")),
1858 node: opt.unwrap_or_else(|| {
1859 Box::new(crate::library::identity::Identity::new(
1860 crate::ast::PortType::U64,
1861 ))
1862 }),
1863 inputs: vec![], })
1865 .collect();
1866 }
1867 }
1868
1869 let node_count = all_nodes.len();
1876 let mut reachable = vec![false; node_count];
1877 {
1878 let mut worklist: Vec<usize> = Vec::new();
1879 for wire_ref in self.outputs.values() {
1881 if let WireRef::Node(node_name, _) = wire_ref
1882 && let Some(&idx) = all_name_to_idx.get(node_name)
1883 {
1884 worklist.push(idx);
1885 }
1886 }
1887 for (idx, pn) in all_nodes.iter().enumerate() {
1897 if matches!(
1898 pn.node.meta().name.as_str(),
1899 "log_debug" | "log_info" | "log_warn" | "log_error"
1900 ) {
1901 worklist.push(idx);
1902 }
1903 }
1904 while let Some(idx) = worklist.pop() {
1906 if reachable[idx] {
1907 continue;
1908 }
1909 reachable[idx] = true;
1910 for source in &resolved_wiring[idx] {
1911 if let WireSource::NodeOutput(upstream, _) = source
1912 && !reachable[*upstream]
1913 {
1914 worklist.push(*upstream);
1915 }
1916 }
1917 }
1918 }
1919 let live_count = reachable.iter().filter(|&&r| r).count();
1920
1921 let mut in_degree = vec![0usize; node_count];
1923 let mut dependents: Vec<Vec<usize>> = vec![Vec::new(); node_count];
1924
1925 for (node_idx, wiring) in resolved_wiring.iter().enumerate() {
1926 if !reachable[node_idx] {
1927 continue;
1928 }
1929 for source in wiring {
1930 if let WireSource::NodeOutput(upstream, _) = source {
1931 in_degree[node_idx] += 1;
1932 dependents[*upstream].push(node_idx);
1933 }
1934 }
1935 }
1936
1937 let mut queue: Vec<usize> = (0..node_count)
1938 .filter(|i| reachable[*i] && in_degree[*i] == 0)
1939 .collect();
1940 let mut sorted_order: Vec<usize> = Vec::with_capacity(live_count);
1941
1942 while let Some(idx) = queue.pop() {
1943 sorted_order.push(idx);
1944 for &dep in &dependents[idx] {
1945 in_degree[dep] -= 1;
1946 if in_degree[dep] == 0 {
1947 queue.push(dep);
1948 }
1949 }
1950 }
1951
1952 if sorted_order.len() != live_count {
1953 return Err(AssemblyError::CycleDetected);
1954 }
1955
1956 let mut old_to_new = vec![0usize; node_count];
1957 for (new_idx, &old_idx) in sorted_order.iter().enumerate() {
1958 old_to_new[old_idx] = new_idx;
1959 }
1960
1961 let mut sorted_nodes: Vec<Option<Box<dyn PolydatNode>>> =
1962 all_nodes.into_iter().map(|pn| Some(pn.node)).collect();
1963
1964 let final_nodes: Vec<Box<dyn PolydatNode>> = sorted_order
1965 .iter()
1966 .map(|&old_idx| sorted_nodes[old_idx].take().unwrap())
1967 .collect();
1968
1969 let final_wiring: Vec<Vec<WireSource>> = sorted_order
1970 .iter()
1971 .map(|&old_idx| {
1972 resolved_wiring[old_idx]
1973 .iter()
1974 .map(|source| match source {
1975 WireSource::Input(c) => WireSource::Input(*c),
1976 WireSource::NodeOutput(old_up, port) => {
1977 WireSource::NodeOutput(old_to_new[*old_up], *port)
1978 }
1979 })
1980 .collect()
1981 })
1982 .collect();
1983
1984 let mut final_output_map: HashMap<String, (usize, usize)> = HashMap::new();
1985 for (name, wire_ref) in &self.outputs {
1986 match wire_ref {
1987 WireRef::Input(coord_name) => {
1988 return Err(AssemblyError::UnknownWire(format!(
1989 "output '{name}' references coordinate '{coord_name}' directly; \
1990 wire through a node instead"
1991 )));
1992 }
1993 WireRef::Node(node_name, port) => {
1994 let old_idx = all_name_to_idx
1995 .get(node_name)
1996 .ok_or_else(|| AssemblyError::UnknownWire(node_name.clone()))?;
1997 final_output_map.insert(name.clone(), (old_to_new[*old_idx], *port));
1998 }
1999 }
2000 }
2001
2002 for f in crate::compile::roundtrip_lint::lint_type_round_trips(
2009 &final_nodes,
2010 &final_wiring,
2011 &self.input_defs,
2012 ) {
2013 if strict_values {
2014 return Err(AssemblyError::Other(f.message()));
2015 }
2016 eprintln!("warning: {}", f.message());
2017 if let Some(ref mut log) = log {
2018 log.push(crate::dsl::events::CompileEvent::Warning {
2019 message: f.message(),
2020 });
2021 }
2022 }
2023
2024 Ok(ResolvedDag {
2025 nodes: final_nodes,
2026 wiring: final_wiring,
2027 input_defs: self.input_defs,
2028 coord_count: self.coord_count,
2029 output_map: final_output_map,
2030 output_order: self.output_order,
2031 source: self.source,
2032 context: self.context,
2033 output_modifiers: self.output_modifiers,
2034 const_outputs: self.const_outputs,
2035 cursor_schemas: self.cursor_schemas,
2036 })
2037 }
2038}
2039
2040fn value_constraint_proven(
2055 all_nodes: &[PendingNode],
2056 src: &WireSource,
2057 _constraint: &crate::dsl::const_constraints::ConstConstraint,
2058) -> bool {
2059 match src {
2060 WireSource::Input(_) => false,
2061 WireSource::NodeOutput(idx, _) => {
2062 let meta = all_nodes[*idx].node.meta();
2063 let no_wire_inputs = meta.wire_inputs().is_empty();
2068 if no_wire_inputs {
2069 return true;
2070 }
2071 if meta.name.starts_with("__assert_v_") || meta.name.starts_with("assert_") {
2076 return true;
2077 }
2078 false
2079 }
2080 }
2081}
2082
2083fn assertion_skip_reason(
2087 strict_values: bool,
2088 all_nodes: &[PendingNode],
2089 src: &WireSource,
2090 _constraint: &crate::dsl::const_constraints::ConstConstraint,
2091) -> String {
2092 if !strict_values {
2093 return "strict_values not enabled".into();
2094 }
2095 match src {
2096 WireSource::Input(_) => "raw input wire".into(),
2097 WireSource::NodeOutput(idx, _) => {
2098 let meta = all_nodes[*idx].node.meta();
2099 if meta.wire_inputs().is_empty() {
2100 "constant source already validated".into()
2101 } else if meta.name.starts_with("__assert_v_") || meta.name.starts_with("assert_") {
2102 "upstream assertion".into()
2103 } else {
2104 "no skip rule matched".into()
2105 }
2106 }
2107 }
2108}
2109
2110pub(crate) fn shared_outputs_of(resolved: &ResolvedDag) -> Vec<&str> {
2143 let mut shared: Vec<&str> = resolved
2144 .output_modifiers
2145 .iter()
2146 .filter(|(_, m)| **m == crate::dsl::ast::BindingModifier::SHARED)
2147 .map(|(name, _)| name.as_str())
2148 .collect();
2149 shared.sort();
2150 shared
2151}
2152
2153pub(crate) fn wire_types_of(resolved: &ResolvedDag, node_idx: usize) -> Vec<PortType> {
2154 resolved.wiring[node_idx]
2155 .iter()
2156 .map(|src| match src {
2157 crate::kernel::WireSource::Input(i) => resolved.input_defs[*i].port_type,
2158 crate::kernel::WireSource::NodeOutput(j, p) => resolved.nodes[*j].meta().outs[*p].typ,
2159 })
2160 .collect()
2161}
2162
2163pub(crate) const UNTYPED_VARIADIC_NODES: &[&str] = &[
2164 "printf",
2165 "pick",
2166 "log_debug",
2167 "log_info",
2168 "log_warn",
2169 "log_error",
2170 "exactly_one_value",
2171 "json_text",
2172 "json_array",
2173 "json_object",
2174 "str_concat",
2175 "emit_row",
2176 "tile_render",
2177];
2178
2179pub fn auto_adapter(from: PortType, to: PortType) -> Option<Box<dyn PolydatNode>> {
2183 use crate::library::convert::{
2184 BoolToStr, BoolToU64, F32ToF64, F32ToString, I32ToF64, I32ToI64, I32ToString, I64ToF64,
2185 I64ToString, U32ToF64, U32ToI64, U32ToString, U32ToU64,
2186 };
2187 use crate::library::polyfill as P;
2188 use crate::library::polyfill_128 as W;
2189 use crate::library::polyfill_complete as C;
2190 use crate::library::polyfill_narrow as N;
2191 match (from, to) {
2192 (PortType::U64, PortType::F64) => Some(Box::new(U64ToF64::new())),
2194 (PortType::U32, PortType::U64) => Some(Box::new(U32ToU64::new())),
2195 (PortType::U32, PortType::I64) => Some(Box::new(U32ToI64::new())),
2196 (PortType::U32, PortType::F64) => Some(Box::new(U32ToF64::new())),
2197 (PortType::I32, PortType::I64) => Some(Box::new(I32ToI64::new())),
2198 (PortType::I32, PortType::F64) => Some(Box::new(I32ToF64::new())),
2199 (PortType::I64, PortType::F64) => Some(Box::new(I64ToF64::new())),
2200 (PortType::F32, PortType::F64) => Some(Box::new(F32ToF64::new())),
2201
2202 (PortType::U64, PortType::Str) => Some(Box::new(U64ToString::new())),
2204 (PortType::F64, PortType::Str) => Some(Box::new(F64ToString::new())),
2205 (PortType::Bool, PortType::Str) => Some(Box::new(BoolToStr::new())),
2206 (PortType::Json, PortType::Str) => Some(Box::new(JsonToStr::new())),
2207 (PortType::U32, PortType::Str) => Some(Box::new(U32ToString::new())),
2208 (PortType::I32, PortType::Str) => Some(Box::new(I32ToString::new())),
2209 (PortType::I64, PortType::Str) => Some(Box::new(I64ToString::new())),
2210 (PortType::F32, PortType::Str) => Some(Box::new(F32ToString::new())),
2211
2212 (PortType::Bool, PortType::U64) => Some(Box::new(BoolToU64::new())),
2214 (PortType::Bool, PortType::U32) => Some(Box::new(P::BoolToU32::new())),
2215 (PortType::Bool, PortType::I64) => Some(Box::new(P::BoolToI64::new())),
2216 (PortType::Bool, PortType::I32) => Some(Box::new(P::BoolToI32::new())),
2217 (PortType::Bool, PortType::F64) => Some(Box::new(P::BoolToF64::new())),
2218 (PortType::Bool, PortType::F32) => Some(Box::new(P::BoolToF32::new())),
2219 (PortType::U64, PortType::Bool) => {
2220 Some(Box::new(crate::library::convert::U64ToBool::new()))
2221 }
2222 (PortType::U32, PortType::Bool) => Some(Box::new(P::U32ToBool::new())),
2223 (PortType::I64, PortType::Bool) => Some(Box::new(P::I64ToBool::new())),
2224 (PortType::I32, PortType::Bool) => Some(Box::new(P::I32ToBool::new())),
2225 (PortType::F64, PortType::Bool) => Some(Box::new(P::F64ToBool::new())),
2226 (PortType::F32, PortType::Bool) => Some(Box::new(P::F32ToBool::new())),
2227
2228 (PortType::U64, PortType::Bytes) => Some(Box::new(P::U64ToBytes::new())),
2230 (PortType::U32, PortType::Bytes) => Some(Box::new(P::U32ToBytes::new())),
2231 (PortType::I64, PortType::Bytes) => Some(Box::new(P::I64ToBytes::new())),
2232 (PortType::I32, PortType::Bytes) => Some(Box::new(P::I32ToBytes::new())),
2233 (PortType::F64, PortType::Bytes) => Some(Box::new(P::F64ToBytes::new())),
2234 (PortType::F32, PortType::Bytes) => Some(Box::new(P::F32ToBytes::new())),
2235 (PortType::Bool, PortType::Bytes) => Some(Box::new(P::BoolToBytes::new())),
2236 (PortType::VecF32, PortType::Bytes) => Some(Box::new(P::VecF32ToBytes::new())),
2237 (PortType::VecI32, PortType::Bytes) => Some(Box::new(P::VecI32ToBytes::new())),
2238
2239 (PortType::U64, PortType::Json) => Some(Box::new(P::U64ToJson::new())),
2243 (PortType::U32, PortType::Json) => Some(Box::new(P::U32ToJson::new())),
2244 (PortType::I64, PortType::Json) => Some(Box::new(P::I64ToJson::new())),
2245 (PortType::I32, PortType::Json) => Some(Box::new(P::I32ToJson::new())),
2246 (PortType::Bool, PortType::Json) => Some(Box::new(P::BoolToJson::new())),
2247 (PortType::VecI32, PortType::Json) => Some(Box::new(P::VecI32ToJson::new())),
2248
2249 (PortType::VecI32, PortType::VecF32) => Some(Box::new(P::VecI32ToVecF32::new())),
2251
2252 (PortType::U8, PortType::U64) => Some(Box::new(N::U8ToU64::new())),
2257 (PortType::U8, PortType::U32) => Some(Box::new(N::U8ToU32::new())),
2258 (PortType::U8, PortType::U16) => Some(Box::new(N::U8ToU16::new())),
2259 (PortType::U8, PortType::F64) => Some(Box::new(N::U8ToF64::new())),
2260 (PortType::U16, PortType::U64) => Some(Box::new(N::U16ToU64::new())),
2261 (PortType::U16, PortType::U32) => Some(Box::new(N::U16ToU32::new())),
2262 (PortType::U16, PortType::F64) => Some(Box::new(N::U16ToF64::new())),
2263 (PortType::I8, PortType::I64) => Some(Box::new(N::I8ToI64::new())),
2264 (PortType::I8, PortType::I32) => Some(Box::new(N::I8ToI32::new())),
2265 (PortType::I8, PortType::I16) => Some(Box::new(N::I8ToI16::new())),
2266 (PortType::I8, PortType::F64) => Some(Box::new(N::I8ToF64::new())),
2267 (PortType::I16, PortType::I64) => Some(Box::new(N::I16ToI64::new())),
2268 (PortType::I16, PortType::I32) => Some(Box::new(N::I16ToI32::new())),
2269 (PortType::I16, PortType::F64) => Some(Box::new(N::I16ToF64::new())),
2270 (PortType::F16, PortType::F32) => Some(Box::new(N::F16ToF32::new())),
2271 (PortType::F16, PortType::F64) => Some(Box::new(N::F16ToF64::new())),
2272 (PortType::U8, PortType::I16) => Some(Box::new(N::U8ToI16::new())),
2275 (PortType::U8, PortType::I32) => Some(Box::new(N::U8ToI32::new())),
2276 (PortType::U8, PortType::I64) => Some(Box::new(N::U8ToI64::new())),
2277 (PortType::U8, PortType::F32) => Some(Box::new(N::U8ToF32::new())),
2278 (PortType::U16, PortType::I32) => Some(Box::new(N::U16ToI32::new())),
2279 (PortType::U16, PortType::I64) => Some(Box::new(N::U16ToI64::new())),
2280 (PortType::U16, PortType::F32) => Some(Box::new(N::U16ToF32::new())),
2281 (PortType::I8, PortType::F32) => Some(Box::new(N::I8ToF32::new())),
2282 (PortType::I16, PortType::F32) => Some(Box::new(N::I16ToF32::new())),
2283 (PortType::U8, PortType::F16) => Some(Box::new(N::U8ToF16::new())),
2284 (PortType::I8, PortType::F16) => Some(Box::new(N::I8ToF16::new())),
2285 (PortType::U8, PortType::Str) => Some(Box::new(N::U8ToString::new())),
2286 (PortType::U16, PortType::Str) => Some(Box::new(N::U16ToString::new())),
2287 (PortType::I8, PortType::Str) => Some(Box::new(N::I8ToString::new())),
2288 (PortType::I16, PortType::Str) => Some(Box::new(N::I16ToString::new())),
2289 (PortType::F16, PortType::Str) => Some(Box::new(N::F16ToString::new())),
2290 (PortType::Bool, PortType::U8) => Some(Box::new(N::BoolToU8::new())),
2291 (PortType::Bool, PortType::U16) => Some(Box::new(N::BoolToU16::new())),
2292 (PortType::Bool, PortType::I8) => Some(Box::new(N::BoolToI8::new())),
2293 (PortType::Bool, PortType::I16) => Some(Box::new(N::BoolToI16::new())),
2294 (PortType::Bool, PortType::F16) => Some(Box::new(N::BoolToF16::new())),
2295 (PortType::U8, PortType::Bool) => Some(Box::new(N::U8ToBool::new())),
2296 (PortType::U16, PortType::Bool) => Some(Box::new(N::U16ToBool::new())),
2297 (PortType::I8, PortType::Bool) => Some(Box::new(N::I8ToBool::new())),
2298 (PortType::I16, PortType::Bool) => Some(Box::new(N::I16ToBool::new())),
2299 (PortType::F16, PortType::Bool) => Some(Box::new(N::F16ToBool::new())),
2300 (PortType::U8, PortType::Bytes) => Some(Box::new(N::U8ToBytes::new())),
2301 (PortType::U16, PortType::Bytes) => Some(Box::new(N::U16ToBytes::new())),
2302 (PortType::I8, PortType::Bytes) => Some(Box::new(N::I8ToBytes::new())),
2303 (PortType::I16, PortType::Bytes) => Some(Box::new(N::I16ToBytes::new())),
2304 (PortType::F16, PortType::Bytes) => Some(Box::new(N::F16ToBytes::new())),
2305 (PortType::U8, PortType::Json) => Some(Box::new(N::U8ToJson::new())),
2306 (PortType::U16, PortType::Json) => Some(Box::new(N::U16ToJson::new())),
2307 (PortType::I8, PortType::Json) => Some(Box::new(N::I8ToJson::new())),
2308 (PortType::I16, PortType::Json) => Some(Box::new(N::I16ToJson::new())),
2309
2310 (PortType::U64, PortType::U128) => Some(Box::new(W::U64ToU128::new())),
2315 (PortType::U64, PortType::I128) => Some(Box::new(W::U64ToI128::new())),
2316 (PortType::I64, PortType::I128) => Some(Box::new(W::I64ToI128::new())),
2317 (PortType::U8, PortType::U128) => Some(Box::new(W::U8ToU128::new())),
2322 (PortType::U8, PortType::I128) => Some(Box::new(W::U8ToI128::new())),
2323 (PortType::U16, PortType::U128) => Some(Box::new(W::U16ToU128::new())),
2324 (PortType::U16, PortType::I128) => Some(Box::new(W::U16ToI128::new())),
2325 (PortType::U32, PortType::U128) => Some(Box::new(W::U32ToU128::new())),
2326 (PortType::U32, PortType::I128) => Some(Box::new(W::U32ToI128::new())),
2327 (PortType::I8, PortType::I128) => Some(Box::new(W::I8ToI128::new())),
2328 (PortType::I16, PortType::I128) => Some(Box::new(W::I16ToI128::new())),
2329 (PortType::I32, PortType::I128) => Some(Box::new(W::I32ToI128::new())),
2330 (PortType::Bool, PortType::U128) => Some(Box::new(W::BoolToU128::new())),
2331 (PortType::Bool, PortType::I128) => Some(Box::new(W::BoolToI128::new())),
2332 (PortType::U128, PortType::Bool) => Some(Box::new(W::U128ToBool::new())),
2333 (PortType::I128, PortType::Bool) => Some(Box::new(W::I128ToBool::new())),
2334 (PortType::U128, PortType::F64) => Some(Box::new(W::U128ToF64::new())),
2335 (PortType::I128, PortType::F64) => Some(Box::new(W::I128ToF64::new())),
2336 (PortType::U128, PortType::Str) => Some(Box::new(W::U128ToString::new())),
2337 (PortType::I128, PortType::Str) => Some(Box::new(W::I128ToString::new())),
2338 (PortType::U128, PortType::Bytes) => Some(Box::new(W::U128ToBytes::new())),
2339 (PortType::I128, PortType::Bytes) => Some(Box::new(W::I128ToBytes::new())),
2340 (PortType::U128, PortType::Json) => Some(Box::new(W::U128ToJson::new())),
2341 (PortType::I128, PortType::Json) => Some(Box::new(W::I128ToJson::new())),
2342
2343 (from, to)
2348 if crate::library::register_view::is_reg_port(from)
2349 && crate::library::register_view::is_reg_port(to) =>
2350 {
2351 Some(Box::new(crate::library::register_view::RegView::new(to)))
2352 }
2353
2354 (PortType::VecI8, PortType::VecI16) => Some(Box::new(C::VecI8ToVecI16::new())),
2358 (PortType::VecI8, PortType::VecI32) => Some(Box::new(C::VecI8ToVecI32::new())),
2359 (PortType::VecI8, PortType::VecI64) => Some(Box::new(C::VecI8ToVecI64::new())),
2360 (PortType::VecI8, PortType::VecF16) => Some(Box::new(C::VecI8ToVecF16::new())),
2361 (PortType::VecI8, PortType::VecF32) => Some(Box::new(C::VecI8ToVecF32::new())),
2362 (PortType::VecI8, PortType::VecF64) => Some(Box::new(C::VecI8ToVecF64::new())),
2363 (PortType::VecI16, PortType::VecI32) => Some(Box::new(C::VecI16ToVecI32::new())),
2364 (PortType::VecI16, PortType::VecI64) => Some(Box::new(C::VecI16ToVecI64::new())),
2365 (PortType::VecI16, PortType::VecF32) => Some(Box::new(C::VecI16ToVecF32::new())),
2366 (PortType::VecI16, PortType::VecF64) => Some(Box::new(C::VecI16ToVecF64::new())),
2367 (PortType::VecI32, PortType::VecI64) => Some(Box::new(C::VecI32ToVecI64::new())),
2368 (PortType::VecI32, PortType::VecF64) => Some(Box::new(C::VecI32ToVecF64::new())),
2369 (PortType::VecI64, PortType::VecF64) => Some(Box::new(C::VecI64ToVecF64::new())),
2370 (PortType::VecF16, PortType::VecF32) => Some(Box::new(C::VecF16ToVecF32::new())),
2371 (PortType::VecF16, PortType::VecF64) => Some(Box::new(C::VecF16ToVecF64::new())),
2372 (PortType::VecF32, PortType::VecF64) => Some(Box::new(C::VecF32ToVecF64::new())),
2373 (PortType::VecF64, PortType::Bytes) => Some(Box::new(C::VecF64ToBytes::new())),
2374 (PortType::VecI64, PortType::Bytes) => Some(Box::new(C::VecI64ToBytes::new())),
2375 (PortType::VecF16, PortType::Bytes) => Some(Box::new(C::VecF16ToBytes::new())),
2376 (PortType::VecI16, PortType::Bytes) => Some(Box::new(C::VecI16ToBytes::new())),
2377 (PortType::VecI8, PortType::Bytes) => Some(Box::new(C::VecI8ToBytes::new())),
2378 (PortType::VecI64, PortType::Json) => Some(Box::new(C::VecI64ToJson::new())),
2379 (PortType::VecI16, PortType::Json) => Some(Box::new(C::VecI16ToJson::new())),
2380 (PortType::VecI8, PortType::Json) => Some(Box::new(C::VecI8ToJson::new())),
2381 (PortType::VecI32, PortType::Str) => Some(Box::new(P::VecI32ToStr::new())),
2382 (PortType::VecI64, PortType::Str) => Some(Box::new(C::VecI64ToStr::new())),
2383 (PortType::VecI16, PortType::Str) => Some(Box::new(C::VecI16ToStr::new())),
2384 (PortType::VecI8, PortType::Str) => Some(Box::new(C::VecI8ToStr::new())),
2385
2386 _ => None,
2387 }
2388}
2389
2390pub fn boundary_adapter(from: PortType, to: PortType) -> Option<Box<dyn PolydatNode>> {
2420 if let Some(adapter) = auto_adapter(from, to) {
2421 return Some(adapter);
2422 }
2423 use crate::library::convert::{StrToBool, StrToF64, StrToU64};
2424 use crate::library::polyfill as P;
2425 use crate::library::polyfill_128 as W;
2426 use crate::library::polyfill_complete as C;
2427 use crate::library::polyfill_narrow as N;
2428 match (from, to) {
2429 (PortType::U64, PortType::U32) => Some(Box::new(P::U64ToU32::new())),
2431 (PortType::U64, PortType::I64) => Some(Box::new(P::U64ToI64::new())),
2432 (PortType::U64, PortType::I32) => Some(Box::new(P::U64ToI32::new())),
2433 (PortType::U64, PortType::F32) => Some(Box::new(P::U64ToF32::new())),
2434 (PortType::U32, PortType::I32) => Some(Box::new(P::U32ToI32::new())),
2435 (PortType::U32, PortType::F32) => Some(Box::new(P::U32ToF32::new())),
2436 (PortType::I64, PortType::U64) => Some(Box::new(P::I64ToU64::new())),
2437 (PortType::I64, PortType::U32) => Some(Box::new(P::I64ToU32::new())),
2438 (PortType::I64, PortType::I32) => Some(Box::new(P::I64ToI32::new())),
2439 (PortType::I64, PortType::F32) => Some(Box::new(P::I64ToF32::new())),
2440 (PortType::I32, PortType::U64) => Some(Box::new(P::I32ToU64::new())),
2441 (PortType::I32, PortType::U32) => Some(Box::new(P::I32ToU32::new())),
2442 (PortType::I32, PortType::F32) => Some(Box::new(P::I32ToF32::new())),
2443 (PortType::F64, PortType::U64) => Some(Box::new(P::F64ToU64Checked::new())),
2444 (PortType::F64, PortType::U32) => Some(Box::new(P::F64ToU32::new())),
2445 (PortType::F64, PortType::I64) => Some(Box::new(P::F64ToI64::new())),
2446 (PortType::F64, PortType::I32) => Some(Box::new(P::F64ToI32::new())),
2447 (PortType::F64, PortType::F32) => Some(Box::new(P::F64ToF32::new())),
2448 (PortType::F32, PortType::U64) => Some(Box::new(P::F32ToU64::new())),
2449 (PortType::F32, PortType::U32) => Some(Box::new(P::F32ToU32::new())),
2450 (PortType::F32, PortType::I64) => Some(Box::new(P::F32ToI64::new())),
2451 (PortType::F32, PortType::I32) => Some(Box::new(P::F32ToI32::new())),
2452
2453 (PortType::Str, PortType::Bool) => Some(Box::new(StrToBool::new())),
2455 (PortType::Str, PortType::U64) => Some(Box::new(StrToU64::new())),
2456 (PortType::Str, PortType::F64) => Some(Box::new(StrToF64::new())),
2457 (PortType::Str, PortType::U32) => Some(Box::new(P::StrToU32::new())),
2458 (PortType::Str, PortType::I64) => Some(Box::new(P::StrToI64::new())),
2459 (PortType::Str, PortType::I32) => Some(Box::new(P::StrToI32::new())),
2460 (PortType::Str, PortType::F32) => Some(Box::new(P::StrToF32::new())),
2461 (PortType::Str, PortType::Bytes) => Some(Box::new(P::StrToBytes::new())),
2462 (PortType::Str, PortType::Json) => Some(Box::new(P::StrToJson::new())),
2463 (PortType::Str, PortType::VecF32) => Some(Box::new(P::StrToVecF32::new())),
2464 (PortType::Str, PortType::VecI32) => Some(Box::new(P::StrToVecI32::new())),
2465
2466 (PortType::Bytes, PortType::U64) => Some(Box::new(P::BytesToU64::new())),
2468 (PortType::Bytes, PortType::U32) => Some(Box::new(P::BytesToU32::new())),
2469 (PortType::Bytes, PortType::I64) => Some(Box::new(P::BytesToI64::new())),
2470 (PortType::Bytes, PortType::I32) => Some(Box::new(P::BytesToI32::new())),
2471 (PortType::Bytes, PortType::F64) => Some(Box::new(P::BytesToF64::new())),
2472 (PortType::Bytes, PortType::F32) => Some(Box::new(P::BytesToF32::new())),
2473 (PortType::Bytes, PortType::Bool) => Some(Box::new(P::BytesToBool::new())),
2474 (PortType::Bytes, PortType::Str) => Some(Box::new(P::BytesToStr::new())),
2475 (PortType::Bytes, PortType::Json) => Some(Box::new(P::BytesToJson::new())),
2476 (PortType::Bytes, PortType::VecF32) => Some(Box::new(P::BytesToVecF32::new())),
2477 (PortType::Bytes, PortType::VecI32) => Some(Box::new(P::BytesToVecI32::new())),
2478
2479 (PortType::Json, PortType::U64) => Some(Box::new(P::JsonToU64::new())),
2481 (PortType::Json, PortType::U32) => Some(Box::new(P::JsonToU32::new())),
2482 (PortType::Json, PortType::I64) => Some(Box::new(P::JsonToI64::new())),
2483 (PortType::Json, PortType::I32) => Some(Box::new(P::JsonToI32::new())),
2484 (PortType::Json, PortType::F64) => Some(Box::new(P::JsonToF64::new())),
2485 (PortType::Json, PortType::F32) => Some(Box::new(P::JsonToF32::new())),
2486 (PortType::Json, PortType::Bool) => Some(Box::new(P::JsonToBool::new())),
2487 (PortType::Json, PortType::Bytes) => Some(Box::new(P::JsonToBytes::new())),
2488 (PortType::Json, PortType::VecF32) => Some(Box::new(P::JsonToVecF32::new())),
2489 (PortType::Json, PortType::VecI32) => Some(Box::new(P::JsonToVecI32::new())),
2490
2491 (PortType::F64, PortType::Json) => Some(Box::new(P::F64ToJson::new())),
2493 (PortType::F32, PortType::Json) => Some(Box::new(P::F32ToJson::new())),
2494 (PortType::VecF32, PortType::Json) => Some(Box::new(P::VecF32ToJson::new())),
2495 (PortType::VecF32, PortType::Str) => Some(Box::new(P::VecF32ToStr::new())),
2496
2497 (PortType::VecF32, PortType::VecI32) => Some(Box::new(P::VecF32ToVecI32::new())),
2499
2500 (PortType::U64, PortType::U8) => Some(Box::new(N::U64ToU8::new())),
2504 (PortType::U32, PortType::U8) => Some(Box::new(N::U32ToU8::new())),
2505 (PortType::U16, PortType::U8) => Some(Box::new(N::U16ToU8::new())),
2506 (PortType::I64, PortType::U8) => Some(Box::new(N::I64ToU8::new())),
2507 (PortType::F64, PortType::U8) => Some(Box::new(N::F64ToU8::new())),
2508 (PortType::U64, PortType::U16) => Some(Box::new(N::U64ToU16::new())),
2509 (PortType::U32, PortType::U16) => Some(Box::new(N::U32ToU16::new())),
2510 (PortType::I64, PortType::U16) => Some(Box::new(N::I64ToU16::new())),
2511 (PortType::F64, PortType::U16) => Some(Box::new(N::F64ToU16::new())),
2512 (PortType::I64, PortType::I8) => Some(Box::new(N::I64ToI8::new())),
2513 (PortType::I32, PortType::I8) => Some(Box::new(N::I32ToI8::new())),
2514 (PortType::U64, PortType::I8) => Some(Box::new(N::U64ToI8::new())),
2515 (PortType::F64, PortType::I8) => Some(Box::new(N::F64ToI8::new())),
2516 (PortType::I64, PortType::I16) => Some(Box::new(N::I64ToI16::new())),
2517 (PortType::I32, PortType::I16) => Some(Box::new(N::I32ToI16::new())),
2518 (PortType::U64, PortType::I16) => Some(Box::new(N::U64ToI16::new())),
2519 (PortType::F64, PortType::I16) => Some(Box::new(N::F64ToI16::new())),
2520 (PortType::F64, PortType::F16) => Some(Box::new(N::F64ToF16::new())),
2521 (PortType::F32, PortType::F16) => Some(Box::new(N::F32ToF16::new())),
2522 (PortType::U64, PortType::F16) => Some(Box::new(N::U64ToF16::new())),
2523 (PortType::Str, PortType::U8) => Some(Box::new(N::StrToU8::new())),
2524 (PortType::Str, PortType::U16) => Some(Box::new(N::StrToU16::new())),
2525 (PortType::Str, PortType::I8) => Some(Box::new(N::StrToI8::new())),
2526 (PortType::Str, PortType::I16) => Some(Box::new(N::StrToI16::new())),
2527 (PortType::Str, PortType::F16) => Some(Box::new(N::StrToF16::new())),
2528 (PortType::Bytes, PortType::U8) => Some(Box::new(N::BytesToU8::new())),
2529 (PortType::Bytes, PortType::U16) => Some(Box::new(N::BytesToU16::new())),
2530 (PortType::Bytes, PortType::I8) => Some(Box::new(N::BytesToI8::new())),
2531 (PortType::Bytes, PortType::I16) => Some(Box::new(N::BytesToI16::new())),
2532 (PortType::Bytes, PortType::F16) => Some(Box::new(N::BytesToF16::new())),
2533 (PortType::Json, PortType::U8) => Some(Box::new(N::JsonToU8::new())),
2534 (PortType::Json, PortType::U16) => Some(Box::new(N::JsonToU16::new())),
2535 (PortType::Json, PortType::I8) => Some(Box::new(N::JsonToI8::new())),
2536 (PortType::Json, PortType::I16) => Some(Box::new(N::JsonToI16::new())),
2537 (PortType::Json, PortType::F16) => Some(Box::new(N::JsonToF16::new())),
2538 (PortType::F16, PortType::Json) => Some(Box::new(N::F16ToJson::new())),
2540
2541 (PortType::U128, PortType::U64) => Some(Box::new(W::U128ToU64::new())),
2543 (PortType::I128, PortType::I64) => Some(Box::new(W::I128ToI64::new())),
2544 (PortType::I64, PortType::U128) => Some(Box::new(W::I64ToU128::new())),
2545 (PortType::U128, PortType::I128) => Some(Box::new(W::U128ToI128::new())),
2546 (PortType::I128, PortType::U128) => Some(Box::new(W::I128ToU128::new())),
2547 (PortType::F64, PortType::U128) => Some(Box::new(W::F64ToU128::new())),
2548 (PortType::F64, PortType::I128) => Some(Box::new(W::F64ToI128::new())),
2549 (PortType::Str, PortType::U128) => Some(Box::new(W::StrToU128::new())),
2550 (PortType::Str, PortType::I128) => Some(Box::new(W::StrToI128::new())),
2551 (PortType::Bytes, PortType::U128) => Some(Box::new(W::BytesToU128::new())),
2552 (PortType::Bytes, PortType::I128) => Some(Box::new(W::BytesToI128::new())),
2553 (PortType::Json, PortType::U128) => Some(Box::new(W::JsonToU128::new())),
2554 (PortType::Json, PortType::I128) => Some(Box::new(W::JsonToI128::new())),
2555
2556 (PortType::U8, PortType::I8) => Some(Box::new(C::U8ToI8::new())),
2561 (PortType::I8, PortType::U8) => Some(Box::new(C::I8ToU8::new())),
2562 (PortType::I8, PortType::U16) => Some(Box::new(C::I8ToU16::new())),
2563 (PortType::I8, PortType::U32) => Some(Box::new(C::I8ToU32::new())),
2564 (PortType::I8, PortType::U64) => Some(Box::new(C::I8ToU64::new())),
2565 (PortType::I8, PortType::U128) => Some(Box::new(C::I8ToU128::new())),
2566 (PortType::U16, PortType::I8) => Some(Box::new(C::U16ToI8::new())),
2567 (PortType::U16, PortType::I16) => Some(Box::new(C::U16ToI16::new())),
2568 (PortType::U16, PortType::F16) => Some(Box::new(C::U16ToF16::new())),
2569 (PortType::I16, PortType::U8) => Some(Box::new(C::I16ToU8::new())),
2570 (PortType::I16, PortType::I8) => Some(Box::new(C::I16ToI8::new())),
2571 (PortType::I16, PortType::U16) => Some(Box::new(C::I16ToU16::new())),
2572 (PortType::I16, PortType::F16) => Some(Box::new(C::I16ToF16::new())),
2573 (PortType::I16, PortType::U32) => Some(Box::new(C::I16ToU32::new())),
2574 (PortType::I16, PortType::U64) => Some(Box::new(C::I16ToU64::new())),
2575 (PortType::I16, PortType::U128) => Some(Box::new(C::I16ToU128::new())),
2576 (PortType::U32, PortType::I8) => Some(Box::new(C::U32ToI8::new())),
2577 (PortType::U32, PortType::I16) => Some(Box::new(C::U32ToI16::new())),
2578 (PortType::U32, PortType::F16) => Some(Box::new(C::U32ToF16::new())),
2579 (PortType::I32, PortType::U8) => Some(Box::new(C::I32ToU8::new())),
2580 (PortType::I32, PortType::U16) => Some(Box::new(C::I32ToU16::new())),
2581 (PortType::I32, PortType::F16) => Some(Box::new(C::I32ToF16::new())),
2582 (PortType::I32, PortType::U128) => Some(Box::new(C::I32ToU128::new())),
2583 (PortType::F16, PortType::U8) => Some(Box::new(C::F16ToU8::new())),
2584 (PortType::F16, PortType::I8) => Some(Box::new(C::F16ToI8::new())),
2585 (PortType::F16, PortType::U16) => Some(Box::new(C::F16ToU16::new())),
2586 (PortType::F16, PortType::I16) => Some(Box::new(C::F16ToI16::new())),
2587 (PortType::F16, PortType::U32) => Some(Box::new(C::F16ToU32::new())),
2588 (PortType::F16, PortType::I32) => Some(Box::new(C::F16ToI32::new())),
2589 (PortType::F16, PortType::U64) => Some(Box::new(C::F16ToU64::new())),
2590 (PortType::F16, PortType::I64) => Some(Box::new(C::F16ToI64::new())),
2591 (PortType::F16, PortType::U128) => Some(Box::new(C::F16ToU128::new())),
2592 (PortType::F16, PortType::I128) => Some(Box::new(C::F16ToI128::new())),
2593 (PortType::F32, PortType::U8) => Some(Box::new(C::F32ToU8::new())),
2594 (PortType::F32, PortType::I8) => Some(Box::new(C::F32ToI8::new())),
2595 (PortType::F32, PortType::U16) => Some(Box::new(C::F32ToU16::new())),
2596 (PortType::F32, PortType::I16) => Some(Box::new(C::F32ToI16::new())),
2597 (PortType::F32, PortType::U128) => Some(Box::new(C::F32ToU128::new())),
2598 (PortType::F32, PortType::I128) => Some(Box::new(C::F32ToI128::new())),
2599 (PortType::I64, PortType::F16) => Some(Box::new(C::I64ToF16::new())),
2600 (PortType::U128, PortType::U8) => Some(Box::new(C::U128ToU8::new())),
2601 (PortType::U128, PortType::I8) => Some(Box::new(C::U128ToI8::new())),
2602 (PortType::U128, PortType::U16) => Some(Box::new(C::U128ToU16::new())),
2603 (PortType::U128, PortType::I16) => Some(Box::new(C::U128ToI16::new())),
2604 (PortType::U128, PortType::F16) => Some(Box::new(C::U128ToF16::new())),
2605 (PortType::U128, PortType::U32) => Some(Box::new(C::U128ToU32::new())),
2606 (PortType::U128, PortType::I32) => Some(Box::new(C::U128ToI32::new())),
2607 (PortType::U128, PortType::F32) => Some(Box::new(C::U128ToF32::new())),
2608 (PortType::U128, PortType::I64) => Some(Box::new(C::U128ToI64::new())),
2609 (PortType::I128, PortType::U8) => Some(Box::new(C::I128ToU8::new())),
2610 (PortType::I128, PortType::I8) => Some(Box::new(C::I128ToI8::new())),
2611 (PortType::I128, PortType::U16) => Some(Box::new(C::I128ToU16::new())),
2612 (PortType::I128, PortType::I16) => Some(Box::new(C::I128ToI16::new())),
2613 (PortType::I128, PortType::F16) => Some(Box::new(C::I128ToF16::new())),
2614 (PortType::I128, PortType::U32) => Some(Box::new(C::I128ToU32::new())),
2615 (PortType::I128, PortType::I32) => Some(Box::new(C::I128ToI32::new())),
2616 (PortType::I128, PortType::F32) => Some(Box::new(C::I128ToF32::new())),
2617 (PortType::I128, PortType::U64) => Some(Box::new(C::I128ToU64::new())),
2618
2619 (PortType::VecI16, PortType::VecI8) => Some(Box::new(C::VecI16ToVecI8::new())),
2623 (PortType::VecI16, PortType::VecF16) => Some(Box::new(C::VecI16ToVecF16::new())),
2624 (PortType::VecI32, PortType::VecI8) => Some(Box::new(C::VecI32ToVecI8::new())),
2625 (PortType::VecI32, PortType::VecI16) => Some(Box::new(C::VecI32ToVecI16::new())),
2626 (PortType::VecI32, PortType::VecF16) => Some(Box::new(C::VecI32ToVecF16::new())),
2627 (PortType::VecI64, PortType::VecI8) => Some(Box::new(C::VecI64ToVecI8::new())),
2628 (PortType::VecI64, PortType::VecI16) => Some(Box::new(C::VecI64ToVecI16::new())),
2629 (PortType::VecI64, PortType::VecI32) => Some(Box::new(C::VecI64ToVecI32::new())),
2630 (PortType::VecI64, PortType::VecF16) => Some(Box::new(C::VecI64ToVecF16::new())),
2631 (PortType::VecI64, PortType::VecF32) => Some(Box::new(C::VecI64ToVecF32::new())),
2632 (PortType::VecF16, PortType::VecI8) => Some(Box::new(C::VecF16ToVecI8::new())),
2633 (PortType::VecF16, PortType::VecI16) => Some(Box::new(C::VecF16ToVecI16::new())),
2634 (PortType::VecF16, PortType::VecI32) => Some(Box::new(C::VecF16ToVecI32::new())),
2635 (PortType::VecF16, PortType::VecI64) => Some(Box::new(C::VecF16ToVecI64::new())),
2636 (PortType::VecF32, PortType::VecI8) => Some(Box::new(C::VecF32ToVecI8::new())),
2637 (PortType::VecF32, PortType::VecI16) => Some(Box::new(C::VecF32ToVecI16::new())),
2638 (PortType::VecF32, PortType::VecI64) => Some(Box::new(C::VecF32ToVecI64::new())),
2639 (PortType::VecF32, PortType::VecF16) => Some(Box::new(C::VecF32ToVecF16::new())),
2640 (PortType::VecF64, PortType::VecI8) => Some(Box::new(C::VecF64ToVecI8::new())),
2641 (PortType::VecF64, PortType::VecI16) => Some(Box::new(C::VecF64ToVecI16::new())),
2642 (PortType::VecF64, PortType::VecI32) => Some(Box::new(C::VecF64ToVecI32::new())),
2643 (PortType::VecF64, PortType::VecI64) => Some(Box::new(C::VecF64ToVecI64::new())),
2644 (PortType::VecF64, PortType::VecF16) => Some(Box::new(C::VecF64ToVecF16::new())),
2645 (PortType::VecF64, PortType::VecF32) => Some(Box::new(C::VecF64ToVecF32::new())),
2646 (PortType::Bytes, PortType::VecF64) => Some(Box::new(C::BytesToVecF64::new())),
2647 (PortType::Bytes, PortType::VecI64) => Some(Box::new(C::BytesToVecI64::new())),
2648 (PortType::Bytes, PortType::VecF16) => Some(Box::new(C::BytesToVecF16::new())),
2649 (PortType::Bytes, PortType::VecI16) => Some(Box::new(C::BytesToVecI16::new())),
2650 (PortType::Bytes, PortType::VecI8) => Some(Box::new(C::BytesToVecI8::new())),
2651 (PortType::VecF64, PortType::Json) => Some(Box::new(C::VecF64ToJson::new())),
2652 (PortType::VecF16, PortType::Json) => Some(Box::new(C::VecF16ToJson::new())),
2653 (PortType::Json, PortType::VecF64) => Some(Box::new(C::JsonToVecF64::new())),
2654 (PortType::Json, PortType::VecI64) => Some(Box::new(C::JsonToVecI64::new())),
2655 (PortType::Json, PortType::VecF16) => Some(Box::new(C::JsonToVecF16::new())),
2656 (PortType::Json, PortType::VecI16) => Some(Box::new(C::JsonToVecI16::new())),
2657 (PortType::Json, PortType::VecI8) => Some(Box::new(C::JsonToVecI8::new())),
2658 (PortType::VecF64, PortType::Str) => Some(Box::new(C::VecF64ToStr::new())),
2659 (PortType::VecF16, PortType::Str) => Some(Box::new(C::VecF16ToStr::new())),
2660 (PortType::Str, PortType::VecF64) => Some(Box::new(C::StrToVecF64::new())),
2661 (PortType::Str, PortType::VecI64) => Some(Box::new(C::StrToVecI64::new())),
2662 (PortType::Str, PortType::VecF16) => Some(Box::new(C::StrToVecF16::new())),
2663 (PortType::Str, PortType::VecI16) => Some(Box::new(C::StrToVecI16::new())),
2664 (PortType::Str, PortType::VecI8) => Some(Box::new(C::StrToVecI8::new())),
2665
2666 _ => None,
2667 }
2668}
2669
2670use crate::compile::select::{Engine, KernelError, Provenance};
2673use crate::kernel::Kernel;
2674
2675impl PolydatAssembler {
2676 pub fn compile_with(self, engine: Engine) -> Result<Box<dyn Kernel>, KernelError> {
2684 self.compile_engine_with_log(engine, None)
2685 }
2686
2687 pub fn compile_kernel(self) -> Result<Box<dyn Kernel>, KernelError> {
2690 self.compile_with(Engine::default())
2691 }
2692
2693 pub fn compile_engine_with_log(
2696 self,
2697 engine: Engine,
2698 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
2699 ) -> Result<Box<dyn Kernel>, KernelError> {
2700 let refused = |reason: String| KernelError::Refused { engine, reason };
2701 let strict = self.strict;
2702 match engine {
2703 Engine::Interpreter(cones) => {
2704 let mut asm = self;
2705 asm.jit_mode = Some(cones);
2706 Ok(Box::new(asm.compile_with_log(log)?))
2707 }
2708 Engine::Closures(prov) => {
2709 let resolved = self.resolve_with_log(log.as_deref_mut())?;
2710 if strict {
2711 Self::refuse_strict(&resolved)?;
2712 }
2713 let folded = log.is_some().then(|| Self::constant_sites(&resolved));
2714 let kernel = Self::closures_from(resolved, prov).map_err(refused)?;
2715 Self::log_folded(kernel.as_ref(), folded, log);
2716 Ok(kernel)
2717 }
2718 Engine::Native(prov) => {
2719 #[cfg(feature = "jit")]
2720 {
2721 let resolved = self.resolve_with_log(log.as_deref_mut())?;
2722 if strict {
2723 Self::refuse_strict(&resolved)?;
2724 }
2725 let folded = log.is_some().then(|| Self::constant_sites(&resolved));
2726 let prov = Self::provenance_for(prov, &resolved);
2727 let kernel = Self::hybrid_from(resolved).map_err(refused)?;
2728 let kernel: Box<dyn Kernel> = match prov {
2732 Provenance::Raw => Box::new(kernel.into_raw()),
2733 Provenance::Pull => Box::new(kernel.into_pull()),
2734 Provenance::Push | Provenance::PushPull | Provenance::Auto => {
2735 Box::new(kernel)
2736 }
2737 };
2738 Self::log_folded(kernel.as_ref(), folded, log);
2739 Ok(kernel)
2740 }
2741 #[cfg(not(feature = "jit"))]
2742 {
2743 let _ = (prov, log);
2744 Err(refused(
2745 "this build has no native code (the `jit` feature is off)".into(),
2746 ))
2747 }
2748 }
2749 }
2750 }
2751
2752 fn constant_sites(resolved: &ResolvedDag) -> Vec<(String, usize, crate::ast::PortType)> {
2757 let classes = PolydatProgram::classify_lifecycle(
2758 &resolved.nodes,
2759 &resolved.wiring,
2760 &resolved.input_defs,
2761 &resolved.output_map,
2762 &resolved.output_modifiers,
2763 );
2764 let layout = slot_layout(resolved);
2765 resolved
2766 .nodes
2767 .iter()
2768 .enumerate()
2769 .filter(|(i, n)| {
2770 classes.lifecycle[*i] == crate::kernel::EvalLifecycle::CompileConst
2771 && n.meta().outs.len() == 1
2772 })
2773 .map(|(i, n)| {
2774 (
2775 n.meta().name.clone(),
2776 layout.port_offsets[i][0],
2777 n.meta().outs[0].typ,
2778 )
2779 })
2780 .collect()
2781 }
2782
2783 fn log_folded(
2786 kernel: &dyn Kernel,
2787 sites: Option<Vec<(String, usize, crate::ast::PortType)>>,
2788 log: Option<&mut crate::dsl::events::CompileEventLog>,
2789 ) {
2790 let (Some(sites), Some(log)) = (sites, log) else {
2791 return;
2792 };
2793 for (node, slot, ty) in sites {
2794 let value = crate::kernel::KernelInternals::slot_value(kernel, slot, ty);
2795 if !matches!(value, crate::ast::Value::None) {
2796 log.push(crate::dsl::events::CompileEvent::ConstantFolded {
2797 node,
2798 value: value.to_display_string(),
2799 });
2800 }
2801 }
2802 }
2803
2804 fn provenance_for(prov: Provenance, resolved: &ResolvedDag) -> Provenance {
2809 match prov {
2810 Provenance::Auto => {
2811 let analysis =
2812 select::analyze_graph(&resolved.nodes, &resolved.wiring, &resolved.output_map);
2813 match select::select_prov_mode(&analysis) {
2814 ProvMode::Raw => Provenance::Raw,
2815 ProvMode::Pull => Provenance::Pull,
2816 ProvMode::PushPull => Provenance::PushPull,
2817 }
2818 }
2819 p => p,
2820 }
2821 }
2822
2823 fn closures_from(resolved: ResolvedDag, prov: Provenance) -> Result<Box<dyn Kernel>, String> {
2826 let prov = Self::provenance_for(prov, &resolved);
2827 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
2828 Self::build_p2_layout(&resolved)?;
2829 let dependents = || {
2830 slot_layout(&resolved).expand_dependents(
2831 &resolved,
2832 &PolydatProgram::compute_dependents(
2833 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
2834 resolved.input_defs.len(),
2835 ),
2836 )
2837 };
2838 Ok(match prov {
2839 Provenance::Raw => Box::new(CompiledKernelRaw::new(
2840 coord_count,
2841 total_slots,
2842 steps,
2843 output_map,
2844 ref_slots,
2845 extras,
2846 )),
2847 Provenance::Push => Box::new(CompiledKernelPush::new(
2848 coord_count,
2849 total_slots,
2850 steps,
2851 output_map,
2852 dependents(),
2853 ref_slots,
2854 extras,
2855 )),
2856 Provenance::Pull => Box::new(CompiledKernelPull::new(
2857 coord_count,
2858 total_slots,
2859 steps,
2860 output_map,
2861 &dependents(),
2862 ref_slots,
2863 extras,
2864 )),
2865 Provenance::PushPull | Provenance::Auto => Box::new(CompiledKernelPushPull::new(
2866 coord_count,
2867 total_slots,
2868 steps,
2869 output_map,
2870 dependents(),
2871 ref_slots,
2872 extras,
2873 )),
2874 })
2875 }
2876}