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 ConstantFold(String),
99 OpenInputs(Vec<(String, PortType)>),
103 Other(String),
105}
106
107impl std::fmt::Display for AssemblyError {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 match self {
110 AssemblyError::UnknownWire(name) => {
111 write!(f, "unknown wire: '{name}'\n\n")?;
112 writeln!(f, " No node output or coordinate named '{name}' exists.")?;
113 write!(
114 f,
115 " Check spelling, or add a node that produces this output."
116 )
117 }
118 AssemblyError::TypeMismatch {
119 from_node,
120 from_port,
121 from_type,
122 to_node,
123 to_port,
124 to_type,
125 } => {
126 writeln!(
127 f,
128 "type mismatch: cannot connect {from_type} output to {to_type} input"
129 )?;
130 writeln!(f)?;
131 writeln!(
132 f,
133 " {from_node} [{from_port}] ──({from_type})──▶ {to_node} [{to_port}] expects {to_type}"
134 )?;
135 writeln!(f)?;
136 let suggestion = match (from_type, to_type) {
138 (PortType::U64, PortType::Str) => {
139 Some("This should auto-convert. If you see this, file a bug.")
140 }
141 (PortType::F64, PortType::Str) => {
142 Some("This should auto-convert. If you see this, file a bug.")
143 }
144 (PortType::U64, PortType::F64) => {
145 Some("This should auto-convert. If you see this, file a bug.")
146 }
147 (PortType::U64, PortType::Bytes) => {
148 Some("Add u64_to_bytes() between them to convert.")
149 }
150 (PortType::Str, PortType::Bytes) => {
151 Some("String cannot be directly used as bytes.")
152 }
153 (PortType::U64, PortType::Json) => {
154 Some("Add to_json() between them to wrap as JSON.")
155 }
156 (PortType::Str, PortType::Json) => {
157 Some("Add str_to_json() to parse the string as JSON.")
158 }
159 (PortType::Bytes, PortType::Str) => {
160 Some("Add to_hex() or to_base64() to convert bytes to string.")
161 }
162 (PortType::Bytes, PortType::U64) => {
163 Some("Bytes cannot be directly converted to u64.")
164 }
165 _ => None,
166 };
167 if let Some(hint) = suggestion {
168 write!(f, " Hint: {hint}")?;
169 }
170 Ok(())
171 }
172 AssemblyError::DuplicateNode(name) => {
173 write!(f, "duplicate node name: '{name}'\n\n")?;
174 write!(f, " Two nodes cannot share the same name.")
175 }
176 AssemblyError::CycleDetected => {
177 write!(f, "cycle detected in DAG\n\n")?;
178 writeln!(
179 f,
180 " The graph contains a loop. Polydat graphs must be acyclic"
181 )?;
182 write!(f, " (data flows in one direction only).")
183 }
184 AssemblyError::ArityMismatch {
185 node_name,
186 expected,
187 got,
188 } => {
189 write!(f, "wrong number of inputs for '{node_name}'\n\n")?;
190 writeln!(f, " Expected {expected} input(s), but got {got}.")?;
191 if *got < *expected {
192 write!(f, " Connect more wires to this node's input ports.")
193 } else {
194 write!(f, " Disconnect extra wires from this node.")
195 }
196 }
197 AssemblyError::ConstantFold(msg) => write!(
198 f,
199 "a value this program computes at build could not be computed: {msg}"
200 ),
201 AssemblyError::OpenInputs(inputs) => {
202 writeln!(
203 f,
204 "these inputs' types were inferred, not declared, and input variance \
205 is set to refuse them:"
206 )?;
207 for (name, ty) in inputs {
208 writeln!(f, " {name} (inferred {ty})")?;
209 }
210 write!(
211 f,
212 "Declare each one's type (`extern name: <type>`), or set \
213 `input_variance` to `Warn` or `Info` to convert what is written to them."
214 )
215 }
216 AssemblyError::Other(msg) => write!(f, "{msg}"),
217 }
218 }
219}
220
221impl std::error::Error for AssemblyError {}
222
223pub(crate) struct ResolvedDag {
225 pub(crate) nodes: Vec<Box<dyn PolydatNode>>,
227 pub(crate) wiring: Vec<Vec<WireSource>>,
229 pub(crate) input_defs: Vec<crate::kernel::InputDef>,
231 pub(crate) coord_count: usize,
233 pub(crate) output_map: HashMap<String, (usize, usize)>,
235 pub(crate) output_order: Vec<String>,
237 pub(crate) source: String,
239 pub(crate) context: String,
241 pub(crate) output_modifiers: HashMap<String, crate::dsl::ast::BindingModifier>,
243 pub(crate) const_outputs: std::collections::HashSet<String>,
245 pub(crate) cursor_schemas: Vec<crate::iteration::source::SourceSchema>,
247 pub(crate) ledger: std::sync::Arc<crate::kernel::CompileLedger>,
249}
250
251impl ResolvedDag {
252 fn input_names(&self) -> Vec<String> {
254 self.input_defs[..self.coord_count]
255 .iter()
256 .map(|d| d.name.clone())
257 .collect()
258 }
259}
260
261struct SlotLayout {
266 input_starts: Vec<usize>,
268 coord_slots: usize,
270 port_offsets: Vec<Vec<usize>>,
272 total_slots: usize,
274}
275
276fn slot_layout(resolved: &ResolvedDag) -> SlotLayout {
277 let mut input_starts = Vec::with_capacity(resolved.coord_count);
278 let mut next = 0usize;
279 for d in &resolved.input_defs {
280 input_starts.push(next);
281 next += d.port_type.slot_width();
282 }
283 let coord_slots = next;
284 let mut port_offsets: Vec<Vec<usize>> = Vec::with_capacity(resolved.nodes.len());
285 for node in &resolved.nodes {
286 let mut po = Vec::with_capacity(node.meta().outs.len());
287 for out in &node.meta().outs {
288 po.push(next);
289 next += out.typ.slot_width();
290 }
291 port_offsets.push(po);
292 }
293 SlotLayout {
294 input_starts,
295 coord_slots,
296 port_offsets,
297 total_slots: next,
298 }
299}
300
301fn node_step_op(
307 node: &dyn crate::ast::PolydatNode,
308 wire_types: &[PortType],
309) -> Option<(
310 crate::compile::closures::StepOp,
311 Vec<crate::ast::ScratchElem>,
312)> {
313 let meta = node.meta();
317 if (meta.name == "identity" || meta.name.starts_with("__port_")) && meta.outs.len() == 1 {
318 return Some(match meta.outs[0].typ.slot_color() {
319 crate::ast::SlotColor::Ref2 => {
320 let kit = ref_copy_kit(meta.outs[0].typ)?;
321 (crate::compile::closures::StepOp::Slot(kit.op), kit.scratch)
322 }
323 _ => (crate::compile::closures::StepOp::Copy, Vec::new()),
324 });
325 }
326 if let Some(op) = node.compiled_u64() {
327 return Some((crate::compile::closures::StepOp::U64(op), Vec::new()));
328 }
329 node.compiled_slot(
330 wire_types,
331 crate::compile::select::Engine::Closures(crate::compile::select::Provenance::Auto),
332 )
333 .map(|kit| (crate::compile::closures::StepOp::Slot(kit.op), kit.scratch))
334}
335
336pub(crate) fn scratch_pairs(
347 name: &str,
348 ref_starts: &[usize],
349 scratch: &[crate::ast::ScratchElem],
350 base: usize,
351) -> Vec<(usize, usize)> {
352 use crate::ast::ScratchElem;
353 let publishing: Vec<usize> = scratch
354 .iter()
355 .enumerate()
356 .filter(|(_, e)| {
357 !matches!(
358 e,
359 ScratchElem::Slots | ScratchElem::Kernels | ScratchElem::State
360 )
361 })
362 .map(|(k, _)| base + k)
363 .collect();
364 assert!(
365 publishing.len() <= ref_starts.len(),
366 "slot-op step '{name}' declares {} publishing scratch entries for {} Ref output ports",
367 publishing.len(),
368 ref_starts.len()
369 );
370 ref_starts.iter().copied().zip(publishing).collect()
371}
372
373pub(crate) fn ref_copy_kit(ty: PortType) -> Option<crate::ast::CompiledSlotKit> {
379 use crate::ast::ScratchBuf;
380 let elem = ty.scratch_elem()?;
381 Some(crate::ast::CompiledSlotKit {
382 scratch: vec![elem],
383 op: Box::new(
384 move |inputs: &[u64], outputs: &mut [u64], scratch: &mut [ScratchBuf]| {
385 let (p, n) = (inputs[0] as usize, inputs[1] as usize);
386 macro_rules! copy_into {
387 ($v:expr, $t:ty) => {{
388 $v.clear();
389 $v.extend_from_slice(unsafe {
393 std::slice::from_raw_parts(p as *const $t, n)
394 });
395 }};
396 }
397 match &mut scratch[0] {
398 ScratchBuf::Str(v) | ScratchBuf::Bytes(v) => copy_into!(v, u8),
399 ScratchBuf::F32(v) => copy_into!(v, f32),
400 ScratchBuf::F64(v) => copy_into!(v, f64),
401 ScratchBuf::F16(v) => copy_into!(v, half::f16),
402 ScratchBuf::I8(v) => copy_into!(v, i8),
403 ScratchBuf::I16(v) => copy_into!(v, i16),
404 ScratchBuf::I32(v) => copy_into!(v, i32),
405 ScratchBuf::I64(v) => copy_into!(v, i64),
406 ScratchBuf::Value(v) => {
407 v.clear();
408 if n > 0 {
409 v.push(unsafe { (*(p as *const crate::ast::Value)).clone() });
411 }
412 }
413 ScratchBuf::Slots(_) | ScratchBuf::Kernels(_) | ScratchBuf::State(_) => {
414 unreachable!("a copy owns only a value entry")
415 }
416 }
417 let (ptr, len) = scratch[0].ptr_len();
418 outputs[0] = ptr;
419 outputs[1] = len;
420 },
421 ),
422 })
423}
424
425pub(crate) fn identity_op(node: &dyn crate::ast::PolydatNode) -> Option<crate::ast::CompiledU64Op> {
431 let meta = node.meta();
432 if meta.name != "identity" || meta.outs.len() != 1 {
433 return None;
434 }
435 if meta.outs[0].typ.slot_color() == crate::ast::SlotColor::Ref2 {
436 return None;
437 }
438 Some(Box::new(|inputs: &[u64], outputs: &mut [u64]| {
439 outputs.copy_from_slice(inputs)
440 }))
441}
442
443impl SlotLayout {
444 fn input_slots(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
447 let mut slots = Vec::new();
448 for source in &resolved.wiring[node_idx] {
449 let (start, w) = match source {
450 WireSource::Input(c) => (
451 self.input_starts.get(*c).copied().unwrap_or(*c),
452 resolved
453 .input_defs
454 .get(*c)
455 .map(|d| d.port_type.slot_width())
456 .unwrap_or(1),
457 ),
458 WireSource::NodeOutput(u, p) => (
459 self.port_offsets[*u][*p],
460 resolved.nodes[*u].meta().outs[*p].typ.slot_width(),
461 ),
462 };
463 slots.extend(start..start + w);
464 }
465 slots
466 }
467
468 fn output_slots(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
470 let mut slots = Vec::new();
471 for (p, out) in resolved.nodes[node_idx].meta().outs.iter().enumerate() {
472 let start = self.port_offsets[node_idx][p];
473 slots.extend(start..start + out.typ.slot_width());
474 }
475 slots
476 }
477
478 fn named_outputs(&self, resolved: &ResolvedDag) -> HashMap<String, usize> {
480 resolved
481 .output_map
482 .iter()
483 .map(|(name, (n, p))| (name.clone(), self.port_offsets[*n][*p]))
484 .collect()
485 }
486
487 fn ref_slot_mask(&self, resolved: &ResolvedDag) -> Vec<bool> {
493 use crate::ast::SlotColor;
494 let mut mask = vec![false; self.total_slots];
495 let mut mark = |start: usize, color: SlotColor| match color {
496 SlotColor::Ref2 => {
497 mask[start] = true;
498 mask[start + 1] = true;
499 }
500 SlotColor::Imm1 | SlotColor::Imm2 => {}
501 };
502 for (i, d) in resolved.input_defs.iter().enumerate() {
503 mark(self.input_starts[i], d.port_type.slot_color());
504 }
505 for (n, node) in resolved.nodes.iter().enumerate() {
506 for (p, out) in node.meta().outs.iter().enumerate() {
507 mark(self.port_offsets[n][p], out.typ.slot_color());
508 }
509 }
510 mask
511 }
512
513 fn ref_output_starts(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
517 resolved.nodes[node_idx]
518 .meta()
519 .outs
520 .iter()
521 .enumerate()
522 .filter(|(_, out)| out.typ.slot_color() == crate::ast::SlotColor::Ref2)
523 .map(|(p, _)| self.port_offsets[node_idx][p])
524 .collect()
525 }
526
527 fn expand_dependents(&self, resolved: &ResolvedDag, deps: &[Vec<usize>]) -> Vec<Vec<usize>> {
533 let mut out = Vec::with_capacity(self.coord_slots);
534 for (i, d) in resolved.input_defs.iter().enumerate() {
535 for _ in 0..d.port_type.slot_width() {
536 out.push(deps.get(i).cloned().unwrap_or_default());
537 }
538 }
539 out
540 }
541}
542
543pub struct PolydatAssembler {
545 input_defs: Vec<crate::kernel::InputDef>,
547 coord_count: usize,
549 nodes: Vec<PendingNode>,
550 output_order: Vec<String>,
552 outputs: HashMap<String, WireRef>,
553 source: String,
555 context: String,
557 output_modifiers: HashMap<String, crate::dsl::ast::BindingModifier>,
559 const_outputs: std::collections::HashSet<String>,
562 pub(crate) strict_values: bool,
567 pub(crate) strict_types: bool,
575 pub(crate) strict: bool,
580 input_variance: crate::dsl::compile::InputVariance,
583 pub(crate) jit_mode: Option<crate::compile::cone::JitMode>,
588 pub(crate) ledger: std::sync::Arc<crate::kernel::CompileLedger>,
592 cursor_schemas: Vec<crate::iteration::source::SourceSchema>,
596}
597
598type P2Layout = (
602 usize,
603 usize,
604 Vec<crate::compile::closures::P2Step>,
605 HashMap<String, usize>,
606 Vec<bool>,
607 crate::compile::closures::P2Extras,
608);
609
610#[cfg(feature = "jit")]
616type JitLayout = (
617 usize,
618 usize,
619 Vec<(crate::compile::jit::JitOp, Vec<usize>, Vec<usize>)>,
620 HashMap<String, usize>,
621 crate::compile::jit::ScratchPlan,
622 Vec<usize>,
623);
624
625impl PolydatAssembler {
626 pub fn new(input_names: Vec<String>) -> Self {
628 let coord_count = input_names.len();
629 let input_defs: Vec<crate::kernel::InputDef> = input_names
630 .into_iter()
631 .map(|name| crate::kernel::InputDef {
632 name,
633 default: crate::ast::Value::U64(0),
634 port_type: crate::ast::PortType::U64,
635 kind: crate::kernel::InputKind::Coordinate,
636 type_origin: crate::kernel::TypeOrigin::Inferred,
638 converts_to: None,
639 })
640 .collect();
641 Self {
642 input_defs,
643 coord_count,
644 nodes: Vec::new(),
645 output_order: Vec::new(),
646 outputs: HashMap::new(),
647 source: String::new(),
648 context: "(assembler)".into(),
649 output_modifiers: HashMap::new(),
650 const_outputs: std::collections::HashSet::new(),
651 strict_values: false,
652 strict_types: false,
653 strict: false,
654 input_variance: crate::dsl::compile::InputVariance::Fixed,
655 jit_mode: None,
656 cursor_schemas: Vec::new(),
657 ledger: crate::kernel::CompileLedger::new(),
658 }
659 }
660
661 pub fn set_cursor_schemas(&mut self, schemas: Vec<crate::iteration::source::SourceSchema>) {
666 self.cursor_schemas = schemas;
667 }
668
669 pub fn cursor_schemas(&self) -> &[crate::iteration::source::SourceSchema] {
671 &self.cursor_schemas
672 }
673
674 pub fn set_strict_wires(&mut self, strict_types: bool, strict_values: bool) {
678 self.strict_types = strict_types;
679 self.strict_values = strict_values;
680 }
681
682 pub fn set_strict(&mut self, strict: bool) {
688 self.strict = strict;
689 }
690
691 pub fn set_jit_mode(&mut self, mode: crate::compile::cone::JitMode) {
694 self.jit_mode = Some(mode);
695 }
696
697 pub fn set_context(&mut self, source: &str, context: &str) {
700 self.source = source.to_string();
701 self.context = context.to_string();
702 }
703
704 pub fn add_node(
706 &mut self,
707 name: impl Into<String>,
708 node: Box<dyn PolydatNode>,
709 inputs: Vec<WireRef>,
710 ) -> &mut Self {
711 self.nodes.push(PendingNode {
712 name: name.into(),
713 node,
714 inputs,
715 });
716 self
717 }
718
719 pub fn set_output_modifier(&mut self, name: &str, modifier: crate::dsl::ast::BindingModifier) {
721 if modifier != crate::dsl::ast::BindingModifier::NONE {
722 self.output_modifiers.insert(name.to_string(), modifier);
723 }
724 }
725
726 pub fn mark_const_output(&mut self, name: &str) {
731 self.const_outputs.insert(name.to_string());
732 }
733
734 pub fn node_count(&self) -> usize {
736 self.nodes.len()
737 }
738
739 pub fn add_output(&mut self, name: impl Into<String>, wire: WireRef) -> &mut Self {
741 let name = name.into();
742 if !self.outputs.contains_key(&name) {
743 self.output_order.push(name.clone());
744 }
745 self.outputs.insert(name, wire);
746 self
747 }
748
749 pub fn add_input(
760 &mut self,
761 name: impl Into<String>,
762 default: crate::ast::Value,
763 port_type: crate::ast::PortType,
764 kind: crate::kernel::InputKind,
765 ) -> &mut Self {
766 self.input_defs.push(crate::kernel::InputDef {
767 name: name.into(),
768 default,
769 port_type,
770 kind,
771 type_origin: crate::kernel::TypeOrigin::Declared,
772 converts_to: None,
773 });
774 self
775 }
776
777 pub fn set_input_type(&mut self, name: &str, port_type: crate::ast::PortType) {
782 if let Some(d) = self.input_defs.iter_mut().find(|d| d.name == name) {
783 d.port_type = port_type;
784 d.type_origin = crate::kernel::TypeOrigin::Declared;
785 }
786 }
787
788 pub fn set_input_origin(&mut self, name: &str, origin: crate::kernel::TypeOrigin) {
792 if let Some(d) = self.input_defs.iter_mut().find(|d| d.name == name) {
793 d.type_origin = origin;
794 }
795 }
796
797 pub fn set_input_variance(&mut self, variance: crate::dsl::compile::InputVariance) {
799 self.input_variance = variance;
800 }
801
802 pub fn input_names(&self) -> Vec<&str> {
804 self.input_defs.iter().map(|d| d.name.as_str()).collect()
805 }
806
807 pub fn node_output_type(&self, name: &str) -> Option<crate::ast::PortType> {
812 self.nodes
813 .iter()
814 .find(|n| n.name == name)
815 .and_then(|n| n.node.meta().outs.first())
816 .map(|p| p.typ)
817 }
818
819 pub fn output_names(&self) -> Vec<&str> {
821 self.outputs.keys().map(|s| s.as_str()).collect()
822 }
823
824 pub fn node_type_of(&self, name: &str) -> Option<String> {
826 self.nodes
827 .iter()
828 .find(|pn| pn.name == name)
829 .map(|pn| pn.node.meta().name.clone())
830 }
831
832 pub fn output_type(&self, name: &str) -> Option<PortType> {
836 self.nodes
837 .iter()
838 .find(|pn| pn.name == name)
839 .and_then(|pn| pn.node.meta().outs.first())
840 .map(|port| port.typ)
841 }
842
843 pub fn input_type(&self, name: &str) -> Option<PortType> {
845 self.input_defs
846 .iter()
847 .find(|d| d.name == name)
848 .map(|d| d.port_type)
849 }
850
851 pub fn wire_type(&self, wire: &WireRef) -> Option<PortType> {
856 match wire {
857 WireRef::Input(name) => self.input_type(name),
858 WireRef::Node(name, port_idx) => self
859 .nodes
860 .iter()
861 .find(|pn| &pn.name == name)
862 .and_then(|pn| pn.node.meta().outs.get(*port_idx))
863 .map(|p| p.typ),
864 }
865 }
866
867 pub fn compile(self) -> Result<PolydatKernel, AssemblyError> {
869 self.compile_with_log(None)
870 }
871
872 pub fn compile_with_log(
874 self,
875 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
876 ) -> Result<PolydatKernel, AssemblyError> {
877 let jit_mode = self.jit_mode.unwrap_or_default();
878 let strict = self.strict;
879 let mut resolved = self.resolve_with_log(log.as_deref_mut())?;
880 let (node_total, output_total) = (resolved.nodes.len(), resolved.output_order.len());
881 crate::compile::cone::extract_jit_cones(&mut resolved, jit_mode);
882 let _coord_names = resolved.input_names();
883 let modifiers = resolved.output_modifiers.clone();
884 let cursors = std::mem::take(&mut resolved.cursor_schemas);
885 let mut kernel = PolydatKernel::new_with_inputs(
886 resolved.nodes,
887 resolved.wiring,
888 resolved.input_defs,
889 resolved.coord_count,
890 resolved.output_map,
891 resolved.output_order,
892 resolved.const_outputs,
893 modifiers,
894 &resolved.source,
895 &resolved.context,
896 log.as_deref_mut(),
897 strict,
898 resolved.ledger.clone(),
899 )?;
900 if !cursors.is_empty() {
901 kernel.set_cursor_schemas(cursors);
902 }
903 kernel.set_cone_mode(jit_mode);
904 Self::log_summary(log, node_total, output_total);
905 Ok(kernel)
906 }
907
908 fn refuse_strict(resolved: &ResolvedDag) -> Result<(), AssemblyError> {
912 let classes = PolydatProgram::classify_lifecycle(
913 &resolved.nodes,
914 &resolved.wiring,
915 &resolved.input_defs,
916 &resolved.output_map,
917 &resolved.output_modifiers,
918 );
919 let is_init: Vec<bool> = classes
920 .lifecycle
921 .iter()
922 .map(|lc| *lc == crate::kernel::EvalLifecycle::CompileConst)
923 .collect();
924 match PolydatProgram::strict_violation(
925 &resolved.nodes,
926 &resolved.wiring,
927 &is_init,
928 &resolved.output_map,
929 &resolved.output_modifiers,
930 ) {
931 Some(violation) => Err(AssemblyError::Other(violation)),
932 None => Ok(()),
933 }
934 }
935
936 fn refused_by_closures(reason: String) -> KernelError {
939 KernelError::Refused {
940 engine: Engine::Closures(Provenance::Auto),
941 reason,
942 }
943 }
944
945 fn refused_by_native(reason: String) -> KernelError {
948 KernelError::Refused {
949 engine: Engine::Native(Provenance::Auto),
950 reason,
951 }
952 }
953
954 #[cfg_attr(not(feature = "jit"), allow(dead_code))]
959 fn refused_by_pure_native(reason: String) -> KernelError {
960 KernelError::Refused {
961 engine: Engine::PureNative(Provenance::Auto),
962 reason,
963 }
964 }
965
966 fn build_p2_layout(resolved: &ResolvedDag) -> Result<P2Layout, String> {
969 let layout = slot_layout(resolved);
970
971 let mut compiled_ops = Vec::with_capacity(resolved.nodes.len());
972 let mut extras = crate::compile::closures::P2Extras::default();
973 for (node_idx, node) in resolved.nodes.iter().enumerate() {
974 compiled_ops.push(
975 node_step_op(node.as_ref(), &wire_types_of(resolved, node_idx)).ok_or_else(
976 || {
977 format!(
978 "node '{}' has no compiled form (docs/design/engines.md §8)",
979 node.meta().name
980 )
981 },
982 )?,
983 );
984 }
985 extras.externs = crate::compile::externs::Externs::new(
986 &resolved.input_defs,
987 resolved.coord_count,
988 &layout.input_starts,
989 &resolved.cursor_schemas,
990 &shared_outputs_of(resolved),
991 resolved.ledger.clone(),
992 )?;
993 extras.externs.set_output_names(&resolved.output_order);
994 extras
995 .externs
996 .set_output_modifiers(&resolved.output_modifiers);
997 extras.output_types = resolved
998 .output_map
999 .iter()
1000 .map(|(name, (n, p))| (name.clone(), resolved.nodes[*n].meta().outs[*p].typ))
1001 .collect();
1002
1003 let classes = PolydatProgram::classify_lifecycle(
1007 &resolved.nodes,
1008 &resolved.wiring,
1009 &resolved.input_defs,
1010 &resolved.output_map,
1011 &resolved.output_modifiers,
1012 );
1013 let inventory = PolydatProgram::compute_node_inventory(&resolved.nodes, &resolved.wiring);
1014 let per_input = PolydatProgram::compute_dependents(
1015 &inventory.input_provenance,
1016 resolved.input_defs.len(),
1017 );
1018 extras.input_dependents = layout.expand_dependents(resolved, &per_input);
1019 extras.attribution = std::sync::Arc::new(Self::attribution_of(resolved));
1020
1021 let mut steps = Vec::with_capacity(resolved.nodes.len());
1022 for (node_idx, (op, scratch)) in compiled_ops.into_iter().enumerate() {
1023 steps.push(crate::compile::closures::P2Step {
1024 name: resolved.nodes[node_idx].meta().name.clone(),
1025 op,
1026 input_slots: layout.input_slots(resolved, node_idx),
1027 output_slots: layout.output_slots(resolved, node_idx),
1028 ref_output_starts: layout.ref_output_starts(resolved, node_idx),
1029 scratch,
1030 accepts_none: resolved.nodes[node_idx].accepts_none_inputs(),
1031 volatile: classes.nondeterministic[node_idx],
1032 constant: classes.lifecycle[node_idx] == crate::kernel::EvalLifecycle::CompileConst,
1033 side: matches!(
1034 resolved.nodes[node_idx].purity(),
1035 crate::ast::Purity::SideChannel { .. }
1036 ),
1037 });
1038 }
1039 let output_map = layout.named_outputs(resolved);
1040 let ref_slots = layout.ref_slot_mask(resolved);
1041
1042 Ok((
1043 layout.coord_slots,
1044 layout.total_slots,
1045 steps,
1046 output_map,
1047 ref_slots,
1048 extras,
1049 ))
1050 }
1051
1052 #[cfg(feature = "jit")]
1054 pub(crate) fn build_jit_layout(resolved: &ResolvedDag) -> Result<JitLayout, String> {
1055 let layout = slot_layout(resolved);
1056
1057 let mut scratch = crate::compile::jit::ScratchPlan::default();
1061 let mut jit_steps = Vec::new();
1062 for (node_idx, node) in resolved.nodes.iter().enumerate() {
1063 let mut jit_op = crate::compile::jit::classify_node_typed(
1064 node.as_ref(),
1065 &wire_types_of(resolved, node_idx),
1066 );
1067 if matches!(jit_op, crate::compile::jit::JitOp::Fallback) {
1068 return Err(format!(
1069 "node '{}' has no native form and no kit; pure native code cannot run it",
1070 node.meta().name
1071 ));
1072 }
1073 let base = scratch.elems.len();
1074 jit_op.place_scratch(base);
1075 let elems = jit_op.scratch_elems().to_vec();
1076 scratch.refs.extend(scratch_pairs(
1077 &node.meta().name,
1078 &layout.ref_output_starts(resolved, node_idx),
1079 &elems,
1080 base,
1081 ));
1082 scratch.elems.extend(elems);
1083 jit_steps.push((
1084 jit_op,
1085 layout.input_slots(resolved, node_idx),
1086 layout.output_slots(resolved, node_idx),
1087 ));
1088 }
1089
1090 let output_map = layout.named_outputs(resolved);
1091 let classes = PolydatProgram::classify_lifecycle(
1095 &resolved.nodes,
1096 &resolved.wiring,
1097 &resolved.input_defs,
1098 &resolved.output_map,
1099 &resolved.output_modifiers,
1100 );
1101 let volatile: Vec<usize> = (0..resolved.nodes.len())
1102 .filter(|&i| classes.nondeterministic[i])
1103 .collect();
1104 Ok((
1105 layout.coord_slots,
1106 layout.total_slots,
1107 jit_steps,
1108 output_map,
1109 scratch,
1110 volatile,
1111 ))
1112 }
1113
1114 #[cfg(feature = "jit")]
1117 fn jit_slot_info(resolved: &ResolvedDag) -> (Vec<bool>, HashMap<String, PortType>) {
1118 let layout = slot_layout(resolved);
1119 let guard = layout.ref_slot_mask(resolved);
1120 let types = resolved
1121 .output_map
1122 .iter()
1123 .map(|(name, (n, p))| (name.clone(), resolved.nodes[*n].meta().outs[*p].typ))
1124 .collect();
1125 (guard, types)
1126 }
1127
1128 #[cfg(feature = "jit")]
1129 fn jit_push_pull_from(
1130 resolved: ResolvedDag,
1131 ) -> Result<crate::compile::jit::JitKernelPushPull, KernelError> {
1132 let _coord_names = resolved.input_names();
1133 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1134 Self::build_jit_layout(&resolved).map_err(Self::refused_by_pure_native)?;
1135 let (guard, types) = Self::jit_slot_info(&resolved);
1136 let deps = slot_layout(&resolved).expand_dependents(
1137 &resolved,
1138 &PolydatProgram::compute_dependents(
1139 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
1140 resolved.input_defs.len(),
1141 ),
1142 );
1143 let externs = Self::externs_of(&resolved).map_err(Self::refused_by_pure_native)?;
1144 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1145 let (folded, origin) = Self::constant_steps(&resolved, &jit_steps);
1146 let alone = Self::side_channels(&resolved);
1147 let mut k = crate::compile::jit::compile_jit_push_pull(
1148 coord_count,
1149 total_slots,
1150 jit_steps,
1151 output_map,
1152 resolved.nodes,
1153 deps,
1154 externs,
1155 scratch,
1156 volatile,
1157 alone,
1158 )
1159 .map_err(Self::refused_by_pure_native)?;
1160 k.set_slot_info(guard, types);
1161 k.set_attribution(attribution);
1162 k.fold_constants(&folded, &origin, total_slots)?;
1165 Ok(k)
1166 }
1167
1168 #[cfg(feature = "jit")]
1176 #[allow(clippy::type_complexity)]
1177 fn constant_steps(
1178 resolved: &ResolvedDag,
1179 jit_steps: &[(crate::compile::jit::JitOp, Vec<usize>, Vec<usize>)],
1180 ) -> (
1181 Vec<(crate::compile::jit::JitOp, Vec<usize>, Vec<usize>)>,
1182 Vec<usize>,
1183 ) {
1184 let classes = PolydatProgram::classify_lifecycle(
1185 &resolved.nodes,
1186 &resolved.wiring,
1187 &resolved.input_defs,
1188 &resolved.output_map,
1189 &resolved.output_modifiers,
1190 );
1191 jit_steps
1194 .iter()
1195 .enumerate()
1196 .filter(|(i, _)| {
1197 classes.lifecycle.get(*i) == Some(&crate::kernel::EvalLifecycle::CompileConst)
1198 })
1199 .map(|(i, s)| (s.clone(), i))
1200 .unzip()
1201 }
1202
1203 #[cfg(feature = "jit")]
1207 fn side_channels(resolved: &ResolvedDag) -> Vec<bool> {
1208 resolved
1209 .nodes
1210 .iter()
1211 .map(|n| matches!(n.purity(), crate::ast::Purity::SideChannel { .. }))
1212 .collect()
1213 }
1214
1215 fn externs_of(resolved: &ResolvedDag) -> Result<crate::compile::externs::Externs, String> {
1218 let layout = slot_layout(resolved);
1219 let mut externs = crate::compile::externs::Externs::new(
1220 &resolved.input_defs,
1221 resolved.coord_count,
1222 &layout.input_starts,
1223 &resolved.cursor_schemas,
1224 &shared_outputs_of(resolved),
1225 resolved.ledger.clone(),
1226 )?;
1227 externs.set_output_names(&resolved.output_order);
1228 externs.set_output_modifiers(&resolved.output_modifiers);
1229 Ok(externs)
1230 }
1231
1232 #[doc(hidden)]
1234 #[cfg(feature = "jit")]
1235 pub(crate) fn try_compile_pure_jit_raw(
1236 self,
1237 ) -> Result<crate::compile::jit::JitKernelRaw, KernelError> {
1238 let resolved = self.resolve().map_err(KernelError::Assembly)?;
1239 Self::jit_raw_from(resolved)
1240 }
1241
1242 #[cfg(feature = "bench-tiers")]
1267 pub fn compile_closures_raw(
1268 self,
1269 ) -> Result<crate::compile::closures::CompiledKernelRaw, KernelError> {
1270 let resolved = self.resolve_with_log(None)?;
1271 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
1272 Self::build_p2_layout(&resolved).map_err(Self::refused_by_closures)?;
1273 crate::compile::closures::CompiledKernelRaw::new(
1274 coord_count,
1275 total_slots,
1276 steps,
1277 output_map,
1278 ref_slots,
1279 extras,
1280 )
1281 }
1282
1283 #[cfg(all(feature = "bench-tiers", feature = "jit"))]
1285 pub fn compile_native_raw(
1286 self,
1287 ) -> Result<crate::compile::hybrid::HybridKernelRaw, KernelError> {
1288 let resolved = self.resolve_with_log(None)?;
1289 Ok(Self::hybrid_from(resolved)?.into_raw())
1290 }
1291
1292 #[cfg(all(feature = "bench-tiers", feature = "jit"))]
1294 pub fn compile_pure_native_raw(self) -> Result<crate::compile::jit::JitKernelRaw, KernelError> {
1295 self.try_compile_pure_jit_raw()
1296 }
1297
1298 pub(crate) fn attribution_of(resolved: &ResolvedDag) -> crate::compile::Attribution {
1303 let layout = slot_layout(resolved);
1304 let sites = resolved
1305 .nodes
1306 .iter()
1307 .enumerate()
1308 .map(|(node_idx, node)| {
1309 let mut outputs: Vec<String> = resolved
1310 .output_map
1311 .iter()
1312 .filter(|(_, (n, _))| *n == node_idx)
1313 .map(|(name, _)| name.clone())
1314 .collect();
1315 outputs.sort();
1316 let inputs = resolved.wiring[node_idx]
1317 .iter()
1318 .map(|source| match source {
1319 WireSource::Input(c) => (
1320 layout.input_starts.get(*c).copied().unwrap_or(*c),
1321 resolved
1322 .input_defs
1323 .get(*c)
1324 .map(|d| d.port_type)
1325 .unwrap_or(PortType::U64),
1326 ),
1327 WireSource::NodeOutput(u, p) => (
1328 layout.port_offsets[*u][*p],
1329 resolved.nodes[*u].meta().outs[*p].typ,
1330 ),
1331 })
1332 .collect();
1333 crate::compile::NodeSite {
1334 name: node.meta().name.to_string(),
1335 outputs,
1336 inputs,
1337 }
1338 })
1339 .collect();
1340 crate::compile::Attribution {
1341 sites,
1342 context: resolved.context.clone(),
1343 }
1344 }
1345
1346 #[cfg(feature = "jit")]
1347 fn jit_raw_from(
1348 resolved: ResolvedDag,
1349 ) -> Result<crate::compile::jit::JitKernelRaw, KernelError> {
1350 let _coord_names = resolved.input_names();
1351 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1352 Self::build_jit_layout(&resolved).map_err(Self::refused_by_pure_native)?;
1353 let (guard, types) = Self::jit_slot_info(&resolved);
1354 let externs = Self::externs_of(&resolved).map_err(Self::refused_by_pure_native)?;
1355 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1356 let (folded, origin) = Self::constant_steps(&resolved, &jit_steps);
1357 let alone = Self::side_channels(&resolved);
1358 let mut k = crate::compile::jit::compile_jit_raw_with(
1359 coord_count,
1360 total_slots,
1361 jit_steps,
1362 output_map,
1363 resolved.nodes,
1364 externs,
1365 scratch,
1366 volatile,
1367 alone,
1368 )
1369 .map_err(Self::refused_by_pure_native)?;
1370 k.set_slot_info(guard, types);
1371 k.set_attribution(attribution);
1372 k.fold_constants(&folded, &origin, total_slots)?;
1375 Ok(k)
1376 }
1377
1378 #[cfg(feature = "jit")]
1384 #[doc(hidden)]
1385 pub fn try_compile_tier1_simd_ordinal(
1386 self,
1387 driving_input: &str,
1388 output: &str,
1389 ) -> Result<
1390 crate::compile::simd_tier1::Tier1SimdExecutor,
1391 crate::compile::simd_tier1::Tier1SimdError,
1392 > {
1393 let resolved = self.resolve().map_err(|error| {
1394 crate::compile::simd_tier1::Tier1SimdError::VectorGraphBuild(error.to_string())
1395 })?;
1396 crate::compile::simd_tier1::compile_tier1_ordinal(resolved, driving_input, output)
1397 }
1398
1399 fn hybrid_from(
1400 resolved: ResolvedDag,
1401 ) -> Result<crate::compile::hybrid::HybridKernel, KernelError> {
1402 let _coord_names = resolved.input_names();
1403 let layout = slot_layout(&resolved);
1404
1405 let output_map = layout.named_outputs(&resolved);
1406 let input_widths: Vec<usize> = resolved
1407 .input_defs
1408 .iter()
1409 .map(|d| d.port_type.slot_width())
1410 .collect();
1411
1412 let ref_slots = layout.ref_slot_mask(&resolved);
1413 let input_types: Vec<PortType> = resolved.input_defs.iter().map(|d| d.port_type).collect();
1414 let externs = Self::externs_of(&resolved).map_err(Self::refused_by_native)?;
1415 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1416 let classes = PolydatProgram::classify_lifecycle(
1419 &resolved.nodes,
1420 &resolved.wiring,
1421 &resolved.input_defs,
1422 &resolved.output_map,
1423 &resolved.output_modifiers,
1424 );
1425 let constant: Vec<bool> = classes
1426 .lifecycle
1427 .iter()
1428 .map(|lc| *lc == crate::kernel::EvalLifecycle::CompileConst)
1429 .collect();
1430 let mut kernel = crate::compile::hybrid::build_hybrid(
1431 &resolved.nodes,
1432 &resolved.wiring,
1433 layout.coord_slots,
1434 layout.total_slots,
1435 &layout.port_offsets,
1436 &layout.input_starts,
1437 &input_widths,
1438 output_map,
1439 ref_slots,
1440 &input_types,
1441 externs,
1442 constant,
1443 classes.nondeterministic,
1444 attribution,
1445 )?;
1446 kernel.retain_nodes(resolved.nodes);
1447 Ok(kernel)
1448 }
1449
1450 fn log_forms(resolved: &ResolvedDag, log: &mut crate::dsl::events::CompileEventLog) {
1458 for (node_idx, node) in resolved.nodes.iter().enumerate() {
1459 let wire_types = wire_types_of(resolved, node_idx);
1460 #[cfg(feature = "jit")]
1464 let native = !matches!(
1465 crate::compile::jit::classify_node_typed(node.as_ref(), &wire_types),
1466 crate::compile::jit::JitOp::Fallback
1467 );
1468 #[cfg(not(feature = "jit"))]
1469 let native = false;
1470 let level = if native {
1471 "native"
1472 } else {
1473 match node_step_op(node.as_ref(), &wire_types) {
1474 Some((crate::compile::closures::StepOp::Copy, _)) => "slot copy",
1475 Some((crate::compile::closures::StepOp::U64(_), _)) => "compiled u64 op",
1476 Some((crate::compile::closures::StepOp::Slot(_), _)) => "slot kit",
1477 None => "interpreted",
1478 }
1479 };
1480 let name = resolved
1481 .output_map
1482 .iter()
1483 .find(|(_, (ni, _))| *ni == node_idx)
1484 .map(|(n, _)| n.clone())
1485 .unwrap_or_else(|| node.meta().name.clone());
1486 log.push(crate::dsl::events::CompileEvent::CompileLevelSelected {
1487 node: name,
1488 level: level.to_string(),
1489 });
1490 }
1491 }
1492
1493 fn log_summary(
1498 log: Option<&mut crate::dsl::events::CompileEventLog>,
1499 nodes: usize,
1500 outputs: usize,
1501 ) {
1502 if let Some(log) = log {
1503 let constants_folded = log
1504 .events()
1505 .iter()
1506 .filter(|e| matches!(e, crate::dsl::events::CompileEvent::ConstantFolded { .. }))
1507 .count();
1508 log.push(crate::dsl::events::CompileEvent::Summary {
1509 nodes,
1510 outputs,
1511 constants_folded,
1512 });
1513 }
1514 }
1515
1516 #[cfg(feature = "jit")]
1519 fn resolve(self) -> Result<ResolvedDag, AssemblyError> {
1520 self.resolve_with_log(None)
1521 }
1522
1523 fn resolve_with_log(
1524 mut self,
1525 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
1526 ) -> Result<ResolvedDag, AssemblyError> {
1527 let open: Vec<usize> = self
1534 .input_defs
1535 .iter()
1536 .enumerate()
1537 .filter(|(_, d)| {
1538 d.kind != crate::kernel::InputKind::Coordinate
1539 && d.type_origin == crate::kernel::TypeOrigin::Inferred
1540 && d.port_type != PortType::Dyn
1541 })
1542 .map(|(i, _)| i)
1543 .collect();
1544 let variance_level = match self.input_variance {
1545 crate::dsl::compile::InputVariance::Fixed => None,
1546 crate::dsl::compile::InputVariance::Error => {
1547 if !open.is_empty() {
1548 return Err(AssemblyError::OpenInputs(
1549 open.iter()
1550 .map(|&i| {
1551 (
1552 self.input_defs[i].name.clone(),
1553 self.input_defs[i].port_type,
1554 )
1555 })
1556 .collect(),
1557 ));
1558 }
1559 None
1560 }
1561 crate::dsl::compile::InputVariance::Warn => {
1562 Some(crate::dsl::events::EventLevel::Warning)
1563 }
1564 crate::dsl::compile::InputVariance::Info => Some(crate::dsl::events::EventLevel::Info),
1565 };
1566 if variance_level.is_some() {
1567 for &i in &open {
1568 let def = &mut self.input_defs[i];
1569 def.converts_to = Some(def.port_type);
1570 def.port_type = PortType::Dyn;
1571 }
1572 }
1573 if let Some(log) = log.as_deref_mut() {
1579 let cursor_slot = |name: &str| {
1580 self.cursor_schemas
1581 .iter()
1582 .any(|s| name.starts_with(&format!("{}__cursor", s.name)))
1583 };
1584 for def in &self.input_defs {
1585 if matches!(
1586 def.kind,
1587 crate::kernel::InputKind::ExternalWrite
1588 | crate::kernel::InputKind::IterationExtern
1589 ) && def.default == crate::ast::Value::None
1590 && !cursor_slot(&def.name)
1591 {
1592 log.push(crate::dsl::events::CompileEvent::ExternWithoutDefault {
1593 name: def.name.clone(),
1594 port_type: def.port_type.to_string(),
1595 });
1596 }
1597 }
1598 }
1599 let mut name_to_idx: HashMap<String, usize> = HashMap::new();
1601 for (i, pn) in self.nodes.iter().enumerate() {
1602 if name_to_idx.contains_key(&pn.name) {
1603 return Err(AssemblyError::DuplicateNode(pn.name.clone()));
1604 }
1605 name_to_idx.insert(pn.name.clone(), i);
1606 }
1607
1608 let input_to_idx: HashMap<String, usize> = self
1610 .input_defs
1611 .iter()
1612 .enumerate()
1613 .map(|(i, d)| (d.name.clone(), i))
1614 .collect();
1615
1616 for pn in &self.nodes {
1618 let expected = pn.node.meta().wire_inputs().len();
1619 let got = pn.inputs.len();
1620 if expected != got {
1621 return Err(AssemblyError::ArityMismatch {
1622 node_name: pn.name.clone(),
1623 expected,
1624 got,
1625 });
1626 }
1627 }
1628
1629 let mut all_nodes: Vec<PendingNode> = Vec::new();
1630 let mut all_name_to_idx: HashMap<String, usize> = HashMap::new();
1631 let mut adapter_count = 0usize;
1632 let mut assertion_count = 0usize;
1633 let strict_values = self.strict_values;
1634 let strict_types = self.strict_types;
1635 let strict = self.strict;
1636
1637 for pn in self.nodes {
1638 let idx = all_nodes.len();
1639 all_name_to_idx.insert(pn.name.clone(), idx);
1640 all_nodes.push(pn);
1641 }
1642
1643 let mut resolved_wiring: Vec<Vec<WireSource>> = Vec::new();
1644 let mut converters: HashMap<(usize, PortType), usize> = HashMap::new();
1646
1647 for node_idx in 0..all_nodes.len() {
1648 let mut node_wiring = Vec::new();
1649
1650 for (port_idx, wire_ref) in all_nodes[node_idx].inputs.clone().iter().enumerate() {
1651 let port = all_nodes[node_idx].node.meta().wire_inputs()[port_idx].clone();
1652 let expected_type = port.typ;
1653
1654 let (source, source_type) = match wire_ref {
1655 WireRef::Input(name) => {
1656 let input_idx = input_to_idx
1657 .get(name)
1658 .ok_or_else(|| AssemblyError::UnknownWire(name.clone()))?;
1659 let source_type = self.input_defs[*input_idx].port_type;
1660 (WireSource::Input(*input_idx), source_type)
1661 }
1662 WireRef::Node(name, out_port) => {
1663 let src_idx = all_name_to_idx
1664 .get(name)
1665 .ok_or_else(|| AssemblyError::UnknownWire(name.clone()))?;
1666 let src_type = all_nodes[*src_idx].node.meta().outs[*out_port].typ;
1667 (WireSource::NodeOutput(*src_idx, *out_port), src_type)
1668 }
1669 };
1670
1671 if source_type == PortType::Dyn
1683 && !port.accepts_any_type
1684 && expected_type != PortType::Dyn
1685 {
1686 let WireSource::Input(input_idx) = source else {
1687 unreachable!("only an input slot is typed `Dyn`")
1688 };
1689 let conv_idx = match converters.get(&(input_idx, expected_type)) {
1690 Some(&idx) => idx,
1691 None => {
1692 let converter = crate::convert::InputConverter::new(
1693 &self.input_defs[input_idx].name,
1694 expected_type,
1695 );
1696 let conv_name = converter.meta().name.clone();
1697 let idx = all_nodes.len();
1698 all_name_to_idx.insert(conv_name.clone(), idx);
1699 while resolved_wiring.len() <= idx {
1700 resolved_wiring.push(Vec::new());
1701 }
1702 resolved_wiring[idx] = vec![source.clone()];
1703 all_nodes.push(PendingNode {
1704 name: conv_name,
1705 node: Box::new(converter),
1706 inputs: vec![],
1707 });
1708 converters.insert((input_idx, expected_type), idx);
1709 idx
1710 }
1711 };
1712 node_wiring.push(WireSource::NodeOutput(conv_idx, 0));
1713 } else if port.accepts_any_type || source_type == expected_type {
1714 node_wiring.push(source);
1715 } else if let Some(adapter) = auto_adapter(source_type, expected_type) {
1716 if strict {
1717 return Err(AssemblyError::Other(format!(
1718 "strict mode: implicit type coercion {source_type} → {expected_type} \
1719 into '{}'. Use an explicit conversion function (e.g., to_f64, \
1720 to_i64, f64_to_u64).",
1721 all_nodes[node_idx].name
1722 )));
1723 }
1724 let adapter_name = format!("__adapt_{adapter_count}");
1725 adapter_count += 1;
1726 let adapter_idx = all_nodes.len();
1727
1728 if let Some(ref mut log) = log {
1729 let from_name = match wire_ref {
1730 WireRef::Input(n) => n.clone(),
1731 WireRef::Node(n, _) => n.clone(),
1732 };
1733 let to_name = all_nodes[node_idx].name.clone();
1734 log.push(if is_lossless_widening(source_type, expected_type) {
1735 crate::dsl::events::CompileEvent::TypeWidening {
1736 from: source_type.to_keyword(),
1737 to: expected_type.to_keyword(),
1738 context: format!("{from_name} → {to_name}"),
1739 }
1740 } else {
1741 crate::dsl::events::CompileEvent::TypeAdapterInserted {
1742 from_node: from_name,
1743 to_node: to_name,
1744 adapter: format!("{source_type:?}→{expected_type:?}"),
1745 }
1746 });
1747 }
1748
1749 all_name_to_idx.insert(adapter_name.clone(), adapter_idx);
1750
1751 let adapter_wiring = vec![source];
1752 while resolved_wiring.len() <= adapter_idx {
1753 resolved_wiring.push(Vec::new());
1754 }
1755 resolved_wiring[adapter_idx] = adapter_wiring;
1756
1757 all_nodes.push(PendingNode {
1758 name: adapter_name,
1759 node: adapter,
1760 inputs: vec![],
1761 });
1762
1763 node_wiring.push(WireSource::NodeOutput(adapter_idx, 0));
1764 } else {
1765 let from_name = match wire_ref {
1766 WireRef::Input(n) => n.clone(),
1767 WireRef::Node(n, _) => n.clone(),
1768 };
1769 return Err(AssemblyError::TypeMismatch {
1770 from_node: from_name,
1771 from_port: match wire_ref {
1772 WireRef::Input(_) => 0,
1773 WireRef::Node(_, p) => *p,
1774 },
1775 from_type: source_type,
1776 to_node: all_nodes[node_idx].name.clone(),
1777 to_port: port_idx,
1778 to_type: expected_type,
1779 });
1780 }
1781
1782 let sink_port = &all_nodes[node_idx].node.meta().wire_inputs()[port_idx];
1795 if let Some(constraint) = sink_port.constraint {
1796 let last_source = node_wiring.last().expect("wire just pushed").clone();
1797 if strict_values
1798 && !value_constraint_proven(&all_nodes, &last_source, &constraint)
1799 {
1800 let assert_name = format!("__assert_v_{assertion_count}");
1801 assertion_count += 1;
1802 let assert_idx = all_nodes.len();
1803
1804 if let Some(ref mut log) = log {
1805 let from_name = match wire_ref {
1806 WireRef::Input(n) => n.clone(),
1807 WireRef::Node(n, _) => n.clone(),
1808 };
1809 log.push(crate::dsl::events::CompileEvent::AssertionInserted {
1810 from_node: from_name,
1811 to_node: all_nodes[node_idx].name.clone(),
1812 kind: format!("{:?} value-assert {:?}", expected_type, constraint),
1813 });
1814 }
1815
1816 all_name_to_idx.insert(assert_name.clone(), assert_idx);
1817 let assert_wiring = vec![last_source];
1818 while resolved_wiring.len() <= assert_idx {
1819 resolved_wiring.push(Vec::new());
1820 }
1821 resolved_wiring[assert_idx] = assert_wiring;
1822
1823 all_nodes.push(PendingNode {
1824 name: assert_name,
1825 node: crate::library::assertions::assert_value_node(
1826 expected_type,
1827 constraint,
1828 ),
1829 inputs: vec![],
1830 });
1831
1832 *node_wiring.last_mut().unwrap() = WireSource::NodeOutput(assert_idx, 0);
1835 } else if let Some(ref mut log) = log {
1836 let from_name = match wire_ref {
1837 WireRef::Input(n) => n.clone(),
1838 WireRef::Node(n, _) => n.clone(),
1839 };
1840 log.push(crate::dsl::events::CompileEvent::AssertionSkipped {
1841 from_node: from_name,
1842 to_node: all_nodes[node_idx].name.clone(),
1843 reason: assertion_skip_reason(
1844 strict_values,
1845 &all_nodes,
1846 &last_source,
1847 &constraint,
1848 ),
1849 });
1850 }
1851 } else if strict_types && source_type != expected_type {
1852 }
1860 }
1861
1862 while resolved_wiring.len() <= node_idx {
1863 resolved_wiring.push(Vec::new());
1864 }
1865 resolved_wiring[node_idx] = node_wiring;
1866 }
1867
1868 while resolved_wiring.len() < all_nodes.len() {
1869 resolved_wiring.push(Vec::new());
1870 }
1871
1872 {
1877 let rules = crate::compile::fusion::default_rules();
1878 if !rules.is_empty() {
1879 let mut output_nodes: Vec<usize> = Vec::new();
1882 for wire_ref in self.outputs.values() {
1883 if let WireRef::Node(node_name, _) = wire_ref
1884 && let Some(&idx) = all_name_to_idx.get(node_name)
1885 {
1886 output_nodes.push(idx);
1887 }
1888 }
1889
1890 let mut opt_nodes: Vec<Option<Box<dyn PolydatNode>>> =
1892 all_nodes.into_iter().map(|pn| Some(pn.node)).collect();
1893
1894 let fused_count = crate::compile::fusion::apply_fusions(
1895 &mut opt_nodes,
1896 &mut resolved_wiring,
1897 &mut all_name_to_idx,
1898 &rules,
1899 &output_nodes,
1900 );
1901 if fused_count > 0
1902 && let Some(ref mut log) = log
1903 {
1904 log.push(crate::dsl::events::CompileEvent::FusionApplied {
1905 pattern: "subgraph".into(),
1906 nodes_replaced: fused_count,
1907 });
1908 }
1909
1910 all_nodes = opt_nodes
1913 .into_iter()
1914 .enumerate()
1915 .map(|(i, opt)| PendingNode {
1916 name: all_name_to_idx
1917 .iter()
1918 .find(|&(_, &idx)| idx == i)
1919 .map(|(n, _)| n.clone())
1920 .unwrap_or_else(|| format!("__removed_{i}")),
1921 node: opt.unwrap_or_else(|| {
1922 Box::new(crate::library::identity::Identity::new(
1923 crate::ast::PortType::U64,
1924 ))
1925 }),
1926 inputs: vec![], })
1928 .collect();
1929 }
1930 }
1931
1932 let node_count = all_nodes.len();
1939 let mut reachable = vec![false; node_count];
1940 {
1941 let mut worklist: Vec<usize> = Vec::new();
1942 for wire_ref in self.outputs.values() {
1944 if let WireRef::Node(node_name, _) = wire_ref
1945 && let Some(&idx) = all_name_to_idx.get(node_name)
1946 {
1947 worklist.push(idx);
1948 }
1949 }
1950 for (idx, pn) in all_nodes.iter().enumerate() {
1960 if matches!(
1961 pn.node.meta().name.as_str(),
1962 "log_debug" | "log_info" | "log_warn" | "log_error"
1963 ) {
1964 worklist.push(idx);
1965 }
1966 }
1967 while let Some(idx) = worklist.pop() {
1969 if reachable[idx] {
1970 continue;
1971 }
1972 reachable[idx] = true;
1973 for source in &resolved_wiring[idx] {
1974 if let WireSource::NodeOutput(upstream, _) = source
1975 && !reachable[*upstream]
1976 {
1977 worklist.push(*upstream);
1978 }
1979 }
1980 }
1981 }
1982 let live_count = reachable.iter().filter(|&&r| r).count();
1983
1984 if let Some(log) = log.as_deref_mut() {
1989 let mut placed: Vec<(usize, usize, PortType)> = converters
1990 .iter()
1991 .filter(|&(_, &idx)| reachable[idx])
1992 .map(|(&(input, to), &idx)| (idx, input, to))
1993 .collect();
1994 placed.sort_unstable_by_key(|&(idx, _, _)| idx);
1995 for &(idx, input, to) in &placed {
1996 let def = &self.input_defs[input];
1997 let (origin, level) = match (def.converts_to, variance_level) {
1998 (Some(_), Some(level)) => ("inferred", level),
1999 _ => ("declared dyn", crate::dsl::events::EventLevel::Info),
2000 };
2001 log.push(crate::dsl::events::CompileEvent::InputConverterInserted {
2002 input: def.name.clone(),
2003 to: to.to_keyword().to_string(),
2004 node: all_nodes[idx].name.clone(),
2005 origin: origin.to_string(),
2006 level,
2007 });
2008 }
2009 if let Some(level) = variance_level {
2010 for &input in &open {
2011 if placed.iter().any(|&(_, i, _)| i == input) {
2012 continue;
2013 }
2014 let def = &self.input_defs[input];
2015 log.push(crate::dsl::events::CompileEvent::InputConverterInserted {
2016 input: def.name.clone(),
2017 to: def
2018 .converts_to
2019 .unwrap_or(def.port_type)
2020 .to_keyword()
2021 .to_string(),
2022 node: "(none: nothing reads it)".to_string(),
2023 origin: "inferred".to_string(),
2024 level,
2025 });
2026 }
2027 }
2028 }
2029
2030 let mut in_degree = vec![0usize; node_count];
2032 let mut dependents: Vec<Vec<usize>> = vec![Vec::new(); node_count];
2033
2034 for (node_idx, wiring) in resolved_wiring.iter().enumerate() {
2035 if !reachable[node_idx] {
2036 continue;
2037 }
2038 for source in wiring {
2039 if let WireSource::NodeOutput(upstream, _) = source {
2040 in_degree[node_idx] += 1;
2041 dependents[*upstream].push(node_idx);
2042 }
2043 }
2044 }
2045
2046 let mut queue: Vec<usize> = (0..node_count)
2047 .filter(|i| reachable[*i] && in_degree[*i] == 0)
2048 .collect();
2049 let mut sorted_order: Vec<usize> = Vec::with_capacity(live_count);
2050
2051 while let Some(idx) = queue.pop() {
2052 sorted_order.push(idx);
2053 for &dep in &dependents[idx] {
2054 in_degree[dep] -= 1;
2055 if in_degree[dep] == 0 {
2056 queue.push(dep);
2057 }
2058 }
2059 }
2060
2061 if sorted_order.len() != live_count {
2062 return Err(AssemblyError::CycleDetected);
2063 }
2064
2065 let mut old_to_new = vec![0usize; node_count];
2066 for (new_idx, &old_idx) in sorted_order.iter().enumerate() {
2067 old_to_new[old_idx] = new_idx;
2068 }
2069
2070 let mut sorted_nodes: Vec<Option<Box<dyn PolydatNode>>> =
2071 all_nodes.into_iter().map(|pn| Some(pn.node)).collect();
2072
2073 let final_nodes: Vec<Box<dyn PolydatNode>> = sorted_order
2074 .iter()
2075 .map(|&old_idx| sorted_nodes[old_idx].take().unwrap())
2076 .collect();
2077
2078 let final_wiring: Vec<Vec<WireSource>> = sorted_order
2079 .iter()
2080 .map(|&old_idx| {
2081 resolved_wiring[old_idx]
2082 .iter()
2083 .map(|source| match source {
2084 WireSource::Input(c) => WireSource::Input(*c),
2085 WireSource::NodeOutput(old_up, port) => {
2086 WireSource::NodeOutput(old_to_new[*old_up], *port)
2087 }
2088 })
2089 .collect()
2090 })
2091 .collect();
2092
2093 let mut final_output_map: HashMap<String, (usize, usize)> = HashMap::new();
2094 for (name, wire_ref) in &self.outputs {
2095 match wire_ref {
2096 WireRef::Input(coord_name) => {
2097 return Err(AssemblyError::UnknownWire(format!(
2098 "output '{name}' references coordinate '{coord_name}' directly; \
2099 wire through a node instead"
2100 )));
2101 }
2102 WireRef::Node(node_name, port) => {
2103 let old_idx = all_name_to_idx
2104 .get(node_name)
2105 .ok_or_else(|| AssemblyError::UnknownWire(node_name.clone()))?;
2106 final_output_map.insert(name.clone(), (old_to_new[*old_idx], *port));
2107 }
2108 }
2109 }
2110
2111 for f in crate::compile::roundtrip_lint::lint_type_round_trips(
2118 &final_nodes,
2119 &final_wiring,
2120 &self.input_defs,
2121 ) {
2122 if strict_values {
2123 return Err(AssemblyError::Other(f.message()));
2124 }
2125 crate::library::support::audit::warn(&f.message());
2128 if let Some(ref mut log) = log {
2129 log.push(crate::dsl::events::CompileEvent::Warning {
2130 message: f.message(),
2131 });
2132 }
2133 }
2134
2135 if let Some(log) = log {
2136 let resolved_view = ResolvedDag {
2137 nodes: final_nodes,
2138 wiring: final_wiring,
2139 input_defs: self.input_defs,
2140 coord_count: self.coord_count,
2141 output_map: final_output_map,
2142 output_order: self.output_order,
2143 source: self.source,
2144 context: self.context,
2145 output_modifiers: self.output_modifiers,
2146 const_outputs: self.const_outputs,
2147 cursor_schemas: self.cursor_schemas,
2148 ledger: self.ledger,
2149 };
2150 Self::log_forms(&resolved_view, log);
2151 return Ok(resolved_view);
2152 }
2153 Ok(ResolvedDag {
2154 nodes: final_nodes,
2155 wiring: final_wiring,
2156 input_defs: self.input_defs,
2157 coord_count: self.coord_count,
2158 output_map: final_output_map,
2159 output_order: self.output_order,
2160 source: self.source,
2161 context: self.context,
2162 output_modifiers: self.output_modifiers,
2163 const_outputs: self.const_outputs,
2164 cursor_schemas: self.cursor_schemas,
2165 ledger: self.ledger,
2166 })
2167 }
2168}
2169
2170fn value_constraint_proven(
2185 all_nodes: &[PendingNode],
2186 src: &WireSource,
2187 _constraint: &crate::dsl::const_constraints::ConstConstraint,
2188) -> bool {
2189 match src {
2190 WireSource::Input(_) => false,
2191 WireSource::NodeOutput(idx, _) => {
2192 let meta = all_nodes[*idx].node.meta();
2193 let no_wire_inputs = meta.wire_inputs().is_empty();
2198 if no_wire_inputs {
2199 return true;
2200 }
2201 if meta.name.starts_with("__assert_v_") || meta.name.starts_with("assert_") {
2206 return true;
2207 }
2208 false
2209 }
2210 }
2211}
2212
2213fn assertion_skip_reason(
2217 strict_values: bool,
2218 all_nodes: &[PendingNode],
2219 src: &WireSource,
2220 _constraint: &crate::dsl::const_constraints::ConstConstraint,
2221) -> String {
2222 if !strict_values {
2223 return "strict_values not enabled".into();
2224 }
2225 match src {
2226 WireSource::Input(_) => "raw input wire".into(),
2227 WireSource::NodeOutput(idx, _) => {
2228 let meta = all_nodes[*idx].node.meta();
2229 if meta.wire_inputs().is_empty() {
2230 "constant source already validated".into()
2231 } else if meta.name.starts_with("__assert_v_") || meta.name.starts_with("assert_") {
2232 "upstream assertion".into()
2233 } else {
2234 "no skip rule matched".into()
2235 }
2236 }
2237 }
2238}
2239
2240pub(crate) fn shared_outputs_of(resolved: &ResolvedDag) -> Vec<&str> {
2243 let mut shared: Vec<&str> = resolved
2244 .output_modifiers
2245 .iter()
2246 .filter(|(_, m)| **m == crate::dsl::ast::BindingModifier::SHARED)
2247 .map(|(name, _)| name.as_str())
2248 .collect();
2249 shared.sort();
2250 shared
2251}
2252
2253fn is_lossless_widening(from: PortType, to: PortType) -> bool {
2259 use PortType as P;
2260 matches!(
2261 (from, to),
2262 (P::U64, P::F64)
2263 | (P::U32, P::U64)
2264 | (P::U32, P::I64)
2265 | (P::U32, P::F64)
2266 | (P::I32, P::I64)
2267 | (P::I32, P::F64)
2268 | (P::I64, P::F64)
2269 | (P::F32, P::F64)
2270 )
2271}
2272
2273pub(crate) fn wire_types_of(resolved: &ResolvedDag, node_idx: usize) -> Vec<PortType> {
2274 resolved.wiring[node_idx]
2275 .iter()
2276 .map(|src| match src {
2277 crate::kernel::WireSource::Input(i) => resolved.input_defs[*i].port_type,
2278 crate::kernel::WireSource::NodeOutput(j, p) => resolved.nodes[*j].meta().outs[*p].typ,
2279 })
2280 .collect()
2281}
2282
2283pub fn auto_adapter(from: PortType, to: PortType) -> Option<Box<dyn PolydatNode>> {
2287 use crate::library::convert::{
2288 BoolToStr, BoolToU64, F32ToF64, F32ToString, I32ToF64, I32ToI64, I32ToString, I64ToF64,
2289 I64ToString, U32ToF64, U32ToI64, U32ToString, U32ToU64,
2290 };
2291 use crate::library::polyfill as P;
2292 use crate::library::polyfill_128 as W;
2293 use crate::library::polyfill_complete as C;
2294 use crate::library::polyfill_narrow as N;
2295 match (from, to) {
2296 (PortType::U64, PortType::F64) => Some(Box::new(U64ToF64::new())),
2298 (PortType::U32, PortType::U64) => Some(Box::new(U32ToU64::new())),
2299 (PortType::U32, PortType::I64) => Some(Box::new(U32ToI64::new())),
2300 (PortType::U32, PortType::F64) => Some(Box::new(U32ToF64::new())),
2301 (PortType::I32, PortType::I64) => Some(Box::new(I32ToI64::new())),
2302 (PortType::I32, PortType::F64) => Some(Box::new(I32ToF64::new())),
2303 (PortType::I32, PortType::F32) => Some(Box::new(P::I32ToF32::new())),
2309 (PortType::I64, PortType::F64) => Some(Box::new(I64ToF64::new())),
2310 (PortType::F32, PortType::F64) => Some(Box::new(F32ToF64::new())),
2311
2312 (PortType::U64, PortType::Str) => Some(Box::new(U64ToString::new())),
2314 (PortType::F64, PortType::Str) => Some(Box::new(F64ToString::new())),
2315 (PortType::Bool, PortType::Str) => Some(Box::new(BoolToStr::new())),
2316 (PortType::Json, PortType::Str) => Some(Box::new(JsonToStr::new())),
2317 (PortType::U32, PortType::Str) => Some(Box::new(U32ToString::new())),
2318 (PortType::I32, PortType::Str) => Some(Box::new(I32ToString::new())),
2319 (PortType::I64, PortType::Str) => Some(Box::new(I64ToString::new())),
2320 (PortType::F32, PortType::Str) => Some(Box::new(F32ToString::new())),
2321
2322 (PortType::Bool, PortType::U64) => Some(Box::new(BoolToU64::new())),
2324 (PortType::Bool, PortType::U32) => Some(Box::new(P::BoolToU32::new())),
2325 (PortType::Bool, PortType::I64) => Some(Box::new(P::BoolToI64::new())),
2326 (PortType::Bool, PortType::I32) => Some(Box::new(P::BoolToI32::new())),
2327 (PortType::Bool, PortType::F64) => Some(Box::new(P::BoolToF64::new())),
2328 (PortType::Bool, PortType::F32) => Some(Box::new(P::BoolToF32::new())),
2329 (PortType::U64, PortType::Bool) => {
2330 Some(Box::new(crate::library::convert::U64ToBool::new()))
2331 }
2332 (PortType::U32, PortType::Bool) => Some(Box::new(P::U32ToBool::new())),
2333 (PortType::I64, PortType::Bool) => Some(Box::new(P::I64ToBool::new())),
2334 (PortType::I32, PortType::Bool) => Some(Box::new(P::I32ToBool::new())),
2335 (PortType::F64, PortType::Bool) => Some(Box::new(P::F64ToBool::new())),
2336 (PortType::F32, PortType::Bool) => Some(Box::new(P::F32ToBool::new())),
2337
2338 (PortType::U64, PortType::Bytes) => Some(Box::new(P::U64ToBytes::new())),
2340 (PortType::U32, PortType::Bytes) => Some(Box::new(P::U32ToBytes::new())),
2341 (PortType::I64, PortType::Bytes) => Some(Box::new(P::I64ToBytes::new())),
2342 (PortType::I32, PortType::Bytes) => Some(Box::new(P::I32ToBytes::new())),
2343 (PortType::F64, PortType::Bytes) => Some(Box::new(P::F64ToBytes::new())),
2344 (PortType::F32, PortType::Bytes) => Some(Box::new(P::F32ToBytes::new())),
2345 (PortType::Bool, PortType::Bytes) => Some(Box::new(P::BoolToBytes::new())),
2346 (PortType::VecF32, PortType::Bytes) => Some(Box::new(P::VecF32ToBytes::new())),
2347 (PortType::VecI32, PortType::Bytes) => Some(Box::new(P::VecI32ToBytes::new())),
2348
2349 (PortType::U64, PortType::Json) => Some(Box::new(P::U64ToJson::new())),
2353 (PortType::U32, PortType::Json) => Some(Box::new(P::U32ToJson::new())),
2354 (PortType::I64, PortType::Json) => Some(Box::new(P::I64ToJson::new())),
2355 (PortType::I32, PortType::Json) => Some(Box::new(P::I32ToJson::new())),
2356 (PortType::Bool, PortType::Json) => Some(Box::new(P::BoolToJson::new())),
2357 (PortType::VecI32, PortType::Json) => Some(Box::new(P::VecI32ToJson::new())),
2358
2359 (PortType::VecI32, PortType::VecF32) => Some(Box::new(P::VecI32ToVecF32::new())),
2361
2362 (PortType::U8, PortType::U64) => Some(Box::new(N::U8ToU64::new())),
2367 (PortType::U8, PortType::U32) => Some(Box::new(N::U8ToU32::new())),
2368 (PortType::U8, PortType::U16) => Some(Box::new(N::U8ToU16::new())),
2369 (PortType::U8, PortType::F64) => Some(Box::new(N::U8ToF64::new())),
2370 (PortType::U16, PortType::U64) => Some(Box::new(N::U16ToU64::new())),
2371 (PortType::U16, PortType::U32) => Some(Box::new(N::U16ToU32::new())),
2372 (PortType::U16, PortType::F64) => Some(Box::new(N::U16ToF64::new())),
2373 (PortType::I8, PortType::I64) => Some(Box::new(N::I8ToI64::new())),
2374 (PortType::I8, PortType::I32) => Some(Box::new(N::I8ToI32::new())),
2375 (PortType::I8, PortType::I16) => Some(Box::new(N::I8ToI16::new())),
2376 (PortType::I8, PortType::F64) => Some(Box::new(N::I8ToF64::new())),
2377 (PortType::I16, PortType::I64) => Some(Box::new(N::I16ToI64::new())),
2378 (PortType::I16, PortType::I32) => Some(Box::new(N::I16ToI32::new())),
2379 (PortType::I16, PortType::F64) => Some(Box::new(N::I16ToF64::new())),
2380 (PortType::F16, PortType::F32) => Some(Box::new(N::F16ToF32::new())),
2381 (PortType::F16, PortType::F64) => Some(Box::new(N::F16ToF64::new())),
2382 (PortType::U8, PortType::I16) => Some(Box::new(N::U8ToI16::new())),
2385 (PortType::U8, PortType::I32) => Some(Box::new(N::U8ToI32::new())),
2386 (PortType::U8, PortType::I64) => Some(Box::new(N::U8ToI64::new())),
2387 (PortType::U8, PortType::F32) => Some(Box::new(N::U8ToF32::new())),
2388 (PortType::U16, PortType::I32) => Some(Box::new(N::U16ToI32::new())),
2389 (PortType::U16, PortType::I64) => Some(Box::new(N::U16ToI64::new())),
2390 (PortType::U16, PortType::F32) => Some(Box::new(N::U16ToF32::new())),
2391 (PortType::I8, PortType::F32) => Some(Box::new(N::I8ToF32::new())),
2392 (PortType::I16, PortType::F32) => Some(Box::new(N::I16ToF32::new())),
2393 (PortType::U8, PortType::F16) => Some(Box::new(N::U8ToF16::new())),
2394 (PortType::I8, PortType::F16) => Some(Box::new(N::I8ToF16::new())),
2395 (PortType::U8, PortType::Str) => Some(Box::new(N::U8ToString::new())),
2396 (PortType::U16, PortType::Str) => Some(Box::new(N::U16ToString::new())),
2397 (PortType::I8, PortType::Str) => Some(Box::new(N::I8ToString::new())),
2398 (PortType::I16, PortType::Str) => Some(Box::new(N::I16ToString::new())),
2399 (PortType::F16, PortType::Str) => Some(Box::new(N::F16ToString::new())),
2400 (PortType::Bool, PortType::U8) => Some(Box::new(N::BoolToU8::new())),
2401 (PortType::Bool, PortType::U16) => Some(Box::new(N::BoolToU16::new())),
2402 (PortType::Bool, PortType::I8) => Some(Box::new(N::BoolToI8::new())),
2403 (PortType::Bool, PortType::I16) => Some(Box::new(N::BoolToI16::new())),
2404 (PortType::Bool, PortType::F16) => Some(Box::new(N::BoolToF16::new())),
2405 (PortType::U8, PortType::Bool) => Some(Box::new(N::U8ToBool::new())),
2406 (PortType::U16, PortType::Bool) => Some(Box::new(N::U16ToBool::new())),
2407 (PortType::I8, PortType::Bool) => Some(Box::new(N::I8ToBool::new())),
2408 (PortType::I16, PortType::Bool) => Some(Box::new(N::I16ToBool::new())),
2409 (PortType::F16, PortType::Bool) => Some(Box::new(N::F16ToBool::new())),
2410 (PortType::U8, PortType::Bytes) => Some(Box::new(N::U8ToBytes::new())),
2411 (PortType::U16, PortType::Bytes) => Some(Box::new(N::U16ToBytes::new())),
2412 (PortType::I8, PortType::Bytes) => Some(Box::new(N::I8ToBytes::new())),
2413 (PortType::I16, PortType::Bytes) => Some(Box::new(N::I16ToBytes::new())),
2414 (PortType::F16, PortType::Bytes) => Some(Box::new(N::F16ToBytes::new())),
2415 (PortType::U8, PortType::Json) => Some(Box::new(N::U8ToJson::new())),
2416 (PortType::U16, PortType::Json) => Some(Box::new(N::U16ToJson::new())),
2417 (PortType::I8, PortType::Json) => Some(Box::new(N::I8ToJson::new())),
2418 (PortType::I16, PortType::Json) => Some(Box::new(N::I16ToJson::new())),
2419
2420 (PortType::U64, PortType::U128) => Some(Box::new(W::U64ToU128::new())),
2425 (PortType::U64, PortType::I128) => Some(Box::new(W::U64ToI128::new())),
2426 (PortType::I64, PortType::I128) => Some(Box::new(W::I64ToI128::new())),
2427 (PortType::U8, PortType::U128) => Some(Box::new(W::U8ToU128::new())),
2432 (PortType::U8, PortType::I128) => Some(Box::new(W::U8ToI128::new())),
2433 (PortType::U16, PortType::U128) => Some(Box::new(W::U16ToU128::new())),
2434 (PortType::U16, PortType::I128) => Some(Box::new(W::U16ToI128::new())),
2435 (PortType::U32, PortType::U128) => Some(Box::new(W::U32ToU128::new())),
2436 (PortType::U32, PortType::I128) => Some(Box::new(W::U32ToI128::new())),
2437 (PortType::I8, PortType::I128) => Some(Box::new(W::I8ToI128::new())),
2438 (PortType::I16, PortType::I128) => Some(Box::new(W::I16ToI128::new())),
2439 (PortType::I32, PortType::I128) => Some(Box::new(W::I32ToI128::new())),
2440 (PortType::Bool, PortType::U128) => Some(Box::new(W::BoolToU128::new())),
2441 (PortType::Bool, PortType::I128) => Some(Box::new(W::BoolToI128::new())),
2442 (PortType::U128, PortType::Bool) => Some(Box::new(W::U128ToBool::new())),
2443 (PortType::I128, PortType::Bool) => Some(Box::new(W::I128ToBool::new())),
2444 (PortType::U128, PortType::F64) => Some(Box::new(W::U128ToF64::new())),
2445 (PortType::I128, PortType::F64) => Some(Box::new(W::I128ToF64::new())),
2446 (PortType::U128, PortType::Str) => Some(Box::new(W::U128ToString::new())),
2447 (PortType::I128, PortType::Str) => Some(Box::new(W::I128ToString::new())),
2448 (PortType::U128, PortType::Bytes) => Some(Box::new(W::U128ToBytes::new())),
2449 (PortType::I128, PortType::Bytes) => Some(Box::new(W::I128ToBytes::new())),
2450 (PortType::U128, PortType::Json) => Some(Box::new(W::U128ToJson::new())),
2451 (PortType::I128, PortType::Json) => Some(Box::new(W::I128ToJson::new())),
2452
2453 (from, to) if crate::library::register_view::is_reg_port(from) => {
2458 crate::library::register_view::reg_view(to)
2459 }
2460
2461 (PortType::VecI8, PortType::VecI16) => Some(Box::new(C::VecI8ToVecI16::new())),
2465 (PortType::VecI8, PortType::VecI32) => Some(Box::new(C::VecI8ToVecI32::new())),
2466 (PortType::VecI8, PortType::VecI64) => Some(Box::new(C::VecI8ToVecI64::new())),
2467 (PortType::VecI8, PortType::VecF16) => Some(Box::new(C::VecI8ToVecF16::new())),
2468 (PortType::VecI8, PortType::VecF32) => Some(Box::new(C::VecI8ToVecF32::new())),
2469 (PortType::VecI8, PortType::VecF64) => Some(Box::new(C::VecI8ToVecF64::new())),
2470 (PortType::VecI16, PortType::VecI32) => Some(Box::new(C::VecI16ToVecI32::new())),
2471 (PortType::VecI16, PortType::VecI64) => Some(Box::new(C::VecI16ToVecI64::new())),
2472 (PortType::VecI16, PortType::VecF32) => Some(Box::new(C::VecI16ToVecF32::new())),
2473 (PortType::VecI16, PortType::VecF64) => Some(Box::new(C::VecI16ToVecF64::new())),
2474 (PortType::VecI32, PortType::VecI64) => Some(Box::new(C::VecI32ToVecI64::new())),
2475 (PortType::VecI32, PortType::VecF64) => Some(Box::new(C::VecI32ToVecF64::new())),
2476 (PortType::VecI64, PortType::VecF64) => Some(Box::new(C::VecI64ToVecF64::new())),
2477 (PortType::VecF16, PortType::VecF32) => Some(Box::new(C::VecF16ToVecF32::new())),
2478 (PortType::VecF16, PortType::VecF64) => Some(Box::new(C::VecF16ToVecF64::new())),
2479 (PortType::VecF32, PortType::VecF64) => Some(Box::new(C::VecF32ToVecF64::new())),
2480 (PortType::VecF64, PortType::Bytes) => Some(Box::new(C::VecF64ToBytes::new())),
2481 (PortType::VecI64, PortType::Bytes) => Some(Box::new(C::VecI64ToBytes::new())),
2482 (PortType::VecF16, PortType::Bytes) => Some(Box::new(C::VecF16ToBytes::new())),
2483 (PortType::VecI16, PortType::Bytes) => Some(Box::new(C::VecI16ToBytes::new())),
2484 (PortType::VecI8, PortType::Bytes) => Some(Box::new(C::VecI8ToBytes::new())),
2485 (PortType::VecI64, PortType::Json) => Some(Box::new(C::VecI64ToJson::new())),
2486 (PortType::VecI16, PortType::Json) => Some(Box::new(C::VecI16ToJson::new())),
2487 (PortType::VecI8, PortType::Json) => Some(Box::new(C::VecI8ToJson::new())),
2488 (PortType::VecI32, PortType::Str) => Some(Box::new(P::VecI32ToStr::new())),
2489 (PortType::VecI64, PortType::Str) => Some(Box::new(C::VecI64ToStr::new())),
2490 (PortType::VecI16, PortType::Str) => Some(Box::new(C::VecI16ToStr::new())),
2491 (PortType::VecI8, PortType::Str) => Some(Box::new(C::VecI8ToStr::new())),
2492
2493 _ => None,
2494 }
2495}
2496
2497pub fn boundary_adapter(from: PortType, to: PortType) -> Option<Box<dyn PolydatNode>> {
2527 if let Some(adapter) = auto_adapter(from, to) {
2528 return Some(adapter);
2529 }
2530 use crate::library::convert::{StrToBool, StrToF64, StrToU64};
2531 use crate::library::polyfill as P;
2532 use crate::library::polyfill_128 as W;
2533 use crate::library::polyfill_complete as C;
2534 use crate::library::polyfill_narrow as N;
2535 match (from, to) {
2536 (PortType::U64, PortType::U32) => Some(Box::new(P::U64ToU32::new())),
2538 (PortType::U64, PortType::I64) => Some(Box::new(P::U64ToI64::new())),
2539 (PortType::U64, PortType::I32) => Some(Box::new(P::U64ToI32::new())),
2540 (PortType::U64, PortType::F32) => Some(Box::new(P::U64ToF32::new())),
2541 (PortType::U32, PortType::I32) => Some(Box::new(P::U32ToI32::new())),
2542 (PortType::U32, PortType::F32) => Some(Box::new(P::U32ToF32::new())),
2543 (PortType::I64, PortType::U64) => Some(Box::new(P::I64ToU64::new())),
2544 (PortType::I64, PortType::U32) => Some(Box::new(P::I64ToU32::new())),
2545 (PortType::I64, PortType::I32) => Some(Box::new(P::I64ToI32::new())),
2546 (PortType::I64, PortType::F32) => Some(Box::new(P::I64ToF32::new())),
2547 (PortType::I32, PortType::U64) => Some(Box::new(P::I32ToU64::new())),
2548 (PortType::I32, PortType::U32) => Some(Box::new(P::I32ToU32::new())),
2549 (PortType::I32, PortType::F32) => Some(Box::new(P::I32ToF32::new())),
2550 (PortType::F64, PortType::U64) => Some(Box::new(P::F64ToU64Checked::new())),
2551 (PortType::F64, PortType::U32) => Some(Box::new(P::F64ToU32::new())),
2552 (PortType::F64, PortType::I64) => Some(Box::new(P::F64ToI64::new())),
2553 (PortType::F64, PortType::I32) => Some(Box::new(P::F64ToI32::new())),
2554 (PortType::F64, PortType::F32) => Some(Box::new(P::F64ToF32::new())),
2555 (PortType::F32, PortType::U64) => Some(Box::new(P::F32ToU64::new())),
2556 (PortType::F32, PortType::U32) => Some(Box::new(P::F32ToU32::new())),
2557 (PortType::F32, PortType::I64) => Some(Box::new(P::F32ToI64::new())),
2558 (PortType::F32, PortType::I32) => Some(Box::new(P::F32ToI32::new())),
2559
2560 (PortType::Str, PortType::Bool) => Some(Box::new(StrToBool::new())),
2562 (PortType::Str, PortType::U64) => Some(Box::new(StrToU64::new())),
2563 (PortType::Str, PortType::F64) => Some(Box::new(StrToF64::new())),
2564 (PortType::Str, PortType::U32) => Some(Box::new(P::StrToU32::new())),
2565 (PortType::Str, PortType::I64) => Some(Box::new(P::StrToI64::new())),
2566 (PortType::Str, PortType::I32) => Some(Box::new(P::StrToI32::new())),
2567 (PortType::Str, PortType::F32) => Some(Box::new(P::StrToF32::new())),
2568 (PortType::Str, PortType::Bytes) => Some(Box::new(P::StrToBytes::new())),
2569 (PortType::Str, PortType::Json) => Some(Box::new(P::StrToJson::new())),
2570 (PortType::Str, PortType::VecF32) => Some(Box::new(P::StrToVecF32::new())),
2571 (PortType::Str, PortType::VecI32) => Some(Box::new(P::StrToVecI32::new())),
2572
2573 (PortType::Bytes, PortType::U64) => Some(Box::new(P::BytesToU64::new())),
2575 (PortType::Bytes, PortType::U32) => Some(Box::new(P::BytesToU32::new())),
2576 (PortType::Bytes, PortType::I64) => Some(Box::new(P::BytesToI64::new())),
2577 (PortType::Bytes, PortType::I32) => Some(Box::new(P::BytesToI32::new())),
2578 (PortType::Bytes, PortType::F64) => Some(Box::new(P::BytesToF64::new())),
2579 (PortType::Bytes, PortType::F32) => Some(Box::new(P::BytesToF32::new())),
2580 (PortType::Bytes, PortType::Bool) => Some(Box::new(P::BytesToBool::new())),
2581 (PortType::Bytes, PortType::Str) => Some(Box::new(P::BytesToStr::new())),
2582 (PortType::Bytes, PortType::Json) => Some(Box::new(P::BytesToJson::new())),
2583 (PortType::Bytes, PortType::VecF32) => Some(Box::new(P::BytesToVecF32::new())),
2584 (PortType::Bytes, PortType::VecI32) => Some(Box::new(P::BytesToVecI32::new())),
2585
2586 (PortType::Json, PortType::U64) => Some(Box::new(P::JsonToU64::new())),
2588 (PortType::Json, PortType::U32) => Some(Box::new(P::JsonToU32::new())),
2589 (PortType::Json, PortType::I64) => Some(Box::new(P::JsonToI64::new())),
2590 (PortType::Json, PortType::I32) => Some(Box::new(P::JsonToI32::new())),
2591 (PortType::Json, PortType::F64) => Some(Box::new(P::JsonToF64::new())),
2592 (PortType::Json, PortType::F32) => Some(Box::new(P::JsonToF32::new())),
2593 (PortType::Json, PortType::Bool) => Some(Box::new(P::JsonToBool::new())),
2594 (PortType::Json, PortType::Bytes) => Some(Box::new(P::JsonToBytes::new())),
2595 (PortType::Json, PortType::VecF32) => Some(Box::new(P::JsonToVecF32::new())),
2596 (PortType::Json, PortType::VecI32) => Some(Box::new(P::JsonToVecI32::new())),
2597
2598 (PortType::F64, PortType::Json) => Some(Box::new(P::F64ToJson::new())),
2600 (PortType::F32, PortType::Json) => Some(Box::new(P::F32ToJson::new())),
2601 (PortType::VecF32, PortType::Json) => Some(Box::new(P::VecF32ToJson::new())),
2602 (PortType::VecF32, PortType::Str) => Some(Box::new(P::VecF32ToStr::new())),
2603
2604 (PortType::VecF32, PortType::VecI32) => Some(Box::new(P::VecF32ToVecI32::new())),
2606
2607 (PortType::U64, PortType::U8) => Some(Box::new(N::U64ToU8::new())),
2611 (PortType::U32, PortType::U8) => Some(Box::new(N::U32ToU8::new())),
2612 (PortType::U16, PortType::U8) => Some(Box::new(N::U16ToU8::new())),
2613 (PortType::I64, PortType::U8) => Some(Box::new(N::I64ToU8::new())),
2614 (PortType::F64, PortType::U8) => Some(Box::new(N::F64ToU8::new())),
2615 (PortType::U64, PortType::U16) => Some(Box::new(N::U64ToU16::new())),
2616 (PortType::U32, PortType::U16) => Some(Box::new(N::U32ToU16::new())),
2617 (PortType::I64, PortType::U16) => Some(Box::new(N::I64ToU16::new())),
2618 (PortType::F64, PortType::U16) => Some(Box::new(N::F64ToU16::new())),
2619 (PortType::I64, PortType::I8) => Some(Box::new(N::I64ToI8::new())),
2620 (PortType::I32, PortType::I8) => Some(Box::new(N::I32ToI8::new())),
2621 (PortType::U64, PortType::I8) => Some(Box::new(N::U64ToI8::new())),
2622 (PortType::F64, PortType::I8) => Some(Box::new(N::F64ToI8::new())),
2623 (PortType::I64, PortType::I16) => Some(Box::new(N::I64ToI16::new())),
2624 (PortType::I32, PortType::I16) => Some(Box::new(N::I32ToI16::new())),
2625 (PortType::U64, PortType::I16) => Some(Box::new(N::U64ToI16::new())),
2626 (PortType::F64, PortType::I16) => Some(Box::new(N::F64ToI16::new())),
2627 (PortType::F64, PortType::F16) => Some(Box::new(N::F64ToF16::new())),
2628 (PortType::F32, PortType::F16) => Some(Box::new(N::F32ToF16::new())),
2629 (PortType::U64, PortType::F16) => Some(Box::new(N::U64ToF16::new())),
2630 (PortType::Str, PortType::U8) => Some(Box::new(N::StrToU8::new())),
2631 (PortType::Str, PortType::U16) => Some(Box::new(N::StrToU16::new())),
2632 (PortType::Str, PortType::I8) => Some(Box::new(N::StrToI8::new())),
2633 (PortType::Str, PortType::I16) => Some(Box::new(N::StrToI16::new())),
2634 (PortType::Str, PortType::F16) => Some(Box::new(N::StrToF16::new())),
2635 (PortType::Bytes, PortType::U8) => Some(Box::new(N::BytesToU8::new())),
2636 (PortType::Bytes, PortType::U16) => Some(Box::new(N::BytesToU16::new())),
2637 (PortType::Bytes, PortType::I8) => Some(Box::new(N::BytesToI8::new())),
2638 (PortType::Bytes, PortType::I16) => Some(Box::new(N::BytesToI16::new())),
2639 (PortType::Bytes, PortType::F16) => Some(Box::new(N::BytesToF16::new())),
2640 (PortType::Json, PortType::U8) => Some(Box::new(N::JsonToU8::new())),
2641 (PortType::Json, PortType::U16) => Some(Box::new(N::JsonToU16::new())),
2642 (PortType::Json, PortType::I8) => Some(Box::new(N::JsonToI8::new())),
2643 (PortType::Json, PortType::I16) => Some(Box::new(N::JsonToI16::new())),
2644 (PortType::Json, PortType::F16) => Some(Box::new(N::JsonToF16::new())),
2645 (PortType::F16, PortType::Json) => Some(Box::new(N::F16ToJson::new())),
2647
2648 (PortType::U128, PortType::U64) => Some(Box::new(W::U128ToU64::new())),
2650 (PortType::I128, PortType::I64) => Some(Box::new(W::I128ToI64::new())),
2651 (PortType::I64, PortType::U128) => Some(Box::new(W::I64ToU128::new())),
2652 (PortType::U128, PortType::I128) => Some(Box::new(W::U128ToI128::new())),
2653 (PortType::I128, PortType::U128) => Some(Box::new(W::I128ToU128::new())),
2654 (PortType::F64, PortType::U128) => Some(Box::new(W::F64ToU128::new())),
2655 (PortType::F64, PortType::I128) => Some(Box::new(W::F64ToI128::new())),
2656 (PortType::Str, PortType::U128) => Some(Box::new(W::StrToU128::new())),
2657 (PortType::Str, PortType::I128) => Some(Box::new(W::StrToI128::new())),
2658 (PortType::Bytes, PortType::U128) => Some(Box::new(W::BytesToU128::new())),
2659 (PortType::Bytes, PortType::I128) => Some(Box::new(W::BytesToI128::new())),
2660 (PortType::Json, PortType::U128) => Some(Box::new(W::JsonToU128::new())),
2661 (PortType::Json, PortType::I128) => Some(Box::new(W::JsonToI128::new())),
2662
2663 (PortType::U8, PortType::I8) => Some(Box::new(C::U8ToI8::new())),
2668 (PortType::I8, PortType::U8) => Some(Box::new(C::I8ToU8::new())),
2669 (PortType::I8, PortType::U16) => Some(Box::new(C::I8ToU16::new())),
2670 (PortType::I8, PortType::U32) => Some(Box::new(C::I8ToU32::new())),
2671 (PortType::I8, PortType::U64) => Some(Box::new(C::I8ToU64::new())),
2672 (PortType::I8, PortType::U128) => Some(Box::new(C::I8ToU128::new())),
2673 (PortType::U16, PortType::I8) => Some(Box::new(C::U16ToI8::new())),
2674 (PortType::U16, PortType::I16) => Some(Box::new(C::U16ToI16::new())),
2675 (PortType::U16, PortType::F16) => Some(Box::new(C::U16ToF16::new())),
2676 (PortType::I16, PortType::U8) => Some(Box::new(C::I16ToU8::new())),
2677 (PortType::I16, PortType::I8) => Some(Box::new(C::I16ToI8::new())),
2678 (PortType::I16, PortType::U16) => Some(Box::new(C::I16ToU16::new())),
2679 (PortType::I16, PortType::F16) => Some(Box::new(C::I16ToF16::new())),
2680 (PortType::I16, PortType::U32) => Some(Box::new(C::I16ToU32::new())),
2681 (PortType::I16, PortType::U64) => Some(Box::new(C::I16ToU64::new())),
2682 (PortType::I16, PortType::U128) => Some(Box::new(C::I16ToU128::new())),
2683 (PortType::U32, PortType::I8) => Some(Box::new(C::U32ToI8::new())),
2684 (PortType::U32, PortType::I16) => Some(Box::new(C::U32ToI16::new())),
2685 (PortType::U32, PortType::F16) => Some(Box::new(C::U32ToF16::new())),
2686 (PortType::I32, PortType::U8) => Some(Box::new(C::I32ToU8::new())),
2687 (PortType::I32, PortType::U16) => Some(Box::new(C::I32ToU16::new())),
2688 (PortType::I32, PortType::F16) => Some(Box::new(C::I32ToF16::new())),
2689 (PortType::I32, PortType::U128) => Some(Box::new(C::I32ToU128::new())),
2690 (PortType::F16, PortType::U8) => Some(Box::new(C::F16ToU8::new())),
2691 (PortType::F16, PortType::I8) => Some(Box::new(C::F16ToI8::new())),
2692 (PortType::F16, PortType::U16) => Some(Box::new(C::F16ToU16::new())),
2693 (PortType::F16, PortType::I16) => Some(Box::new(C::F16ToI16::new())),
2694 (PortType::F16, PortType::U32) => Some(Box::new(C::F16ToU32::new())),
2695 (PortType::F16, PortType::I32) => Some(Box::new(C::F16ToI32::new())),
2696 (PortType::F16, PortType::U64) => Some(Box::new(C::F16ToU64::new())),
2697 (PortType::F16, PortType::I64) => Some(Box::new(C::F16ToI64::new())),
2698 (PortType::F16, PortType::U128) => Some(Box::new(C::F16ToU128::new())),
2699 (PortType::F16, PortType::I128) => Some(Box::new(C::F16ToI128::new())),
2700 (PortType::F32, PortType::U8) => Some(Box::new(C::F32ToU8::new())),
2701 (PortType::F32, PortType::I8) => Some(Box::new(C::F32ToI8::new())),
2702 (PortType::F32, PortType::U16) => Some(Box::new(C::F32ToU16::new())),
2703 (PortType::F32, PortType::I16) => Some(Box::new(C::F32ToI16::new())),
2704 (PortType::F32, PortType::U128) => Some(Box::new(C::F32ToU128::new())),
2705 (PortType::F32, PortType::I128) => Some(Box::new(C::F32ToI128::new())),
2706 (PortType::I64, PortType::F16) => Some(Box::new(C::I64ToF16::new())),
2707 (PortType::U128, PortType::U8) => Some(Box::new(C::U128ToU8::new())),
2708 (PortType::U128, PortType::I8) => Some(Box::new(C::U128ToI8::new())),
2709 (PortType::U128, PortType::U16) => Some(Box::new(C::U128ToU16::new())),
2710 (PortType::U128, PortType::I16) => Some(Box::new(C::U128ToI16::new())),
2711 (PortType::U128, PortType::F16) => Some(Box::new(C::U128ToF16::new())),
2712 (PortType::U128, PortType::U32) => Some(Box::new(C::U128ToU32::new())),
2713 (PortType::U128, PortType::I32) => Some(Box::new(C::U128ToI32::new())),
2714 (PortType::U128, PortType::F32) => Some(Box::new(C::U128ToF32::new())),
2715 (PortType::U128, PortType::I64) => Some(Box::new(C::U128ToI64::new())),
2716 (PortType::I128, PortType::U8) => Some(Box::new(C::I128ToU8::new())),
2717 (PortType::I128, PortType::I8) => Some(Box::new(C::I128ToI8::new())),
2718 (PortType::I128, PortType::U16) => Some(Box::new(C::I128ToU16::new())),
2719 (PortType::I128, PortType::I16) => Some(Box::new(C::I128ToI16::new())),
2720 (PortType::I128, PortType::F16) => Some(Box::new(C::I128ToF16::new())),
2721 (PortType::I128, PortType::U32) => Some(Box::new(C::I128ToU32::new())),
2722 (PortType::I128, PortType::I32) => Some(Box::new(C::I128ToI32::new())),
2723 (PortType::I128, PortType::F32) => Some(Box::new(C::I128ToF32::new())),
2724 (PortType::I128, PortType::U64) => Some(Box::new(C::I128ToU64::new())),
2725
2726 (PortType::VecI16, PortType::VecI8) => Some(Box::new(C::VecI16ToVecI8::new())),
2730 (PortType::VecI16, PortType::VecF16) => Some(Box::new(C::VecI16ToVecF16::new())),
2731 (PortType::VecI32, PortType::VecI8) => Some(Box::new(C::VecI32ToVecI8::new())),
2732 (PortType::VecI32, PortType::VecI16) => Some(Box::new(C::VecI32ToVecI16::new())),
2733 (PortType::VecI32, PortType::VecF16) => Some(Box::new(C::VecI32ToVecF16::new())),
2734 (PortType::VecI64, PortType::VecI8) => Some(Box::new(C::VecI64ToVecI8::new())),
2735 (PortType::VecI64, PortType::VecI16) => Some(Box::new(C::VecI64ToVecI16::new())),
2736 (PortType::VecI64, PortType::VecI32) => Some(Box::new(C::VecI64ToVecI32::new())),
2737 (PortType::VecI64, PortType::VecF16) => Some(Box::new(C::VecI64ToVecF16::new())),
2738 (PortType::VecI64, PortType::VecF32) => Some(Box::new(C::VecI64ToVecF32::new())),
2739 (PortType::VecF16, PortType::VecI8) => Some(Box::new(C::VecF16ToVecI8::new())),
2740 (PortType::VecF16, PortType::VecI16) => Some(Box::new(C::VecF16ToVecI16::new())),
2741 (PortType::VecF16, PortType::VecI32) => Some(Box::new(C::VecF16ToVecI32::new())),
2742 (PortType::VecF16, PortType::VecI64) => Some(Box::new(C::VecF16ToVecI64::new())),
2743 (PortType::VecF32, PortType::VecI8) => Some(Box::new(C::VecF32ToVecI8::new())),
2744 (PortType::VecF32, PortType::VecI16) => Some(Box::new(C::VecF32ToVecI16::new())),
2745 (PortType::VecF32, PortType::VecI64) => Some(Box::new(C::VecF32ToVecI64::new())),
2746 (PortType::VecF32, PortType::VecF16) => Some(Box::new(C::VecF32ToVecF16::new())),
2747 (PortType::VecF64, PortType::VecI8) => Some(Box::new(C::VecF64ToVecI8::new())),
2748 (PortType::VecF64, PortType::VecI16) => Some(Box::new(C::VecF64ToVecI16::new())),
2749 (PortType::VecF64, PortType::VecI32) => Some(Box::new(C::VecF64ToVecI32::new())),
2750 (PortType::VecF64, PortType::VecI64) => Some(Box::new(C::VecF64ToVecI64::new())),
2751 (PortType::VecF64, PortType::VecF16) => Some(Box::new(C::VecF64ToVecF16::new())),
2752 (PortType::VecF64, PortType::VecF32) => Some(Box::new(C::VecF64ToVecF32::new())),
2753 (PortType::Bytes, PortType::VecF64) => Some(Box::new(C::BytesToVecF64::new())),
2754 (PortType::Bytes, PortType::VecI64) => Some(Box::new(C::BytesToVecI64::new())),
2755 (PortType::Bytes, PortType::VecF16) => Some(Box::new(C::BytesToVecF16::new())),
2756 (PortType::Bytes, PortType::VecI16) => Some(Box::new(C::BytesToVecI16::new())),
2757 (PortType::Bytes, PortType::VecI8) => Some(Box::new(C::BytesToVecI8::new())),
2758 (PortType::VecF64, PortType::Json) => Some(Box::new(C::VecF64ToJson::new())),
2759 (PortType::VecF16, PortType::Json) => Some(Box::new(C::VecF16ToJson::new())),
2760 (PortType::Json, PortType::VecF64) => Some(Box::new(C::JsonToVecF64::new())),
2761 (PortType::Json, PortType::VecI64) => Some(Box::new(C::JsonToVecI64::new())),
2762 (PortType::Json, PortType::VecF16) => Some(Box::new(C::JsonToVecF16::new())),
2763 (PortType::Json, PortType::VecI16) => Some(Box::new(C::JsonToVecI16::new())),
2764 (PortType::Json, PortType::VecI8) => Some(Box::new(C::JsonToVecI8::new())),
2765 (PortType::VecF64, PortType::Str) => Some(Box::new(C::VecF64ToStr::new())),
2766 (PortType::VecF16, PortType::Str) => Some(Box::new(C::VecF16ToStr::new())),
2767 (PortType::Str, PortType::VecF64) => Some(Box::new(C::StrToVecF64::new())),
2768 (PortType::Str, PortType::VecI64) => Some(Box::new(C::StrToVecI64::new())),
2769 (PortType::Str, PortType::VecF16) => Some(Box::new(C::StrToVecF16::new())),
2770 (PortType::Str, PortType::VecI16) => Some(Box::new(C::StrToVecI16::new())),
2771 (PortType::Str, PortType::VecI8) => Some(Box::new(C::StrToVecI8::new())),
2772
2773 _ => None,
2774 }
2775}
2776
2777use crate::compile::select::{Engine, KernelError, Provenance};
2780use crate::kernel::Kernel;
2781
2782impl PolydatAssembler {
2783 pub fn compile_with(self, engine: Engine) -> Result<Box<dyn Kernel>, KernelError> {
2791 self.compile_engine_with_log(engine, None)
2792 }
2793
2794 pub fn compile_kernel(self) -> Result<Box<dyn Kernel>, KernelError> {
2797 self.compile_with(Engine::default())
2798 }
2799
2800 pub fn compile_engine_with_log(
2803 self,
2804 engine: Engine,
2805 log: Option<&mut crate::dsl::events::CompileEventLog>,
2806 ) -> Result<Box<dyn Kernel>, KernelError> {
2807 match engine {
2808 Engine::Interpreter(cones) => {
2811 let mut asm = self;
2812 asm.jit_mode = Some(cones);
2813 Ok(Box::new(asm.compile_with_log(log)?))
2814 }
2815 _ => Ok(self.compile_slots_with_log(engine, log)?),
2819 }
2820 }
2821
2822 pub fn compile_slots(
2838 self,
2839 engine: Engine,
2840 ) -> Result<Box<dyn crate::compile::SlotKernel>, KernelError> {
2841 self.compile_slots_with_log(engine, None)
2842 }
2843
2844 pub fn compile_slots_with_log(
2846 self,
2847 engine: Engine,
2848 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
2849 ) -> Result<Box<dyn crate::compile::SlotKernel>, KernelError> {
2850 let refused = |reason: String| KernelError::Refused { engine, reason };
2851 let asked = |e: KernelError| match e {
2856 KernelError::Refused { reason, .. } => KernelError::Refused { engine, reason },
2857 other => other,
2858 };
2859 let strict = self.strict;
2860 match engine {
2861 Engine::Interpreter(_) => Err(refused(
2862 "the interpreter has no slot buffer: its buffers are typed `Value`s, so \
2863 there is no slot to name. Ask for `closures`, `native` or `pure-native` \
2864 for the slot surface, or compile with `compile_with` and drive the \
2865 kernel through the `Kernel` trait, which every engine answers."
2866 .into(),
2867 )),
2868 Engine::Closures(prov) => {
2869 let resolved = self.resolve_with_log(log.as_deref_mut())?;
2870 if strict {
2871 Self::refuse_strict(&resolved)?;
2872 }
2873 let folded = log.is_some().then(|| Self::constant_sites(&resolved));
2874 let (node_total, output_total) =
2875 (resolved.nodes.len(), resolved.output_order.len());
2876 let kernel = Self::closures_from(resolved, prov).map_err(asked)?;
2877 Self::log_folded(kernel.as_ref(), folded, log.as_deref_mut());
2878 Self::log_summary(log, node_total, output_total);
2879 Ok(kernel)
2880 }
2881 Engine::Native(prov) => {
2890 {
2891 let resolved = self.resolve_with_log(log.as_deref_mut())?;
2892 if strict {
2893 Self::refuse_strict(&resolved)?;
2894 }
2895 let folded = log.is_some().then(|| Self::constant_sites(&resolved));
2896 let (node_total, output_total) =
2897 (resolved.nodes.len(), resolved.output_order.len());
2898 if prov == Provenance::Push {
2904 return Err(refused(
2905 "native code has no push-only kernel: push-side invalidation \
2906 without the cone guard has no native form. Ask for `pushpull` \
2907 for both, `pull` for the guard alone, or `auto` to let the \
2908 selector choose; `push` alone is available on the closure tier."
2909 .into(),
2910 ));
2911 }
2912 let prov = Self::provenance_for(prov, &resolved);
2913 let kernel = Self::hybrid_from(resolved).map_err(asked)?;
2914 let kernel: Box<dyn crate::compile::SlotKernel> = match prov {
2915 Provenance::Raw => Box::new(kernel.into_raw()),
2916 Provenance::Pull => Box::new(kernel.into_pull()),
2917 Provenance::PushPull | Provenance::Auto => Box::new(kernel),
2923 Provenance::Push => {
2924 return Err(refused("native code has no push-only kernel".into()));
2925 }
2926 };
2927 Self::log_folded(kernel.as_ref(), folded, log.as_deref_mut());
2928 Self::log_summary(log, node_total, output_total);
2929 Ok(kernel)
2930 }
2931 }
2932 Engine::PureNative(prov) => {
2933 #[cfg(feature = "jit")]
2934 {
2935 let resolved = self.resolve_with_log(log.as_deref_mut())?;
2936 if strict {
2937 Self::refuse_strict(&resolved)?;
2938 }
2939 let folded = log.is_some().then(|| Self::constant_sites(&resolved));
2940 let (node_total, output_total) =
2941 (resolved.nodes.len(), resolved.output_order.len());
2942 let prov = match prov {
2949 Provenance::Auto => match Self::provenance_for(prov, &resolved) {
2950 Provenance::Raw => Provenance::Raw,
2951 _ => Provenance::PushPull,
2952 },
2953 named @ (Provenance::Raw | Provenance::PushPull) => named,
2954 other => {
2955 return Err(refused(format!(
2956 "pure native code has no {} kernel: the tier keeps only the \
2957 two forms the differential needs. Ask for `raw` or \
2958 `pushpull`, or `auto` to let the selector choose; every \
2959 mode is available on `native`.",
2960 format!("{other:?}").to_lowercase(),
2961 )));
2962 }
2963 };
2964 let kernel: Box<dyn crate::compile::SlotKernel> = match prov {
2965 Provenance::Raw => Box::new(Self::jit_raw_from(resolved).map_err(asked)?),
2966 _ => Box::new(Self::jit_push_pull_from(resolved).map_err(asked)?),
2967 };
2968 Self::log_folded(kernel.as_ref(), folded, log.as_deref_mut());
2969 Self::log_summary(log, node_total, output_total);
2970 Ok(kernel)
2971 }
2972 #[cfg(not(feature = "jit"))]
2973 {
2974 let _ = (prov, log);
2975 Err(refused(
2976 "this build has no native code (the `jit` feature is off)".into(),
2977 ))
2978 }
2979 }
2980 }
2981 }
2982
2983 fn constant_sites(resolved: &ResolvedDag) -> Vec<(String, usize, crate::ast::PortType)> {
2988 let classes = PolydatProgram::classify_lifecycle(
2989 &resolved.nodes,
2990 &resolved.wiring,
2991 &resolved.input_defs,
2992 &resolved.output_map,
2993 &resolved.output_modifiers,
2994 );
2995 let layout = slot_layout(resolved);
2996 resolved
2997 .nodes
2998 .iter()
2999 .enumerate()
3000 .filter(|(i, n)| {
3001 classes.lifecycle[*i] == crate::kernel::EvalLifecycle::CompileConst
3002 && n.meta().outs.len() == 1
3003 })
3004 .map(|(i, n)| {
3005 (
3006 n.meta().name.clone(),
3007 layout.port_offsets[i][0],
3008 n.meta().outs[0].typ,
3009 )
3010 })
3011 .collect()
3012 }
3013
3014 fn log_folded(
3017 kernel: &dyn Kernel,
3018 sites: Option<Vec<(String, usize, crate::ast::PortType)>>,
3019 log: Option<&mut crate::dsl::events::CompileEventLog>,
3020 ) {
3021 let (Some(sites), Some(log)) = (sites, log) else {
3022 return;
3023 };
3024 for (node, slot, ty) in sites {
3025 let value = crate::kernel::KernelInternals::slot_value(kernel, slot, ty);
3026 if !matches!(value, crate::ast::Value::None) {
3027 log.push(crate::dsl::events::CompileEvent::ConstantFolded {
3028 node,
3029 value: value.to_display_string(),
3030 });
3031 }
3032 }
3033 }
3034
3035 fn provenance_for(prov: Provenance, resolved: &ResolvedDag) -> Provenance {
3040 match prov {
3041 Provenance::Auto => {
3042 let analysis =
3043 select::analyze_graph(&resolved.nodes, &resolved.wiring, &resolved.output_map);
3044 match select::select_prov_mode(&analysis) {
3045 ProvMode::Raw => Provenance::Raw,
3046 ProvMode::Pull => Provenance::Pull,
3047 ProvMode::PushPull => Provenance::PushPull,
3048 }
3049 }
3050 p => p,
3051 }
3052 }
3053
3054 fn closures_from(
3057 resolved: ResolvedDag,
3058 prov: Provenance,
3059 ) -> Result<Box<dyn crate::compile::SlotKernel>, KernelError> {
3060 let prov = Self::provenance_for(prov, &resolved);
3061 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
3062 Self::build_p2_layout(&resolved).map_err(Self::refused_by_closures)?;
3063 let dependents = || {
3064 slot_layout(&resolved).expand_dependents(
3065 &resolved,
3066 &PolydatProgram::compute_dependents(
3067 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
3068 resolved.input_defs.len(),
3069 ),
3070 )
3071 };
3072 Ok(match prov {
3073 Provenance::Raw => Box::new(CompiledKernelRaw::new(
3074 coord_count,
3075 total_slots,
3076 steps,
3077 output_map,
3078 ref_slots,
3079 extras,
3080 )?),
3081 Provenance::Push => Box::new(CompiledKernelPush::new(
3082 coord_count,
3083 total_slots,
3084 steps,
3085 output_map,
3086 dependents(),
3087 ref_slots,
3088 extras,
3089 )?),
3090 Provenance::Pull => Box::new(CompiledKernelPull::new(
3091 coord_count,
3092 total_slots,
3093 steps,
3094 output_map,
3095 &dependents(),
3096 ref_slots,
3097 extras,
3098 )?),
3099 Provenance::PushPull | Provenance::Auto => Box::new(CompiledKernelPushPull::new(
3100 coord_count,
3101 total_slots,
3102 steps,
3103 output_map,
3104 dependents(),
3105 ref_slots,
3106 extras,
3107 )?),
3108 })
3109 }
3110}