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 ConstInit {
103 name: String,
105 reason: String,
107 },
108 OpenInputs(Vec<(String, PortType)>),
112 Other(String),
114}
115
116impl std::fmt::Display for AssemblyError {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 match self {
119 AssemblyError::UnknownWire(name) => {
120 write!(f, "unknown wire: '{name}'\n\n")?;
121 writeln!(f, " No node output or coordinate named '{name}' exists.")?;
122 write!(
123 f,
124 " Check spelling, or add a node that produces this output."
125 )
126 }
127 AssemblyError::TypeMismatch {
128 from_node,
129 from_port,
130 from_type,
131 to_node,
132 to_port,
133 to_type,
134 } => {
135 writeln!(
136 f,
137 "type mismatch: cannot connect {from_type} output to {to_type} input"
138 )?;
139 writeln!(f)?;
140 writeln!(
141 f,
142 " {from_node} [{from_port}] ──({from_type})──▶ {to_node} [{to_port}] expects {to_type}"
143 )?;
144 writeln!(f)?;
145 let suggestion = match (from_type, to_type) {
147 (PortType::U64, PortType::Str) => {
148 Some("This should auto-convert. If you see this, file a bug.")
149 }
150 (PortType::F64, PortType::Str) => {
151 Some("This should auto-convert. If you see this, file a bug.")
152 }
153 (PortType::U64, PortType::F64) => {
154 Some("This should auto-convert. If you see this, file a bug.")
155 }
156 (PortType::U64, PortType::Bytes) => {
157 Some("Add u64_to_bytes() between them to convert.")
158 }
159 (PortType::Str, PortType::Bytes) => {
160 Some("String cannot be directly used as bytes.")
161 }
162 (PortType::U64, PortType::Json) => {
163 Some("Add to_json() between them to wrap as JSON.")
164 }
165 (PortType::Str, PortType::Json) => {
166 Some("Add str_to_json() to parse the string as JSON.")
167 }
168 (PortType::Bytes, PortType::Str) => {
169 Some("Add to_hex() or to_base64() to convert bytes to string.")
170 }
171 (PortType::Bytes, PortType::U64) => {
172 Some("Bytes cannot be directly converted to u64.")
173 }
174 _ => None,
175 };
176 if let Some(hint) = suggestion {
177 write!(f, " Hint: {hint}")?;
178 }
179 Ok(())
180 }
181 AssemblyError::DuplicateNode(name) => {
182 write!(f, "duplicate node name: '{name}'\n\n")?;
183 write!(f, " Two nodes cannot share the same name.")
184 }
185 AssemblyError::CycleDetected => {
186 write!(f, "cycle detected in DAG\n\n")?;
187 writeln!(
188 f,
189 " The graph contains a loop. Polydat graphs must be acyclic"
190 )?;
191 write!(f, " (data flows in one direction only).")
192 }
193 AssemblyError::ArityMismatch {
194 node_name,
195 expected,
196 got,
197 } => {
198 write!(f, "wrong number of inputs for '{node_name}'\n\n")?;
199 writeln!(f, " Expected {expected} input(s), but got {got}.")?;
200 if *got < *expected {
201 write!(f, " Connect more wires to this node's input ports.")
202 } else {
203 write!(f, " Disconnect extra wires from this node.")
204 }
205 }
206 AssemblyError::ConstantFold(msg) => write!(
207 f,
208 "a value this program computes at build could not be computed: {msg}"
209 ),
210 AssemblyError::ConstInit { name, reason } => write!(
211 f,
212 "the const '{name}' could not be computed when the kernel was initialized: {reason}"
213 ),
214 AssemblyError::OpenInputs(inputs) => {
215 writeln!(
216 f,
217 "these inputs' types were inferred, not declared, and input variance \
218 is set to refuse them:"
219 )?;
220 for (name, ty) in inputs {
221 writeln!(f, " {name} (inferred {ty})")?;
222 }
223 write!(
224 f,
225 "Declare each one's type (`extern name: <type>`), or set \
226 `input_variance` to `Warn` or `Info` to convert what is written to them."
227 )
228 }
229 AssemblyError::Other(msg) => write!(f, "{msg}"),
230 }
231 }
232}
233
234impl std::error::Error for AssemblyError {}
235
236pub(crate) struct ResolvedDag {
238 pub(crate) nodes: Vec<Box<dyn PolydatNode>>,
240 pub(crate) wiring: Vec<Vec<WireSource>>,
242 pub(crate) input_defs: Vec<crate::kernel::InputDef>,
244 pub(crate) coord_count: usize,
246 pub(crate) output_map: HashMap<String, (usize, usize)>,
248 pub(crate) output_order: Vec<String>,
250 pub(crate) source: String,
252 pub(crate) context: String,
254 pub(crate) output_modifiers: HashMap<String, crate::dsl::ast::BindingModifier>,
256 pub(crate) const_outputs: std::collections::HashSet<String>,
258 pub(crate) const_inits: Vec<crate::kernel::ConstInit>,
260 pub(crate) cursor_schemas: Vec<crate::iteration::source::SourceSchema>,
262 pub(crate) ledger: std::sync::Arc<crate::kernel::CompileLedger>,
264}
265
266impl ResolvedDag {
267 fn input_names(&self) -> Vec<String> {
269 self.input_defs[..self.coord_count]
270 .iter()
271 .map(|d| d.name.clone())
272 .collect()
273 }
274}
275
276struct SlotLayout {
281 input_starts: Vec<usize>,
283 coord_slots: usize,
285 port_offsets: Vec<Vec<usize>>,
287 total_slots: usize,
289}
290
291fn slot_layout(resolved: &ResolvedDag) -> SlotLayout {
292 let mut input_starts = Vec::with_capacity(resolved.coord_count);
293 let mut next = 0usize;
294 for d in &resolved.input_defs {
295 input_starts.push(next);
296 next += d.port_type.slot_width();
297 }
298 let coord_slots = next;
299 let mut port_offsets: Vec<Vec<usize>> = Vec::with_capacity(resolved.nodes.len());
300 for node in &resolved.nodes {
301 let mut po = Vec::with_capacity(node.meta().outs.len());
302 for out in &node.meta().outs {
303 po.push(next);
304 next += out.typ.slot_width();
305 }
306 port_offsets.push(po);
307 }
308 SlotLayout {
309 input_starts,
310 coord_slots,
311 port_offsets,
312 total_slots: next,
313 }
314}
315
316fn node_step_op(
322 node: &dyn crate::ast::PolydatNode,
323 wire_types: &[PortType],
324) -> Option<(
325 crate::compile::closures::StepOp,
326 Vec<crate::ast::ScratchElem>,
327)> {
328 let meta = node.meta();
332 if (meta.name == "identity" || meta.name.starts_with("__port_")) && meta.outs.len() == 1 {
333 return Some(match meta.outs[0].typ.slot_color() {
334 crate::ast::SlotColor::Ref2 => {
335 let kit = ref_copy_kit(meta.outs[0].typ)?;
336 (crate::compile::closures::StepOp::Slot(kit.op), kit.scratch)
337 }
338 _ => (crate::compile::closures::StepOp::Copy, Vec::new()),
339 });
340 }
341 if let Some(op) = node.compiled_u64() {
342 return Some((crate::compile::closures::StepOp::U64(op), Vec::new()));
343 }
344 node.compiled_slot(
345 wire_types,
346 crate::compile::select::Engine::Closures(crate::compile::select::Provenance::Auto),
347 )
348 .map(|kit| (crate::compile::closures::StepOp::Slot(kit.op), kit.scratch))
349}
350
351pub(crate) fn scratch_pairs(
362 name: &str,
363 ref_starts: &[usize],
364 scratch: &[crate::ast::ScratchElem],
365 base: usize,
366) -> Vec<(usize, usize)> {
367 use crate::ast::ScratchElem;
368 let publishing: Vec<usize> = scratch
369 .iter()
370 .enumerate()
371 .filter(|(_, e)| {
372 !matches!(
373 e,
374 ScratchElem::Slots | ScratchElem::Kernels | ScratchElem::State
375 )
376 })
377 .map(|(k, _)| base + k)
378 .collect();
379 assert!(
380 publishing.len() <= ref_starts.len(),
381 "slot-op step '{name}' declares {} publishing scratch entries for {} Ref output ports",
382 publishing.len(),
383 ref_starts.len()
384 );
385 ref_starts.iter().copied().zip(publishing).collect()
386}
387
388pub(crate) fn ref_copy_kit(ty: PortType) -> Option<crate::ast::CompiledSlotKit> {
394 use crate::ast::ScratchBuf;
395 let elem = ty.scratch_elem()?;
396 Some(crate::ast::CompiledSlotKit {
397 scratch: vec![elem],
398 op: Box::new(
399 move |inputs: &[u64], outputs: &mut [u64], scratch: &mut [ScratchBuf]| {
400 let (p, n) = (inputs[0] as usize, inputs[1] as usize);
401 macro_rules! copy_into {
402 ($v:expr, $t:ty) => {{
403 $v.clear();
404 $v.extend_from_slice(unsafe {
408 std::slice::from_raw_parts(p as *const $t, n)
409 });
410 }};
411 }
412 match &mut scratch[0] {
413 ScratchBuf::Str(v) | ScratchBuf::Bytes(v) => copy_into!(v, u8),
414 ScratchBuf::F32(v) => copy_into!(v, f32),
415 ScratchBuf::F64(v) => copy_into!(v, f64),
416 ScratchBuf::F16(v) => copy_into!(v, half::f16),
417 ScratchBuf::I8(v) => copy_into!(v, i8),
418 ScratchBuf::I16(v) => copy_into!(v, i16),
419 ScratchBuf::I32(v) => copy_into!(v, i32),
420 ScratchBuf::I64(v) => copy_into!(v, i64),
421 ScratchBuf::Value(v) => {
422 v.clear();
423 if n > 0 {
424 v.push(unsafe { (*(p as *const crate::ast::Value)).clone() });
426 }
427 }
428 ScratchBuf::Slots(_) | ScratchBuf::Kernels(_) | ScratchBuf::State(_) => {
429 unreachable!("a copy owns only a value entry")
430 }
431 }
432 let (ptr, len) = scratch[0].ptr_len();
433 outputs[0] = ptr;
434 outputs[1] = len;
435 },
436 ),
437 })
438}
439
440pub(crate) fn identity_op(node: &dyn crate::ast::PolydatNode) -> Option<crate::ast::CompiledU64Op> {
446 let meta = node.meta();
447 if meta.name != "identity" || meta.outs.len() != 1 {
448 return None;
449 }
450 if meta.outs[0].typ.slot_color() == crate::ast::SlotColor::Ref2 {
451 return None;
452 }
453 Some(Box::new(|inputs: &[u64], outputs: &mut [u64]| {
454 outputs.copy_from_slice(inputs)
455 }))
456}
457
458impl SlotLayout {
459 fn input_slots(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
462 let mut slots = Vec::new();
463 for source in &resolved.wiring[node_idx] {
464 let (start, w) = match source {
465 WireSource::Input(c) => (
466 self.input_starts.get(*c).copied().unwrap_or(*c),
467 resolved
468 .input_defs
469 .get(*c)
470 .map(|d| d.port_type.slot_width())
471 .unwrap_or(1),
472 ),
473 WireSource::NodeOutput(u, p) => (
474 self.port_offsets[*u][*p],
475 resolved.nodes[*u].meta().outs[*p].typ.slot_width(),
476 ),
477 };
478 slots.extend(start..start + w);
479 }
480 slots
481 }
482
483 fn output_slots(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
485 let mut slots = Vec::new();
486 for (p, out) in resolved.nodes[node_idx].meta().outs.iter().enumerate() {
487 let start = self.port_offsets[node_idx][p];
488 slots.extend(start..start + out.typ.slot_width());
489 }
490 slots
491 }
492
493 fn named_outputs(&self, resolved: &ResolvedDag) -> HashMap<String, usize> {
495 resolved
496 .output_map
497 .iter()
498 .map(|(name, (n, p))| (name.clone(), self.port_offsets[*n][*p]))
499 .collect()
500 }
501
502 fn ref_slot_mask(&self, resolved: &ResolvedDag) -> Vec<bool> {
508 use crate::ast::SlotColor;
509 let mut mask = vec![false; self.total_slots];
510 let mut mark = |start: usize, color: SlotColor| match color {
511 SlotColor::Ref2 => {
512 mask[start] = true;
513 mask[start + 1] = true;
514 }
515 SlotColor::Imm1 | SlotColor::Imm2 => {}
516 };
517 for (i, d) in resolved.input_defs.iter().enumerate() {
518 mark(self.input_starts[i], d.port_type.slot_color());
519 }
520 for (n, node) in resolved.nodes.iter().enumerate() {
521 for (p, out) in node.meta().outs.iter().enumerate() {
522 mark(self.port_offsets[n][p], out.typ.slot_color());
523 }
524 }
525 mask
526 }
527
528 fn ref_output_starts(&self, resolved: &ResolvedDag, node_idx: usize) -> Vec<usize> {
532 resolved.nodes[node_idx]
533 .meta()
534 .outs
535 .iter()
536 .enumerate()
537 .filter(|(_, out)| out.typ.slot_color() == crate::ast::SlotColor::Ref2)
538 .map(|(p, _)| self.port_offsets[node_idx][p])
539 .collect()
540 }
541
542 fn expand_dependents(&self, resolved: &ResolvedDag, deps: &[Vec<usize>]) -> Vec<Vec<usize>> {
548 let mut out = Vec::with_capacity(self.coord_slots);
549 for (i, d) in resolved.input_defs.iter().enumerate() {
550 for _ in 0..d.port_type.slot_width() {
551 out.push(deps.get(i).cloned().unwrap_or_default());
552 }
553 }
554 out
555 }
556}
557
558pub struct PolydatAssembler {
560 input_defs: Vec<crate::kernel::InputDef>,
562 coord_count: usize,
564 nodes: Vec<PendingNode>,
565 output_order: Vec<String>,
567 outputs: HashMap<String, WireRef>,
568 source: String,
570 context: String,
572 output_modifiers: HashMap<String, crate::dsl::ast::BindingModifier>,
574 const_inits: Vec<crate::kernel::ConstInit>,
576 pub(crate) template: bool,
579 const_outputs: std::collections::HashSet<String>,
582 pub(crate) strict_values: bool,
587 pub(crate) strict_types: bool,
595 pub(crate) strict: bool,
600 input_variance: crate::dsl::compile::InputVariance,
603 pub(crate) jit_mode: Option<crate::compile::cone::JitMode>,
608 pub(crate) ledger: std::sync::Arc<crate::kernel::CompileLedger>,
612 cursor_schemas: Vec<crate::iteration::source::SourceSchema>,
616}
617
618type P2Layout = (
622 usize,
623 usize,
624 Vec<crate::compile::closures::P2Step>,
625 HashMap<String, usize>,
626 Vec<bool>,
627 crate::compile::closures::P2Extras,
628);
629
630#[cfg(feature = "jit")]
636type JitLayout = (
637 usize,
638 usize,
639 Vec<(crate::compile::jit::JitOp, Vec<usize>, Vec<usize>)>,
640 HashMap<String, usize>,
641 crate::compile::jit::ScratchPlan,
642 Vec<usize>,
643);
644
645impl PolydatAssembler {
646 pub fn new(input_names: Vec<String>) -> Self {
648 let coord_count = input_names.len();
649 let input_defs: Vec<crate::kernel::InputDef> = input_names
650 .into_iter()
651 .map(|name| crate::kernel::InputDef {
652 name,
653 default: crate::ast::Value::U64(0),
654 port_type: crate::ast::PortType::U64,
655 kind: crate::kernel::InputKind::Coordinate,
656 type_origin: crate::kernel::TypeOrigin::Inferred,
658 converts_to: None,
659 })
660 .collect();
661 Self {
662 input_defs,
663 coord_count,
664 nodes: Vec::new(),
665 output_order: Vec::new(),
666 outputs: HashMap::new(),
667 source: String::new(),
668 context: "(assembler)".into(),
669 output_modifiers: HashMap::new(),
670 const_inits: Vec::new(),
671 template: false,
672 const_outputs: std::collections::HashSet::new(),
673 strict_values: false,
674 strict_types: false,
675 strict: false,
676 input_variance: crate::dsl::compile::InputVariance::Fixed,
677 jit_mode: None,
678 cursor_schemas: Vec::new(),
679 ledger: crate::kernel::CompileLedger::new(),
680 }
681 }
682
683 pub fn set_cursor_schemas(&mut self, schemas: Vec<crate::iteration::source::SourceSchema>) {
688 self.cursor_schemas = schemas;
689 }
690
691 pub fn cursor_schemas(&self) -> &[crate::iteration::source::SourceSchema] {
693 &self.cursor_schemas
694 }
695
696 pub fn set_strict_wires(&mut self, strict_types: bool, strict_values: bool) {
700 self.strict_types = strict_types;
701 self.strict_values = strict_values;
702 }
703
704 pub fn set_strict(&mut self, strict: bool) {
710 self.strict = strict;
711 }
712
713 pub fn set_jit_mode(&mut self, mode: crate::compile::cone::JitMode) {
716 self.jit_mode = Some(mode);
717 }
718
719 pub fn set_context(&mut self, source: &str, context: &str) {
722 self.source = source.to_string();
723 self.context = context.to_string();
724 }
725
726 pub fn add_node(
728 &mut self,
729 name: impl Into<String>,
730 node: Box<dyn PolydatNode>,
731 inputs: Vec<WireRef>,
732 ) -> &mut Self {
733 self.nodes.push(PendingNode {
734 name: name.into(),
735 node,
736 inputs,
737 });
738 self
739 }
740
741 pub fn set_output_modifier(&mut self, name: &str, modifier: crate::dsl::ast::BindingModifier) {
743 if modifier != crate::dsl::ast::BindingModifier::NONE {
744 self.output_modifiers.insert(name.to_string(), modifier);
745 }
746 }
747
748 pub(crate) fn set_const_inits(&mut self, inits: Vec<crate::kernel::ConstInit>) {
751 self.const_inits = inits;
752 }
753
754 pub fn mark_const_output(&mut self, name: &str) {
759 self.const_outputs.insert(name.to_string());
760 }
761
762 pub fn node_count(&self) -> usize {
764 self.nodes.len()
765 }
766
767 pub fn add_output(&mut self, name: impl Into<String>, wire: WireRef) -> &mut Self {
769 let name = name.into();
770 if !self.outputs.contains_key(&name) {
771 self.output_order.push(name.clone());
772 }
773 self.outputs.insert(name, wire);
774 self
775 }
776
777 pub fn add_input(
788 &mut self,
789 name: impl Into<String>,
790 default: crate::ast::Value,
791 port_type: crate::ast::PortType,
792 kind: crate::kernel::InputKind,
793 ) -> &mut Self {
794 self.input_defs.push(crate::kernel::InputDef {
795 name: name.into(),
796 default,
797 port_type,
798 kind,
799 type_origin: crate::kernel::TypeOrigin::Declared,
800 converts_to: None,
801 });
802 self
803 }
804
805 pub fn set_input_type(&mut self, name: &str, port_type: crate::ast::PortType) {
810 if let Some(d) = self.input_defs.iter_mut().find(|d| d.name == name) {
811 d.port_type = port_type;
812 d.type_origin = crate::kernel::TypeOrigin::Declared;
813 }
814 }
815
816 pub fn set_input_origin(&mut self, name: &str, origin: crate::kernel::TypeOrigin) {
820 if let Some(d) = self.input_defs.iter_mut().find(|d| d.name == name) {
821 d.type_origin = origin;
822 }
823 }
824
825 pub fn set_input_variance(&mut self, variance: crate::dsl::compile::InputVariance) {
827 self.input_variance = variance;
828 }
829
830 pub fn input_names(&self) -> Vec<&str> {
832 self.input_defs.iter().map(|d| d.name.as_str()).collect()
833 }
834
835 pub fn node_output_type(&self, name: &str) -> Option<crate::ast::PortType> {
840 self.nodes
841 .iter()
842 .find(|n| n.name == name)
843 .and_then(|n| n.node.meta().outs.first())
844 .map(|p| p.typ)
845 }
846
847 pub fn output_names(&self) -> Vec<&str> {
849 self.outputs.keys().map(|s| s.as_str()).collect()
850 }
851
852 pub fn node_type_of(&self, name: &str) -> Option<String> {
854 self.nodes
855 .iter()
856 .find(|pn| pn.name == name)
857 .map(|pn| pn.node.meta().name.clone())
858 }
859
860 pub fn output_type(&self, name: &str) -> Option<PortType> {
864 self.nodes
865 .iter()
866 .find(|pn| pn.name == name)
867 .and_then(|pn| pn.node.meta().outs.first())
868 .map(|port| port.typ)
869 }
870
871 pub fn input_type(&self, name: &str) -> Option<PortType> {
873 self.input_defs
874 .iter()
875 .find(|d| d.name == name)
876 .map(|d| d.port_type)
877 }
878
879 pub fn wire_type(&self, wire: &WireRef) -> Option<PortType> {
884 match wire {
885 WireRef::Input(name) => self.input_type(name),
886 WireRef::Node(name, port_idx) => self
887 .nodes
888 .iter()
889 .find(|pn| &pn.name == name)
890 .and_then(|pn| pn.node.meta().outs.get(*port_idx))
891 .map(|p| p.typ),
892 }
893 }
894
895 pub fn compile(self) -> Result<PolydatKernel, AssemblyError> {
897 self.compile_with_log(None)
898 }
899
900 pub fn compile_with_log(
902 self,
903 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
904 ) -> Result<PolydatKernel, AssemblyError> {
905 let jit_mode = self.jit_mode.unwrap_or_default();
906 let strict = self.strict;
907 let template = self.template;
908 let mut resolved = self.resolve_with_log(log.as_deref_mut())?;
909 let (node_total, output_total) = (resolved.nodes.len(), resolved.output_order.len());
910 crate::compile::cone::extract_jit_cones(&mut resolved, jit_mode);
911 let _coord_names = resolved.input_names();
912 let modifiers = resolved.output_modifiers.clone();
913 let cursors = std::mem::take(&mut resolved.cursor_schemas);
914 let mut kernel = PolydatKernel::new_with_inputs(
915 resolved.nodes,
916 resolved.wiring,
917 resolved.input_defs,
918 resolved.coord_count,
919 resolved.output_map,
920 resolved.output_order,
921 resolved.const_outputs,
922 modifiers,
923 &resolved.source,
924 &resolved.context,
925 log.as_deref_mut(),
926 strict,
927 resolved.ledger.clone(),
928 )?;
929 if !cursors.is_empty() {
930 kernel.set_cursor_schemas(cursors);
931 }
932 kernel.set_cone_mode(jit_mode);
933 kernel.set_const_inits(resolved.const_inits);
934 Self::log_summary(log, node_total, output_total);
935 if template {
936 return Ok(kernel);
937 }
938 crate::kernel::Kernel::init(&mut kernel).map_err(|e| match e {
939 KernelError::ConstInit { name, reason } => AssemblyError::ConstInit { name, reason },
940 other => AssemblyError::ConstInit {
941 name: String::new(),
942 reason: other.to_string(),
943 },
944 })?;
945 Ok(kernel)
946 }
947
948 fn refuse_strict(resolved: &ResolvedDag) -> Result<(), AssemblyError> {
952 let classes = PolydatProgram::classify_lifecycle(
953 &resolved.nodes,
954 &resolved.wiring,
955 &resolved.input_defs,
956 &resolved.output_map,
957 &resolved.output_modifiers,
958 );
959 let is_init: Vec<bool> = classes
960 .lifecycle
961 .iter()
962 .map(|lc| *lc == crate::kernel::EvalLifecycle::CompileConst)
963 .collect();
964 match PolydatProgram::strict_violation(
965 &resolved.nodes,
966 &resolved.wiring,
967 &is_init,
968 &resolved.output_map,
969 &resolved.output_modifiers,
970 ) {
971 Some(violation) => Err(AssemblyError::Other(violation)),
972 None => Ok(()),
973 }
974 }
975
976 fn refused_by_closures(reason: String) -> KernelError {
979 KernelError::Refused {
980 engine: Engine::Closures(Provenance::Auto),
981 reason,
982 }
983 }
984
985 fn refused_by_native(reason: String) -> KernelError {
988 KernelError::Refused {
989 engine: Engine::Native(Provenance::Auto),
990 reason,
991 }
992 }
993
994 #[cfg_attr(not(feature = "jit"), allow(dead_code))]
999 fn refused_by_pure_native(reason: String) -> KernelError {
1000 KernelError::Refused {
1001 engine: Engine::PureNative(Provenance::Auto),
1002 reason,
1003 }
1004 }
1005
1006 fn build_p2_layout(resolved: &ResolvedDag) -> Result<P2Layout, String> {
1009 let layout = slot_layout(resolved);
1010
1011 let mut compiled_ops = Vec::with_capacity(resolved.nodes.len());
1012 let mut extras = crate::compile::closures::P2Extras::default();
1013 for (node_idx, node) in resolved.nodes.iter().enumerate() {
1014 compiled_ops.push(
1015 node_step_op(node.as_ref(), &wire_types_of(resolved, node_idx)).ok_or_else(
1016 || {
1017 format!(
1018 "node '{}' has no compiled form (docs/design/engines.md §8)",
1019 node.meta().name
1020 )
1021 },
1022 )?,
1023 );
1024 }
1025 extras.externs = crate::compile::externs::Externs::new(
1026 &resolved.input_defs,
1027 resolved.coord_count,
1028 &layout.input_starts,
1029 &resolved.cursor_schemas,
1030 &shared_outputs_of(resolved),
1031 resolved.ledger.clone(),
1032 )?;
1033 extras.externs.set_output_names(&resolved.output_order);
1034 extras
1035 .externs
1036 .set_output_modifiers(&resolved.output_modifiers);
1037 extras.externs.set_const_inits(&resolved.const_inits);
1038 extras.externs.set_fixed_outputs(fixed_outputs(resolved));
1039 extras.output_types = resolved
1040 .output_map
1041 .iter()
1042 .map(|(name, (n, p))| (name.clone(), resolved.nodes[*n].meta().outs[*p].typ))
1043 .collect();
1044
1045 let classes = PolydatProgram::classify_lifecycle(
1049 &resolved.nodes,
1050 &resolved.wiring,
1051 &resolved.input_defs,
1052 &resolved.output_map,
1053 &resolved.output_modifiers,
1054 );
1055 let inventory = PolydatProgram::compute_node_inventory(&resolved.nodes, &resolved.wiring);
1056 let per_input = PolydatProgram::compute_dependents(
1057 &inventory.input_provenance,
1058 resolved.input_defs.len(),
1059 );
1060 extras.input_dependents = layout.expand_dependents(resolved, &per_input);
1061 extras.attribution = std::sync::Arc::new(Self::attribution_of(resolved));
1062
1063 let mut steps = Vec::with_capacity(resolved.nodes.len());
1064 for (node_idx, (op, scratch)) in compiled_ops.into_iter().enumerate() {
1065 steps.push(crate::compile::closures::P2Step {
1066 name: resolved.nodes[node_idx].meta().name.clone(),
1067 op,
1068 input_slots: layout.input_slots(resolved, node_idx),
1069 output_slots: layout.output_slots(resolved, node_idx),
1070 ref_output_starts: layout.ref_output_starts(resolved, node_idx),
1071 scratch,
1072 accepts_none: resolved.nodes[node_idx].accepts_none_inputs(),
1073 volatile: classes.nondeterministic[node_idx],
1074 constant: classes.lifecycle[node_idx] == crate::kernel::EvalLifecycle::CompileConst,
1075 side: matches!(
1076 resolved.nodes[node_idx].purity(),
1077 crate::ast::Purity::SideChannel { .. }
1078 ),
1079 });
1080 }
1081 let output_map = layout.named_outputs(resolved);
1082 let ref_slots = layout.ref_slot_mask(resolved);
1083
1084 Ok((
1085 layout.coord_slots,
1086 layout.total_slots,
1087 steps,
1088 output_map,
1089 ref_slots,
1090 extras,
1091 ))
1092 }
1093
1094 #[cfg(feature = "jit")]
1096 pub(crate) fn build_jit_layout(resolved: &ResolvedDag) -> Result<JitLayout, String> {
1097 let layout = slot_layout(resolved);
1098
1099 let mut scratch = crate::compile::jit::ScratchPlan::default();
1103 let mut jit_steps = Vec::new();
1104 for (node_idx, node) in resolved.nodes.iter().enumerate() {
1105 let mut jit_op = crate::compile::jit::classify_node_typed(
1106 node.as_ref(),
1107 &wire_types_of(resolved, node_idx),
1108 );
1109 if matches!(jit_op, crate::compile::jit::JitOp::Fallback) {
1110 return Err(format!(
1111 "node '{}' has no native form and no kit; pure native code cannot run it",
1112 node.meta().name
1113 ));
1114 }
1115 let base = scratch.elems.len();
1116 jit_op.place_scratch(base);
1117 let elems = jit_op.scratch_elems().to_vec();
1118 scratch.refs.extend(scratch_pairs(
1119 &node.meta().name,
1120 &layout.ref_output_starts(resolved, node_idx),
1121 &elems,
1122 base,
1123 ));
1124 scratch.elems.extend(elems);
1125 jit_steps.push((
1126 jit_op,
1127 layout.input_slots(resolved, node_idx),
1128 layout.output_slots(resolved, node_idx),
1129 ));
1130 }
1131
1132 let output_map = layout.named_outputs(resolved);
1133 let classes = PolydatProgram::classify_lifecycle(
1137 &resolved.nodes,
1138 &resolved.wiring,
1139 &resolved.input_defs,
1140 &resolved.output_map,
1141 &resolved.output_modifiers,
1142 );
1143 let volatile: Vec<usize> = (0..resolved.nodes.len())
1144 .filter(|&i| classes.nondeterministic[i])
1145 .collect();
1146 Ok((
1147 layout.coord_slots,
1148 layout.total_slots,
1149 jit_steps,
1150 output_map,
1151 scratch,
1152 volatile,
1153 ))
1154 }
1155
1156 #[cfg(feature = "jit")]
1159 fn jit_slot_info(resolved: &ResolvedDag) -> (Vec<bool>, HashMap<String, PortType>) {
1160 let layout = slot_layout(resolved);
1161 let guard = layout.ref_slot_mask(resolved);
1162 let types = resolved
1163 .output_map
1164 .iter()
1165 .map(|(name, (n, p))| (name.clone(), resolved.nodes[*n].meta().outs[*p].typ))
1166 .collect();
1167 (guard, types)
1168 }
1169
1170 #[cfg(feature = "jit")]
1171 fn jit_push_pull_from(
1172 resolved: ResolvedDag,
1173 ) -> Result<crate::compile::jit::JitKernelPushPull, KernelError> {
1174 let _coord_names = resolved.input_names();
1175 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1176 Self::build_jit_layout(&resolved).map_err(Self::refused_by_pure_native)?;
1177 let (guard, types) = Self::jit_slot_info(&resolved);
1178 let deps = slot_layout(&resolved).expand_dependents(
1179 &resolved,
1180 &PolydatProgram::compute_dependents(
1181 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
1182 resolved.input_defs.len(),
1183 ),
1184 );
1185 let externs = Self::externs_of(&resolved).map_err(Self::refused_by_pure_native)?;
1186 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1187 let (folded, origin) = Self::constant_steps(&resolved, &jit_steps);
1188 let alone = Self::side_channels(&resolved);
1189 let mut k = crate::compile::jit::compile_jit_push_pull(
1190 coord_count,
1191 total_slots,
1192 jit_steps,
1193 output_map,
1194 resolved.nodes,
1195 deps,
1196 externs,
1197 scratch,
1198 volatile,
1199 alone,
1200 )
1201 .map_err(Self::refused_by_pure_native)?;
1202 k.set_slot_info(guard, types);
1203 k.set_attribution(attribution);
1204 k.fold_constants(&folded, &origin, total_slots)?;
1207 Ok(k)
1208 }
1209
1210 #[cfg(feature = "jit")]
1218 #[allow(clippy::type_complexity)]
1219 fn constant_steps(
1220 resolved: &ResolvedDag,
1221 jit_steps: &[(crate::compile::jit::JitOp, Vec<usize>, Vec<usize>)],
1222 ) -> (
1223 Vec<(crate::compile::jit::JitOp, Vec<usize>, Vec<usize>)>,
1224 Vec<usize>,
1225 ) {
1226 let classes = PolydatProgram::classify_lifecycle(
1227 &resolved.nodes,
1228 &resolved.wiring,
1229 &resolved.input_defs,
1230 &resolved.output_map,
1231 &resolved.output_modifiers,
1232 );
1233 jit_steps
1236 .iter()
1237 .enumerate()
1238 .filter(|(i, _)| {
1239 classes.lifecycle.get(*i) == Some(&crate::kernel::EvalLifecycle::CompileConst)
1240 })
1241 .map(|(i, s)| (s.clone(), i))
1242 .unzip()
1243 }
1244
1245 #[cfg(feature = "jit")]
1249 fn side_channels(resolved: &ResolvedDag) -> Vec<bool> {
1250 resolved
1251 .nodes
1252 .iter()
1253 .map(|n| matches!(n.purity(), crate::ast::Purity::SideChannel { .. }))
1254 .collect()
1255 }
1256
1257 fn externs_of(resolved: &ResolvedDag) -> Result<crate::compile::externs::Externs, String> {
1260 let layout = slot_layout(resolved);
1261 let mut externs = crate::compile::externs::Externs::new(
1262 &resolved.input_defs,
1263 resolved.coord_count,
1264 &layout.input_starts,
1265 &resolved.cursor_schemas,
1266 &shared_outputs_of(resolved),
1267 resolved.ledger.clone(),
1268 )?;
1269 externs.set_output_names(&resolved.output_order);
1270 externs.set_output_modifiers(&resolved.output_modifiers);
1271 externs.set_const_inits(&resolved.const_inits);
1272 externs.set_fixed_outputs(fixed_outputs(resolved));
1273 Ok(externs)
1274 }
1275
1276 #[doc(hidden)]
1278 #[cfg(feature = "jit")]
1279 pub(crate) fn try_compile_pure_jit_raw(
1280 self,
1281 ) -> Result<crate::compile::jit::JitKernelRaw, KernelError> {
1282 let resolved = self.resolve().map_err(KernelError::Assembly)?;
1283 Self::jit_raw_from(resolved)
1284 }
1285
1286 #[cfg(feature = "bench-tiers")]
1311 pub fn compile_closures_raw(
1312 self,
1313 ) -> Result<crate::compile::closures::CompiledKernelRaw, KernelError> {
1314 let resolved = self.resolve_with_log(None)?;
1315 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
1316 Self::build_p2_layout(&resolved).map_err(Self::refused_by_closures)?;
1317 crate::compile::closures::CompiledKernelRaw::new(
1318 coord_count,
1319 total_slots,
1320 steps,
1321 output_map,
1322 ref_slots,
1323 extras,
1324 )
1325 }
1326
1327 #[cfg(all(feature = "bench-tiers", feature = "jit"))]
1329 pub fn compile_native_raw(
1330 self,
1331 ) -> Result<crate::compile::hybrid::HybridKernelRaw, KernelError> {
1332 let resolved = self.resolve_with_log(None)?;
1333 Ok(Self::hybrid_from(resolved)?.into_raw())
1334 }
1335
1336 #[cfg(all(feature = "bench-tiers", feature = "jit"))]
1338 pub fn compile_pure_native_raw(self) -> Result<crate::compile::jit::JitKernelRaw, KernelError> {
1339 self.try_compile_pure_jit_raw()
1340 }
1341
1342 pub(crate) fn attribution_of(resolved: &ResolvedDag) -> crate::compile::Attribution {
1347 let layout = slot_layout(resolved);
1348 let sites = resolved
1349 .nodes
1350 .iter()
1351 .enumerate()
1352 .map(|(node_idx, node)| {
1353 let mut outputs: Vec<String> = resolved
1354 .output_map
1355 .iter()
1356 .filter(|(_, (n, _))| *n == node_idx)
1357 .map(|(name, _)| name.clone())
1358 .collect();
1359 outputs.sort();
1360 let inputs = resolved.wiring[node_idx]
1361 .iter()
1362 .map(|source| match source {
1363 WireSource::Input(c) => (
1364 layout.input_starts.get(*c).copied().unwrap_or(*c),
1365 resolved
1366 .input_defs
1367 .get(*c)
1368 .map(|d| d.port_type)
1369 .unwrap_or(PortType::U64),
1370 ),
1371 WireSource::NodeOutput(u, p) => (
1372 layout.port_offsets[*u][*p],
1373 resolved.nodes[*u].meta().outs[*p].typ,
1374 ),
1375 })
1376 .collect();
1377 crate::compile::NodeSite {
1378 name: node.meta().name.to_string(),
1379 outputs,
1380 inputs,
1381 }
1382 })
1383 .collect();
1384 crate::compile::Attribution {
1385 sites,
1386 context: resolved.context.clone(),
1387 }
1388 }
1389
1390 #[cfg(feature = "jit")]
1391 fn jit_raw_from(
1392 resolved: ResolvedDag,
1393 ) -> Result<crate::compile::jit::JitKernelRaw, KernelError> {
1394 let _coord_names = resolved.input_names();
1395 let (coord_count, total_slots, jit_steps, output_map, scratch, volatile) =
1396 Self::build_jit_layout(&resolved).map_err(Self::refused_by_pure_native)?;
1397 let (guard, types) = Self::jit_slot_info(&resolved);
1398 let externs = Self::externs_of(&resolved).map_err(Self::refused_by_pure_native)?;
1399 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1400 let (folded, origin) = Self::constant_steps(&resolved, &jit_steps);
1401 let alone = Self::side_channels(&resolved);
1402 let mut k = crate::compile::jit::compile_jit_raw_with(
1403 coord_count,
1404 total_slots,
1405 jit_steps,
1406 output_map,
1407 resolved.nodes,
1408 externs,
1409 scratch,
1410 volatile,
1411 alone,
1412 )
1413 .map_err(Self::refused_by_pure_native)?;
1414 k.set_slot_info(guard, types);
1415 k.set_attribution(attribution);
1416 k.fold_constants(&folded, &origin, total_slots)?;
1419 Ok(k)
1420 }
1421
1422 #[cfg(feature = "jit")]
1428 #[doc(hidden)]
1429 pub fn try_compile_tier1_simd_ordinal(
1430 self,
1431 driving_input: &str,
1432 output: &str,
1433 ) -> Result<
1434 crate::compile::simd_tier1::Tier1SimdExecutor,
1435 crate::compile::simd_tier1::Tier1SimdError,
1436 > {
1437 let resolved = self.resolve().map_err(|error| {
1438 crate::compile::simd_tier1::Tier1SimdError::VectorGraphBuild(error.to_string())
1439 })?;
1440 crate::compile::simd_tier1::compile_tier1_ordinal(resolved, driving_input, output)
1441 }
1442
1443 fn hybrid_from(
1444 resolved: ResolvedDag,
1445 ) -> Result<crate::compile::hybrid::HybridKernel, KernelError> {
1446 let _coord_names = resolved.input_names();
1447 let layout = slot_layout(&resolved);
1448
1449 let output_map = layout.named_outputs(&resolved);
1450 let input_widths: Vec<usize> = resolved
1451 .input_defs
1452 .iter()
1453 .map(|d| d.port_type.slot_width())
1454 .collect();
1455
1456 let ref_slots = layout.ref_slot_mask(&resolved);
1457 let input_types: Vec<PortType> = resolved.input_defs.iter().map(|d| d.port_type).collect();
1458 let externs = Self::externs_of(&resolved).map_err(Self::refused_by_native)?;
1459 let attribution = std::sync::Arc::new(Self::attribution_of(&resolved));
1460 let classes = PolydatProgram::classify_lifecycle(
1463 &resolved.nodes,
1464 &resolved.wiring,
1465 &resolved.input_defs,
1466 &resolved.output_map,
1467 &resolved.output_modifiers,
1468 );
1469 let constant: Vec<bool> = classes
1470 .lifecycle
1471 .iter()
1472 .map(|lc| *lc == crate::kernel::EvalLifecycle::CompileConst)
1473 .collect();
1474 let mut kernel = crate::compile::hybrid::build_hybrid(
1475 &resolved.nodes,
1476 &resolved.wiring,
1477 layout.coord_slots,
1478 layout.total_slots,
1479 &layout.port_offsets,
1480 &layout.input_starts,
1481 &input_widths,
1482 output_map,
1483 ref_slots,
1484 &input_types,
1485 externs,
1486 constant,
1487 classes.nondeterministic,
1488 attribution,
1489 )?;
1490 kernel.retain_nodes(resolved.nodes);
1491 Ok(kernel)
1492 }
1493
1494 fn log_forms(resolved: &ResolvedDag, log: &mut crate::dsl::events::CompileEventLog) {
1502 for (node_idx, node) in resolved.nodes.iter().enumerate() {
1503 let wire_types = wire_types_of(resolved, node_idx);
1504 #[cfg(feature = "jit")]
1508 let native = !matches!(
1509 crate::compile::jit::classify_node_typed(node.as_ref(), &wire_types),
1510 crate::compile::jit::JitOp::Fallback
1511 );
1512 #[cfg(not(feature = "jit"))]
1513 let native = false;
1514 let level = if native {
1515 "native"
1516 } else {
1517 match node_step_op(node.as_ref(), &wire_types) {
1518 Some((crate::compile::closures::StepOp::Copy, _)) => "slot copy",
1519 Some((crate::compile::closures::StepOp::U64(_), _)) => "compiled u64 op",
1520 Some((crate::compile::closures::StepOp::Slot(_), _)) => "slot kit",
1521 None => "interpreted",
1522 }
1523 };
1524 let name = resolved
1525 .output_map
1526 .iter()
1527 .find(|(_, (ni, _))| *ni == node_idx)
1528 .map(|(n, _)| n.clone())
1529 .unwrap_or_else(|| node.meta().name.clone());
1530 log.push(crate::dsl::events::CompileEvent::CompileLevelSelected {
1531 node: name,
1532 level: level.to_string(),
1533 });
1534 }
1535 }
1536
1537 fn log_summary(
1542 log: Option<&mut crate::dsl::events::CompileEventLog>,
1543 nodes: usize,
1544 outputs: usize,
1545 ) {
1546 if let Some(log) = log {
1547 let constants_folded = log
1548 .events()
1549 .iter()
1550 .filter(|e| matches!(e, crate::dsl::events::CompileEvent::ConstantFolded { .. }))
1551 .count();
1552 log.push(crate::dsl::events::CompileEvent::Summary {
1553 nodes,
1554 outputs,
1555 constants_folded,
1556 });
1557 }
1558 }
1559
1560 #[cfg(feature = "jit")]
1563 fn resolve(self) -> Result<ResolvedDag, AssemblyError> {
1564 self.resolve_with_log(None)
1565 }
1566
1567 fn resolve_with_log(
1568 mut self,
1569 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
1570 ) -> Result<ResolvedDag, AssemblyError> {
1571 let open: Vec<usize> = self
1578 .input_defs
1579 .iter()
1580 .enumerate()
1581 .filter(|(_, d)| {
1582 d.kind != crate::kernel::InputKind::Coordinate
1583 && d.type_origin == crate::kernel::TypeOrigin::Inferred
1584 && d.port_type != PortType::Dyn
1585 })
1586 .map(|(i, _)| i)
1587 .collect();
1588 let variance_level = match self.input_variance {
1589 crate::dsl::compile::InputVariance::Fixed => None,
1590 crate::dsl::compile::InputVariance::Error => {
1591 if !open.is_empty() {
1592 return Err(AssemblyError::OpenInputs(
1593 open.iter()
1594 .map(|&i| {
1595 (
1596 self.input_defs[i].name.clone(),
1597 self.input_defs[i].port_type,
1598 )
1599 })
1600 .collect(),
1601 ));
1602 }
1603 None
1604 }
1605 crate::dsl::compile::InputVariance::Warn => {
1606 Some(crate::dsl::events::EventLevel::Warning)
1607 }
1608 crate::dsl::compile::InputVariance::Info => Some(crate::dsl::events::EventLevel::Info),
1609 };
1610 if variance_level.is_some() {
1611 for &i in &open {
1612 let def = &mut self.input_defs[i];
1613 def.converts_to = Some(def.port_type);
1614 def.port_type = PortType::Dyn;
1615 }
1616 }
1617 if let Some(log) = log.as_deref_mut() {
1623 let cursor_slot = |name: &str| {
1624 self.cursor_schemas
1625 .iter()
1626 .any(|s| name.starts_with(&format!("{}__cursor", s.name)))
1627 };
1628 for def in &self.input_defs {
1629 if matches!(
1630 def.kind,
1631 crate::kernel::InputKind::ExternalWrite
1632 | crate::kernel::InputKind::IterationExtern
1633 ) && def.default == crate::ast::Value::None
1634 && !cursor_slot(&def.name)
1635 {
1636 log.push(crate::dsl::events::CompileEvent::ExternWithoutDefault {
1637 name: def.name.clone(),
1638 port_type: def.port_type.to_string(),
1639 });
1640 }
1641 }
1642 }
1643 let mut name_to_idx: HashMap<String, usize> = HashMap::new();
1645 for (i, pn) in self.nodes.iter().enumerate() {
1646 if name_to_idx.contains_key(&pn.name) {
1647 return Err(AssemblyError::DuplicateNode(pn.name.clone()));
1648 }
1649 name_to_idx.insert(pn.name.clone(), i);
1650 }
1651
1652 let input_to_idx: HashMap<String, usize> = self
1654 .input_defs
1655 .iter()
1656 .enumerate()
1657 .map(|(i, d)| (d.name.clone(), i))
1658 .collect();
1659
1660 for pn in &self.nodes {
1662 let expected = pn.node.meta().wire_inputs().len();
1663 let got = pn.inputs.len();
1664 if expected != got {
1665 return Err(AssemblyError::ArityMismatch {
1666 node_name: pn.name.clone(),
1667 expected,
1668 got,
1669 });
1670 }
1671 }
1672
1673 let mut all_nodes: Vec<PendingNode> = Vec::new();
1674 let mut all_name_to_idx: HashMap<String, usize> = HashMap::new();
1675 let mut adapter_count = 0usize;
1676 let mut assertion_count = 0usize;
1677 let strict_values = self.strict_values;
1678 let strict_types = self.strict_types;
1679 let strict = self.strict;
1680
1681 for pn in self.nodes {
1682 let idx = all_nodes.len();
1683 all_name_to_idx.insert(pn.name.clone(), idx);
1684 all_nodes.push(pn);
1685 }
1686
1687 let mut resolved_wiring: Vec<Vec<WireSource>> = Vec::new();
1688 let mut converters: HashMap<(usize, PortType), usize> = HashMap::new();
1690
1691 for node_idx in 0..all_nodes.len() {
1692 let mut node_wiring = Vec::new();
1693
1694 for (port_idx, wire_ref) in all_nodes[node_idx].inputs.clone().iter().enumerate() {
1695 let port = all_nodes[node_idx].node.meta().wire_inputs()[port_idx].clone();
1696 let expected_type = port.typ;
1697
1698 let (source, source_type) = match wire_ref {
1699 WireRef::Input(name) => {
1700 let input_idx = input_to_idx
1701 .get(name)
1702 .ok_or_else(|| AssemblyError::UnknownWire(name.clone()))?;
1703 let source_type = self.input_defs[*input_idx].port_type;
1704 (WireSource::Input(*input_idx), source_type)
1705 }
1706 WireRef::Node(name, out_port) => {
1707 let src_idx = all_name_to_idx
1708 .get(name)
1709 .ok_or_else(|| AssemblyError::UnknownWire(name.clone()))?;
1710 let src_type = all_nodes[*src_idx].node.meta().outs[*out_port].typ;
1711 (WireSource::NodeOutput(*src_idx, *out_port), src_type)
1712 }
1713 };
1714
1715 if source_type == PortType::Dyn
1727 && !port.accepts_any_type
1728 && expected_type != PortType::Dyn
1729 {
1730 let WireSource::Input(input_idx) = source else {
1731 unreachable!("only an input slot is typed `Dyn`")
1732 };
1733 let conv_idx = match converters.get(&(input_idx, expected_type)) {
1734 Some(&idx) => idx,
1735 None => {
1736 let converter = crate::convert::InputConverter::new(
1737 &self.input_defs[input_idx].name,
1738 expected_type,
1739 );
1740 let conv_name = converter.meta().name.clone();
1741 let idx = all_nodes.len();
1742 all_name_to_idx.insert(conv_name.clone(), idx);
1743 while resolved_wiring.len() <= idx {
1744 resolved_wiring.push(Vec::new());
1745 }
1746 resolved_wiring[idx] = vec![source.clone()];
1747 all_nodes.push(PendingNode {
1748 name: conv_name,
1749 node: Box::new(converter),
1750 inputs: vec![],
1751 });
1752 converters.insert((input_idx, expected_type), idx);
1753 idx
1754 }
1755 };
1756 node_wiring.push(WireSource::NodeOutput(conv_idx, 0));
1757 } else if port.accepts_any_type || source_type == expected_type {
1758 node_wiring.push(source);
1759 } else if let Some(adapter) = auto_adapter(source_type, expected_type) {
1760 if strict {
1761 return Err(AssemblyError::Other(format!(
1762 "strict mode: implicit type coercion {source_type} → {expected_type} \
1763 into '{}'. Use an explicit conversion function (e.g., to_f64, \
1764 to_i64, f64_to_u64).",
1765 all_nodes[node_idx].name
1766 )));
1767 }
1768 let adapter_name = format!("__adapt_{adapter_count}");
1769 adapter_count += 1;
1770 let adapter_idx = all_nodes.len();
1771
1772 if let Some(ref mut log) = log {
1773 let from_name = match wire_ref {
1774 WireRef::Input(n) => n.clone(),
1775 WireRef::Node(n, _) => n.clone(),
1776 };
1777 let to_name = all_nodes[node_idx].name.clone();
1778 log.push(if is_lossless_widening(source_type, expected_type) {
1779 crate::dsl::events::CompileEvent::TypeWidening {
1780 from: source_type.to_keyword(),
1781 to: expected_type.to_keyword(),
1782 context: format!("{from_name} → {to_name}"),
1783 }
1784 } else {
1785 crate::dsl::events::CompileEvent::TypeAdapterInserted {
1786 from_node: from_name,
1787 to_node: to_name,
1788 adapter: format!("{source_type:?}→{expected_type:?}"),
1789 }
1790 });
1791 }
1792
1793 all_name_to_idx.insert(adapter_name.clone(), adapter_idx);
1794
1795 let adapter_wiring = vec![source];
1796 while resolved_wiring.len() <= adapter_idx {
1797 resolved_wiring.push(Vec::new());
1798 }
1799 resolved_wiring[adapter_idx] = adapter_wiring;
1800
1801 all_nodes.push(PendingNode {
1802 name: adapter_name,
1803 node: adapter,
1804 inputs: vec![],
1805 });
1806
1807 node_wiring.push(WireSource::NodeOutput(adapter_idx, 0));
1808 } else {
1809 let from_name = match wire_ref {
1810 WireRef::Input(n) => n.clone(),
1811 WireRef::Node(n, _) => n.clone(),
1812 };
1813 return Err(AssemblyError::TypeMismatch {
1814 from_node: from_name,
1815 from_port: match wire_ref {
1816 WireRef::Input(_) => 0,
1817 WireRef::Node(_, p) => *p,
1818 },
1819 from_type: source_type,
1820 to_node: all_nodes[node_idx].name.clone(),
1821 to_port: port_idx,
1822 to_type: expected_type,
1823 });
1824 }
1825
1826 let sink_port = &all_nodes[node_idx].node.meta().wire_inputs()[port_idx];
1839 if let Some(constraint) = sink_port.constraint {
1840 let last_source = node_wiring.last().expect("wire just pushed").clone();
1841 if strict_values
1842 && !value_constraint_proven(&all_nodes, &last_source, &constraint)
1843 {
1844 let assert_name = format!("__assert_v_{assertion_count}");
1845 assertion_count += 1;
1846 let assert_idx = all_nodes.len();
1847
1848 if let Some(ref mut log) = log {
1849 let from_name = match wire_ref {
1850 WireRef::Input(n) => n.clone(),
1851 WireRef::Node(n, _) => n.clone(),
1852 };
1853 log.push(crate::dsl::events::CompileEvent::AssertionInserted {
1854 from_node: from_name,
1855 to_node: all_nodes[node_idx].name.clone(),
1856 kind: format!("{:?} value-assert {:?}", expected_type, constraint),
1857 });
1858 }
1859
1860 all_name_to_idx.insert(assert_name.clone(), assert_idx);
1861 let assert_wiring = vec![last_source];
1862 while resolved_wiring.len() <= assert_idx {
1863 resolved_wiring.push(Vec::new());
1864 }
1865 resolved_wiring[assert_idx] = assert_wiring;
1866
1867 all_nodes.push(PendingNode {
1868 name: assert_name,
1869 node: crate::library::assertions::assert_value_node(
1870 expected_type,
1871 constraint,
1872 ),
1873 inputs: vec![],
1874 });
1875
1876 *node_wiring.last_mut().unwrap() = WireSource::NodeOutput(assert_idx, 0);
1879 } else if let Some(ref mut log) = log {
1880 let from_name = match wire_ref {
1881 WireRef::Input(n) => n.clone(),
1882 WireRef::Node(n, _) => n.clone(),
1883 };
1884 log.push(crate::dsl::events::CompileEvent::AssertionSkipped {
1885 from_node: from_name,
1886 to_node: all_nodes[node_idx].name.clone(),
1887 reason: assertion_skip_reason(
1888 strict_values,
1889 &all_nodes,
1890 &last_source,
1891 &constraint,
1892 ),
1893 });
1894 }
1895 } else if strict_types && source_type != expected_type {
1896 }
1904 }
1905
1906 while resolved_wiring.len() <= node_idx {
1907 resolved_wiring.push(Vec::new());
1908 }
1909 resolved_wiring[node_idx] = node_wiring;
1910 }
1911
1912 while resolved_wiring.len() < all_nodes.len() {
1913 resolved_wiring.push(Vec::new());
1914 }
1915
1916 {
1921 let rules = crate::compile::fusion::default_rules();
1922 if !rules.is_empty() {
1923 let mut output_nodes: Vec<usize> = Vec::new();
1926 for wire_ref in self.outputs.values() {
1927 if let WireRef::Node(node_name, _) = wire_ref
1928 && let Some(&idx) = all_name_to_idx.get(node_name)
1929 {
1930 output_nodes.push(idx);
1931 }
1932 }
1933
1934 let mut opt_nodes: Vec<Option<Box<dyn PolydatNode>>> =
1936 all_nodes.into_iter().map(|pn| Some(pn.node)).collect();
1937
1938 let fused_count = crate::compile::fusion::apply_fusions(
1939 &mut opt_nodes,
1940 &mut resolved_wiring,
1941 &mut all_name_to_idx,
1942 &rules,
1943 &output_nodes,
1944 );
1945 if fused_count > 0
1946 && let Some(ref mut log) = log
1947 {
1948 log.push(crate::dsl::events::CompileEvent::FusionApplied {
1949 pattern: "subgraph".into(),
1950 nodes_replaced: fused_count,
1951 });
1952 }
1953
1954 all_nodes = opt_nodes
1957 .into_iter()
1958 .enumerate()
1959 .map(|(i, opt)| PendingNode {
1960 name: all_name_to_idx
1961 .iter()
1962 .find(|&(_, &idx)| idx == i)
1963 .map(|(n, _)| n.clone())
1964 .unwrap_or_else(|| format!("__removed_{i}")),
1965 node: opt.unwrap_or_else(|| {
1966 Box::new(crate::library::identity::Identity::new(
1967 crate::ast::PortType::U64,
1968 ))
1969 }),
1970 inputs: vec![], })
1972 .collect();
1973 }
1974 }
1975
1976 let node_count = all_nodes.len();
1983 let mut reachable = vec![false; node_count];
1984 {
1985 let mut worklist: Vec<usize> = Vec::new();
1986 for wire_ref in self.outputs.values() {
1988 if let WireRef::Node(node_name, _) = wire_ref
1989 && let Some(&idx) = all_name_to_idx.get(node_name)
1990 {
1991 worklist.push(idx);
1992 }
1993 }
1994 for (idx, pn) in all_nodes.iter().enumerate() {
2004 if matches!(
2005 pn.node.meta().name.as_str(),
2006 "log_debug" | "log_info" | "log_warn" | "log_error"
2007 ) {
2008 worklist.push(idx);
2009 }
2010 }
2011 while let Some(idx) = worklist.pop() {
2013 if reachable[idx] {
2014 continue;
2015 }
2016 reachable[idx] = true;
2017 for source in &resolved_wiring[idx] {
2018 if let WireSource::NodeOutput(upstream, _) = source
2019 && !reachable[*upstream]
2020 {
2021 worklist.push(*upstream);
2022 }
2023 }
2024 }
2025 }
2026 let live_count = reachable.iter().filter(|&&r| r).count();
2027
2028 if let Some(log) = log.as_deref_mut() {
2033 let mut placed: Vec<(usize, usize, PortType)> = converters
2034 .iter()
2035 .filter(|&(_, &idx)| reachable[idx])
2036 .map(|(&(input, to), &idx)| (idx, input, to))
2037 .collect();
2038 placed.sort_unstable_by_key(|&(idx, _, _)| idx);
2039 for &(idx, input, to) in &placed {
2040 let def = &self.input_defs[input];
2041 let (origin, level) = match (def.converts_to, variance_level) {
2042 (Some(_), Some(level)) => ("inferred", level),
2043 _ => ("declared dyn", crate::dsl::events::EventLevel::Info),
2044 };
2045 log.push(crate::dsl::events::CompileEvent::InputConverterInserted {
2046 input: def.name.clone(),
2047 to: to.to_keyword().to_string(),
2048 node: all_nodes[idx].name.clone(),
2049 origin: origin.to_string(),
2050 level,
2051 });
2052 }
2053 if let Some(level) = variance_level {
2054 for &input in &open {
2055 if placed.iter().any(|&(_, i, _)| i == input) {
2056 continue;
2057 }
2058 let def = &self.input_defs[input];
2059 log.push(crate::dsl::events::CompileEvent::InputConverterInserted {
2060 input: def.name.clone(),
2061 to: def
2062 .converts_to
2063 .unwrap_or(def.port_type)
2064 .to_keyword()
2065 .to_string(),
2066 node: "(none: nothing reads it)".to_string(),
2067 origin: "inferred".to_string(),
2068 level,
2069 });
2070 }
2071 }
2072 }
2073
2074 let mut in_degree = vec![0usize; node_count];
2076 let mut dependents: Vec<Vec<usize>> = vec![Vec::new(); node_count];
2077
2078 for (node_idx, wiring) in resolved_wiring.iter().enumerate() {
2079 if !reachable[node_idx] {
2080 continue;
2081 }
2082 for source in wiring {
2083 if let WireSource::NodeOutput(upstream, _) = source {
2084 in_degree[node_idx] += 1;
2085 dependents[*upstream].push(node_idx);
2086 }
2087 }
2088 }
2089
2090 let mut queue: Vec<usize> = (0..node_count)
2091 .filter(|i| reachable[*i] && in_degree[*i] == 0)
2092 .collect();
2093 let mut sorted_order: Vec<usize> = Vec::with_capacity(live_count);
2094
2095 while let Some(idx) = queue.pop() {
2096 sorted_order.push(idx);
2097 for &dep in &dependents[idx] {
2098 in_degree[dep] -= 1;
2099 if in_degree[dep] == 0 {
2100 queue.push(dep);
2101 }
2102 }
2103 }
2104
2105 if sorted_order.len() != live_count {
2106 return Err(AssemblyError::CycleDetected);
2107 }
2108
2109 let mut old_to_new = vec![0usize; node_count];
2110 for (new_idx, &old_idx) in sorted_order.iter().enumerate() {
2111 old_to_new[old_idx] = new_idx;
2112 }
2113
2114 let mut sorted_nodes: Vec<Option<Box<dyn PolydatNode>>> =
2115 all_nodes.into_iter().map(|pn| Some(pn.node)).collect();
2116
2117 let final_nodes: Vec<Box<dyn PolydatNode>> = sorted_order
2118 .iter()
2119 .map(|&old_idx| sorted_nodes[old_idx].take().unwrap())
2120 .collect();
2121
2122 let final_wiring: Vec<Vec<WireSource>> = sorted_order
2123 .iter()
2124 .map(|&old_idx| {
2125 resolved_wiring[old_idx]
2126 .iter()
2127 .map(|source| match source {
2128 WireSource::Input(c) => WireSource::Input(*c),
2129 WireSource::NodeOutput(old_up, port) => {
2130 WireSource::NodeOutput(old_to_new[*old_up], *port)
2131 }
2132 })
2133 .collect()
2134 })
2135 .collect();
2136
2137 let mut final_output_map: HashMap<String, (usize, usize)> = HashMap::new();
2138 for (name, wire_ref) in &self.outputs {
2139 match wire_ref {
2140 WireRef::Input(coord_name) => {
2141 return Err(AssemblyError::UnknownWire(format!(
2142 "output '{name}' references coordinate '{coord_name}' directly; \
2143 wire through a node instead"
2144 )));
2145 }
2146 WireRef::Node(node_name, port) => {
2147 let old_idx = all_name_to_idx
2148 .get(node_name)
2149 .ok_or_else(|| AssemblyError::UnknownWire(node_name.clone()))?;
2150 final_output_map.insert(name.clone(), (old_to_new[*old_idx], *port));
2151 }
2152 }
2153 }
2154
2155 for f in crate::compile::roundtrip_lint::lint_type_round_trips(
2162 &final_nodes,
2163 &final_wiring,
2164 &self.input_defs,
2165 ) {
2166 if strict_values {
2167 return Err(AssemblyError::Other(f.message()));
2168 }
2169 crate::library::support::audit::warn(&f.message());
2172 if let Some(ref mut log) = log {
2173 log.push(crate::dsl::events::CompileEvent::Warning {
2174 message: f.message(),
2175 });
2176 }
2177 }
2178
2179 if let Some(log) = log {
2180 let resolved_view = ResolvedDag {
2181 nodes: final_nodes,
2182 wiring: final_wiring,
2183 input_defs: self.input_defs,
2184 coord_count: self.coord_count,
2185 output_map: final_output_map,
2186 output_order: self.output_order,
2187 source: self.source,
2188 context: self.context,
2189 output_modifiers: self.output_modifiers,
2190 const_outputs: self.const_outputs,
2191 const_inits: self.const_inits,
2192 cursor_schemas: self.cursor_schemas,
2193 ledger: self.ledger,
2194 };
2195 Self::log_forms(&resolved_view, log);
2196 return Ok(resolved_view);
2197 }
2198 Ok(ResolvedDag {
2199 nodes: final_nodes,
2200 wiring: final_wiring,
2201 input_defs: self.input_defs,
2202 coord_count: self.coord_count,
2203 output_map: final_output_map,
2204 output_order: self.output_order,
2205 source: self.source,
2206 context: self.context,
2207 output_modifiers: self.output_modifiers,
2208 const_outputs: self.const_outputs,
2209 const_inits: self.const_inits,
2210 cursor_schemas: self.cursor_schemas,
2211 ledger: self.ledger,
2212 })
2213 }
2214}
2215
2216fn value_constraint_proven(
2231 all_nodes: &[PendingNode],
2232 src: &WireSource,
2233 _constraint: &crate::dsl::const_constraints::ConstConstraint,
2234) -> bool {
2235 match src {
2236 WireSource::Input(_) => false,
2237 WireSource::NodeOutput(idx, _) => {
2238 let meta = all_nodes[*idx].node.meta();
2239 let no_wire_inputs = meta.wire_inputs().is_empty();
2244 if no_wire_inputs {
2245 return true;
2246 }
2247 if meta.name.starts_with("__assert_v_") || meta.name.starts_with("assert_") {
2252 return true;
2253 }
2254 false
2255 }
2256 }
2257}
2258
2259fn assertion_skip_reason(
2263 strict_values: bool,
2264 all_nodes: &[PendingNode],
2265 src: &WireSource,
2266 _constraint: &crate::dsl::const_constraints::ConstConstraint,
2267) -> String {
2268 if !strict_values {
2269 return "strict_values not enabled".into();
2270 }
2271 match src {
2272 WireSource::Input(_) => "raw input wire".into(),
2273 WireSource::NodeOutput(idx, _) => {
2274 let meta = all_nodes[*idx].node.meta();
2275 if meta.wire_inputs().is_empty() {
2276 "constant source already validated".into()
2277 } else if meta.name.starts_with("__assert_v_") || meta.name.starts_with("assert_") {
2278 "upstream assertion".into()
2279 } else {
2280 "no skip rule matched".into()
2281 }
2282 }
2283 }
2284}
2285
2286pub(crate) fn shared_outputs_of(resolved: &ResolvedDag) -> Vec<&str> {
2289 let mut shared: Vec<&str> = resolved
2290 .output_modifiers
2291 .iter()
2292 .filter(|(_, m)| **m == crate::dsl::ast::BindingModifier::SHARED)
2293 .map(|(name, _)| name.as_str())
2294 .collect();
2295 shared.sort();
2296 shared
2297}
2298
2299fn is_lossless_widening(from: PortType, to: PortType) -> bool {
2305 use PortType as P;
2306 matches!(
2307 (from, to),
2308 (P::U64, P::F64)
2309 | (P::U32, P::U64)
2310 | (P::U32, P::I64)
2311 | (P::U32, P::F64)
2312 | (P::I32, P::I64)
2313 | (P::I32, P::F64)
2314 | (P::I64, P::F64)
2315 | (P::F32, P::F64)
2316 )
2317}
2318
2319pub(crate) fn wire_types_of(resolved: &ResolvedDag, node_idx: usize) -> Vec<PortType> {
2320 resolved.wiring[node_idx]
2321 .iter()
2322 .map(|src| match src {
2323 crate::kernel::WireSource::Input(i) => resolved.input_defs[*i].port_type,
2324 crate::kernel::WireSource::NodeOutput(j, p) => resolved.nodes[*j].meta().outs[*p].typ,
2325 })
2326 .collect()
2327}
2328
2329pub fn auto_adapter(from: PortType, to: PortType) -> Option<Box<dyn PolydatNode>> {
2333 use crate::library::convert::{
2334 BoolToStr, BoolToU64, F32ToF64, F32ToString, I32ToF64, I32ToI64, I32ToString, I64ToF64,
2335 I64ToString, U32ToF64, U32ToI64, U32ToString, U32ToU64,
2336 };
2337 use crate::library::polyfill as P;
2338 use crate::library::polyfill_128 as W;
2339 use crate::library::polyfill_complete as C;
2340 use crate::library::polyfill_narrow as N;
2341 match (from, to) {
2342 (PortType::U64, PortType::F64) => Some(Box::new(U64ToF64::new())),
2344 (PortType::U32, PortType::U64) => Some(Box::new(U32ToU64::new())),
2345 (PortType::U32, PortType::I64) => Some(Box::new(U32ToI64::new())),
2346 (PortType::U32, PortType::F64) => Some(Box::new(U32ToF64::new())),
2347 (PortType::I32, PortType::I64) => Some(Box::new(I32ToI64::new())),
2348 (PortType::I32, PortType::F64) => Some(Box::new(I32ToF64::new())),
2349 (PortType::I32, PortType::F32) => Some(Box::new(P::I32ToF32::new())),
2355 (PortType::I64, PortType::F64) => Some(Box::new(I64ToF64::new())),
2356 (PortType::F32, PortType::F64) => Some(Box::new(F32ToF64::new())),
2357
2358 (PortType::U64, PortType::Str) => Some(Box::new(U64ToString::new())),
2360 (PortType::F64, PortType::Str) => Some(Box::new(F64ToString::new())),
2361 (PortType::Bool, PortType::Str) => Some(Box::new(BoolToStr::new())),
2362 (PortType::Json, PortType::Str) => Some(Box::new(JsonToStr::new())),
2363 (PortType::U32, PortType::Str) => Some(Box::new(U32ToString::new())),
2364 (PortType::I32, PortType::Str) => Some(Box::new(I32ToString::new())),
2365 (PortType::I64, PortType::Str) => Some(Box::new(I64ToString::new())),
2366 (PortType::F32, PortType::Str) => Some(Box::new(F32ToString::new())),
2367
2368 (PortType::Bool, PortType::U64) => Some(Box::new(BoolToU64::new())),
2370 (PortType::Bool, PortType::U32) => Some(Box::new(P::BoolToU32::new())),
2371 (PortType::Bool, PortType::I64) => Some(Box::new(P::BoolToI64::new())),
2372 (PortType::Bool, PortType::I32) => Some(Box::new(P::BoolToI32::new())),
2373 (PortType::Bool, PortType::F64) => Some(Box::new(P::BoolToF64::new())),
2374 (PortType::Bool, PortType::F32) => Some(Box::new(P::BoolToF32::new())),
2375 (PortType::U64, PortType::Bool) => {
2376 Some(Box::new(crate::library::convert::U64ToBool::new()))
2377 }
2378 (PortType::U32, PortType::Bool) => Some(Box::new(P::U32ToBool::new())),
2379 (PortType::I64, PortType::Bool) => Some(Box::new(P::I64ToBool::new())),
2380 (PortType::I32, PortType::Bool) => Some(Box::new(P::I32ToBool::new())),
2381 (PortType::F64, PortType::Bool) => Some(Box::new(P::F64ToBool::new())),
2382 (PortType::F32, PortType::Bool) => Some(Box::new(P::F32ToBool::new())),
2383
2384 (PortType::U64, PortType::Bytes) => Some(Box::new(P::U64ToBytes::new())),
2386 (PortType::U32, PortType::Bytes) => Some(Box::new(P::U32ToBytes::new())),
2387 (PortType::I64, PortType::Bytes) => Some(Box::new(P::I64ToBytes::new())),
2388 (PortType::I32, PortType::Bytes) => Some(Box::new(P::I32ToBytes::new())),
2389 (PortType::F64, PortType::Bytes) => Some(Box::new(P::F64ToBytes::new())),
2390 (PortType::F32, PortType::Bytes) => Some(Box::new(P::F32ToBytes::new())),
2391 (PortType::Bool, PortType::Bytes) => Some(Box::new(P::BoolToBytes::new())),
2392 (PortType::VecF32, PortType::Bytes) => Some(Box::new(P::VecF32ToBytes::new())),
2393 (PortType::VecI32, PortType::Bytes) => Some(Box::new(P::VecI32ToBytes::new())),
2394
2395 (PortType::U64, PortType::Json) => Some(Box::new(P::U64ToJson::new())),
2399 (PortType::U32, PortType::Json) => Some(Box::new(P::U32ToJson::new())),
2400 (PortType::I64, PortType::Json) => Some(Box::new(P::I64ToJson::new())),
2401 (PortType::I32, PortType::Json) => Some(Box::new(P::I32ToJson::new())),
2402 (PortType::Bool, PortType::Json) => Some(Box::new(P::BoolToJson::new())),
2403 (PortType::VecI32, PortType::Json) => Some(Box::new(P::VecI32ToJson::new())),
2404
2405 (PortType::VecI32, PortType::VecF32) => Some(Box::new(P::VecI32ToVecF32::new())),
2407
2408 (PortType::U8, PortType::U64) => Some(Box::new(N::U8ToU64::new())),
2413 (PortType::U8, PortType::U32) => Some(Box::new(N::U8ToU32::new())),
2414 (PortType::U8, PortType::U16) => Some(Box::new(N::U8ToU16::new())),
2415 (PortType::U8, PortType::F64) => Some(Box::new(N::U8ToF64::new())),
2416 (PortType::U16, PortType::U64) => Some(Box::new(N::U16ToU64::new())),
2417 (PortType::U16, PortType::U32) => Some(Box::new(N::U16ToU32::new())),
2418 (PortType::U16, PortType::F64) => Some(Box::new(N::U16ToF64::new())),
2419 (PortType::I8, PortType::I64) => Some(Box::new(N::I8ToI64::new())),
2420 (PortType::I8, PortType::I32) => Some(Box::new(N::I8ToI32::new())),
2421 (PortType::I8, PortType::I16) => Some(Box::new(N::I8ToI16::new())),
2422 (PortType::I8, PortType::F64) => Some(Box::new(N::I8ToF64::new())),
2423 (PortType::I16, PortType::I64) => Some(Box::new(N::I16ToI64::new())),
2424 (PortType::I16, PortType::I32) => Some(Box::new(N::I16ToI32::new())),
2425 (PortType::I16, PortType::F64) => Some(Box::new(N::I16ToF64::new())),
2426 (PortType::F16, PortType::F32) => Some(Box::new(N::F16ToF32::new())),
2427 (PortType::F16, PortType::F64) => Some(Box::new(N::F16ToF64::new())),
2428 (PortType::U8, PortType::I16) => Some(Box::new(N::U8ToI16::new())),
2431 (PortType::U8, PortType::I32) => Some(Box::new(N::U8ToI32::new())),
2432 (PortType::U8, PortType::I64) => Some(Box::new(N::U8ToI64::new())),
2433 (PortType::U8, PortType::F32) => Some(Box::new(N::U8ToF32::new())),
2434 (PortType::U16, PortType::I32) => Some(Box::new(N::U16ToI32::new())),
2435 (PortType::U16, PortType::I64) => Some(Box::new(N::U16ToI64::new())),
2436 (PortType::U16, PortType::F32) => Some(Box::new(N::U16ToF32::new())),
2437 (PortType::I8, PortType::F32) => Some(Box::new(N::I8ToF32::new())),
2438 (PortType::I16, PortType::F32) => Some(Box::new(N::I16ToF32::new())),
2439 (PortType::U8, PortType::F16) => Some(Box::new(N::U8ToF16::new())),
2440 (PortType::I8, PortType::F16) => Some(Box::new(N::I8ToF16::new())),
2441 (PortType::U8, PortType::Str) => Some(Box::new(N::U8ToString::new())),
2442 (PortType::U16, PortType::Str) => Some(Box::new(N::U16ToString::new())),
2443 (PortType::I8, PortType::Str) => Some(Box::new(N::I8ToString::new())),
2444 (PortType::I16, PortType::Str) => Some(Box::new(N::I16ToString::new())),
2445 (PortType::F16, PortType::Str) => Some(Box::new(N::F16ToString::new())),
2446 (PortType::Bool, PortType::U8) => Some(Box::new(N::BoolToU8::new())),
2447 (PortType::Bool, PortType::U16) => Some(Box::new(N::BoolToU16::new())),
2448 (PortType::Bool, PortType::I8) => Some(Box::new(N::BoolToI8::new())),
2449 (PortType::Bool, PortType::I16) => Some(Box::new(N::BoolToI16::new())),
2450 (PortType::Bool, PortType::F16) => Some(Box::new(N::BoolToF16::new())),
2451 (PortType::U8, PortType::Bool) => Some(Box::new(N::U8ToBool::new())),
2452 (PortType::U16, PortType::Bool) => Some(Box::new(N::U16ToBool::new())),
2453 (PortType::I8, PortType::Bool) => Some(Box::new(N::I8ToBool::new())),
2454 (PortType::I16, PortType::Bool) => Some(Box::new(N::I16ToBool::new())),
2455 (PortType::F16, PortType::Bool) => Some(Box::new(N::F16ToBool::new())),
2456 (PortType::U8, PortType::Bytes) => Some(Box::new(N::U8ToBytes::new())),
2457 (PortType::U16, PortType::Bytes) => Some(Box::new(N::U16ToBytes::new())),
2458 (PortType::I8, PortType::Bytes) => Some(Box::new(N::I8ToBytes::new())),
2459 (PortType::I16, PortType::Bytes) => Some(Box::new(N::I16ToBytes::new())),
2460 (PortType::F16, PortType::Bytes) => Some(Box::new(N::F16ToBytes::new())),
2461 (PortType::U8, PortType::Json) => Some(Box::new(N::U8ToJson::new())),
2462 (PortType::U16, PortType::Json) => Some(Box::new(N::U16ToJson::new())),
2463 (PortType::I8, PortType::Json) => Some(Box::new(N::I8ToJson::new())),
2464 (PortType::I16, PortType::Json) => Some(Box::new(N::I16ToJson::new())),
2465
2466 (PortType::U64, PortType::U128) => Some(Box::new(W::U64ToU128::new())),
2471 (PortType::U64, PortType::I128) => Some(Box::new(W::U64ToI128::new())),
2472 (PortType::I64, PortType::I128) => Some(Box::new(W::I64ToI128::new())),
2473 (PortType::U8, PortType::U128) => Some(Box::new(W::U8ToU128::new())),
2478 (PortType::U8, PortType::I128) => Some(Box::new(W::U8ToI128::new())),
2479 (PortType::U16, PortType::U128) => Some(Box::new(W::U16ToU128::new())),
2480 (PortType::U16, PortType::I128) => Some(Box::new(W::U16ToI128::new())),
2481 (PortType::U32, PortType::U128) => Some(Box::new(W::U32ToU128::new())),
2482 (PortType::U32, PortType::I128) => Some(Box::new(W::U32ToI128::new())),
2483 (PortType::I8, PortType::I128) => Some(Box::new(W::I8ToI128::new())),
2484 (PortType::I16, PortType::I128) => Some(Box::new(W::I16ToI128::new())),
2485 (PortType::I32, PortType::I128) => Some(Box::new(W::I32ToI128::new())),
2486 (PortType::Bool, PortType::U128) => Some(Box::new(W::BoolToU128::new())),
2487 (PortType::Bool, PortType::I128) => Some(Box::new(W::BoolToI128::new())),
2488 (PortType::U128, PortType::Bool) => Some(Box::new(W::U128ToBool::new())),
2489 (PortType::I128, PortType::Bool) => Some(Box::new(W::I128ToBool::new())),
2490 (PortType::U128, PortType::F64) => Some(Box::new(W::U128ToF64::new())),
2491 (PortType::I128, PortType::F64) => Some(Box::new(W::I128ToF64::new())),
2492 (PortType::U128, PortType::Str) => Some(Box::new(W::U128ToString::new())),
2493 (PortType::I128, PortType::Str) => Some(Box::new(W::I128ToString::new())),
2494 (PortType::U128, PortType::Bytes) => Some(Box::new(W::U128ToBytes::new())),
2495 (PortType::I128, PortType::Bytes) => Some(Box::new(W::I128ToBytes::new())),
2496 (PortType::U128, PortType::Json) => Some(Box::new(W::U128ToJson::new())),
2497 (PortType::I128, PortType::Json) => Some(Box::new(W::I128ToJson::new())),
2498
2499 (from, to) if crate::library::register_view::is_reg_port(from) => {
2504 crate::library::register_view::reg_view(to)
2505 }
2506
2507 (PortType::VecI8, PortType::VecI16) => Some(Box::new(C::VecI8ToVecI16::new())),
2511 (PortType::VecI8, PortType::VecI32) => Some(Box::new(C::VecI8ToVecI32::new())),
2512 (PortType::VecI8, PortType::VecI64) => Some(Box::new(C::VecI8ToVecI64::new())),
2513 (PortType::VecI8, PortType::VecF16) => Some(Box::new(C::VecI8ToVecF16::new())),
2514 (PortType::VecI8, PortType::VecF32) => Some(Box::new(C::VecI8ToVecF32::new())),
2515 (PortType::VecI8, PortType::VecF64) => Some(Box::new(C::VecI8ToVecF64::new())),
2516 (PortType::VecI16, PortType::VecI32) => Some(Box::new(C::VecI16ToVecI32::new())),
2517 (PortType::VecI16, PortType::VecI64) => Some(Box::new(C::VecI16ToVecI64::new())),
2518 (PortType::VecI16, PortType::VecF32) => Some(Box::new(C::VecI16ToVecF32::new())),
2519 (PortType::VecI16, PortType::VecF64) => Some(Box::new(C::VecI16ToVecF64::new())),
2520 (PortType::VecI32, PortType::VecI64) => Some(Box::new(C::VecI32ToVecI64::new())),
2521 (PortType::VecI32, PortType::VecF64) => Some(Box::new(C::VecI32ToVecF64::new())),
2522 (PortType::VecI64, PortType::VecF64) => Some(Box::new(C::VecI64ToVecF64::new())),
2523 (PortType::VecF16, PortType::VecF32) => Some(Box::new(C::VecF16ToVecF32::new())),
2524 (PortType::VecF16, PortType::VecF64) => Some(Box::new(C::VecF16ToVecF64::new())),
2525 (PortType::VecF32, PortType::VecF64) => Some(Box::new(C::VecF32ToVecF64::new())),
2526 (PortType::VecF64, PortType::Bytes) => Some(Box::new(C::VecF64ToBytes::new())),
2527 (PortType::VecI64, PortType::Bytes) => Some(Box::new(C::VecI64ToBytes::new())),
2528 (PortType::VecF16, PortType::Bytes) => Some(Box::new(C::VecF16ToBytes::new())),
2529 (PortType::VecI16, PortType::Bytes) => Some(Box::new(C::VecI16ToBytes::new())),
2530 (PortType::VecI8, PortType::Bytes) => Some(Box::new(C::VecI8ToBytes::new())),
2531 (PortType::VecI64, PortType::Json) => Some(Box::new(C::VecI64ToJson::new())),
2532 (PortType::VecI16, PortType::Json) => Some(Box::new(C::VecI16ToJson::new())),
2533 (PortType::VecI8, PortType::Json) => Some(Box::new(C::VecI8ToJson::new())),
2534 (PortType::VecI32, PortType::Str) => Some(Box::new(P::VecI32ToStr::new())),
2535 (PortType::VecI64, PortType::Str) => Some(Box::new(C::VecI64ToStr::new())),
2536 (PortType::VecI16, PortType::Str) => Some(Box::new(C::VecI16ToStr::new())),
2537 (PortType::VecI8, PortType::Str) => Some(Box::new(C::VecI8ToStr::new())),
2538
2539 _ => None,
2540 }
2541}
2542
2543pub fn boundary_adapter(from: PortType, to: PortType) -> Option<Box<dyn PolydatNode>> {
2573 if let Some(adapter) = auto_adapter(from, to) {
2574 return Some(adapter);
2575 }
2576 use crate::library::convert::{StrToBool, StrToF64, StrToU64};
2577 use crate::library::polyfill as P;
2578 use crate::library::polyfill_128 as W;
2579 use crate::library::polyfill_complete as C;
2580 use crate::library::polyfill_narrow as N;
2581 match (from, to) {
2582 (PortType::U64, PortType::U32) => Some(Box::new(P::U64ToU32::new())),
2584 (PortType::U64, PortType::I64) => Some(Box::new(P::U64ToI64::new())),
2585 (PortType::U64, PortType::I32) => Some(Box::new(P::U64ToI32::new())),
2586 (PortType::U64, PortType::F32) => Some(Box::new(P::U64ToF32::new())),
2587 (PortType::U32, PortType::I32) => Some(Box::new(P::U32ToI32::new())),
2588 (PortType::U32, PortType::F32) => Some(Box::new(P::U32ToF32::new())),
2589 (PortType::I64, PortType::U64) => Some(Box::new(P::I64ToU64::new())),
2590 (PortType::I64, PortType::U32) => Some(Box::new(P::I64ToU32::new())),
2591 (PortType::I64, PortType::I32) => Some(Box::new(P::I64ToI32::new())),
2592 (PortType::I64, PortType::F32) => Some(Box::new(P::I64ToF32::new())),
2593 (PortType::I32, PortType::U64) => Some(Box::new(P::I32ToU64::new())),
2594 (PortType::I32, PortType::U32) => Some(Box::new(P::I32ToU32::new())),
2595 (PortType::I32, PortType::F32) => Some(Box::new(P::I32ToF32::new())),
2596 (PortType::F64, PortType::U64) => Some(Box::new(P::F64ToU64Checked::new())),
2597 (PortType::F64, PortType::U32) => Some(Box::new(P::F64ToU32::new())),
2598 (PortType::F64, PortType::I64) => Some(Box::new(P::F64ToI64::new())),
2599 (PortType::F64, PortType::I32) => Some(Box::new(P::F64ToI32::new())),
2600 (PortType::F64, PortType::F32) => Some(Box::new(P::F64ToF32::new())),
2601 (PortType::F32, PortType::U64) => Some(Box::new(P::F32ToU64::new())),
2602 (PortType::F32, PortType::U32) => Some(Box::new(P::F32ToU32::new())),
2603 (PortType::F32, PortType::I64) => Some(Box::new(P::F32ToI64::new())),
2604 (PortType::F32, PortType::I32) => Some(Box::new(P::F32ToI32::new())),
2605
2606 (PortType::Str, PortType::Bool) => Some(Box::new(StrToBool::new())),
2608 (PortType::Str, PortType::U64) => Some(Box::new(StrToU64::new())),
2609 (PortType::Str, PortType::F64) => Some(Box::new(StrToF64::new())),
2610 (PortType::Str, PortType::U32) => Some(Box::new(P::StrToU32::new())),
2611 (PortType::Str, PortType::I64) => Some(Box::new(P::StrToI64::new())),
2612 (PortType::Str, PortType::I32) => Some(Box::new(P::StrToI32::new())),
2613 (PortType::Str, PortType::F32) => Some(Box::new(P::StrToF32::new())),
2614 (PortType::Str, PortType::Bytes) => Some(Box::new(P::StrToBytes::new())),
2615 (PortType::Str, PortType::Json) => Some(Box::new(P::StrToJson::new())),
2616 (PortType::Str, PortType::VecF32) => Some(Box::new(P::StrToVecF32::new())),
2617 (PortType::Str, PortType::VecI32) => Some(Box::new(P::StrToVecI32::new())),
2618
2619 (PortType::Bytes, PortType::U64) => Some(Box::new(P::BytesToU64::new())),
2621 (PortType::Bytes, PortType::U32) => Some(Box::new(P::BytesToU32::new())),
2622 (PortType::Bytes, PortType::I64) => Some(Box::new(P::BytesToI64::new())),
2623 (PortType::Bytes, PortType::I32) => Some(Box::new(P::BytesToI32::new())),
2624 (PortType::Bytes, PortType::F64) => Some(Box::new(P::BytesToF64::new())),
2625 (PortType::Bytes, PortType::F32) => Some(Box::new(P::BytesToF32::new())),
2626 (PortType::Bytes, PortType::Bool) => Some(Box::new(P::BytesToBool::new())),
2627 (PortType::Bytes, PortType::Str) => Some(Box::new(P::BytesToStr::new())),
2628 (PortType::Bytes, PortType::Json) => Some(Box::new(P::BytesToJson::new())),
2629 (PortType::Bytes, PortType::VecF32) => Some(Box::new(P::BytesToVecF32::new())),
2630 (PortType::Bytes, PortType::VecI32) => Some(Box::new(P::BytesToVecI32::new())),
2631
2632 (PortType::Json, PortType::U64) => Some(Box::new(P::JsonToU64::new())),
2634 (PortType::Json, PortType::U32) => Some(Box::new(P::JsonToU32::new())),
2635 (PortType::Json, PortType::I64) => Some(Box::new(P::JsonToI64::new())),
2636 (PortType::Json, PortType::I32) => Some(Box::new(P::JsonToI32::new())),
2637 (PortType::Json, PortType::F64) => Some(Box::new(P::JsonToF64::new())),
2638 (PortType::Json, PortType::F32) => Some(Box::new(P::JsonToF32::new())),
2639 (PortType::Json, PortType::Bool) => Some(Box::new(P::JsonToBool::new())),
2640 (PortType::Json, PortType::Bytes) => Some(Box::new(P::JsonToBytes::new())),
2641 (PortType::Json, PortType::VecF32) => Some(Box::new(P::JsonToVecF32::new())),
2642 (PortType::Json, PortType::VecI32) => Some(Box::new(P::JsonToVecI32::new())),
2643
2644 (PortType::F64, PortType::Json) => Some(Box::new(P::F64ToJson::new())),
2646 (PortType::F32, PortType::Json) => Some(Box::new(P::F32ToJson::new())),
2647 (PortType::VecF32, PortType::Json) => Some(Box::new(P::VecF32ToJson::new())),
2648 (PortType::VecF32, PortType::Str) => Some(Box::new(P::VecF32ToStr::new())),
2649
2650 (PortType::VecF32, PortType::VecI32) => Some(Box::new(P::VecF32ToVecI32::new())),
2652
2653 (PortType::U64, PortType::U8) => Some(Box::new(N::U64ToU8::new())),
2657 (PortType::U32, PortType::U8) => Some(Box::new(N::U32ToU8::new())),
2658 (PortType::U16, PortType::U8) => Some(Box::new(N::U16ToU8::new())),
2659 (PortType::I64, PortType::U8) => Some(Box::new(N::I64ToU8::new())),
2660 (PortType::F64, PortType::U8) => Some(Box::new(N::F64ToU8::new())),
2661 (PortType::U64, PortType::U16) => Some(Box::new(N::U64ToU16::new())),
2662 (PortType::U32, PortType::U16) => Some(Box::new(N::U32ToU16::new())),
2663 (PortType::I64, PortType::U16) => Some(Box::new(N::I64ToU16::new())),
2664 (PortType::F64, PortType::U16) => Some(Box::new(N::F64ToU16::new())),
2665 (PortType::I64, PortType::I8) => Some(Box::new(N::I64ToI8::new())),
2666 (PortType::I32, PortType::I8) => Some(Box::new(N::I32ToI8::new())),
2667 (PortType::U64, PortType::I8) => Some(Box::new(N::U64ToI8::new())),
2668 (PortType::F64, PortType::I8) => Some(Box::new(N::F64ToI8::new())),
2669 (PortType::I64, PortType::I16) => Some(Box::new(N::I64ToI16::new())),
2670 (PortType::I32, PortType::I16) => Some(Box::new(N::I32ToI16::new())),
2671 (PortType::U64, PortType::I16) => Some(Box::new(N::U64ToI16::new())),
2672 (PortType::F64, PortType::I16) => Some(Box::new(N::F64ToI16::new())),
2673 (PortType::F64, PortType::F16) => Some(Box::new(N::F64ToF16::new())),
2674 (PortType::F32, PortType::F16) => Some(Box::new(N::F32ToF16::new())),
2675 (PortType::U64, PortType::F16) => Some(Box::new(N::U64ToF16::new())),
2676 (PortType::Str, PortType::U8) => Some(Box::new(N::StrToU8::new())),
2677 (PortType::Str, PortType::U16) => Some(Box::new(N::StrToU16::new())),
2678 (PortType::Str, PortType::I8) => Some(Box::new(N::StrToI8::new())),
2679 (PortType::Str, PortType::I16) => Some(Box::new(N::StrToI16::new())),
2680 (PortType::Str, PortType::F16) => Some(Box::new(N::StrToF16::new())),
2681 (PortType::Bytes, PortType::U8) => Some(Box::new(N::BytesToU8::new())),
2682 (PortType::Bytes, PortType::U16) => Some(Box::new(N::BytesToU16::new())),
2683 (PortType::Bytes, PortType::I8) => Some(Box::new(N::BytesToI8::new())),
2684 (PortType::Bytes, PortType::I16) => Some(Box::new(N::BytesToI16::new())),
2685 (PortType::Bytes, PortType::F16) => Some(Box::new(N::BytesToF16::new())),
2686 (PortType::Json, PortType::U8) => Some(Box::new(N::JsonToU8::new())),
2687 (PortType::Json, PortType::U16) => Some(Box::new(N::JsonToU16::new())),
2688 (PortType::Json, PortType::I8) => Some(Box::new(N::JsonToI8::new())),
2689 (PortType::Json, PortType::I16) => Some(Box::new(N::JsonToI16::new())),
2690 (PortType::Json, PortType::F16) => Some(Box::new(N::JsonToF16::new())),
2691 (PortType::F16, PortType::Json) => Some(Box::new(N::F16ToJson::new())),
2693
2694 (PortType::U128, PortType::U64) => Some(Box::new(W::U128ToU64::new())),
2696 (PortType::I128, PortType::I64) => Some(Box::new(W::I128ToI64::new())),
2697 (PortType::I64, PortType::U128) => Some(Box::new(W::I64ToU128::new())),
2698 (PortType::U128, PortType::I128) => Some(Box::new(W::U128ToI128::new())),
2699 (PortType::I128, PortType::U128) => Some(Box::new(W::I128ToU128::new())),
2700 (PortType::F64, PortType::U128) => Some(Box::new(W::F64ToU128::new())),
2701 (PortType::F64, PortType::I128) => Some(Box::new(W::F64ToI128::new())),
2702 (PortType::Str, PortType::U128) => Some(Box::new(W::StrToU128::new())),
2703 (PortType::Str, PortType::I128) => Some(Box::new(W::StrToI128::new())),
2704 (PortType::Bytes, PortType::U128) => Some(Box::new(W::BytesToU128::new())),
2705 (PortType::Bytes, PortType::I128) => Some(Box::new(W::BytesToI128::new())),
2706 (PortType::Json, PortType::U128) => Some(Box::new(W::JsonToU128::new())),
2707 (PortType::Json, PortType::I128) => Some(Box::new(W::JsonToI128::new())),
2708
2709 (PortType::U8, PortType::I8) => Some(Box::new(C::U8ToI8::new())),
2714 (PortType::I8, PortType::U8) => Some(Box::new(C::I8ToU8::new())),
2715 (PortType::I8, PortType::U16) => Some(Box::new(C::I8ToU16::new())),
2716 (PortType::I8, PortType::U32) => Some(Box::new(C::I8ToU32::new())),
2717 (PortType::I8, PortType::U64) => Some(Box::new(C::I8ToU64::new())),
2718 (PortType::I8, PortType::U128) => Some(Box::new(C::I8ToU128::new())),
2719 (PortType::U16, PortType::I8) => Some(Box::new(C::U16ToI8::new())),
2720 (PortType::U16, PortType::I16) => Some(Box::new(C::U16ToI16::new())),
2721 (PortType::U16, PortType::F16) => Some(Box::new(C::U16ToF16::new())),
2722 (PortType::I16, PortType::U8) => Some(Box::new(C::I16ToU8::new())),
2723 (PortType::I16, PortType::I8) => Some(Box::new(C::I16ToI8::new())),
2724 (PortType::I16, PortType::U16) => Some(Box::new(C::I16ToU16::new())),
2725 (PortType::I16, PortType::F16) => Some(Box::new(C::I16ToF16::new())),
2726 (PortType::I16, PortType::U32) => Some(Box::new(C::I16ToU32::new())),
2727 (PortType::I16, PortType::U64) => Some(Box::new(C::I16ToU64::new())),
2728 (PortType::I16, PortType::U128) => Some(Box::new(C::I16ToU128::new())),
2729 (PortType::U32, PortType::I8) => Some(Box::new(C::U32ToI8::new())),
2730 (PortType::U32, PortType::I16) => Some(Box::new(C::U32ToI16::new())),
2731 (PortType::U32, PortType::F16) => Some(Box::new(C::U32ToF16::new())),
2732 (PortType::I32, PortType::U8) => Some(Box::new(C::I32ToU8::new())),
2733 (PortType::I32, PortType::U16) => Some(Box::new(C::I32ToU16::new())),
2734 (PortType::I32, PortType::F16) => Some(Box::new(C::I32ToF16::new())),
2735 (PortType::I32, PortType::U128) => Some(Box::new(C::I32ToU128::new())),
2736 (PortType::F16, PortType::U8) => Some(Box::new(C::F16ToU8::new())),
2737 (PortType::F16, PortType::I8) => Some(Box::new(C::F16ToI8::new())),
2738 (PortType::F16, PortType::U16) => Some(Box::new(C::F16ToU16::new())),
2739 (PortType::F16, PortType::I16) => Some(Box::new(C::F16ToI16::new())),
2740 (PortType::F16, PortType::U32) => Some(Box::new(C::F16ToU32::new())),
2741 (PortType::F16, PortType::I32) => Some(Box::new(C::F16ToI32::new())),
2742 (PortType::F16, PortType::U64) => Some(Box::new(C::F16ToU64::new())),
2743 (PortType::F16, PortType::I64) => Some(Box::new(C::F16ToI64::new())),
2744 (PortType::F16, PortType::U128) => Some(Box::new(C::F16ToU128::new())),
2745 (PortType::F16, PortType::I128) => Some(Box::new(C::F16ToI128::new())),
2746 (PortType::F32, PortType::U8) => Some(Box::new(C::F32ToU8::new())),
2747 (PortType::F32, PortType::I8) => Some(Box::new(C::F32ToI8::new())),
2748 (PortType::F32, PortType::U16) => Some(Box::new(C::F32ToU16::new())),
2749 (PortType::F32, PortType::I16) => Some(Box::new(C::F32ToI16::new())),
2750 (PortType::F32, PortType::U128) => Some(Box::new(C::F32ToU128::new())),
2751 (PortType::F32, PortType::I128) => Some(Box::new(C::F32ToI128::new())),
2752 (PortType::I64, PortType::F16) => Some(Box::new(C::I64ToF16::new())),
2753 (PortType::U128, PortType::U8) => Some(Box::new(C::U128ToU8::new())),
2754 (PortType::U128, PortType::I8) => Some(Box::new(C::U128ToI8::new())),
2755 (PortType::U128, PortType::U16) => Some(Box::new(C::U128ToU16::new())),
2756 (PortType::U128, PortType::I16) => Some(Box::new(C::U128ToI16::new())),
2757 (PortType::U128, PortType::F16) => Some(Box::new(C::U128ToF16::new())),
2758 (PortType::U128, PortType::U32) => Some(Box::new(C::U128ToU32::new())),
2759 (PortType::U128, PortType::I32) => Some(Box::new(C::U128ToI32::new())),
2760 (PortType::U128, PortType::F32) => Some(Box::new(C::U128ToF32::new())),
2761 (PortType::U128, PortType::I64) => Some(Box::new(C::U128ToI64::new())),
2762 (PortType::I128, PortType::U8) => Some(Box::new(C::I128ToU8::new())),
2763 (PortType::I128, PortType::I8) => Some(Box::new(C::I128ToI8::new())),
2764 (PortType::I128, PortType::U16) => Some(Box::new(C::I128ToU16::new())),
2765 (PortType::I128, PortType::I16) => Some(Box::new(C::I128ToI16::new())),
2766 (PortType::I128, PortType::F16) => Some(Box::new(C::I128ToF16::new())),
2767 (PortType::I128, PortType::U32) => Some(Box::new(C::I128ToU32::new())),
2768 (PortType::I128, PortType::I32) => Some(Box::new(C::I128ToI32::new())),
2769 (PortType::I128, PortType::F32) => Some(Box::new(C::I128ToF32::new())),
2770 (PortType::I128, PortType::U64) => Some(Box::new(C::I128ToU64::new())),
2771
2772 (PortType::VecI16, PortType::VecI8) => Some(Box::new(C::VecI16ToVecI8::new())),
2776 (PortType::VecI16, PortType::VecF16) => Some(Box::new(C::VecI16ToVecF16::new())),
2777 (PortType::VecI32, PortType::VecI8) => Some(Box::new(C::VecI32ToVecI8::new())),
2778 (PortType::VecI32, PortType::VecI16) => Some(Box::new(C::VecI32ToVecI16::new())),
2779 (PortType::VecI32, PortType::VecF16) => Some(Box::new(C::VecI32ToVecF16::new())),
2780 (PortType::VecI64, PortType::VecI8) => Some(Box::new(C::VecI64ToVecI8::new())),
2781 (PortType::VecI64, PortType::VecI16) => Some(Box::new(C::VecI64ToVecI16::new())),
2782 (PortType::VecI64, PortType::VecI32) => Some(Box::new(C::VecI64ToVecI32::new())),
2783 (PortType::VecI64, PortType::VecF16) => Some(Box::new(C::VecI64ToVecF16::new())),
2784 (PortType::VecI64, PortType::VecF32) => Some(Box::new(C::VecI64ToVecF32::new())),
2785 (PortType::VecF16, PortType::VecI8) => Some(Box::new(C::VecF16ToVecI8::new())),
2786 (PortType::VecF16, PortType::VecI16) => Some(Box::new(C::VecF16ToVecI16::new())),
2787 (PortType::VecF16, PortType::VecI32) => Some(Box::new(C::VecF16ToVecI32::new())),
2788 (PortType::VecF16, PortType::VecI64) => Some(Box::new(C::VecF16ToVecI64::new())),
2789 (PortType::VecF32, PortType::VecI8) => Some(Box::new(C::VecF32ToVecI8::new())),
2790 (PortType::VecF32, PortType::VecI16) => Some(Box::new(C::VecF32ToVecI16::new())),
2791 (PortType::VecF32, PortType::VecI64) => Some(Box::new(C::VecF32ToVecI64::new())),
2792 (PortType::VecF32, PortType::VecF16) => Some(Box::new(C::VecF32ToVecF16::new())),
2793 (PortType::VecF64, PortType::VecI8) => Some(Box::new(C::VecF64ToVecI8::new())),
2794 (PortType::VecF64, PortType::VecI16) => Some(Box::new(C::VecF64ToVecI16::new())),
2795 (PortType::VecF64, PortType::VecI32) => Some(Box::new(C::VecF64ToVecI32::new())),
2796 (PortType::VecF64, PortType::VecI64) => Some(Box::new(C::VecF64ToVecI64::new())),
2797 (PortType::VecF64, PortType::VecF16) => Some(Box::new(C::VecF64ToVecF16::new())),
2798 (PortType::VecF64, PortType::VecF32) => Some(Box::new(C::VecF64ToVecF32::new())),
2799 (PortType::Bytes, PortType::VecF64) => Some(Box::new(C::BytesToVecF64::new())),
2800 (PortType::Bytes, PortType::VecI64) => Some(Box::new(C::BytesToVecI64::new())),
2801 (PortType::Bytes, PortType::VecF16) => Some(Box::new(C::BytesToVecF16::new())),
2802 (PortType::Bytes, PortType::VecI16) => Some(Box::new(C::BytesToVecI16::new())),
2803 (PortType::Bytes, PortType::VecI8) => Some(Box::new(C::BytesToVecI8::new())),
2804 (PortType::VecF64, PortType::Json) => Some(Box::new(C::VecF64ToJson::new())),
2805 (PortType::VecF16, PortType::Json) => Some(Box::new(C::VecF16ToJson::new())),
2806 (PortType::Json, PortType::VecF64) => Some(Box::new(C::JsonToVecF64::new())),
2807 (PortType::Json, PortType::VecI64) => Some(Box::new(C::JsonToVecI64::new())),
2808 (PortType::Json, PortType::VecF16) => Some(Box::new(C::JsonToVecF16::new())),
2809 (PortType::Json, PortType::VecI16) => Some(Box::new(C::JsonToVecI16::new())),
2810 (PortType::Json, PortType::VecI8) => Some(Box::new(C::JsonToVecI8::new())),
2811 (PortType::VecF64, PortType::Str) => Some(Box::new(C::VecF64ToStr::new())),
2812 (PortType::VecF16, PortType::Str) => Some(Box::new(C::VecF16ToStr::new())),
2813 (PortType::Str, PortType::VecF64) => Some(Box::new(C::StrToVecF64::new())),
2814 (PortType::Str, PortType::VecI64) => Some(Box::new(C::StrToVecI64::new())),
2815 (PortType::Str, PortType::VecF16) => Some(Box::new(C::StrToVecF16::new())),
2816 (PortType::Str, PortType::VecI16) => Some(Box::new(C::StrToVecI16::new())),
2817 (PortType::Str, PortType::VecI8) => Some(Box::new(C::StrToVecI8::new())),
2818
2819 _ => None,
2820 }
2821}
2822
2823use crate::compile::select::{Engine, KernelError, Provenance};
2826use crate::kernel::Kernel;
2827
2828fn fixed_outputs(resolved: &ResolvedDag) -> std::collections::HashSet<String> {
2831 let classes = PolydatProgram::classify_lifecycle(
2832 &resolved.nodes,
2833 &resolved.wiring,
2834 &resolved.input_defs,
2835 &resolved.output_map,
2836 &resolved.output_modifiers,
2837 );
2838 resolved
2839 .output_map
2840 .iter()
2841 .filter(|(name, (node, _))| {
2842 resolved.const_outputs.contains(*name)
2843 || classes.lifecycle[*node] == crate::kernel::EvalLifecycle::CompileConst
2844 })
2845 .map(|(name, _)| name.clone())
2846 .collect()
2847}
2848
2849impl PolydatAssembler {
2850 pub fn compile_with(self, engine: Engine) -> Result<Box<dyn Kernel>, KernelError> {
2858 self.compile_engine_with_log(engine, None)
2859 }
2860
2861 pub fn compile_kernel(self) -> Result<Box<dyn Kernel>, KernelError> {
2864 self.compile_with(Engine::default())
2865 }
2866
2867 pub fn compile_engine_with_log(
2870 self,
2871 engine: Engine,
2872 log: Option<&mut crate::dsl::events::CompileEventLog>,
2873 ) -> Result<Box<dyn Kernel>, KernelError> {
2874 match engine {
2875 Engine::Interpreter(cones) => {
2878 let mut asm = self;
2879 asm.jit_mode = Some(cones);
2880 Ok(Box::new(asm.compile_with_log(log)?))
2881 }
2882 _ => Ok(self.compile_slots_with_log(engine, log)?),
2886 }
2887 }
2888
2889 pub fn compile_slots(
2905 self,
2906 engine: Engine,
2907 ) -> Result<Box<dyn crate::compile::SlotKernel>, KernelError> {
2908 self.compile_slots_with_log(engine, None)
2909 }
2910
2911 pub fn compile_slots_with_log(
2914 self,
2915 engine: Engine,
2916 log: Option<&mut crate::dsl::events::CompileEventLog>,
2917 ) -> Result<Box<dyn crate::compile::SlotKernel>, KernelError> {
2918 let template = self.template;
2919 let mut kernel = self.build_slots_with_log(engine, log)?;
2920 if !template {
2921 crate::kernel::Kernel::init(kernel.as_mut())?;
2922 }
2923 Ok(kernel)
2924 }
2925
2926 fn build_slots_with_log(
2928 self,
2929 engine: Engine,
2930 mut log: Option<&mut crate::dsl::events::CompileEventLog>,
2931 ) -> Result<Box<dyn crate::compile::SlotKernel>, KernelError> {
2932 let refused = |reason: String| KernelError::Refused { engine, reason };
2933 let asked = |e: KernelError| match e {
2938 KernelError::Refused { reason, .. } => KernelError::Refused { engine, reason },
2939 other => other,
2940 };
2941 let strict = self.strict;
2942 match engine {
2943 Engine::Interpreter(_) => Err(refused(
2944 "the interpreter has no slot buffer: its buffers are typed `Value`s, so \
2945 there is no slot to name. Ask for `closures`, `native` or `pure-native` \
2946 for the slot surface, or compile with `compile_with` and drive the \
2947 kernel through the `Kernel` trait, which every engine answers."
2948 .into(),
2949 )),
2950 Engine::Closures(prov) => {
2951 let resolved = self.resolve_with_log(log.as_deref_mut())?;
2952 if strict {
2953 Self::refuse_strict(&resolved)?;
2954 }
2955 let folded = log.is_some().then(|| Self::constant_sites(&resolved));
2956 let (node_total, output_total) =
2957 (resolved.nodes.len(), resolved.output_order.len());
2958 let kernel = Self::closures_from(resolved, prov).map_err(asked)?;
2959 Self::log_folded(kernel.as_ref(), folded, log.as_deref_mut());
2960 Self::log_summary(log, node_total, output_total);
2961 Ok(kernel)
2962 }
2963 Engine::Native(prov) => {
2972 {
2973 let resolved = self.resolve_with_log(log.as_deref_mut())?;
2974 if strict {
2975 Self::refuse_strict(&resolved)?;
2976 }
2977 let folded = log.is_some().then(|| Self::constant_sites(&resolved));
2978 let (node_total, output_total) =
2979 (resolved.nodes.len(), resolved.output_order.len());
2980 if prov == Provenance::Push {
2986 return Err(refused(
2987 "native code has no push-only kernel: push-side invalidation \
2988 without the cone guard has no native form. Ask for `pushpull` \
2989 for both, `pull` for the guard alone, or `auto` to let the \
2990 selector choose; `push` alone is available on the closure tier."
2991 .into(),
2992 ));
2993 }
2994 let prov = Self::provenance_for(prov, &resolved);
2995 let kernel = Self::hybrid_from(resolved).map_err(asked)?;
2996 let kernel: Box<dyn crate::compile::SlotKernel> = match prov {
2997 Provenance::Raw => Box::new(kernel.into_raw()),
2998 Provenance::Pull => Box::new(kernel.into_pull()),
2999 Provenance::PushPull | Provenance::Auto => Box::new(kernel),
3005 Provenance::Push => {
3006 return Err(refused("native code has no push-only kernel".into()));
3007 }
3008 };
3009 Self::log_folded(kernel.as_ref(), folded, log.as_deref_mut());
3010 Self::log_summary(log, node_total, output_total);
3011 Ok(kernel)
3012 }
3013 }
3014 Engine::PureNative(prov) => {
3015 #[cfg(feature = "jit")]
3016 {
3017 let resolved = self.resolve_with_log(log.as_deref_mut())?;
3018 if strict {
3019 Self::refuse_strict(&resolved)?;
3020 }
3021 let folded = log.is_some().then(|| Self::constant_sites(&resolved));
3022 let (node_total, output_total) =
3023 (resolved.nodes.len(), resolved.output_order.len());
3024 let prov = match prov {
3031 Provenance::Auto => match Self::provenance_for(prov, &resolved) {
3032 Provenance::Raw => Provenance::Raw,
3033 _ => Provenance::PushPull,
3034 },
3035 named @ (Provenance::Raw | Provenance::PushPull) => named,
3036 other => {
3037 return Err(refused(format!(
3038 "pure native code has no {} kernel: the tier keeps only the \
3039 two forms the differential needs. Ask for `raw` or \
3040 `pushpull`, or `auto` to let the selector choose; every \
3041 mode is available on `native`.",
3042 format!("{other:?}").to_lowercase(),
3043 )));
3044 }
3045 };
3046 let kernel: Box<dyn crate::compile::SlotKernel> = match prov {
3047 Provenance::Raw => Box::new(Self::jit_raw_from(resolved).map_err(asked)?),
3048 _ => Box::new(Self::jit_push_pull_from(resolved).map_err(asked)?),
3049 };
3050 Self::log_folded(kernel.as_ref(), folded, log.as_deref_mut());
3051 Self::log_summary(log, node_total, output_total);
3052 Ok(kernel)
3053 }
3054 #[cfg(not(feature = "jit"))]
3055 {
3056 let _ = (prov, log);
3057 Err(refused(
3058 "this build has no native code (the `jit` feature is off)".into(),
3059 ))
3060 }
3061 }
3062 }
3063 }
3064
3065 fn constant_sites(resolved: &ResolvedDag) -> Vec<(String, usize, crate::ast::PortType)> {
3070 let classes = PolydatProgram::classify_lifecycle(
3071 &resolved.nodes,
3072 &resolved.wiring,
3073 &resolved.input_defs,
3074 &resolved.output_map,
3075 &resolved.output_modifiers,
3076 );
3077 let layout = slot_layout(resolved);
3078 resolved
3079 .nodes
3080 .iter()
3081 .enumerate()
3082 .filter(|(i, n)| {
3083 classes.lifecycle[*i] == crate::kernel::EvalLifecycle::CompileConst
3084 && n.meta().outs.len() == 1
3085 })
3086 .map(|(i, n)| {
3087 (
3088 n.meta().name.clone(),
3089 layout.port_offsets[i][0],
3090 n.meta().outs[0].typ,
3091 )
3092 })
3093 .collect()
3094 }
3095
3096 fn log_folded(
3099 kernel: &dyn Kernel,
3100 sites: Option<Vec<(String, usize, crate::ast::PortType)>>,
3101 log: Option<&mut crate::dsl::events::CompileEventLog>,
3102 ) {
3103 let (Some(sites), Some(log)) = (sites, log) else {
3104 return;
3105 };
3106 for (node, slot, ty) in sites {
3107 let value = crate::kernel::KernelInternals::slot_value(kernel, slot, ty);
3108 if !matches!(value, crate::ast::Value::None) {
3109 log.push(crate::dsl::events::CompileEvent::ConstantFolded {
3110 node,
3111 value: value.to_display_string(),
3112 });
3113 }
3114 }
3115 }
3116
3117 fn provenance_for(prov: Provenance, resolved: &ResolvedDag) -> Provenance {
3122 match prov {
3123 Provenance::Auto => {
3124 let analysis =
3125 select::analyze_graph(&resolved.nodes, &resolved.wiring, &resolved.output_map);
3126 match select::select_prov_mode(&analysis) {
3127 ProvMode::Raw => Provenance::Raw,
3128 ProvMode::Pull => Provenance::Pull,
3129 ProvMode::PushPull => Provenance::PushPull,
3130 }
3131 }
3132 p => p,
3133 }
3134 }
3135
3136 fn closures_from(
3139 resolved: ResolvedDag,
3140 prov: Provenance,
3141 ) -> Result<Box<dyn crate::compile::SlotKernel>, KernelError> {
3142 let prov = Self::provenance_for(prov, &resolved);
3143 let (coord_count, total_slots, steps, output_map, ref_slots, extras) =
3144 Self::build_p2_layout(&resolved).map_err(Self::refused_by_closures)?;
3145 let dependents = || {
3146 slot_layout(&resolved).expand_dependents(
3147 &resolved,
3148 &PolydatProgram::compute_dependents(
3149 &PolydatProgram::compute_provenance(&resolved.nodes, &resolved.wiring),
3150 resolved.input_defs.len(),
3151 ),
3152 )
3153 };
3154 Ok(match prov {
3155 Provenance::Raw => Box::new(CompiledKernelRaw::new(
3156 coord_count,
3157 total_slots,
3158 steps,
3159 output_map,
3160 ref_slots,
3161 extras,
3162 )?),
3163 Provenance::Push => Box::new(CompiledKernelPush::new(
3164 coord_count,
3165 total_slots,
3166 steps,
3167 output_map,
3168 dependents(),
3169 ref_slots,
3170 extras,
3171 )?),
3172 Provenance::Pull => Box::new(CompiledKernelPull::new(
3173 coord_count,
3174 total_slots,
3175 steps,
3176 output_map,
3177 &dependents(),
3178 ref_slots,
3179 extras,
3180 )?),
3181 Provenance::PushPull | Provenance::Auto => Box::new(CompiledKernelPushPull::new(
3182 coord_count,
3183 total_slots,
3184 steps,
3185 output_map,
3186 dependents(),
3187 ref_slots,
3188 extras,
3189 )?),
3190 })
3191 }
3192}