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 pub(crate) ledger: std::sync::Arc<crate::kernel::CompileLedger>,
220}
221
222impl ResolvedDag {
223 fn input_names(&self) -> Vec<String> {
225 self.input_defs[..self.coord_count]
226 .iter()
227 .map(|d| d.name.clone())
228 .collect()
229 }
230}
231
232struct SlotLayout {
237 input_starts: Vec<usize>,
239 coord_slots: usize,
241 port_offsets: Vec<Vec<usize>>,
243 total_slots: usize,
245}
246
247fn slot_layout(resolved: &ResolvedDag) -> SlotLayout {
248 let mut input_starts = Vec::with_capacity(resolved.coord_count);
249 let mut next = 0usize;
250 for d in &resolved.input_defs {
251 input_starts.push(next);
252 next += d.port_type.slot_width();
253 }
254 let coord_slots = next;
255 let mut port_offsets: Vec<Vec<usize>> = Vec::with_capacity(resolved.nodes.len());
256 for node in &resolved.nodes {
257 let mut po = Vec::with_capacity(node.meta().outs.len());
258 for out in &node.meta().outs {
259 po.push(next);
260 next += out.typ.slot_width();
261 }
262 port_offsets.push(po);
263 }
264 SlotLayout {
265 input_starts,
266 coord_slots,
267 port_offsets,
268 total_slots: next,
269 }
270}
271
272fn node_step_op(
278 node: &dyn crate::ast::PolydatNode,
279 wire_types: &[PortType],
280) -> Option<(
281 crate::compile::closures::StepOp,
282 Vec<crate::ast::ScratchElem>,
283)> {
284 let meta = node.meta();
288 if (meta.name == "identity" || meta.name.starts_with("__port_")) && meta.outs.len() == 1 {
289 return Some(match meta.outs[0].typ.slot_color() {
290 crate::ast::SlotColor::Ref2 => {
291 let kit = ref_copy_kit(meta.outs[0].typ)?;
292 (crate::compile::closures::StepOp::Slot(kit.op), kit.scratch)
293 }
294 _ => (crate::compile::closures::StepOp::Copy, Vec::new()),
295 });
296 }
297 if let Some(op) = node.compiled_u64() {
298 return Some((crate::compile::closures::StepOp::U64(op), Vec::new()));
299 }
300 node.compiled_slot(wire_types)
301 .map(|kit| (crate::compile::closures::StepOp::Slot(kit.op), kit.scratch))
302}
303
304pub(crate) fn scratch_pairs(
315 name: &str,
316 ref_starts: &[usize],
317 scratch: &[crate::ast::ScratchElem],
318 base: usize,
319) -> Vec<(usize, usize)> {
320 use crate::ast::ScratchElem;
321 let publishing: Vec<usize> = scratch
322 .iter()
323 .enumerate()
324 .filter(|(_, e)| {
325 !matches!(
326 e,
327 ScratchElem::Slots | ScratchElem::Kernels | ScratchElem::State
328 )
329 })
330 .map(|(k, _)| base + k)
331 .collect();
332 assert!(
333 publishing.len() <= ref_starts.len(),
334 "slot-op step '{name}' declares {} publishing scratch entries for {} Ref output ports",
335 publishing.len(),
336 ref_starts.len()
337 );
338 ref_starts.iter().copied().zip(publishing).collect()
339}
340
341pub(crate) fn ref_copy_kit(ty: PortType) -> Option<crate::ast::CompiledSlotKit> {
347 use crate::ast::ScratchBuf;
348 let elem = ty.scratch_elem()?;
349 Some(crate::ast::CompiledSlotKit {
350 scratch: vec![elem],
351 op: Box::new(
352 move |inputs: &[u64], outputs: &mut [u64], scratch: &mut [ScratchBuf]| {
353 let (p, n) = (inputs[0] as usize, inputs[1] as usize);
354 macro_rules! copy_into {
355 ($v:expr, $t:ty) => {{
356 $v.clear();
357 $v.extend_from_slice(unsafe {
361 std::slice::from_raw_parts(p as *const $t, n)
362 });
363 }};
364 }
365 match &mut scratch[0] {
366 ScratchBuf::Str(v) | ScratchBuf::Bytes(v) => copy_into!(v, u8),
367 ScratchBuf::F32(v) => copy_into!(v, f32),
368 ScratchBuf::F64(v) => copy_into!(v, f64),
369 ScratchBuf::F16(v) => copy_into!(v, half::f16),
370 ScratchBuf::I8(v) => copy_into!(v, i8),
371 ScratchBuf::I16(v) => copy_into!(v, i16),
372 ScratchBuf::I32(v) => copy_into!(v, i32),
373 ScratchBuf::I64(v) => copy_into!(v, i64),
374 ScratchBuf::Value(v) => {
375 v.clear();
376 if n > 0 {
377 v.push(unsafe { (*(p as *const crate::ast::Value)).clone() });
379 }
380 }
381 ScratchBuf::Slots(_) | ScratchBuf::Kernels(_) | ScratchBuf::State(_) => {
382 unreachable!("a copy owns only a value entry")
383 }
384 }
385 let (ptr, len) = scratch[0].ptr_len();
386 outputs[0] = ptr;
387 outputs[1] = len;
388 },
389 ),
390 })
391}
392
393pub(crate) fn identity_op(node: &dyn crate::ast::PolydatNode) -> Option<crate::ast::CompiledU64Op> {
399 let meta = node.meta();
400 if meta.name != "identity" || meta.outs.len() != 1 {
401 return None;
402 }
403 if meta.outs[0].typ.slot_color() == crate::ast::SlotColor::Ref2 {
404 return None;
405 }
406 Some(Box::new(|inputs: &[u64], outputs: &mut [u64]| {
407 outputs.copy_from_slice(inputs)
408 }))
409}
410
411impl SlotLayout {
412 fn input_slots(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
415 let mut slots = Vec::new();
416 for source in &resolved.wiring[node_idx] {
417 let (start, w) = match source {
418 WireSource::Input(c) => (
419 self.input_starts.get(*c).copied().unwrap_or(*c),
420 resolved
421 .input_defs
422 .get(*c)
423 .map(|d| d.port_type.slot_width())
424 .unwrap_or(1),
425 ),
426 WireSource::NodeOutput(u, p) => (
427 self.port_offsets[*u][*p],
428 resolved.nodes[*u].meta().outs[*p].typ.slot_width(),
429 ),
430 };
431 slots.extend(start..start + w);
432 }
433 slots
434 }
435
436 fn output_slots(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
438 let mut slots = Vec::new();
439 for (p, out) in resolved.nodes[node_idx].meta().outs.iter().enumerate() {
440 let start = self.port_offsets[node_idx][p];
441 slots.extend(start..start + out.typ.slot_width());
442 }
443 slots
444 }
445
446 fn named_outputs(&self, resolved: &ResolvedDag) -> HashMap<String, usize> {
448 resolved
449 .output_map
450 .iter()
451 .map(|(name, (n, p))| (name.clone(), self.port_offsets[*n][*p]))
452 .collect()
453 }
454
455 fn ref_slot_mask(&self, resolved: &ResolvedDag) -> Vec<bool> {
461 use crate::ast::SlotColor;
462 let mut mask = vec![false; self.total_slots];
463 let mut mark = |start: usize, color: SlotColor| match color {
464 SlotColor::Ref2 => {
465 mask[start] = true;
466 mask[start + 1] = true;
467 }
468 SlotColor::Imm1 | SlotColor::Imm2 => {}
469 };
470 for (i, d) in resolved.input_defs.iter().enumerate() {
471 mark(self.input_starts[i], d.port_type.slot_color());
472 }
473 for (n, node) in resolved.nodes.iter().enumerate() {
474 for (p, out) in node.meta().outs.iter().enumerate() {
475 mark(self.port_offsets[n][p], out.typ.slot_color());
476 }
477 }
478 mask
479 }
480
481 fn ref_output_starts(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
485 resolved.nodes[node_idx]
486 .meta()
487 .outs
488 .iter()
489 .enumerate()
490 .filter(|(_, out)| out.typ.slot_color() == crate::ast::SlotColor::Ref2)
491 .map(|(p, _)| self.port_offsets[node_idx][p])
492 .collect()
493 }
494
495 fn expand_dependents(&self, resolved: &ResolvedDag, deps: &[Vec<usize>]) -> Vec<Vec<usize>> {
501 let mut out = Vec::with_capacity(self.coord_slots);
502 for (i, d) in resolved.input_defs.iter().enumerate() {
503 for _ in 0..d.port_type.slot_width() {
504 out.push(deps.get(i).cloned().unwrap_or_default());
505 }
506 }
507 out
508 }
509}
510
511pub struct PolydatAssembler {
513 input_defs: Vec<crate::kernel::InputDef>,
515 coord_count: usize,
517 nodes: Vec<PendingNode>,
518 output_order: Vec<String>,
520 outputs: HashMap<String, WireRef>,
521 source: String,
523 context: String,
525 output_modifiers: HashMap<String, crate::dsl::ast::BindingModifier>,
527 const_outputs: std::collections::HashSet<String>,
530 pub(crate) strict_values: bool,
535 pub(crate) strict_types: bool,
543 pub(crate) strict: bool,
548 pub(crate) jit_mode: Option<crate::compile::cone::JitMode>,
553 pub(crate) ledger: std::sync::Arc<crate::kernel::CompileLedger>,
557 cursor_schemas: Vec<crate::iteration::source::SourceSchema>,
561}
562
563type P2Layout = (
567 usize,
568 usize,
569 Vec<crate::compile::closures::P2Step>,
570 HashMap<String, usize>,
571 Vec<bool>,
572 crate::compile::closures::P2Extras,
573);
574
575#[cfg(feature = "jit")]
581type JitLayout = (
582 usize,
583 usize,
584 Vec<(crate::compile::jit::JitOp, Vec<usize>, Vec<usize>)>,
585 HashMap<String, usize>,
586 crate::compile::jit::ScratchPlan,
587 Vec<usize>,
588);
589
590impl PolydatAssembler {
591 pub fn new(input_names: Vec<String>) -> Self {
593 let coord_count = input_names.len();
594 let input_defs: Vec<crate::kernel::InputDef> = input_names
595 .into_iter()
596 .map(|name| crate::kernel::InputDef {
597 name,
598 default: crate::ast::Value::U64(0),
599 port_type: crate::ast::PortType::U64,
600 kind: crate::kernel::InputKind::Coordinate,
601 })
602 .collect();
603 Self {
604 input_defs,
605 coord_count,
606 nodes: Vec::new(),
607 output_order: Vec::new(),
608 outputs: HashMap::new(),
609 source: String::new(),
610 context: "(assembler)".into(),
611 output_modifiers: HashMap::new(),
612 const_outputs: std::collections::HashSet::new(),
613 strict_values: false,
614 strict_types: false,
615 strict: false,
616 jit_mode: None,
617 cursor_schemas: Vec::new(),
618 ledger: crate::kernel::CompileLedger::new(),
619 }
620 }
621
622 pub fn set_cursor_schemas(&mut self, schemas: Vec<crate::iteration::source::SourceSchema>) {
627 self.cursor_schemas = schemas;
628 }
629
630 pub fn cursor_schemas(&self) -> &[crate::iteration::source::SourceSchema] {
632 &self.cursor_schemas
633 }
634
635 pub fn set_strict_wires(&mut self, strict_types: bool, strict_values: bool) {
639 self.strict_types = strict_types;
640 self.strict_values = strict_values;
641 }
642
643 pub fn set_strict(&mut self, strict: bool) {
649 self.strict = strict;
650 }
651
652 pub fn set_jit_mode(&mut self, mode: crate::compile::cone::JitMode) {
655 self.jit_mode = Some(mode);
656 }
657
658 pub fn set_context(&mut self, source: &str, context: &str) {
661 self.source = source.to_string();
662 self.context = context.to_string();
663 }
664
665 pub fn add_node(
667 &mut self,
668 name: impl Into<String>,
669 node: Box<dyn PolydatNode>,
670 inputs: Vec<WireRef>,
671 ) -> &mut Self {
672 self.nodes.push(PendingNode {
673 name: name.into(),
674 node,
675 inputs,
676 });
677 self
678 }
679
680 pub fn set_output_modifier(&mut self, name: &str, modifier: crate::dsl::ast::BindingModifier) {
682 if modifier != crate::dsl::ast::BindingModifier::NONE {
683 self.output_modifiers.insert(name.to_string(), modifier);
684 }
685 }
686
687 pub fn mark_const_output(&mut self, name: &str) {
692 self.const_outputs.insert(name.to_string());
693 }
694
695 pub fn add_output(&mut self, name: impl Into<String>, wire: WireRef) -> &mut Self {
697 let name = name.into();
698 if !self.outputs.contains_key(&name) {
699 self.output_order.push(name.clone());
700 }
701 self.outputs.insert(name, wire);
702 self
703 }
704
705 pub fn add_input(
716 &mut self,
717 name: impl Into<String>,
718 default: crate::ast::Value,
719 port_type: crate::ast::PortType,
720 kind: crate::kernel::InputKind,
721 ) -> &mut Self {
722 self.input_defs.push(crate::kernel::InputDef {
723 name: name.into(),
724 default,
725 port_type,
726 kind,
727 });
728 self
729 }
730
731 pub fn set_input_type(&mut self, name: &str, port_type: crate::ast::PortType) {
736 if let Some(d) = self.input_defs.iter_mut().find(|d| d.name == name) {
737 d.port_type = port_type;
738 }
739 }
740
741 pub fn input_names(&self) -> Vec<&str> {
743 self.input_defs.iter().map(|d| d.name.as_str()).collect()
744 }
745
746 pub fn node_output_type(&self, name: &str) -> Option<crate::ast::PortType> {
751 self.nodes
752 .iter()
753 .find(|n| n.name == name)
754 .and_then(|n| n.node.meta().outs.first())
755 .map(|p| p.typ)
756 }
757
758 pub fn output_names(&self) -> Vec<&str> {
760 self.outputs.keys().map(|s| s.as_str()).collect()
761 }
762
763 pub fn output_type(&self, name: &str) -> Option<PortType> {
767 self.nodes
768 .iter()
769 .find(|pn| pn.name == name)
770 .and_then(|pn| pn.node.meta().outs.first())
771 .map(|port| port.typ)
772 }
773
774 pub fn input_type(&self, name: &str) -> Option<PortType> {
776 self.input_defs
777 .iter()
778 .find(|d| d.name == name)
779 .map(|d| d.port_type)
780 }
781
782 pub fn wire_type(&self, wire: &WireRef) -> Option<PortType> {
787 match wire {
788 WireRef::Input(name) => self.input_type(name),
789 WireRef::Node(name, port_idx) => self
790 .nodes
791 .iter()
792 .find(|pn| &pn.name == name)
793 .and_then(|pn| pn.node.meta().outs.get(*port_idx))
794 .map(|p| p.typ),
795 }
796 }
797
798 pub fn compile(self) -> Result<PolydatKernel, AssemblyError> {
800 self.compile_with_log(None)
801 }
802
803 pub fn compile_with_log(
805 self,
806 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
807 ) -> Result<PolydatKernel, AssemblyError> {
808 let jit_mode = self.jit_mode.unwrap_or_default();
809 let strict = self.strict;
810 let mut resolved = self.resolve_with_log(log.as_deref_mut())?;
811 crate::compile::cone::extract_jit_cones(&mut resolved, jit_mode);
812 let _coord_names = resolved.input_names();
813 let modifiers = resolved.output_modifiers.clone();
814 let cursors = std::mem::take(&mut resolved.cursor_schemas);
815 let mut kernel = PolydatKernel::new_with_inputs(
816 resolved.nodes,
817 resolved.wiring,
818 resolved.input_defs,
819 resolved.coord_count,
820 resolved.output_map,
821 resolved.output_order,
822 resolved.const_outputs,
823 modifiers,
824 &resolved.source,
825 &resolved.context,
826 log,
827 strict,
828 resolved.ledger.clone(),
829 )
830 .map_err(AssemblyError::Other)?;
831 if !cursors.is_empty() {
832 kernel.set_cursor_schemas(cursors);
833 }
834 kernel.set_cone_mode(jit_mode);
835 Ok(kernel)
836 }
837
838 fn refuse_strict(resolved: &ResolvedDag) -> Result<(), AssemblyError> {
842 let classes = PolydatProgram::classify_lifecycle(
843 &resolved.nodes,
844 &resolved.wiring,
845 &resolved.input_defs,
846 &resolved.output_map,
847 &resolved.output_modifiers,
848 );
849 let is_init: Vec<bool> = classes
850 .lifecycle
851 .iter()
852 .map(|lc| *lc == crate::kernel::EvalLifecycle::CompileConst)
853 .collect();
854 match PolydatProgram::strict_violation(
855 &resolved.nodes,
856 &resolved.wiring,
857 &is_init,
858 &resolved.output_map,
859 &resolved.output_modifiers,
860 ) {
861 Some(violation) => Err(AssemblyError::Other(violation)),
862 None => Ok(()),
863 }
864 }
865
866 pub fn try_compile(self) -> Result<CompiledKernelPushPull, Box<PolydatKernel>> {
874 let resolved = self.resolve().expect("assembly validation failed");
875 let coord_names = resolved.input_names();
876 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
877 match Self::build_p2_layout(&resolved) {
878 Ok(r) => r,
879 Err(_) => {
881 return Err(Box::new(PolydatKernel::new(
882 resolved.nodes,
883 resolved.wiring,
884 coord_names,
885 resolved.output_map,
886 &resolved.source,
887 &resolved.context,
888 resolved.ledger.clone(),
889 )));
890 }
891 };
892 let dependents = slot_layout(&resolved).expand_dependents(
893 &resolved,
894 &PolydatProgram::compute_dependents(
895 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
896 resolved.input_defs.len(),
897 ),
898 );
899 Ok(CompiledKernelPushPull::new(
900 coord_count,
901 total_slots,
902 steps,
903 output_map,
904 dependents,
905 ref_slots,
906 extras,
907 ))
908 }
909
910 pub fn try_compile_raw(self) -> Result<CompiledKernelRaw, Box<PolydatKernel>> {
912 let resolved = match self.resolve() {
913 Ok(r) => r,
914 Err(_) => {
915 return Err(Box::new(PolydatKernel::new(
916 vec![],
917 vec![],
918 vec![],
919 HashMap::new(),
920 "",
921 "(fallback)",
922 crate::kernel::CompileLedger::new(),
923 )));
924 }
925 };
926 let coord_names = resolved.input_names();
927 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
928 match Self::build_p2_layout(&resolved) {
929 Ok(r) => r,
930 Err(_) => {
931 return Err(Box::new(PolydatKernel::new(
932 resolved.nodes,
933 resolved.wiring,
934 coord_names,
935 resolved.output_map,
936 &resolved.source,
937 &resolved.context,
938 resolved.ledger.clone(),
939 )));
940 }
941 };
942 Ok(CompiledKernelRaw::new(
943 coord_count,
944 total_slots,
945 steps,
946 output_map,
947 ref_slots,
948 extras,
949 ))
950 }
951
952 pub fn try_compile_push(self) -> Result<CompiledKernelPush, Box<PolydatKernel>> {
954 let resolved = match self.resolve() {
955 Ok(r) => r,
956 Err(_) => {
957 return Err(Box::new(PolydatKernel::new(
958 vec![],
959 vec![],
960 vec![],
961 HashMap::new(),
962 "",
963 "(fallback)",
964 crate::kernel::CompileLedger::new(),
965 )));
966 }
967 };
968 let coord_names = resolved.input_names();
969 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
970 match Self::build_p2_layout(&resolved) {
971 Ok(r) => r,
972 Err(_) => {
973 return Err(Box::new(PolydatKernel::new(
974 resolved.nodes,
975 resolved.wiring,
976 coord_names,
977 resolved.output_map,
978 &resolved.source,
979 &resolved.context,
980 resolved.ledger.clone(),
981 )));
982 }
983 };
984 let dependents = slot_layout(&resolved).expand_dependents(
985 &resolved,
986 &PolydatProgram::compute_dependents(
987 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
988 resolved.input_defs.len(),
989 ),
990 );
991 Ok(CompiledKernelPush::new(
992 coord_count,
993 total_slots,
994 steps,
995 output_map,
996 dependents,
997 ref_slots,
998 extras,
999 ))
1000 }
1001
1002 pub fn try_compile_pull(self) -> Result<CompiledKernelPull, Box<PolydatKernel>> {
1004 let resolved = match self.resolve() {
1005 Ok(r) => r,
1006 Err(_) => {
1007 return Err(Box::new(PolydatKernel::new(
1008 vec![],
1009 vec![],
1010 vec![],
1011 HashMap::new(),
1012 "",
1013 "(fallback)",
1014 crate::kernel::CompileLedger::new(),
1015 )));
1016 }
1017 };
1018 let coord_names = resolved.input_names();
1019 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
1020 match Self::build_p2_layout(&resolved) {
1021 Ok(r) => r,
1022 Err(_) => {
1023 return Err(Box::new(PolydatKernel::new(
1024 resolved.nodes,
1025 resolved.wiring,
1026 coord_names,
1027 resolved.output_map,
1028 &resolved.source,
1029 &resolved.context,
1030 resolved.ledger.clone(),
1031 )));
1032 }
1033 };
1034 let dependents = slot_layout(&resolved).expand_dependents(
1035 &resolved,
1036 &PolydatProgram::compute_dependents(
1037 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
1038 resolved.input_defs.len(),
1039 ),
1040 );
1041 Ok(CompiledKernelPull::new(
1042 coord_count,
1043 total_slots,
1044 steps,
1045 output_map,
1046 &dependents,
1047 ref_slots,
1048 extras,
1049 ))
1050 }
1051
1052 fn build_p2_layout(resolved: &ResolvedDag) -> Result<P2Layout, String> {
1055 let layout = slot_layout(resolved);
1056
1057 let mut compiled_ops = Vec::with_capacity(resolved.nodes.len());
1058 let mut extras = crate::compile::closures::P2Extras::default();
1059 for (node_idx, node) in resolved.nodes.iter().enumerate() {
1060 compiled_ops.push(
1061 node_step_op(node.as_ref(), &wire_types_of(resolved, node_idx)).ok_or_else(
1062 || {
1063 format!(
1064 "node '{}' has no compiled form (docs/design/engine_parity.md)",
1065 node.meta().name
1066 )
1067 },
1068 )?,
1069 );
1070 }
1071 extras.externs = crate::compile::externs::Externs::new(
1072 &resolved.input_defs,
1073 resolved.coord_count,
1074 &layout.input_starts,
1075 &resolved.cursor_schemas,
1076 &shared_outputs_of(resolved),
1077 resolved.ledger.clone(),
1078 )?;
1079 extras.externs.set_output_names(&resolved.output_order);
1080 extras.output_types = resolved
1081 .output_map
1082 .iter()
1083 .map(|(name, (n, p))| (name.clone(), resolved.nodes[*n].meta().outs[*p].typ))
1084 .collect();
1085
1086 let classes = PolydatProgram::classify_lifecycle(
1090 &resolved.nodes,
1091 &resolved.wiring,
1092 &resolved.input_defs,
1093 &resolved.output_map,
1094 &resolved.output_modifiers,
1095 );
1096 let inventory = PolydatProgram::compute_node_inventory(&resolved.nodes, &resolved.wiring);
1097 let per_input = PolydatProgram::compute_dependents(
1098 &inventory.input_provenance,
1099 resolved.input_defs.len(),
1100 );
1101 extras.input_dependents = layout.expand_dependents(resolved, &per_input);
1102 extras.attribution = std::sync::Arc::new(Self::attribution_of(resolved));
1103
1104 let mut steps = Vec::with_capacity(resolved.nodes.len());
1105 for (node_idx, (op, scratch)) in compiled_ops.into_iter().enumerate() {
1106 steps.push(crate::compile::closures::P2Step {
1107 name: resolved.nodes[node_idx].meta().name.clone(),
1108 op,
1109 input_slots: layout.input_slots(resolved, node_idx),
1110 output_slots: layout.output_slots(resolved, node_idx),
1111 ref_output_starts: layout.ref_output_starts(resolved, node_idx),
1112 scratch,
1113 accepts_none: resolved.nodes[node_idx].accepts_none_inputs(),
1114 volatile: classes.nondeterministic[node_idx],
1115 constant: classes.lifecycle[node_idx] == crate::kernel::EvalLifecycle::CompileConst,
1116 side: matches!(
1117 resolved.nodes[node_idx].purity(),
1118 crate::ast::Purity::SideChannel { .. }
1119 ),
1120 });
1121 }
1122 let output_map = layout.named_outputs(resolved);
1123 let ref_slots = layout.ref_slot_mask(resolved);
1124
1125 Ok((
1126 layout.coord_slots,
1127 layout.total_slots,
1128 steps,
1129 output_map,
1130 ref_slots,
1131 extras,
1132 ))
1133 }
1134
1135 #[cfg(feature = "jit")]
1137 pub(crate) fn build_jit_layout(resolved: &ResolvedDag) -> Result<JitLayout, String> {
1138 let layout = slot_layout(resolved);
1139
1140 let mut scratch = crate::compile::jit::ScratchPlan::default();
1144 let mut jit_steps = Vec::new();
1145 for (node_idx, node) in resolved.nodes.iter().enumerate() {
1146 let mut jit_op = crate::compile::jit::classify_node_typed(
1147 node.as_ref(),
1148 &wire_types_of(resolved, node_idx),
1149 );
1150 if matches!(jit_op, crate::compile::jit::JitOp::Fallback) {
1151 return Err(format!(
1152 "node '{}' has no native form and no kit; pure native code cannot run it",
1153 node.meta().name
1154 ));
1155 }
1156 let base = scratch.elems.len();
1157 jit_op.place_scratch(base);
1158 let elems = jit_op.scratch_elems().to_vec();
1159 scratch.refs.extend(scratch_pairs(
1160 &node.meta().name,
1161 &layout.ref_output_starts(resolved, node_idx),
1162 &elems,
1163 base,
1164 ));
1165 scratch.elems.extend(elems);
1166 jit_steps.push((
1167 jit_op,
1168 layout.input_slots(resolved, node_idx),
1169 layout.output_slots(resolved, node_idx),
1170 ));
1171 }
1172
1173 let output_map = layout.named_outputs(resolved);
1174 let classes = PolydatProgram::classify_lifecycle(
1178 &resolved.nodes,
1179 &resolved.wiring,
1180 &resolved.input_defs,
1181 &resolved.output_map,
1182 &resolved.output_modifiers,
1183 );
1184 let volatile: Vec<usize> = (0..resolved.nodes.len())
1185 .filter(|&i| classes.nondeterministic[i])
1186 .collect();
1187 Ok((
1188 layout.coord_slots,
1189 layout.total_slots,
1190 jit_steps,
1191 output_map,
1192 scratch,
1193 volatile,
1194 ))
1195 }
1196
1197 #[cfg(feature = "jit")]
1200 fn jit_slot_info(resolved: &ResolvedDag) -> (Vec<bool>, HashMap<String, PortType>) {
1201 let layout = slot_layout(resolved);
1202 let guard = layout.ref_slot_mask(resolved);
1203 let types = resolved
1204 .output_map
1205 .iter()
1206 .map(|(name, (n, p))| (name.clone(), resolved.nodes[*n].meta().outs[*p].typ))
1207 .collect();
1208 (guard, types)
1209 }
1210
1211 #[cfg(feature = "jit")]
1216 pub fn try_compile_jit(self) -> Result<crate::compile::hybrid::HybridKernelPushPull, String> {
1217 self.compile_hybrid()
1218 }
1219
1220 #[cfg(feature = "jit")]
1222 pub fn try_compile_jit_raw(self) -> Result<crate::compile::hybrid::HybridKernelRaw, String> {
1223 Ok(self.compile_hybrid()?.into_raw())
1224 }
1225
1226 #[cfg(feature = "jit")]
1230 pub fn try_compile_jit_push(
1231 self,
1232 ) -> Result<crate::compile::hybrid::HybridKernelPushPull, String> {
1233 self.compile_hybrid()
1234 }
1235
1236 #[cfg(feature = "jit")]
1238 pub fn try_compile_jit_pull(self) -> Result<crate::compile::hybrid::HybridKernelPull, String> {
1239 Ok(self.compile_hybrid()?.into_pull())
1240 }
1241
1242 #[doc(hidden)]
1246 #[cfg(feature = "jit")]
1247 pub fn try_compile_pure_jit(self) -> Result<crate::compile::jit::JitKernelPushPull, String> {
1248 let resolved = self.resolve().map_err(|e| format!("{e}"))?;
1249 Self::jit_push_pull_from(resolved)
1250 }
1251
1252 #[cfg(feature = "jit")]
1253 fn jit_push_pull_from(
1254 resolved: ResolvedDag,
1255 ) -> Result<crate::compile::jit::JitKernelPushPull, String> {
1256 let _coord_names = resolved.input_names();
1257 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1258 Self::build_jit_layout(&resolved)?;
1259 let (guard, types) = Self::jit_slot_info(&resolved);
1260 let deps = slot_layout(&resolved).expand_dependents(
1261 &resolved,
1262 &PolydatProgram::compute_dependents(
1263 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
1264 resolved.input_defs.len(),
1265 ),
1266 );
1267 let externs = Self::externs_of(&resolved)?;
1268 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1269 let mut k = crate::compile::jit::compile_jit_push_pull(
1270 coord_count,
1271 total_slots,
1272 jit_steps,
1273 output_map,
1274 resolved.nodes,
1275 deps,
1276 externs,
1277 scratch,
1278 volatile,
1279 )?;
1280 k.set_slot_info(guard, types);
1281 k.set_attribution(attribution);
1282 Ok(k)
1283 }
1284
1285 fn externs_of(resolved: &ResolvedDag) -> Result<crate::compile::externs::Externs, String> {
1288 let layout = slot_layout(resolved);
1289 let mut externs = crate::compile::externs::Externs::new(
1290 &resolved.input_defs,
1291 resolved.coord_count,
1292 &layout.input_starts,
1293 &resolved.cursor_schemas,
1294 &shared_outputs_of(resolved),
1295 resolved.ledger.clone(),
1296 )?;
1297 externs.set_output_names(&resolved.output_order);
1298 Ok(externs)
1299 }
1300
1301 #[doc(hidden)]
1303 #[cfg(feature = "jit")]
1304 pub fn try_compile_pure_jit_raw(self) -> Result<crate::compile::jit::JitKernelRaw, String> {
1305 let resolved = self.resolve().map_err(|e| format!("{e}"))?;
1306 Self::jit_raw_from(resolved)
1307 }
1308
1309 pub(crate) fn attribution_of(resolved: &ResolvedDag) -> crate::compile::Attribution {
1314 let layout = slot_layout(resolved);
1315 let sites = resolved
1316 .nodes
1317 .iter()
1318 .enumerate()
1319 .map(|(node_idx, node)| {
1320 let mut outputs: Vec<String> = resolved
1321 .output_map
1322 .iter()
1323 .filter(|(_, (n, _))| *n == node_idx)
1324 .map(|(name, _)| name.clone())
1325 .collect();
1326 outputs.sort();
1327 let inputs = resolved.wiring[node_idx]
1328 .iter()
1329 .map(|source| match source {
1330 WireSource::Input(c) => (
1331 layout.input_starts.get(*c).copied().unwrap_or(*c),
1332 resolved
1333 .input_defs
1334 .get(*c)
1335 .map(|d| d.port_type)
1336 .unwrap_or(PortType::U64),
1337 ),
1338 WireSource::NodeOutput(u, p) => (
1339 layout.port_offsets[*u][*p],
1340 resolved.nodes[*u].meta().outs[*p].typ,
1341 ),
1342 })
1343 .collect();
1344 crate::compile::NodeSite {
1345 name: node.meta().name.to_string(),
1346 outputs,
1347 inputs,
1348 }
1349 })
1350 .collect();
1351 crate::compile::Attribution {
1352 sites,
1353 context: resolved.context.clone(),
1354 }
1355 }
1356
1357 #[cfg(feature = "jit")]
1358 fn jit_raw_from(resolved: ResolvedDag) -> Result<crate::compile::jit::JitKernelRaw, String> {
1359 let _coord_names = resolved.input_names();
1360 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1361 Self::build_jit_layout(&resolved)?;
1362 let (guard, types) = Self::jit_slot_info(&resolved);
1363 let externs = Self::externs_of(&resolved)?;
1364 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1365 let mut k = crate::compile::jit::compile_jit_raw_with(
1366 coord_count,
1367 total_slots,
1368 jit_steps,
1369 output_map,
1370 resolved.nodes,
1371 externs,
1372 scratch,
1373 volatile,
1374 )?;
1375 k.set_slot_info(guard, types);
1376 k.set_attribution(attribution);
1377 Ok(k)
1378 }
1379
1380 #[cfg(feature = "jit")]
1386 pub fn try_compile_tier1_simd_ordinal(
1387 self,
1388 driving_input: &str,
1389 output: &str,
1390 ) -> Result<
1391 crate::compile::simd_tier1::Tier1SimdExecutor,
1392 crate::compile::simd_tier1::Tier1SimdError,
1393 > {
1394 let resolved = self.resolve().map_err(|error| {
1395 crate::compile::simd_tier1::Tier1SimdError::VectorGraphBuild(error.to_string())
1396 })?;
1397 crate::compile::simd_tier1::compile_tier1_ordinal(resolved, driving_input, output)
1398 }
1399
1400 #[doc(hidden)]
1402 #[cfg(feature = "jit")]
1403 pub fn try_compile_pure_jit_push(self) -> Result<crate::compile::jit::JitKernelPush, String> {
1404 let resolved = self.resolve().map_err(|e| format!("{e}"))?;
1405 Self::jit_push_from(resolved)
1406 }
1407
1408 #[cfg(feature = "jit")]
1409 fn jit_push_from(resolved: ResolvedDag) -> Result<crate::compile::jit::JitKernelPush, String> {
1410 let _coord_names = resolved.input_names();
1411 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1412 Self::build_jit_layout(&resolved)?;
1413 let deps = slot_layout(&resolved).expand_dependents(
1414 &resolved,
1415 &PolydatProgram::compute_dependents(
1416 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
1417 resolved.input_defs.len(),
1418 ),
1419 );
1420 let (guard, types) = Self::jit_slot_info(&resolved);
1421 let externs = Self::externs_of(&resolved)?;
1422 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1423 let mut k = crate::compile::jit::compile_jit_push(
1424 coord_count,
1425 total_slots,
1426 jit_steps,
1427 output_map,
1428 resolved.nodes,
1429 deps,
1430 externs,
1431 scratch,
1432 volatile,
1433 )?;
1434 k.set_slot_info(guard, types);
1435 k.set_attribution(attribution);
1436 Ok(k)
1437 }
1438
1439 #[doc(hidden)]
1441 #[cfg(feature = "jit")]
1442 pub fn try_compile_pure_jit_pull(self) -> Result<crate::compile::jit::JitKernelPull, String> {
1443 let resolved = self.resolve().map_err(|e| format!("{e}"))?;
1444 Self::jit_pull_from(resolved)
1445 }
1446
1447 #[cfg(feature = "jit")]
1448 fn jit_pull_from(resolved: ResolvedDag) -> Result<crate::compile::jit::JitKernelPull, String> {
1449 let _coord_names = resolved.input_names();
1450 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1451 Self::build_jit_layout(&resolved)?;
1452 let deps = slot_layout(&resolved).expand_dependents(
1453 &resolved,
1454 &PolydatProgram::compute_dependents(
1455 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
1456 resolved.input_defs.len(),
1457 ),
1458 );
1459 let (guard, types) = Self::jit_slot_info(&resolved);
1460 let externs = Self::externs_of(&resolved)?;
1461 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1462 let mut k = crate::compile::jit::compile_jit_pull(
1463 coord_count,
1464 total_slots,
1465 jit_steps,
1466 output_map,
1467 resolved.nodes,
1468 &deps,
1469 externs,
1470 scratch,
1471 volatile,
1472 )?;
1473 k.set_slot_info(guard, types);
1474 k.set_attribution(attribution);
1475 Ok(k)
1476 }
1477
1478 #[doc(hidden)]
1483 pub fn compile_hybrid(self) -> Result<crate::compile::hybrid::HybridKernel, String> {
1484 let resolved = self.resolve().map_err(|e| format!("{e}"))?;
1485 Self::hybrid_from(resolved)
1486 }
1487
1488 fn hybrid_from(resolved: ResolvedDag) -> Result<crate::compile::hybrid::HybridKernel, String> {
1489 let _coord_names = resolved.input_names();
1490 let layout = slot_layout(&resolved);
1491
1492 let output_map = layout.named_outputs(&resolved);
1493 let input_widths: Vec<usize> = resolved
1494 .input_defs
1495 .iter()
1496 .map(|d| d.port_type.slot_width())
1497 .collect();
1498
1499 let ref_slots = layout.ref_slot_mask(&resolved);
1500 let input_types: Vec<PortType> = resolved.input_defs.iter().map(|d| d.port_type).collect();
1501 let externs = Self::externs_of(&resolved)?;
1502 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1503 let classes = PolydatProgram::classify_lifecycle(
1506 &resolved.nodes,
1507 &resolved.wiring,
1508 &resolved.input_defs,
1509 &resolved.output_map,
1510 &resolved.output_modifiers,
1511 );
1512 let constant: Vec<bool> = classes
1513 .lifecycle
1514 .iter()
1515 .map(|lc| *lc == crate::kernel::EvalLifecycle::CompileConst)
1516 .collect();
1517 let mut kernel = crate::compile::hybrid::build_hybrid(
1518 &resolved.nodes,
1519 &resolved.wiring,
1520 layout.coord_slots,
1521 layout.total_slots,
1522 &layout.port_offsets,
1523 &layout.input_starts,
1524 &input_widths,
1525 output_map,
1526 ref_slots,
1527 &input_types,
1528 externs,
1529 constant,
1530 classes.nondeterministic,
1531 attribution,
1532 )?;
1533 kernel.retain_nodes(resolved.nodes);
1534 Ok(kernel)
1535 }
1536
1537 fn resolve(self) -> Result<ResolvedDag, AssemblyError> {
1539 self.resolve_with_log(None)
1540 }
1541
1542 fn resolve_with_log(
1543 self,
1544 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
1545 ) -> Result<ResolvedDag, AssemblyError> {
1546 if let Some(log) = log.as_deref_mut() {
1552 let cursor_slot = |name: &str| {
1553 self.cursor_schemas
1554 .iter()
1555 .any(|s| name.starts_with(&format!("{}__cursor", s.name)))
1556 };
1557 for def in &self.input_defs {
1558 if matches!(
1559 def.kind,
1560 crate::kernel::InputKind::ExternalWrite
1561 | crate::kernel::InputKind::IterationExtern
1562 ) && def.default == crate::ast::Value::None
1563 && !cursor_slot(&def.name)
1564 {
1565 log.push(crate::dsl::events::CompileEvent::ExternWithoutDefault {
1566 name: def.name.clone(),
1567 port_type: def.port_type.to_string(),
1568 });
1569 }
1570 }
1571 }
1572 let mut name_to_idx: HashMap<String, usize> = HashMap::new();
1574 for (i, pn) in self.nodes.iter().enumerate() {
1575 if name_to_idx.contains_key(&pn.name) {
1576 return Err(AssemblyError::DuplicateNode(pn.name.clone()));
1577 }
1578 name_to_idx.insert(pn.name.clone(), i);
1579 }
1580
1581 let input_to_idx: HashMap<String, usize> = self
1583 .input_defs
1584 .iter()
1585 .enumerate()
1586 .map(|(i, d)| (d.name.clone(), i))
1587 .collect();
1588
1589 for pn in &self.nodes {
1591 let expected = pn.node.meta().wire_inputs().len();
1592 let got = pn.inputs.len();
1593 if expected != got {
1594 return Err(AssemblyError::ArityMismatch {
1595 node_name: pn.name.clone(),
1596 expected,
1597 got,
1598 });
1599 }
1600 }
1601
1602 let mut all_nodes: Vec<PendingNode> = Vec::new();
1603 let mut all_name_to_idx: HashMap<String, usize> = HashMap::new();
1604 let mut adapter_count = 0usize;
1605 let mut assertion_count = 0usize;
1606 let strict_values = self.strict_values;
1607 let strict_types = self.strict_types;
1608 let strict = self.strict;
1609
1610 for pn in self.nodes {
1611 let idx = all_nodes.len();
1612 all_name_to_idx.insert(pn.name.clone(), idx);
1613 all_nodes.push(pn);
1614 }
1615
1616 let mut resolved_wiring: Vec<Vec<WireSource>> = Vec::new();
1617
1618 for node_idx in 0..all_nodes.len() {
1619 let mut node_wiring = Vec::new();
1620
1621 for (port_idx, wire_ref) in all_nodes[node_idx].inputs.clone().iter().enumerate() {
1622 let expected_type = all_nodes[node_idx].node.meta().wire_inputs()[port_idx].typ;
1623
1624 let (source, source_type) = match wire_ref {
1625 WireRef::Input(name) => {
1626 let input_idx = input_to_idx
1627 .get(name)
1628 .ok_or_else(|| AssemblyError::UnknownWire(name.clone()))?;
1629 let source_type = self.input_defs[*input_idx].port_type;
1630 (WireSource::Input(*input_idx), source_type)
1631 }
1632 WireRef::Node(name, out_port) => {
1633 let src_idx = all_name_to_idx
1634 .get(name)
1635 .ok_or_else(|| AssemblyError::UnknownWire(name.clone()))?;
1636 let src_type = all_nodes[*src_idx].node.meta().outs[*out_port].typ;
1637 (WireSource::NodeOutput(*src_idx, *out_port), src_type)
1638 }
1639 };
1640
1641 let node_name_for_typing = &all_nodes[node_idx].node.meta().name;
1673 let skip_type_check =
1674 UNTYPED_VARIADIC_NODES.contains(&node_name_for_typing.as_str());
1675
1676 if skip_type_check || source_type == expected_type {
1677 node_wiring.push(source);
1678 } else if let Some(adapter) = auto_adapter(source_type, expected_type) {
1679 if strict {
1680 return Err(AssemblyError::Other(format!(
1681 "strict mode: implicit type coercion {source_type} → {expected_type} \
1682 into '{}'. Use an explicit conversion function (e.g., u64_to_f64, \
1683 f64_to_u64).",
1684 all_nodes[node_idx].name
1685 )));
1686 }
1687 let adapter_name = format!("__adapt_{adapter_count}");
1688 adapter_count += 1;
1689 let adapter_idx = all_nodes.len();
1690
1691 if let Some(ref mut log) = log {
1692 let from_name = match wire_ref {
1693 WireRef::Input(n) => n.clone(),
1694 WireRef::Node(n, _) => n.clone(),
1695 };
1696 log.push(crate::dsl::events::CompileEvent::TypeAdapterInserted {
1697 from_node: from_name,
1698 to_node: all_nodes[node_idx].name.clone(),
1699 adapter: format!("{source_type:?}→{expected_type:?}"),
1700 });
1701 }
1702
1703 all_name_to_idx.insert(adapter_name.clone(), adapter_idx);
1704
1705 let adapter_wiring = vec![source];
1706 while resolved_wiring.len() <= adapter_idx {
1707 resolved_wiring.push(Vec::new());
1708 }
1709 resolved_wiring[adapter_idx] = adapter_wiring;
1710
1711 all_nodes.push(PendingNode {
1712 name: adapter_name,
1713 node: adapter,
1714 inputs: vec![],
1715 });
1716
1717 node_wiring.push(WireSource::NodeOutput(adapter_idx, 0));
1718 } else {
1719 let from_name = match wire_ref {
1720 WireRef::Input(n) => n.clone(),
1721 WireRef::Node(n, _) => n.clone(),
1722 };
1723 return Err(AssemblyError::TypeMismatch {
1724 from_node: from_name,
1725 from_port: match wire_ref {
1726 WireRef::Input(_) => 0,
1727 WireRef::Node(_, p) => *p,
1728 },
1729 from_type: source_type,
1730 to_node: all_nodes[node_idx].name.clone(),
1731 to_port: port_idx,
1732 to_type: expected_type,
1733 });
1734 }
1735
1736 let sink_port = &all_nodes[node_idx].node.meta().wire_inputs()[port_idx];
1749 if let Some(constraint) = sink_port.constraint {
1750 let last_source = node_wiring.last().expect("wire just pushed").clone();
1751 if strict_values
1752 && !value_constraint_proven(&all_nodes, &last_source, &constraint)
1753 {
1754 let assert_name = format!("__assert_v_{assertion_count}");
1755 assertion_count += 1;
1756 let assert_idx = all_nodes.len();
1757
1758 if let Some(ref mut log) = log {
1759 let from_name = match wire_ref {
1760 WireRef::Input(n) => n.clone(),
1761 WireRef::Node(n, _) => n.clone(),
1762 };
1763 log.push(crate::dsl::events::CompileEvent::AssertionInserted {
1764 from_node: from_name,
1765 to_node: all_nodes[node_idx].name.clone(),
1766 kind: format!("{:?} value-assert {:?}", expected_type, constraint),
1767 });
1768 }
1769
1770 all_name_to_idx.insert(assert_name.clone(), assert_idx);
1771 let assert_wiring = vec![last_source];
1772 while resolved_wiring.len() <= assert_idx {
1773 resolved_wiring.push(Vec::new());
1774 }
1775 resolved_wiring[assert_idx] = assert_wiring;
1776
1777 all_nodes.push(PendingNode {
1778 name: assert_name,
1779 node: crate::library::assertions::assert_value_node(
1780 expected_type,
1781 constraint,
1782 ),
1783 inputs: vec![],
1784 });
1785
1786 *node_wiring.last_mut().unwrap() = WireSource::NodeOutput(assert_idx, 0);
1789 } else if let Some(ref mut log) = log {
1790 let from_name = match wire_ref {
1791 WireRef::Input(n) => n.clone(),
1792 WireRef::Node(n, _) => n.clone(),
1793 };
1794 log.push(crate::dsl::events::CompileEvent::AssertionSkipped {
1795 from_node: from_name,
1796 to_node: all_nodes[node_idx].name.clone(),
1797 reason: assertion_skip_reason(
1798 strict_values,
1799 &all_nodes,
1800 &last_source,
1801 &constraint,
1802 ),
1803 });
1804 }
1805 } else if strict_types && source_type != expected_type {
1806 }
1814 }
1815
1816 while resolved_wiring.len() <= node_idx {
1817 resolved_wiring.push(Vec::new());
1818 }
1819 resolved_wiring[node_idx] = node_wiring;
1820 }
1821
1822 while resolved_wiring.len() < all_nodes.len() {
1823 resolved_wiring.push(Vec::new());
1824 }
1825
1826 {
1831 let rules = crate::compile::fusion::default_rules();
1832 if !rules.is_empty() {
1833 let mut output_nodes: Vec<usize> = Vec::new();
1836 for wire_ref in self.outputs.values() {
1837 if let WireRef::Node(node_name, _) = wire_ref
1838 && let Some(&idx) = all_name_to_idx.get(node_name)
1839 {
1840 output_nodes.push(idx);
1841 }
1842 }
1843
1844 let mut opt_nodes: Vec<Option<Box<dyn PolydatNode>>> =
1846 all_nodes.into_iter().map(|pn| Some(pn.node)).collect();
1847
1848 let fused_count = crate::compile::fusion::apply_fusions(
1849 &mut opt_nodes,
1850 &mut resolved_wiring,
1851 &mut all_name_to_idx,
1852 &rules,
1853 &output_nodes,
1854 );
1855 if fused_count > 0
1856 && let Some(ref mut log) = log
1857 {
1858 log.push(crate::dsl::events::CompileEvent::FusionApplied {
1859 pattern: "subgraph".into(),
1860 nodes_replaced: fused_count,
1861 });
1862 }
1863
1864 all_nodes = opt_nodes
1867 .into_iter()
1868 .enumerate()
1869 .map(|(i, opt)| PendingNode {
1870 name: all_name_to_idx
1871 .iter()
1872 .find(|&(_, &idx)| idx == i)
1873 .map(|(n, _)| n.clone())
1874 .unwrap_or_else(|| format!("__removed_{i}")),
1875 node: opt.unwrap_or_else(|| {
1876 Box::new(crate::library::identity::Identity::new(
1877 crate::ast::PortType::U64,
1878 ))
1879 }),
1880 inputs: vec![], })
1882 .collect();
1883 }
1884 }
1885
1886 let node_count = all_nodes.len();
1893 let mut reachable = vec![false; node_count];
1894 {
1895 let mut worklist: Vec<usize> = Vec::new();
1896 for wire_ref in self.outputs.values() {
1898 if let WireRef::Node(node_name, _) = wire_ref
1899 && let Some(&idx) = all_name_to_idx.get(node_name)
1900 {
1901 worklist.push(idx);
1902 }
1903 }
1904 for (idx, pn) in all_nodes.iter().enumerate() {
1914 if matches!(
1915 pn.node.meta().name.as_str(),
1916 "log_debug" | "log_info" | "log_warn" | "log_error"
1917 ) {
1918 worklist.push(idx);
1919 }
1920 }
1921 while let Some(idx) = worklist.pop() {
1923 if reachable[idx] {
1924 continue;
1925 }
1926 reachable[idx] = true;
1927 for source in &resolved_wiring[idx] {
1928 if let WireSource::NodeOutput(upstream, _) = source
1929 && !reachable[*upstream]
1930 {
1931 worklist.push(*upstream);
1932 }
1933 }
1934 }
1935 }
1936 let live_count = reachable.iter().filter(|&&r| r).count();
1937
1938 let mut in_degree = vec![0usize; node_count];
1940 let mut dependents: Vec<Vec<usize>> = vec![Vec::new(); node_count];
1941
1942 for (node_idx, wiring) in resolved_wiring.iter().enumerate() {
1943 if !reachable[node_idx] {
1944 continue;
1945 }
1946 for source in wiring {
1947 if let WireSource::NodeOutput(upstream, _) = source {
1948 in_degree[node_idx] += 1;
1949 dependents[*upstream].push(node_idx);
1950 }
1951 }
1952 }
1953
1954 let mut queue: Vec<usize> = (0..node_count)
1955 .filter(|i| reachable[*i] && in_degree[*i] == 0)
1956 .collect();
1957 let mut sorted_order: Vec<usize> = Vec::with_capacity(live_count);
1958
1959 while let Some(idx) = queue.pop() {
1960 sorted_order.push(idx);
1961 for &dep in &dependents[idx] {
1962 in_degree[dep] -= 1;
1963 if in_degree[dep] == 0 {
1964 queue.push(dep);
1965 }
1966 }
1967 }
1968
1969 if sorted_order.len() != live_count {
1970 return Err(AssemblyError::CycleDetected);
1971 }
1972
1973 let mut old_to_new = vec![0usize; node_count];
1974 for (new_idx, &old_idx) in sorted_order.iter().enumerate() {
1975 old_to_new[old_idx] = new_idx;
1976 }
1977
1978 let mut sorted_nodes: Vec<Option<Box<dyn PolydatNode>>> =
1979 all_nodes.into_iter().map(|pn| Some(pn.node)).collect();
1980
1981 let final_nodes: Vec<Box<dyn PolydatNode>> = sorted_order
1982 .iter()
1983 .map(|&old_idx| sorted_nodes[old_idx].take().unwrap())
1984 .collect();
1985
1986 let final_wiring: Vec<Vec<WireSource>> = sorted_order
1987 .iter()
1988 .map(|&old_idx| {
1989 resolved_wiring[old_idx]
1990 .iter()
1991 .map(|source| match source {
1992 WireSource::Input(c) => WireSource::Input(*c),
1993 WireSource::NodeOutput(old_up, port) => {
1994 WireSource::NodeOutput(old_to_new[*old_up], *port)
1995 }
1996 })
1997 .collect()
1998 })
1999 .collect();
2000
2001 let mut final_output_map: HashMap<String, (usize, usize)> = HashMap::new();
2002 for (name, wire_ref) in &self.outputs {
2003 match wire_ref {
2004 WireRef::Input(coord_name) => {
2005 return Err(AssemblyError::UnknownWire(format!(
2006 "output '{name}' references coordinate '{coord_name}' directly; \
2007 wire through a node instead"
2008 )));
2009 }
2010 WireRef::Node(node_name, port) => {
2011 let old_idx = all_name_to_idx
2012 .get(node_name)
2013 .ok_or_else(|| AssemblyError::UnknownWire(node_name.clone()))?;
2014 final_output_map.insert(name.clone(), (old_to_new[*old_idx], *port));
2015 }
2016 }
2017 }
2018
2019 for f in crate::compile::roundtrip_lint::lint_type_round_trips(
2026 &final_nodes,
2027 &final_wiring,
2028 &self.input_defs,
2029 ) {
2030 if strict_values {
2031 return Err(AssemblyError::Other(f.message()));
2032 }
2033 eprintln!("warning: {}", f.message());
2034 if let Some(ref mut log) = log {
2035 log.push(crate::dsl::events::CompileEvent::Warning {
2036 message: f.message(),
2037 });
2038 }
2039 }
2040
2041 Ok(ResolvedDag {
2042 nodes: final_nodes,
2043 wiring: final_wiring,
2044 input_defs: self.input_defs,
2045 coord_count: self.coord_count,
2046 output_map: final_output_map,
2047 output_order: self.output_order,
2048 source: self.source,
2049 context: self.context,
2050 output_modifiers: self.output_modifiers,
2051 const_outputs: self.const_outputs,
2052 cursor_schemas: self.cursor_schemas,
2053 ledger: self.ledger,
2054 })
2055 }
2056}
2057
2058fn value_constraint_proven(
2073 all_nodes: &[PendingNode],
2074 src: &WireSource,
2075 _constraint: &crate::dsl::const_constraints::ConstConstraint,
2076) -> bool {
2077 match src {
2078 WireSource::Input(_) => false,
2079 WireSource::NodeOutput(idx, _) => {
2080 let meta = all_nodes[*idx].node.meta();
2081 let no_wire_inputs = meta.wire_inputs().is_empty();
2086 if no_wire_inputs {
2087 return true;
2088 }
2089 if meta.name.starts_with("__assert_v_") || meta.name.starts_with("assert_") {
2094 return true;
2095 }
2096 false
2097 }
2098 }
2099}
2100
2101fn assertion_skip_reason(
2105 strict_values: bool,
2106 all_nodes: &[PendingNode],
2107 src: &WireSource,
2108 _constraint: &crate::dsl::const_constraints::ConstConstraint,
2109) -> String {
2110 if !strict_values {
2111 return "strict_values not enabled".into();
2112 }
2113 match src {
2114 WireSource::Input(_) => "raw input wire".into(),
2115 WireSource::NodeOutput(idx, _) => {
2116 let meta = all_nodes[*idx].node.meta();
2117 if meta.wire_inputs().is_empty() {
2118 "constant source already validated".into()
2119 } else if meta.name.starts_with("__assert_v_") || meta.name.starts_with("assert_") {
2120 "upstream assertion".into()
2121 } else {
2122 "no skip rule matched".into()
2123 }
2124 }
2125 }
2126}
2127
2128pub(crate) fn shared_outputs_of(resolved: &ResolvedDag) -> Vec<&str> {
2131 let mut shared: Vec<&str> = resolved
2132 .output_modifiers
2133 .iter()
2134 .filter(|(_, m)| **m == crate::dsl::ast::BindingModifier::SHARED)
2135 .map(|(name, _)| name.as_str())
2136 .collect();
2137 shared.sort();
2138 shared
2139}
2140
2141pub(crate) fn wire_types_of(resolved: &ResolvedDag, node_idx: usize) -> Vec<PortType> {
2144 resolved.wiring[node_idx]
2145 .iter()
2146 .map(|src| match src {
2147 crate::kernel::WireSource::Input(i) => resolved.input_defs[*i].port_type,
2148 crate::kernel::WireSource::NodeOutput(j, p) => resolved.nodes[*j].meta().outs[*p].typ,
2149 })
2150 .collect()
2151}
2152
2153pub(crate) const UNTYPED_VARIADIC_NODES: &[&str] = &[
2162 "printf",
2163 "pick",
2164 "log_debug",
2165 "log_info",
2166 "log_warn",
2167 "log_error",
2168 "exactly_one_value",
2169 "json_text",
2170 "json_array",
2171 "json_object",
2172 "str_concat",
2173 "emit_row",
2174 "tile_render",
2175];
2176
2177pub fn auto_adapter(from: PortType, to: PortType) -> Option<Box<dyn PolydatNode>> {
2181 use crate::library::convert::{
2182 BoolToStr, BoolToU64, F32ToF64, F32ToString, I32ToF64, I32ToI64, I32ToString, I64ToF64,
2183 I64ToString, U32ToF64, U32ToI64, U32ToString, U32ToU64,
2184 };
2185 use crate::library::polyfill as P;
2186 use crate::library::polyfill_128 as W;
2187 use crate::library::polyfill_complete as C;
2188 use crate::library::polyfill_narrow as N;
2189 match (from, to) {
2190 (PortType::U64, PortType::F64) => Some(Box::new(U64ToF64::new())),
2192 (PortType::U32, PortType::U64) => Some(Box::new(U32ToU64::new())),
2193 (PortType::U32, PortType::I64) => Some(Box::new(U32ToI64::new())),
2194 (PortType::U32, PortType::F64) => Some(Box::new(U32ToF64::new())),
2195 (PortType::I32, PortType::I64) => Some(Box::new(I32ToI64::new())),
2196 (PortType::I32, PortType::F64) => Some(Box::new(I32ToF64::new())),
2197 (PortType::I64, PortType::F64) => Some(Box::new(I64ToF64::new())),
2198 (PortType::F32, PortType::F64) => Some(Box::new(F32ToF64::new())),
2199
2200 (PortType::U64, PortType::Str) => Some(Box::new(U64ToString::new())),
2202 (PortType::F64, PortType::Str) => Some(Box::new(F64ToString::new())),
2203 (PortType::Bool, PortType::Str) => Some(Box::new(BoolToStr::new())),
2204 (PortType::Json, PortType::Str) => Some(Box::new(JsonToStr::new())),
2205 (PortType::U32, PortType::Str) => Some(Box::new(U32ToString::new())),
2206 (PortType::I32, PortType::Str) => Some(Box::new(I32ToString::new())),
2207 (PortType::I64, PortType::Str) => Some(Box::new(I64ToString::new())),
2208 (PortType::F32, PortType::Str) => Some(Box::new(F32ToString::new())),
2209
2210 (PortType::Bool, PortType::U64) => Some(Box::new(BoolToU64::new())),
2212 (PortType::Bool, PortType::U32) => Some(Box::new(P::BoolToU32::new())),
2213 (PortType::Bool, PortType::I64) => Some(Box::new(P::BoolToI64::new())),
2214 (PortType::Bool, PortType::I32) => Some(Box::new(P::BoolToI32::new())),
2215 (PortType::Bool, PortType::F64) => Some(Box::new(P::BoolToF64::new())),
2216 (PortType::Bool, PortType::F32) => Some(Box::new(P::BoolToF32::new())),
2217 (PortType::U64, PortType::Bool) => {
2218 Some(Box::new(crate::library::convert::U64ToBool::new()))
2219 }
2220 (PortType::U32, PortType::Bool) => Some(Box::new(P::U32ToBool::new())),
2221 (PortType::I64, PortType::Bool) => Some(Box::new(P::I64ToBool::new())),
2222 (PortType::I32, PortType::Bool) => Some(Box::new(P::I32ToBool::new())),
2223 (PortType::F64, PortType::Bool) => Some(Box::new(P::F64ToBool::new())),
2224 (PortType::F32, PortType::Bool) => Some(Box::new(P::F32ToBool::new())),
2225
2226 (PortType::U64, PortType::Bytes) => Some(Box::new(P::U64ToBytes::new())),
2228 (PortType::U32, PortType::Bytes) => Some(Box::new(P::U32ToBytes::new())),
2229 (PortType::I64, PortType::Bytes) => Some(Box::new(P::I64ToBytes::new())),
2230 (PortType::I32, PortType::Bytes) => Some(Box::new(P::I32ToBytes::new())),
2231 (PortType::F64, PortType::Bytes) => Some(Box::new(P::F64ToBytes::new())),
2232 (PortType::F32, PortType::Bytes) => Some(Box::new(P::F32ToBytes::new())),
2233 (PortType::Bool, PortType::Bytes) => Some(Box::new(P::BoolToBytes::new())),
2234 (PortType::VecF32, PortType::Bytes) => Some(Box::new(P::VecF32ToBytes::new())),
2235 (PortType::VecI32, PortType::Bytes) => Some(Box::new(P::VecI32ToBytes::new())),
2236
2237 (PortType::U64, PortType::Json) => Some(Box::new(P::U64ToJson::new())),
2241 (PortType::U32, PortType::Json) => Some(Box::new(P::U32ToJson::new())),
2242 (PortType::I64, PortType::Json) => Some(Box::new(P::I64ToJson::new())),
2243 (PortType::I32, PortType::Json) => Some(Box::new(P::I32ToJson::new())),
2244 (PortType::Bool, PortType::Json) => Some(Box::new(P::BoolToJson::new())),
2245 (PortType::VecI32, PortType::Json) => Some(Box::new(P::VecI32ToJson::new())),
2246
2247 (PortType::VecI32, PortType::VecF32) => Some(Box::new(P::VecI32ToVecF32::new())),
2249
2250 (PortType::U8, PortType::U64) => Some(Box::new(N::U8ToU64::new())),
2255 (PortType::U8, PortType::U32) => Some(Box::new(N::U8ToU32::new())),
2256 (PortType::U8, PortType::U16) => Some(Box::new(N::U8ToU16::new())),
2257 (PortType::U8, PortType::F64) => Some(Box::new(N::U8ToF64::new())),
2258 (PortType::U16, PortType::U64) => Some(Box::new(N::U16ToU64::new())),
2259 (PortType::U16, PortType::U32) => Some(Box::new(N::U16ToU32::new())),
2260 (PortType::U16, PortType::F64) => Some(Box::new(N::U16ToF64::new())),
2261 (PortType::I8, PortType::I64) => Some(Box::new(N::I8ToI64::new())),
2262 (PortType::I8, PortType::I32) => Some(Box::new(N::I8ToI32::new())),
2263 (PortType::I8, PortType::I16) => Some(Box::new(N::I8ToI16::new())),
2264 (PortType::I8, PortType::F64) => Some(Box::new(N::I8ToF64::new())),
2265 (PortType::I16, PortType::I64) => Some(Box::new(N::I16ToI64::new())),
2266 (PortType::I16, PortType::I32) => Some(Box::new(N::I16ToI32::new())),
2267 (PortType::I16, PortType::F64) => Some(Box::new(N::I16ToF64::new())),
2268 (PortType::F16, PortType::F32) => Some(Box::new(N::F16ToF32::new())),
2269 (PortType::F16, PortType::F64) => Some(Box::new(N::F16ToF64::new())),
2270 (PortType::U8, PortType::I16) => Some(Box::new(N::U8ToI16::new())),
2273 (PortType::U8, PortType::I32) => Some(Box::new(N::U8ToI32::new())),
2274 (PortType::U8, PortType::I64) => Some(Box::new(N::U8ToI64::new())),
2275 (PortType::U8, PortType::F32) => Some(Box::new(N::U8ToF32::new())),
2276 (PortType::U16, PortType::I32) => Some(Box::new(N::U16ToI32::new())),
2277 (PortType::U16, PortType::I64) => Some(Box::new(N::U16ToI64::new())),
2278 (PortType::U16, PortType::F32) => Some(Box::new(N::U16ToF32::new())),
2279 (PortType::I8, PortType::F32) => Some(Box::new(N::I8ToF32::new())),
2280 (PortType::I16, PortType::F32) => Some(Box::new(N::I16ToF32::new())),
2281 (PortType::U8, PortType::F16) => Some(Box::new(N::U8ToF16::new())),
2282 (PortType::I8, PortType::F16) => Some(Box::new(N::I8ToF16::new())),
2283 (PortType::U8, PortType::Str) => Some(Box::new(N::U8ToString::new())),
2284 (PortType::U16, PortType::Str) => Some(Box::new(N::U16ToString::new())),
2285 (PortType::I8, PortType::Str) => Some(Box::new(N::I8ToString::new())),
2286 (PortType::I16, PortType::Str) => Some(Box::new(N::I16ToString::new())),
2287 (PortType::F16, PortType::Str) => Some(Box::new(N::F16ToString::new())),
2288 (PortType::Bool, PortType::U8) => Some(Box::new(N::BoolToU8::new())),
2289 (PortType::Bool, PortType::U16) => Some(Box::new(N::BoolToU16::new())),
2290 (PortType::Bool, PortType::I8) => Some(Box::new(N::BoolToI8::new())),
2291 (PortType::Bool, PortType::I16) => Some(Box::new(N::BoolToI16::new())),
2292 (PortType::Bool, PortType::F16) => Some(Box::new(N::BoolToF16::new())),
2293 (PortType::U8, PortType::Bool) => Some(Box::new(N::U8ToBool::new())),
2294 (PortType::U16, PortType::Bool) => Some(Box::new(N::U16ToBool::new())),
2295 (PortType::I8, PortType::Bool) => Some(Box::new(N::I8ToBool::new())),
2296 (PortType::I16, PortType::Bool) => Some(Box::new(N::I16ToBool::new())),
2297 (PortType::F16, PortType::Bool) => Some(Box::new(N::F16ToBool::new())),
2298 (PortType::U8, PortType::Bytes) => Some(Box::new(N::U8ToBytes::new())),
2299 (PortType::U16, PortType::Bytes) => Some(Box::new(N::U16ToBytes::new())),
2300 (PortType::I8, PortType::Bytes) => Some(Box::new(N::I8ToBytes::new())),
2301 (PortType::I16, PortType::Bytes) => Some(Box::new(N::I16ToBytes::new())),
2302 (PortType::F16, PortType::Bytes) => Some(Box::new(N::F16ToBytes::new())),
2303 (PortType::U8, PortType::Json) => Some(Box::new(N::U8ToJson::new())),
2304 (PortType::U16, PortType::Json) => Some(Box::new(N::U16ToJson::new())),
2305 (PortType::I8, PortType::Json) => Some(Box::new(N::I8ToJson::new())),
2306 (PortType::I16, PortType::Json) => Some(Box::new(N::I16ToJson::new())),
2307
2308 (PortType::U64, PortType::U128) => Some(Box::new(W::U64ToU128::new())),
2313 (PortType::U64, PortType::I128) => Some(Box::new(W::U64ToI128::new())),
2314 (PortType::I64, PortType::I128) => Some(Box::new(W::I64ToI128::new())),
2315 (PortType::U8, PortType::U128) => Some(Box::new(W::U8ToU128::new())),
2320 (PortType::U8, PortType::I128) => Some(Box::new(W::U8ToI128::new())),
2321 (PortType::U16, PortType::U128) => Some(Box::new(W::U16ToU128::new())),
2322 (PortType::U16, PortType::I128) => Some(Box::new(W::U16ToI128::new())),
2323 (PortType::U32, PortType::U128) => Some(Box::new(W::U32ToU128::new())),
2324 (PortType::U32, PortType::I128) => Some(Box::new(W::U32ToI128::new())),
2325 (PortType::I8, PortType::I128) => Some(Box::new(W::I8ToI128::new())),
2326 (PortType::I16, PortType::I128) => Some(Box::new(W::I16ToI128::new())),
2327 (PortType::I32, PortType::I128) => Some(Box::new(W::I32ToI128::new())),
2328 (PortType::Bool, PortType::U128) => Some(Box::new(W::BoolToU128::new())),
2329 (PortType::Bool, PortType::I128) => Some(Box::new(W::BoolToI128::new())),
2330 (PortType::U128, PortType::Bool) => Some(Box::new(W::U128ToBool::new())),
2331 (PortType::I128, PortType::Bool) => Some(Box::new(W::I128ToBool::new())),
2332 (PortType::U128, PortType::F64) => Some(Box::new(W::U128ToF64::new())),
2333 (PortType::I128, PortType::F64) => Some(Box::new(W::I128ToF64::new())),
2334 (PortType::U128, PortType::Str) => Some(Box::new(W::U128ToString::new())),
2335 (PortType::I128, PortType::Str) => Some(Box::new(W::I128ToString::new())),
2336 (PortType::U128, PortType::Bytes) => Some(Box::new(W::U128ToBytes::new())),
2337 (PortType::I128, PortType::Bytes) => Some(Box::new(W::I128ToBytes::new())),
2338 (PortType::U128, PortType::Json) => Some(Box::new(W::U128ToJson::new())),
2339 (PortType::I128, PortType::Json) => Some(Box::new(W::I128ToJson::new())),
2340
2341 (from, to)
2346 if crate::library::register_view::is_reg_port(from)
2347 && crate::library::register_view::is_reg_port(to) =>
2348 {
2349 Some(Box::new(crate::library::register_view::RegView::new(to)))
2350 }
2351
2352 (PortType::VecI8, PortType::VecI16) => Some(Box::new(C::VecI8ToVecI16::new())),
2356 (PortType::VecI8, PortType::VecI32) => Some(Box::new(C::VecI8ToVecI32::new())),
2357 (PortType::VecI8, PortType::VecI64) => Some(Box::new(C::VecI8ToVecI64::new())),
2358 (PortType::VecI8, PortType::VecF16) => Some(Box::new(C::VecI8ToVecF16::new())),
2359 (PortType::VecI8, PortType::VecF32) => Some(Box::new(C::VecI8ToVecF32::new())),
2360 (PortType::VecI8, PortType::VecF64) => Some(Box::new(C::VecI8ToVecF64::new())),
2361 (PortType::VecI16, PortType::VecI32) => Some(Box::new(C::VecI16ToVecI32::new())),
2362 (PortType::VecI16, PortType::VecI64) => Some(Box::new(C::VecI16ToVecI64::new())),
2363 (PortType::VecI16, PortType::VecF32) => Some(Box::new(C::VecI16ToVecF32::new())),
2364 (PortType::VecI16, PortType::VecF64) => Some(Box::new(C::VecI16ToVecF64::new())),
2365 (PortType::VecI32, PortType::VecI64) => Some(Box::new(C::VecI32ToVecI64::new())),
2366 (PortType::VecI32, PortType::VecF64) => Some(Box::new(C::VecI32ToVecF64::new())),
2367 (PortType::VecI64, PortType::VecF64) => Some(Box::new(C::VecI64ToVecF64::new())),
2368 (PortType::VecF16, PortType::VecF32) => Some(Box::new(C::VecF16ToVecF32::new())),
2369 (PortType::VecF16, PortType::VecF64) => Some(Box::new(C::VecF16ToVecF64::new())),
2370 (PortType::VecF32, PortType::VecF64) => Some(Box::new(C::VecF32ToVecF64::new())),
2371 (PortType::VecF64, PortType::Bytes) => Some(Box::new(C::VecF64ToBytes::new())),
2372 (PortType::VecI64, PortType::Bytes) => Some(Box::new(C::VecI64ToBytes::new())),
2373 (PortType::VecF16, PortType::Bytes) => Some(Box::new(C::VecF16ToBytes::new())),
2374 (PortType::VecI16, PortType::Bytes) => Some(Box::new(C::VecI16ToBytes::new())),
2375 (PortType::VecI8, PortType::Bytes) => Some(Box::new(C::VecI8ToBytes::new())),
2376 (PortType::VecI64, PortType::Json) => Some(Box::new(C::VecI64ToJson::new())),
2377 (PortType::VecI16, PortType::Json) => Some(Box::new(C::VecI16ToJson::new())),
2378 (PortType::VecI8, PortType::Json) => Some(Box::new(C::VecI8ToJson::new())),
2379 (PortType::VecI32, PortType::Str) => Some(Box::new(P::VecI32ToStr::new())),
2380 (PortType::VecI64, PortType::Str) => Some(Box::new(C::VecI64ToStr::new())),
2381 (PortType::VecI16, PortType::Str) => Some(Box::new(C::VecI16ToStr::new())),
2382 (PortType::VecI8, PortType::Str) => Some(Box::new(C::VecI8ToStr::new())),
2383
2384 _ => None,
2385 }
2386}
2387
2388pub fn boundary_adapter(from: PortType, to: PortType) -> Option<Box<dyn PolydatNode>> {
2418 if let Some(adapter) = auto_adapter(from, to) {
2419 return Some(adapter);
2420 }
2421 use crate::library::convert::{StrToBool, StrToF64, StrToU64};
2422 use crate::library::polyfill as P;
2423 use crate::library::polyfill_128 as W;
2424 use crate::library::polyfill_complete as C;
2425 use crate::library::polyfill_narrow as N;
2426 match (from, to) {
2427 (PortType::U64, PortType::U32) => Some(Box::new(P::U64ToU32::new())),
2429 (PortType::U64, PortType::I64) => Some(Box::new(P::U64ToI64::new())),
2430 (PortType::U64, PortType::I32) => Some(Box::new(P::U64ToI32::new())),
2431 (PortType::U64, PortType::F32) => Some(Box::new(P::U64ToF32::new())),
2432 (PortType::U32, PortType::I32) => Some(Box::new(P::U32ToI32::new())),
2433 (PortType::U32, PortType::F32) => Some(Box::new(P::U32ToF32::new())),
2434 (PortType::I64, PortType::U64) => Some(Box::new(P::I64ToU64::new())),
2435 (PortType::I64, PortType::U32) => Some(Box::new(P::I64ToU32::new())),
2436 (PortType::I64, PortType::I32) => Some(Box::new(P::I64ToI32::new())),
2437 (PortType::I64, PortType::F32) => Some(Box::new(P::I64ToF32::new())),
2438 (PortType::I32, PortType::U64) => Some(Box::new(P::I32ToU64::new())),
2439 (PortType::I32, PortType::U32) => Some(Box::new(P::I32ToU32::new())),
2440 (PortType::I32, PortType::F32) => Some(Box::new(P::I32ToF32::new())),
2441 (PortType::F64, PortType::U64) => Some(Box::new(P::F64ToU64Checked::new())),
2442 (PortType::F64, PortType::U32) => Some(Box::new(P::F64ToU32::new())),
2443 (PortType::F64, PortType::I64) => Some(Box::new(P::F64ToI64::new())),
2444 (PortType::F64, PortType::I32) => Some(Box::new(P::F64ToI32::new())),
2445 (PortType::F64, PortType::F32) => Some(Box::new(P::F64ToF32::new())),
2446 (PortType::F32, PortType::U64) => Some(Box::new(P::F32ToU64::new())),
2447 (PortType::F32, PortType::U32) => Some(Box::new(P::F32ToU32::new())),
2448 (PortType::F32, PortType::I64) => Some(Box::new(P::F32ToI64::new())),
2449 (PortType::F32, PortType::I32) => Some(Box::new(P::F32ToI32::new())),
2450
2451 (PortType::Str, PortType::Bool) => Some(Box::new(StrToBool::new())),
2453 (PortType::Str, PortType::U64) => Some(Box::new(StrToU64::new())),
2454 (PortType::Str, PortType::F64) => Some(Box::new(StrToF64::new())),
2455 (PortType::Str, PortType::U32) => Some(Box::new(P::StrToU32::new())),
2456 (PortType::Str, PortType::I64) => Some(Box::new(P::StrToI64::new())),
2457 (PortType::Str, PortType::I32) => Some(Box::new(P::StrToI32::new())),
2458 (PortType::Str, PortType::F32) => Some(Box::new(P::StrToF32::new())),
2459 (PortType::Str, PortType::Bytes) => Some(Box::new(P::StrToBytes::new())),
2460 (PortType::Str, PortType::Json) => Some(Box::new(P::StrToJson::new())),
2461 (PortType::Str, PortType::VecF32) => Some(Box::new(P::StrToVecF32::new())),
2462 (PortType::Str, PortType::VecI32) => Some(Box::new(P::StrToVecI32::new())),
2463
2464 (PortType::Bytes, PortType::U64) => Some(Box::new(P::BytesToU64::new())),
2466 (PortType::Bytes, PortType::U32) => Some(Box::new(P::BytesToU32::new())),
2467 (PortType::Bytes, PortType::I64) => Some(Box::new(P::BytesToI64::new())),
2468 (PortType::Bytes, PortType::I32) => Some(Box::new(P::BytesToI32::new())),
2469 (PortType::Bytes, PortType::F64) => Some(Box::new(P::BytesToF64::new())),
2470 (PortType::Bytes, PortType::F32) => Some(Box::new(P::BytesToF32::new())),
2471 (PortType::Bytes, PortType::Bool) => Some(Box::new(P::BytesToBool::new())),
2472 (PortType::Bytes, PortType::Str) => Some(Box::new(P::BytesToStr::new())),
2473 (PortType::Bytes, PortType::Json) => Some(Box::new(P::BytesToJson::new())),
2474 (PortType::Bytes, PortType::VecF32) => Some(Box::new(P::BytesToVecF32::new())),
2475 (PortType::Bytes, PortType::VecI32) => Some(Box::new(P::BytesToVecI32::new())),
2476
2477 (PortType::Json, PortType::U64) => Some(Box::new(P::JsonToU64::new())),
2479 (PortType::Json, PortType::U32) => Some(Box::new(P::JsonToU32::new())),
2480 (PortType::Json, PortType::I64) => Some(Box::new(P::JsonToI64::new())),
2481 (PortType::Json, PortType::I32) => Some(Box::new(P::JsonToI32::new())),
2482 (PortType::Json, PortType::F64) => Some(Box::new(P::JsonToF64::new())),
2483 (PortType::Json, PortType::F32) => Some(Box::new(P::JsonToF32::new())),
2484 (PortType::Json, PortType::Bool) => Some(Box::new(P::JsonToBool::new())),
2485 (PortType::Json, PortType::Bytes) => Some(Box::new(P::JsonToBytes::new())),
2486 (PortType::Json, PortType::VecF32) => Some(Box::new(P::JsonToVecF32::new())),
2487 (PortType::Json, PortType::VecI32) => Some(Box::new(P::JsonToVecI32::new())),
2488
2489 (PortType::F64, PortType::Json) => Some(Box::new(P::F64ToJson::new())),
2491 (PortType::F32, PortType::Json) => Some(Box::new(P::F32ToJson::new())),
2492 (PortType::VecF32, PortType::Json) => Some(Box::new(P::VecF32ToJson::new())),
2493 (PortType::VecF32, PortType::Str) => Some(Box::new(P::VecF32ToStr::new())),
2494
2495 (PortType::VecF32, PortType::VecI32) => Some(Box::new(P::VecF32ToVecI32::new())),
2497
2498 (PortType::U64, PortType::U8) => Some(Box::new(N::U64ToU8::new())),
2502 (PortType::U32, PortType::U8) => Some(Box::new(N::U32ToU8::new())),
2503 (PortType::U16, PortType::U8) => Some(Box::new(N::U16ToU8::new())),
2504 (PortType::I64, PortType::U8) => Some(Box::new(N::I64ToU8::new())),
2505 (PortType::F64, PortType::U8) => Some(Box::new(N::F64ToU8::new())),
2506 (PortType::U64, PortType::U16) => Some(Box::new(N::U64ToU16::new())),
2507 (PortType::U32, PortType::U16) => Some(Box::new(N::U32ToU16::new())),
2508 (PortType::I64, PortType::U16) => Some(Box::new(N::I64ToU16::new())),
2509 (PortType::F64, PortType::U16) => Some(Box::new(N::F64ToU16::new())),
2510 (PortType::I64, PortType::I8) => Some(Box::new(N::I64ToI8::new())),
2511 (PortType::I32, PortType::I8) => Some(Box::new(N::I32ToI8::new())),
2512 (PortType::U64, PortType::I8) => Some(Box::new(N::U64ToI8::new())),
2513 (PortType::F64, PortType::I8) => Some(Box::new(N::F64ToI8::new())),
2514 (PortType::I64, PortType::I16) => Some(Box::new(N::I64ToI16::new())),
2515 (PortType::I32, PortType::I16) => Some(Box::new(N::I32ToI16::new())),
2516 (PortType::U64, PortType::I16) => Some(Box::new(N::U64ToI16::new())),
2517 (PortType::F64, PortType::I16) => Some(Box::new(N::F64ToI16::new())),
2518 (PortType::F64, PortType::F16) => Some(Box::new(N::F64ToF16::new())),
2519 (PortType::F32, PortType::F16) => Some(Box::new(N::F32ToF16::new())),
2520 (PortType::U64, PortType::F16) => Some(Box::new(N::U64ToF16::new())),
2521 (PortType::Str, PortType::U8) => Some(Box::new(N::StrToU8::new())),
2522 (PortType::Str, PortType::U16) => Some(Box::new(N::StrToU16::new())),
2523 (PortType::Str, PortType::I8) => Some(Box::new(N::StrToI8::new())),
2524 (PortType::Str, PortType::I16) => Some(Box::new(N::StrToI16::new())),
2525 (PortType::Str, PortType::F16) => Some(Box::new(N::StrToF16::new())),
2526 (PortType::Bytes, PortType::U8) => Some(Box::new(N::BytesToU8::new())),
2527 (PortType::Bytes, PortType::U16) => Some(Box::new(N::BytesToU16::new())),
2528 (PortType::Bytes, PortType::I8) => Some(Box::new(N::BytesToI8::new())),
2529 (PortType::Bytes, PortType::I16) => Some(Box::new(N::BytesToI16::new())),
2530 (PortType::Bytes, PortType::F16) => Some(Box::new(N::BytesToF16::new())),
2531 (PortType::Json, PortType::U8) => Some(Box::new(N::JsonToU8::new())),
2532 (PortType::Json, PortType::U16) => Some(Box::new(N::JsonToU16::new())),
2533 (PortType::Json, PortType::I8) => Some(Box::new(N::JsonToI8::new())),
2534 (PortType::Json, PortType::I16) => Some(Box::new(N::JsonToI16::new())),
2535 (PortType::Json, PortType::F16) => Some(Box::new(N::JsonToF16::new())),
2536 (PortType::F16, PortType::Json) => Some(Box::new(N::F16ToJson::new())),
2538
2539 (PortType::U128, PortType::U64) => Some(Box::new(W::U128ToU64::new())),
2541 (PortType::I128, PortType::I64) => Some(Box::new(W::I128ToI64::new())),
2542 (PortType::I64, PortType::U128) => Some(Box::new(W::I64ToU128::new())),
2543 (PortType::U128, PortType::I128) => Some(Box::new(W::U128ToI128::new())),
2544 (PortType::I128, PortType::U128) => Some(Box::new(W::I128ToU128::new())),
2545 (PortType::F64, PortType::U128) => Some(Box::new(W::F64ToU128::new())),
2546 (PortType::F64, PortType::I128) => Some(Box::new(W::F64ToI128::new())),
2547 (PortType::Str, PortType::U128) => Some(Box::new(W::StrToU128::new())),
2548 (PortType::Str, PortType::I128) => Some(Box::new(W::StrToI128::new())),
2549 (PortType::Bytes, PortType::U128) => Some(Box::new(W::BytesToU128::new())),
2550 (PortType::Bytes, PortType::I128) => Some(Box::new(W::BytesToI128::new())),
2551 (PortType::Json, PortType::U128) => Some(Box::new(W::JsonToU128::new())),
2552 (PortType::Json, PortType::I128) => Some(Box::new(W::JsonToI128::new())),
2553
2554 (PortType::U8, PortType::I8) => Some(Box::new(C::U8ToI8::new())),
2559 (PortType::I8, PortType::U8) => Some(Box::new(C::I8ToU8::new())),
2560 (PortType::I8, PortType::U16) => Some(Box::new(C::I8ToU16::new())),
2561 (PortType::I8, PortType::U32) => Some(Box::new(C::I8ToU32::new())),
2562 (PortType::I8, PortType::U64) => Some(Box::new(C::I8ToU64::new())),
2563 (PortType::I8, PortType::U128) => Some(Box::new(C::I8ToU128::new())),
2564 (PortType::U16, PortType::I8) => Some(Box::new(C::U16ToI8::new())),
2565 (PortType::U16, PortType::I16) => Some(Box::new(C::U16ToI16::new())),
2566 (PortType::U16, PortType::F16) => Some(Box::new(C::U16ToF16::new())),
2567 (PortType::I16, PortType::U8) => Some(Box::new(C::I16ToU8::new())),
2568 (PortType::I16, PortType::I8) => Some(Box::new(C::I16ToI8::new())),
2569 (PortType::I16, PortType::U16) => Some(Box::new(C::I16ToU16::new())),
2570 (PortType::I16, PortType::F16) => Some(Box::new(C::I16ToF16::new())),
2571 (PortType::I16, PortType::U32) => Some(Box::new(C::I16ToU32::new())),
2572 (PortType::I16, PortType::U64) => Some(Box::new(C::I16ToU64::new())),
2573 (PortType::I16, PortType::U128) => Some(Box::new(C::I16ToU128::new())),
2574 (PortType::U32, PortType::I8) => Some(Box::new(C::U32ToI8::new())),
2575 (PortType::U32, PortType::I16) => Some(Box::new(C::U32ToI16::new())),
2576 (PortType::U32, PortType::F16) => Some(Box::new(C::U32ToF16::new())),
2577 (PortType::I32, PortType::U8) => Some(Box::new(C::I32ToU8::new())),
2578 (PortType::I32, PortType::U16) => Some(Box::new(C::I32ToU16::new())),
2579 (PortType::I32, PortType::F16) => Some(Box::new(C::I32ToF16::new())),
2580 (PortType::I32, PortType::U128) => Some(Box::new(C::I32ToU128::new())),
2581 (PortType::F16, PortType::U8) => Some(Box::new(C::F16ToU8::new())),
2582 (PortType::F16, PortType::I8) => Some(Box::new(C::F16ToI8::new())),
2583 (PortType::F16, PortType::U16) => Some(Box::new(C::F16ToU16::new())),
2584 (PortType::F16, PortType::I16) => Some(Box::new(C::F16ToI16::new())),
2585 (PortType::F16, PortType::U32) => Some(Box::new(C::F16ToU32::new())),
2586 (PortType::F16, PortType::I32) => Some(Box::new(C::F16ToI32::new())),
2587 (PortType::F16, PortType::U64) => Some(Box::new(C::F16ToU64::new())),
2588 (PortType::F16, PortType::I64) => Some(Box::new(C::F16ToI64::new())),
2589 (PortType::F16, PortType::U128) => Some(Box::new(C::F16ToU128::new())),
2590 (PortType::F16, PortType::I128) => Some(Box::new(C::F16ToI128::new())),
2591 (PortType::F32, PortType::U8) => Some(Box::new(C::F32ToU8::new())),
2592 (PortType::F32, PortType::I8) => Some(Box::new(C::F32ToI8::new())),
2593 (PortType::F32, PortType::U16) => Some(Box::new(C::F32ToU16::new())),
2594 (PortType::F32, PortType::I16) => Some(Box::new(C::F32ToI16::new())),
2595 (PortType::F32, PortType::U128) => Some(Box::new(C::F32ToU128::new())),
2596 (PortType::F32, PortType::I128) => Some(Box::new(C::F32ToI128::new())),
2597 (PortType::I64, PortType::F16) => Some(Box::new(C::I64ToF16::new())),
2598 (PortType::U128, PortType::U8) => Some(Box::new(C::U128ToU8::new())),
2599 (PortType::U128, PortType::I8) => Some(Box::new(C::U128ToI8::new())),
2600 (PortType::U128, PortType::U16) => Some(Box::new(C::U128ToU16::new())),
2601 (PortType::U128, PortType::I16) => Some(Box::new(C::U128ToI16::new())),
2602 (PortType::U128, PortType::F16) => Some(Box::new(C::U128ToF16::new())),
2603 (PortType::U128, PortType::U32) => Some(Box::new(C::U128ToU32::new())),
2604 (PortType::U128, PortType::I32) => Some(Box::new(C::U128ToI32::new())),
2605 (PortType::U128, PortType::F32) => Some(Box::new(C::U128ToF32::new())),
2606 (PortType::U128, PortType::I64) => Some(Box::new(C::U128ToI64::new())),
2607 (PortType::I128, PortType::U8) => Some(Box::new(C::I128ToU8::new())),
2608 (PortType::I128, PortType::I8) => Some(Box::new(C::I128ToI8::new())),
2609 (PortType::I128, PortType::U16) => Some(Box::new(C::I128ToU16::new())),
2610 (PortType::I128, PortType::I16) => Some(Box::new(C::I128ToI16::new())),
2611 (PortType::I128, PortType::F16) => Some(Box::new(C::I128ToF16::new())),
2612 (PortType::I128, PortType::U32) => Some(Box::new(C::I128ToU32::new())),
2613 (PortType::I128, PortType::I32) => Some(Box::new(C::I128ToI32::new())),
2614 (PortType::I128, PortType::F32) => Some(Box::new(C::I128ToF32::new())),
2615 (PortType::I128, PortType::U64) => Some(Box::new(C::I128ToU64::new())),
2616
2617 (PortType::VecI16, PortType::VecI8) => Some(Box::new(C::VecI16ToVecI8::new())),
2621 (PortType::VecI16, PortType::VecF16) => Some(Box::new(C::VecI16ToVecF16::new())),
2622 (PortType::VecI32, PortType::VecI8) => Some(Box::new(C::VecI32ToVecI8::new())),
2623 (PortType::VecI32, PortType::VecI16) => Some(Box::new(C::VecI32ToVecI16::new())),
2624 (PortType::VecI32, PortType::VecF16) => Some(Box::new(C::VecI32ToVecF16::new())),
2625 (PortType::VecI64, PortType::VecI8) => Some(Box::new(C::VecI64ToVecI8::new())),
2626 (PortType::VecI64, PortType::VecI16) => Some(Box::new(C::VecI64ToVecI16::new())),
2627 (PortType::VecI64, PortType::VecI32) => Some(Box::new(C::VecI64ToVecI32::new())),
2628 (PortType::VecI64, PortType::VecF16) => Some(Box::new(C::VecI64ToVecF16::new())),
2629 (PortType::VecI64, PortType::VecF32) => Some(Box::new(C::VecI64ToVecF32::new())),
2630 (PortType::VecF16, PortType::VecI8) => Some(Box::new(C::VecF16ToVecI8::new())),
2631 (PortType::VecF16, PortType::VecI16) => Some(Box::new(C::VecF16ToVecI16::new())),
2632 (PortType::VecF16, PortType::VecI32) => Some(Box::new(C::VecF16ToVecI32::new())),
2633 (PortType::VecF16, PortType::VecI64) => Some(Box::new(C::VecF16ToVecI64::new())),
2634 (PortType::VecF32, PortType::VecI8) => Some(Box::new(C::VecF32ToVecI8::new())),
2635 (PortType::VecF32, PortType::VecI16) => Some(Box::new(C::VecF32ToVecI16::new())),
2636 (PortType::VecF32, PortType::VecI64) => Some(Box::new(C::VecF32ToVecI64::new())),
2637 (PortType::VecF32, PortType::VecF16) => Some(Box::new(C::VecF32ToVecF16::new())),
2638 (PortType::VecF64, PortType::VecI8) => Some(Box::new(C::VecF64ToVecI8::new())),
2639 (PortType::VecF64, PortType::VecI16) => Some(Box::new(C::VecF64ToVecI16::new())),
2640 (PortType::VecF64, PortType::VecI32) => Some(Box::new(C::VecF64ToVecI32::new())),
2641 (PortType::VecF64, PortType::VecI64) => Some(Box::new(C::VecF64ToVecI64::new())),
2642 (PortType::VecF64, PortType::VecF16) => Some(Box::new(C::VecF64ToVecF16::new())),
2643 (PortType::VecF64, PortType::VecF32) => Some(Box::new(C::VecF64ToVecF32::new())),
2644 (PortType::Bytes, PortType::VecF64) => Some(Box::new(C::BytesToVecF64::new())),
2645 (PortType::Bytes, PortType::VecI64) => Some(Box::new(C::BytesToVecI64::new())),
2646 (PortType::Bytes, PortType::VecF16) => Some(Box::new(C::BytesToVecF16::new())),
2647 (PortType::Bytes, PortType::VecI16) => Some(Box::new(C::BytesToVecI16::new())),
2648 (PortType::Bytes, PortType::VecI8) => Some(Box::new(C::BytesToVecI8::new())),
2649 (PortType::VecF64, PortType::Json) => Some(Box::new(C::VecF64ToJson::new())),
2650 (PortType::VecF16, PortType::Json) => Some(Box::new(C::VecF16ToJson::new())),
2651 (PortType::Json, PortType::VecF64) => Some(Box::new(C::JsonToVecF64::new())),
2652 (PortType::Json, PortType::VecI64) => Some(Box::new(C::JsonToVecI64::new())),
2653 (PortType::Json, PortType::VecF16) => Some(Box::new(C::JsonToVecF16::new())),
2654 (PortType::Json, PortType::VecI16) => Some(Box::new(C::JsonToVecI16::new())),
2655 (PortType::Json, PortType::VecI8) => Some(Box::new(C::JsonToVecI8::new())),
2656 (PortType::VecF64, PortType::Str) => Some(Box::new(C::VecF64ToStr::new())),
2657 (PortType::VecF16, PortType::Str) => Some(Box::new(C::VecF16ToStr::new())),
2658 (PortType::Str, PortType::VecF64) => Some(Box::new(C::StrToVecF64::new())),
2659 (PortType::Str, PortType::VecI64) => Some(Box::new(C::StrToVecI64::new())),
2660 (PortType::Str, PortType::VecF16) => Some(Box::new(C::StrToVecF16::new())),
2661 (PortType::Str, PortType::VecI16) => Some(Box::new(C::StrToVecI16::new())),
2662 (PortType::Str, PortType::VecI8) => Some(Box::new(C::StrToVecI8::new())),
2663
2664 _ => None,
2665 }
2666}
2667
2668use crate::compile::select::{Engine, KernelError, Provenance};
2671use crate::kernel::Kernel;
2672
2673impl PolydatAssembler {
2674 pub fn compile_with(self, engine: Engine) -> Result<Box<dyn Kernel>, KernelError> {
2682 self.compile_engine_with_log(engine, None)
2683 }
2684
2685 pub fn compile_kernel(self) -> Result<Box<dyn Kernel>, KernelError> {
2688 self.compile_with(Engine::default())
2689 }
2690
2691 pub fn compile_engine_with_log(
2694 self,
2695 engine: Engine,
2696 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
2697 ) -> Result<Box<dyn Kernel>, KernelError> {
2698 let refused = |reason: String| KernelError::Refused { engine, reason };
2699 let strict = self.strict;
2700 match engine {
2701 Engine::Interpreter(cones) => {
2702 let mut asm = self;
2703 asm.jit_mode = Some(cones);
2704 Ok(Box::new(asm.compile_with_log(log)?))
2705 }
2706 Engine::Closures(prov) => {
2707 let resolved = self.resolve_with_log(log.as_deref_mut())?;
2708 if strict {
2709 Self::refuse_strict(&resolved)?;
2710 }
2711 let folded = log.is_some().then(|| Self::constant_sites(&resolved));
2712 let kernel = Self::closures_from(resolved, prov).map_err(refused)?;
2713 Self::log_folded(kernel.as_ref(), folded, log);
2714 Ok(kernel)
2715 }
2716 Engine::Native(prov) => {
2717 #[cfg(feature = "jit")]
2718 {
2719 let resolved = self.resolve_with_log(log.as_deref_mut())?;
2720 if strict {
2721 Self::refuse_strict(&resolved)?;
2722 }
2723 let folded = log.is_some().then(|| Self::constant_sites(&resolved));
2724 let prov = Self::provenance_for(prov, &resolved);
2725 let kernel = Self::hybrid_from(resolved).map_err(refused)?;
2726 let kernel: Box<dyn Kernel> = match prov {
2730 Provenance::Raw => Box::new(kernel.into_raw()),
2731 Provenance::Pull => Box::new(kernel.into_pull()),
2732 Provenance::Push | Provenance::PushPull | Provenance::Auto => {
2733 Box::new(kernel)
2734 }
2735 };
2736 Self::log_folded(kernel.as_ref(), folded, log);
2737 Ok(kernel)
2738 }
2739 #[cfg(not(feature = "jit"))]
2740 {
2741 let _ = (prov, log);
2742 Err(refused(
2743 "this build has no native code (the `jit` feature is off)".into(),
2744 ))
2745 }
2746 }
2747 }
2748 }
2749
2750 fn constant_sites(resolved: &ResolvedDag) -> Vec<(String, usize, crate::ast::PortType)> {
2755 let classes = PolydatProgram::classify_lifecycle(
2756 &resolved.nodes,
2757 &resolved.wiring,
2758 &resolved.input_defs,
2759 &resolved.output_map,
2760 &resolved.output_modifiers,
2761 );
2762 let layout = slot_layout(resolved);
2763 resolved
2764 .nodes
2765 .iter()
2766 .enumerate()
2767 .filter(|(i, n)| {
2768 classes.lifecycle[*i] == crate::kernel::EvalLifecycle::CompileConst
2769 && n.meta().outs.len() == 1
2770 })
2771 .map(|(i, n)| {
2772 (
2773 n.meta().name.clone(),
2774 layout.port_offsets[i][0],
2775 n.meta().outs[0].typ,
2776 )
2777 })
2778 .collect()
2779 }
2780
2781 fn log_folded(
2784 kernel: &dyn Kernel,
2785 sites: Option<Vec<(String, usize, crate::ast::PortType)>>,
2786 log: Option<&mut crate::dsl::events::CompileEventLog>,
2787 ) {
2788 let (Some(sites), Some(log)) = (sites, log) else {
2789 return;
2790 };
2791 for (node, slot, ty) in sites {
2792 let value = crate::kernel::KernelInternals::slot_value(kernel, slot, ty);
2793 if !matches!(value, crate::ast::Value::None) {
2794 log.push(crate::dsl::events::CompileEvent::ConstantFolded {
2795 node,
2796 value: value.to_display_string(),
2797 });
2798 }
2799 }
2800 }
2801
2802 fn provenance_for(prov: Provenance, resolved: &ResolvedDag) -> Provenance {
2807 match prov {
2808 Provenance::Auto => {
2809 let analysis =
2810 select::analyze_graph(&resolved.nodes, &resolved.wiring, &resolved.output_map);
2811 match select::select_prov_mode(&analysis) {
2812 ProvMode::Raw => Provenance::Raw,
2813 ProvMode::Pull => Provenance::Pull,
2814 ProvMode::PushPull => Provenance::PushPull,
2815 }
2816 }
2817 p => p,
2818 }
2819 }
2820
2821 fn closures_from(resolved: ResolvedDag, prov: Provenance) -> Result<Box<dyn Kernel>, String> {
2824 let prov = Self::provenance_for(prov, &resolved);
2825 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
2826 Self::build_p2_layout(&resolved)?;
2827 let dependents = || {
2828 slot_layout(&resolved).expand_dependents(
2829 &resolved,
2830 &PolydatProgram::compute_dependents(
2831 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
2832 resolved.input_defs.len(),
2833 ),
2834 )
2835 };
2836 Ok(match prov {
2837 Provenance::Raw => Box::new(CompiledKernelRaw::new(
2838 coord_count,
2839 total_slots,
2840 steps,
2841 output_map,
2842 ref_slots,
2843 extras,
2844 )),
2845 Provenance::Push => Box::new(CompiledKernelPush::new(
2846 coord_count,
2847 total_slots,
2848 steps,
2849 output_map,
2850 dependents(),
2851 ref_slots,
2852 extras,
2853 )),
2854 Provenance::Pull => Box::new(CompiledKernelPull::new(
2855 coord_count,
2856 total_slots,
2857 steps,
2858 output_map,
2859 &dependents(),
2860 ref_slots,
2861 extras,
2862 )),
2863 Provenance::PushPull | Provenance::Auto => Box::new(CompiledKernelPushPull::new(
2864 coord_count,
2865 total_slots,
2866 steps,
2867 output_map,
2868 dependents(),
2869 ref_slots,
2870 extras,
2871 )),
2872 })
2873 }
2874}