pub mod assembly;
pub mod closures;
pub mod cone;
#[cfg(all(test, feature = "jit"))]
mod cone_tests;
pub(crate) mod externs;
pub mod fusion;
#[cfg(feature = "jit")]
pub(crate) mod fusion_units;
pub mod hybrid;
#[cfg(feature = "jit")]
pub mod jit;
pub mod lattice;
pub mod marshal;
pub mod roundtrip_lint;
pub mod select;
pub mod simd_plan;
#[cfg(feature = "jit")]
pub mod simd_tier1;
macro_rules! ref_readers {
() => {
pub fn read_vec_f32(&self, slot: usize) -> &[f32] {
match self.core.ref_entry(slot) {
crate::ast::ScratchBuf::F32(v) => v,
other => panic!("slot {slot} is not f32-lane scratch: {other:?}"),
}
}
pub fn read_vec_f64(&self, slot: usize) -> &[f64] {
match self.core.ref_entry(slot) {
crate::ast::ScratchBuf::F64(v) => v,
other => panic!("slot {slot} is not f64-lane scratch: {other:?}"),
}
}
pub fn read_vec_f16(&self, slot: usize) -> &[half::f16] {
match self.core.ref_entry(slot) {
crate::ast::ScratchBuf::F16(v) => v,
other => panic!("slot {slot} is not f16-lane scratch: {other:?}"),
}
}
pub fn read_vec_i8(&self, slot: usize) -> &[i8] {
match self.core.ref_entry(slot) {
crate::ast::ScratchBuf::I8(v) => v,
other => panic!("slot {slot} is not i8-lane scratch: {other:?}"),
}
}
pub fn read_vec_i16(&self, slot: usize) -> &[i16] {
match self.core.ref_entry(slot) {
crate::ast::ScratchBuf::I16(v) => v,
other => panic!("slot {slot} is not i16-lane scratch: {other:?}"),
}
}
pub fn read_vec_i32(&self, slot: usize) -> &[i32] {
match self.core.ref_entry(slot) {
crate::ast::ScratchBuf::I32(v) => v,
other => panic!("slot {slot} is not i32-lane scratch: {other:?}"),
}
}
pub fn read_vec_i64(&self, slot: usize) -> &[i64] {
match self.core.ref_entry(slot) {
crate::ast::ScratchBuf::I64(v) => v,
other => panic!("slot {slot} is not i64-lane scratch: {other:?}"),
}
}
};
}
pub(crate) use ref_readers;
macro_rules! kernel_accessors {
($set_coords:ident) => {
pub fn coord_count(&self) -> usize {
self.core.externs.coordinate_count()
}
pub fn resolve_output(&self, name: &str) -> Option<usize> {
self.core.output_map.get(name).copied()
}
#[inline]
pub fn get_slot(&self, slot: usize) -> u64 {
self.core.guard_ref_slot(slot);
self.core.buffer[slot]
}
#[inline]
pub fn get(&self, name: &str) -> u64 {
let slot = self.core.output_map[name];
self.core.guard_ref_slot(slot);
self.core.buffer[slot]
}
pub fn get_value(&self, name: &str) -> crate::ast::Value {
self.core.value_of(name)
}
pub fn pull_output(&mut self, name: &str) -> crate::ast::Value {
self.core.pull_named(name)
}
fn pull_value(&mut self, name: &str) -> crate::ast::Value {
self.apply_pending_coords();
self.pull_output(name)
}
fn pull_value_at(&mut self, index: usize) -> crate::ast::Value {
self.apply_pending_coords();
self.core.pull_at(index)
}
#[inline]
fn apply_pending_coords(&mut self) {
if self.core.drive.stale {
let coords = std::mem::take(&mut self.core.drive.coords);
self.$set_coords(&coords);
self.core.drive.coords = coords;
}
}
fn eval_pending(&mut self) {
let coords = std::mem::take(&mut self.core.drive.coords);
self.eval(&coords);
self.core.drive.coords = coords;
}
pub fn externs(&self) -> Vec<(&str, crate::ast::PortType)> {
self.core.externs.names()
}
pub fn cursor_schemas(&self) -> &[crate::iteration::source::SourceSchema] {
self.core.externs.cursor_schemas()
}
pub fn set_cursor(
&mut self,
name: &str,
partition: &crate::iteration::cursor_partition::Partition,
) -> Result<(), crate::kernel::WriteError> {
for (slot, value) in self.core.externs.cursor_writes(name, partition)? {
self.set_input(&slot, value)?;
}
Ok(())
}
crate::compile::ref_readers!();
};
}
pub(crate) use kernel_accessors;
#[cfg(feature = "jit")]
pub(crate) fn none_rule_admits(
accepts_none: bool,
wiring: &[crate::kernel::WireSource],
eligible: &[bool],
) -> bool {
!accepts_none
|| wiring
.iter()
.all(|src| matches!(src, crate::kernel::WireSource::NodeOutput(j, _) if eligible[*j]))
}
pub fn node_tier(
node: &dyn crate::ast::PolydatNode,
wire_types: &[crate::ast::PortType],
) -> crate::ast::CompileLevel {
#[cfg(feature = "jit")]
if !matches!(
crate::compile::jit::classify_node_typed(node, wire_types),
crate::compile::jit::JitOp::Fallback
) {
return crate::ast::CompileLevel::Phase3;
}
let meta = node.meta();
let is_copy =
(meta.name == "identity" || meta.name.starts_with("__port_")) && meta.outs.len() == 1;
let has_kit = node
.compiled_slot(
wire_types,
crate::compile::select::Engine::Closures(crate::compile::select::Provenance::Auto),
)
.is_some();
if is_copy || node.compiled_u64().is_some() || has_kit {
crate::ast::CompileLevel::Phase2
} else {
crate::ast::CompileLevel::Phase1
}
}
pub(crate) fn slot_provenance(
coord_count: usize,
total_slots: usize,
step_output_slots: &[&[usize]],
input_dependents: &[Vec<usize>],
) -> Vec<crate::kernel::ProvMask> {
use crate::kernel::ProvMask;
let step_count = step_output_slots.len();
let mut step_prov: Vec<ProvMask> = (0..step_count).map(|_| ProvMask::empty()).collect();
for (input_slot, deps) in input_dependents.iter().enumerate() {
for &step in deps {
if step < step_count {
step_prov[step].set(input_slot);
}
}
}
let mut slots: Vec<ProvMask> = (0..total_slots).map(|_| ProvMask::empty()).collect();
for (i, slot) in slots.iter_mut().enumerate().take(coord_count) {
slot.set(i);
}
for (step, outs) in step_output_slots.iter().enumerate() {
for &slot in outs.iter() {
if slot < slots.len() {
slots[slot] = step_prov[step].clone();
}
}
}
slots
}
#[derive(Clone, Default)]
pub(crate) struct Drive {
pub(crate) coords: Vec<u64>,
pub(crate) stale: bool,
}
pub trait SlotKernel: crate::kernel::Kernel {
fn resolve_output(&self, name: &str) -> Option<usize>;
fn get_slot(&self, slot: usize) -> u64;
fn get(&self, name: &str) -> u64;
fn get_value(&self, name: &str) -> crate::ast::Value;
fn eval_for_slot(&mut self, coords: &[u64], slot: usize) -> u64;
fn eval_at(&mut self, coords: &[u64]);
fn read_vec_f32(&self, slot: usize) -> &[f32];
fn read_vec_f64(&self, slot: usize) -> &[f64];
fn read_vec_f16(&self, slot: usize) -> &[half::f16];
fn read_vec_i8(&self, slot: usize) -> &[i8];
fn read_vec_i16(&self, slot: usize) -> &[i16];
fn read_vec_i32(&self, slot: usize) -> &[i32];
fn read_vec_i64(&self, slot: usize) -> &[i64];
}
macro_rules! impl_slot_kernel {
($ty:ident) => {
impl crate::compile::SlotKernel for $ty {
fn resolve_output(&self, name: &str) -> Option<usize> {
$ty::resolve_output(self, name)
}
fn get_slot(&self, slot: usize) -> u64 {
$ty::get_slot(self, slot)
}
fn get(&self, name: &str) -> u64 {
$ty::get(self, name)
}
fn get_value(&self, name: &str) -> crate::ast::Value {
$ty::get_value(self, name)
}
fn eval_for_slot(&mut self, coords: &[u64], slot: usize) -> u64 {
$ty::eval_for_slot(self, coords, slot)
}
fn eval_at(&mut self, coords: &[u64]) {
$ty::eval(self, coords)
}
fn read_vec_f32(&self, slot: usize) -> &[f32] {
$ty::read_vec_f32(self, slot)
}
fn read_vec_f64(&self, slot: usize) -> &[f64] {
$ty::read_vec_f64(self, slot)
}
fn read_vec_f16(&self, slot: usize) -> &[half::f16] {
$ty::read_vec_f16(self, slot)
}
fn read_vec_i8(&self, slot: usize) -> &[i8] {
$ty::read_vec_i8(self, slot)
}
fn read_vec_i16(&self, slot: usize) -> &[i16] {
$ty::read_vec_i16(self, slot)
}
fn read_vec_i32(&self, slot: usize) -> &[i32] {
$ty::read_vec_i32(self, slot)
}
fn read_vec_i64(&self, slot: usize) -> &[i64] {
$ty::read_vec_i64(self, slot)
}
}
};
}
pub(crate) use impl_slot_kernel;
macro_rules! impl_kernel_trait {
($ty:ident) => {
impl crate::kernel::Kernel for $ty {
fn engine(&self) -> crate::compile::select::Engine {
self.core.engine
}
fn set_inputs(&mut self, coords: &[u64]) {
self.core.drive.coords.clear();
self.core.drive.coords.extend_from_slice(coords);
self.core.drive.stale = true;
}
fn set_input(
&mut self,
name: &str,
value: crate::ast::Value,
) -> Result<(), crate::kernel::WriteError> {
if self.core.externs.is_const_name(name) {
return Err(crate::kernel::WriteError::ConstSlot {
slot: name.to_string(),
});
}
self.core.drive.stale = true;
$ty::set_input(self, name, value)
}
fn const_inits(&self) -> &[crate::kernel::ConstInit] {
self.core.externs.const_inits()
}
fn init_input_at(
&mut self,
index: usize,
value: crate::ast::Value,
) -> Result<(), crate::kernel::WriteError> {
self.core.drive.stale = true;
$ty::set_input_at(self, index, value)
}
fn set_cursor(
&mut self,
name: &str,
partition: &crate::iteration::cursor_partition::Partition,
) -> Result<(), crate::kernel::WriteError> {
self.core.drive.stale = true;
$ty::set_cursor(self, name, partition)
}
fn eval(&mut self) {
self.eval_pending();
self.core.drive.stale = false;
}
fn pull(&mut self, name: &str) -> crate::ast::Value {
self.pull_value(name)
}
fn input_names(&self) -> Vec<String> {
self.core.externs.input_names().to_vec()
}
fn output_names(&self) -> Vec<String> {
self.core.externs.output_names().to_vec()
}
fn output_type(&self, name: &str) -> Option<crate::ast::PortType> {
self.core.output_types.get(name).copied()
}
fn externs(&self) -> Vec<(String, crate::ast::PortType)> {
self.core
.externs
.names()
.into_iter()
.map(|(n, t)| (n.to_string(), t))
.collect()
}
fn cursor_schemas(&self) -> &[crate::iteration::source::SourceSchema] {
self.core.externs.cursor_schemas()
}
fn input_value(&self, name: &str) -> Option<crate::ast::Value> {
self.core.externs.value(name).or_else(|| {
let i = self
.core
.externs
.input_names()
.iter()
.position(|n| n == name)?;
if i < self.core.externs.coordinate_count() {
let pending = self.core.drive.coords.get(i).copied();
Some(crate::ast::Value::U64(
pending.unwrap_or(self.core.buffer[i]),
))
} else {
None
}
})
}
fn traversals(&self) -> &[crate::dsl::traversal::Traversal] {
&self.core.traversals
}
fn plan(&self) -> crate::EnginePlan {
self.core.plan()
}
fn input_index(&self, name: &str) -> Option<usize> {
self.core
.externs
.input_names()
.iter()
.position(|n| n == name)
}
fn set_input_at(
&mut self,
index: usize,
value: crate::ast::Value,
) -> Result<(), crate::kernel::WriteError> {
if self.core.externs.is_const_index(index) {
return Err(crate::kernel::WriteError::ConstSlot {
slot: self.core.externs.input_names()[index].clone(),
});
}
self.core.drive.stale = true;
$ty::set_input_at(self, index, value)
}
fn output_index(&self, name: &str) -> Option<usize> {
self.core
.externs
.output_names()
.iter()
.position(|n| n == name)
}
fn pull_at(&mut self, index: usize) -> crate::ast::Value {
self.pull_value_at(index)
}
fn traverse(&mut self, index: usize) -> Result<crate::kernel::TraversalStream, String> {
let traversal = self.core.traversals.get(index).cloned().ok_or_else(|| {
format!(
"no traversal at index {index}; the program declares {}",
self.core.traversals.len()
)
})?;
crate::kernel::activation::open_traversal(self, traversal)
}
fn invalidate_all(&mut self) {
self.mark_all_dirty();
self.core.invalidate_all();
}
fn shared_cells(&self) -> Vec<crate::kernel::SharedCellEntry> {
self.core.externs.shared_cells()
}
fn output_cell(&self, name: &str) -> Option<crate::kernel::SharedCell> {
self.core.output_cell_for(name)
}
fn output_modifier(&self, name: &str) -> crate::dsl::ast::BindingModifier {
self.core.externs.output_modifier(name)
}
fn cells_in_scope(&self) -> Vec<crate::kernel::SharedCellEntry> {
self.core.externs.cells_in_scope()
}
fn set_transit_cells(&mut self, cells: Vec<crate::kernel::SharedCellEntry>) {
self.core.externs.set_transit_cells(cells);
}
fn input_port_type(&self, name: &str) -> Option<crate::ast::PortType> {
self.core.externs.input_port_type(name)
}
fn bind_input_cell(&mut self, name: &str, cell: crate::kernel::SharedCell) -> bool {
let Some(slot) = self.core.externs.bind_cell(name, cell) else {
return false;
};
self.core.dirty_input(slot);
true
}
fn attach_shared_cell(
&mut self,
name: &str,
cell: crate::kernel::SharedCell,
) -> Result<(), String> {
self.core.attach_cell(name, cell)
}
fn into_program(
mut self: Box<Self>,
) -> std::sync::Arc<dyn crate::kernel::KernelProgram> {
self.mark_all_dirty();
self.core.drive.stale = true;
std::sync::Arc::new(crate::kernel::SharedKernel(*self))
}
fn ledger(&self) -> &std::sync::Arc<crate::kernel::CompileLedger> {
self.core.externs.ledger()
}
fn resources(&self) -> &crate::resource::ResourceScope {
self.core.externs.resources()
}
fn canonical_hash(&self) -> [u8; 32] {
crate::kernel::program_identity(
self.core.externs.graph_identity(),
self.core.externs.inherited_outputs().iter(),
crate::kernel::Kernel::cursor_schemas(self),
crate::kernel::Kernel::traversals(self),
)
}
fn coord_count(&self) -> usize {
self.core.externs.coordinate_count()
}
fn input_value_at(&self, index: usize) -> Option<crate::ast::Value> {
if index < self.core.externs.coordinate_count() {
let pending = self.core.drive.coords.get(index).copied();
return Some(crate::ast::Value::U64(
pending.unwrap_or(self.core.buffer[index]),
));
}
self.core.externs.value_at(index)
}
fn input_default_at(&self, index: usize) -> Option<crate::ast::Value> {
if index < self.core.externs.coordinate_count() {
return Some(crate::ast::Value::U64(0));
}
self.core.externs.default_at(index)
}
fn input_is_cell_bound(&self, index: usize) -> bool {
self.core.externs.is_cell_bound_at(index)
}
fn reset_inputs(&mut self) {
let count = self.core.externs.input_names().len();
for index in self.core.externs.coordinate_count()..count {
if self.core.externs.is_cell_bound_at(index) {
continue;
}
let (Some(now), Some(default)) = (
self.core.externs.value_at(index),
self.core.externs.default_at(index),
) else {
continue;
};
if now != default {
let _ = crate::kernel::Kernel::set_input_at(self, index, default);
}
}
}
fn fork(&self) -> Box<dyn crate::kernel::Kernel> {
Box::new(self.clone())
}
fn publish_broadcasts(&mut self) {
if !self.core.externs.broadcasts() {
return;
}
let names: Vec<String> = self.core.externs.output_names().to_vec();
for name in names {
let Some(&slot) = self.core.output_map.get(&name) else {
continue;
};
if self.core.externs.published_output(slot).is_none() {
continue;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
self.pull_value(&name);
}));
}
}
fn commit_write_throughs(&mut self) -> Result<(), String> {
let pairs = self.core.externs.write_throughs().to_vec();
let mut pending = Vec::with_capacity(pairs.len());
for (export, source) in &pairs {
let Some(slot_type) = self.core.externs.input_port_type(export) else {
continue;
};
let value = self.pull_value(source);
let value =
crate::kernel::check_write_through_type(export, source, slot_type, value)?;
pending.push((export.clone(), value));
}
for (export, value) in pending {
crate::kernel::Kernel::set_input(self, &export, value)
.map_err(|e| format!("write-through into `{export}`: {e}"))?;
}
Ok(())
}
fn program_id(&self) -> crate::kernel::ProgramId {
crate::kernel::ProgramId(self.core.program_identity())
}
fn input_type_origin(&self, name: &str) -> Option<crate::kernel::TypeOrigin> {
self.core.externs.input_type_origin(name)
}
}
impl crate::kernel::KernelInternals for $ty {
fn set_write_throughs(&mut self, pairs: Vec<(String, String)>) {
self.core.externs.set_write_throughs(pairs);
}
fn set_inherited_outputs(&mut self, names: Vec<String>) {
self.core.externs.set_inherited_outputs(names);
}
fn set_traversals(
&mut self,
traversals: Vec<crate::dsl::traversal::Traversal>,
_producers: Vec<crate::dsl::traversal::Producer>,
) {
self.core.traversals = traversals.into();
}
fn slot_value(&self, slot: usize, ty: crate::ast::PortType) -> crate::ast::Value {
self.core.slot_value(slot, ty)
}
fn folded_value(&self, name: &str) -> Option<crate::ast::Value> {
if !self.core.externs.is_fixed_output(name) {
return None;
}
if let Some(c) = self
.core
.externs
.const_inits()
.iter()
.find(|c| c.name == name)
{
return crate::kernel::Kernel::input_value_at(self, c.slot_index);
}
let slot = *self.core.output_map.get(name)?;
let ty = *self.core.output_types.get(name)?;
Some(self.core.slot_value(slot, ty))
}
fn set_cursor_extent(&mut self, index: usize, extent: u64) {
self.core.externs.set_cursor_extent(index, extent);
}
fn reset_to_program(&mut self) {
self.core.externs.reset_to_program(&mut self.core.buffer);
self.mark_all_dirty();
}
}
};
}
pub(crate) use impl_kernel_trait;
macro_rules! shared_core_methods {
() => {
fn ref_entry(&self, slot: usize) -> &crate::ast::ScratchBuf {
match self.ref_scratch.iter().find(|(s, _)| *s == slot) {
Some(&(_, idx)) => &self.scratch[idx],
None if self.ref_slots.get(slot).copied().unwrap_or(false) => panic!(
"slot {slot} is a Ref pair owned by the CALLER (a kernel \
input) โ read it on the caller side"
),
None => panic!("slot {slot} is not a Ref2-colored slot"),
}
}
#[inline]
fn run_guarded(&mut self, body: impl FnOnce(&mut Self)) {
let capture = crate::kernel::engines::EvalPanicCaptureGuard::arm();
let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| body(self)));
drop(capture);
if let Err(payload) = outcome {
let sites = std::sync::Arc::clone(&self.sites);
let node = self.failing_node();
sites.reraise(payload, node, &self.buffer, Some(&self.none));
}
#[cfg(debug_assertions)]
self.validate_refs();
}
#[cfg(debug_assertions)]
fn validate_refs(&self) {
for &(slot, idx) in &self.ref_scratch {
let unpublished = self.none[slot]
|| matches!(self.slot_step.get(slot), Some(Some(step)) if self.ran[*step] == 0);
if unpublished {
continue;
}
let (p, l) = self.scratch[idx].ptr_len();
assert!(
self.buffer[slot] == p && self.buffer[slot + 1] == l,
"S9 ref-validator: slot pair ({slot}, {}) = ({:#x}, {}) \
does not match scratch[{idx}] = ({p:#x}, {l}) โ a slot \
op failed to republish or wrote the wrong slots",
slot + 1,
self.buffer[slot],
self.buffer[slot + 1],
);
}
}
fn attach_cell(
&mut self,
name: &str,
cell: crate::kernel::SharedCell,
) -> Result<(), String> {
let slot = self.externs.attach_cell(name, cell)?;
self.dirty_input(slot);
self.drive.stale = true;
Ok(())
}
#[inline]
fn begin_epoch(&mut self) {
if self.externs.cells_dirty() {
self.externs.refresh_cells(&mut self.buffer);
}
self.dirty_refreshed();
self.epoch += 1;
self.all_ran = false;
for &i in self.volatile_steps.iter() {
self.clean[i] = false;
}
self.drive.stale = false;
}
#[inline]
fn rearm_volatile(&mut self) {
if self.volatile_steps.is_empty() {
return;
}
for &i in self.volatile_steps.iter() {
self.ran[i] = 0;
}
self.all_ran = false;
}
#[inline]
fn dirty_input(&mut self, slot: usize) {
if let Some(deps) = self.dirty.get(slot) {
for &i in deps {
self.clean[i] = false;
}
}
}
#[inline]
fn dirty_refreshed(&mut self) {
if self.externs.has_changed() {
self.dirty_refreshed_slots();
}
}
#[cold]
#[inline(never)]
fn dirty_refreshed_slots(&mut self) {
let changed = self.externs.take_changed();
for &slot in &changed {
if let Some(mask) = self.none.get_mut(slot) {
*mask = self.externs.slot_is_unset(slot);
}
if let Some(deps) = self.plan.input_dependents.get(slot) {
for &i in deps {
self.ran[i] = 0;
self.clean[i] = false;
}
self.all_ran = false;
}
}
self.externs.return_changed(changed);
let was = self.any_none;
self.any_none = self.externs.any_unset();
if was && !self.any_none {
self.none.fill(false);
}
}
#[inline]
fn eval_all(&mut self) {
let fresh = self.drive.stale;
if fresh {
self.begin_epoch();
} else {
self.refresh_cells();
self.rearm_volatile();
}
if fresh && !self.use_clean && !self.any_none {
self.run_guarded(|core| core.run_fresh());
} else {
let all = std::sync::Arc::clone(&self.all);
self.run_steps(&all);
}
}
fn extern_written(&mut self, slot: usize, unset: bool) {
self.none[slot] = unset;
let was = self.any_none;
self.any_none = self.externs.any_unset();
if was && !self.any_none {
self.none.fill(false);
}
self.dirty_input(slot);
self.drive.stale = true;
}
#[inline]
fn guard_ref_slot(&self, slot: usize) {
if self.ref_slots.get(slot).copied().unwrap_or(false) {
panic!(
"S2 pointer containment: slot {slot} is Ref2-colored; raw u64 readers \
would leak an interior address. Use the typed borrow-checked accessor \
(read_vec_*), the boundary decode, or copy out."
);
}
}
fn invalidate_all(&mut self) {
self.clean.fill(false);
self.all_ran = false;
self.drive.stale = true;
}
#[inline]
fn pull_at(&mut self, index: usize) -> crate::ast::Value {
if self.externs.broadcasts() {
return self.pull_at_publishing(index);
}
self.pull_at_value(index)
}
#[cold]
#[inline(never)]
fn pull_at_publishing(&mut self, index: usize) -> crate::ast::Value {
let value = self.pull_at_value(index);
let slot = self.resolved_outputs[index]
.as_ref()
.expect("resolved by the pull")
.0;
self.publish_slot(slot, &value);
value
}
#[inline(always)]
fn pull_at_value(&mut self, index: usize) -> crate::ast::Value {
if self.resolved_outputs.len() <= index {
self.resolved_outputs.resize(index + 1, None);
}
if self.resolved_outputs[index].is_none() {
let name = self
.externs
.output_names()
.get(index)
.cloned()
.unwrap_or_else(|| {
panic!(
"no output at index {index}; this kernel declares {}",
self.externs.output_names().len()
)
});
let slot = self.output_map[&name];
let ty = self
.output_types
.get(&name)
.copied()
.unwrap_or(crate::ast::PortType::U64);
let cone = self
.plan
.cones
.get(&name)
.map(|c| std::sync::Arc::<[usize]>::from(c.as_slice()));
let can_fail = cone
.as_ref()
.is_some_and(|c| c.iter().any(|&i| self.step_can_fail(i)));
self.resolved_outputs[index] = Some((slot, ty, cone, can_fail));
}
if self.drive.stale {
self.begin_epoch();
} else {
self.refresh_cells();
self.rearm_volatile();
}
let resolved = self.resolved_outputs[index]
.as_ref()
.expect("resolved above");
let (slot, ty, can_fail) = (resolved.0, resolved.1, resolved.3);
if let Some(order) = &resolved.2 {
let order: *const [usize] = &**order;
let order = unsafe { &*order };
if can_fail {
self.run_steps(order);
} else {
self.run_order(order);
}
}
self.slot_value(slot, ty)
}
fn publish_slot(&self, slot: usize, value: &crate::ast::Value) {
if let Some(cell) = self.externs.published_output(slot) {
cell.publish(value.clone());
}
}
fn pull_named(&mut self, name: &str) -> crate::ast::Value {
if self.drive.stale {
self.begin_epoch();
} else {
self.refresh_cells();
self.rearm_volatile();
}
let plan = std::sync::Arc::clone(&self.plan);
if let Some(order) = plan.cones.get(name) {
self.run_steps(order);
}
let value = self.value_of(name);
self.broadcast(name, &value);
value
}
#[inline]
fn broadcast(&mut self, name: &str, value: &crate::ast::Value) {
if !self.externs.broadcasts() {
return;
}
if let Some(&slot) = self.output_map.get(name) {
self.publish_slot(slot, value);
}
}
fn output_cell_for(&self, name: &str) -> Option<crate::kernel::SharedCell> {
let slot = *self.output_map.get(name)?;
let uncomputed = !self.all_ran
&& matches!(self.slot_step.get(slot), Some(Some(step)) if self.ran[*step] == 0);
let initial = if uncomputed {
crate::ast::Value::None
} else {
self.value_of(name)
};
Some(self.externs.output_cell(slot, initial))
}
#[inline]
fn refresh_cells(&mut self) {
if self.externs.cells_dirty() {
self.externs.refresh_cells(&mut self.buffer);
self.dirty_refreshed();
}
}
fn republish_refs(&mut self) {
for &(slot, idx) in &self.ref_scratch {
let (p, l) = self.scratch[idx].ptr_len();
self.buffer[slot] = p;
self.buffer[slot + 1] = l;
}
self.externs.seed(&mut self.buffer, None);
}
#[inline]
fn run_steps(&mut self, order: &[usize]) {
self.run_guarded(|core| core.run_order(order));
}
fn fold_steps(&mut self, order: &[usize]) -> Result<(), crate::KernelError> {
let capture = crate::kernel::engines::EvalPanicCaptureGuard::arm();
let outcome =
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| self.run_order(order)));
drop(capture);
if let Err(payload) = outcome {
let sites = std::sync::Arc::clone(&self.sites);
let node = self.failing_node();
return Err(crate::KernelError::ConstantFold {
reason: sites.describe(payload, node, &self.buffer, Some(&self.none)),
});
}
Ok(())
}
fn set_extern(
&mut self,
name: &str,
value: crate::ast::Value,
) -> Result<usize, crate::kernel::WriteError> {
let (slot, unset) = self.externs.set(name, value, &mut self.buffer)?;
self.extern_written(slot, unset);
Ok(slot)
}
fn set_extern_at(
&mut self,
index: usize,
value: crate::ast::Value,
) -> Result<usize, crate::kernel::WriteError> {
let (slot, unset) = self.externs.set_at(index, value, &mut self.buffer)?;
self.extern_written(slot, unset);
Ok(slot)
}
fn slot_value(&self, slot: usize, ty: crate::ast::PortType) -> crate::ast::Value {
if self.none.get(slot).copied().unwrap_or(false) {
return crate::ast::Value::None;
}
crate::compile::marshal::decode_output(&self.buffer, slot, ty)
}
fn value_of(&self, name: &str) -> crate::ast::Value {
let slot = self.output_map[name];
let ty = self
.output_types
.get(name)
.copied()
.unwrap_or(crate::ast::PortType::U64);
self.slot_value(slot, ty)
}
};
}
pub(crate) use shared_core_methods;
pub(crate) struct Invalidation {
pub(crate) input_dependents: Vec<Vec<usize>>,
pub(crate) cones: std::collections::HashMap<String, Vec<usize>>,
}
impl Invalidation {
pub(crate) fn from_provenance(
input_dependents: Vec<Vec<usize>>,
step_inputs: &[&[usize]],
step_outputs: &[&[usize]],
output_slots: &std::collections::HashMap<String, usize>,
total_slots: usize,
) -> Self {
let step_count = step_inputs.len();
let mut slot_step: Vec<Option<usize>> = vec![None; total_slots];
for (i, outs) in step_outputs.iter().enumerate() {
for &s in outs.iter() {
slot_step[s] = Some(i);
}
}
let cones = output_slots
.iter()
.map(|(name, &slot)| {
let mut wanted = vec![false; step_count];
let mut stack: Vec<usize> = slot_step[slot].into_iter().collect();
while let Some(i) = stack.pop() {
if wanted[i] {
continue;
}
wanted[i] = true;
stack.extend(step_inputs[i].iter().filter_map(|&s| slot_step[s]));
}
(
name.clone(),
(0..step_count).filter(|&i| wanted[i]).collect(),
)
})
.collect();
Self {
input_dependents,
cones,
}
}
}
#[derive(Default)]
pub(crate) struct Attribution {
pub(crate) sites: Vec<NodeSite>,
pub(crate) context: String,
}
pub(crate) struct NodeSite {
pub(crate) name: String,
pub(crate) outputs: Vec<String>,
pub(crate) inputs: Vec<(usize, crate::ast::PortType)>,
}
impl Attribution {
fn inputs_of(&self, step: usize, buffer: &[u64], none: Option<&[bool]>) -> Vec<String> {
let Some(site) = self.sites.get(step) else {
return Vec::new();
};
let _quiet = crate::kernel::engines::EvalPanicCaptureGuard::arm();
site.inputs
.iter()
.map(|&(slot, ty)| {
if none.is_some_and(|m| m.get(slot).copied().unwrap_or(false)) {
return "None".to_string();
}
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
crate::kernel::engines::format_value_for_diag(&marshal::decode_output(
buffer, slot, ty,
))
}))
.unwrap_or_else(|_| format!("{ty:?}"))
})
.collect()
}
pub(crate) fn reraise(
&self,
payload: Box<dyn std::any::Any + Send>,
step: usize,
buffer: &[u64],
none: Option<&[bool]>,
) -> ! {
crate::kernel::engines::reraise_enriched(self.describe(payload, step, buffer, none))
}
pub(crate) fn describe(
&self,
payload: Box<dyn std::any::Any + Send>,
step: usize,
buffer: &[u64],
none: Option<&[bool]>,
) -> String {
let site = self.sites.get(step);
let name = site
.map(|s| s.name.clone())
.unwrap_or_else(|| format!("<unknown node #{step}>"));
let outputs: Vec<&str> = site
.map(|s| s.outputs.iter().map(String::as_str).collect())
.unwrap_or_default();
let inputs = self.inputs_of(step, buffer, none);
crate::kernel::engines::enrich_panic(payload, &name, &outputs, &self.context, &inputs)
}
}