1pub mod assembly;
34pub mod closures;
35pub mod cone;
36#[cfg(all(test, feature = "jit"))]
37mod cone_tests;
38pub(crate) mod externs;
39pub mod fusion;
40pub mod hybrid;
41#[cfg(feature = "jit")]
42pub mod jit;
43pub mod lattice;
44pub mod marshal;
48pub mod roundtrip_lint;
49pub mod select;
50pub mod simd_plan;
51#[cfg(feature = "jit")]
52pub mod simd_tier1;
53
54macro_rules! ref_readers {
60 () => {
61 pub fn read_vec_f32(&self, slot: usize) -> &[f32] {
63 match self.core.ref_entry(slot) {
64 crate::ast::ScratchBuf::F32(v) => v,
65 other => panic!("slot {slot} is not f32-lane scratch: {other:?}"),
66 }
67 }
68 pub fn read_vec_f64(&self, slot: usize) -> &[f64] {
70 match self.core.ref_entry(slot) {
71 crate::ast::ScratchBuf::F64(v) => v,
72 other => panic!("slot {slot} is not f64-lane scratch: {other:?}"),
73 }
74 }
75 pub fn read_vec_f16(&self, slot: usize) -> &[half::f16] {
77 match self.core.ref_entry(slot) {
78 crate::ast::ScratchBuf::F16(v) => v,
79 other => panic!("slot {slot} is not f16-lane scratch: {other:?}"),
80 }
81 }
82 pub fn read_vec_i8(&self, slot: usize) -> &[i8] {
84 match self.core.ref_entry(slot) {
85 crate::ast::ScratchBuf::I8(v) => v,
86 other => panic!("slot {slot} is not i8-lane scratch: {other:?}"),
87 }
88 }
89 pub fn read_vec_i16(&self, slot: usize) -> &[i16] {
91 match self.core.ref_entry(slot) {
92 crate::ast::ScratchBuf::I16(v) => v,
93 other => panic!("slot {slot} is not i16-lane scratch: {other:?}"),
94 }
95 }
96 pub fn read_vec_i32(&self, slot: usize) -> &[i32] {
98 match self.core.ref_entry(slot) {
99 crate::ast::ScratchBuf::I32(v) => v,
100 other => panic!("slot {slot} is not i32-lane scratch: {other:?}"),
101 }
102 }
103 pub fn read_vec_i64(&self, slot: usize) -> &[i64] {
105 match self.core.ref_entry(slot) {
106 crate::ast::ScratchBuf::I64(v) => v,
107 other => panic!("slot {slot} is not i64-lane scratch: {other:?}"),
108 }
109 }
110 };
111}
112pub(crate) use ref_readers;
113
114pub(crate) fn slot_provenance(
122 coord_count: usize,
123 total_slots: usize,
124 step_output_slots: &[&[usize]],
125 input_dependents: &[Vec<usize>],
126) -> Vec<crate::kernel::ProvMask> {
127 use crate::kernel::ProvMask;
128 let step_count = step_output_slots.len();
129 let mut step_prov: Vec<ProvMask> = (0..step_count).map(|_| ProvMask::empty()).collect();
130 for (input_slot, deps) in input_dependents.iter().enumerate() {
131 for &step in deps {
132 if step < step_count {
133 step_prov[step].set(input_slot);
134 }
135 }
136 }
137 let mut slots: Vec<ProvMask> = (0..total_slots).map(|_| ProvMask::empty()).collect();
138 for (i, slot) in slots.iter_mut().enumerate().take(coord_count) {
139 slot.set(i);
140 }
141 for (step, outs) in step_output_slots.iter().enumerate() {
142 for &slot in outs.iter() {
143 if slot < slots.len() {
144 slots[slot] = step_prov[step].clone();
145 }
146 }
147 }
148 slots
149}
150
151#[derive(Clone, Default)]
155pub(crate) struct Drive {
156 pub(crate) coords: Vec<u64>,
157 pub(crate) stale: bool,
158}
159
160macro_rules! impl_kernel_trait {
165 ($ty:ident, $engine:expr) => {
166 impl crate::kernel::Kernel for $ty {
167 fn engine(&self) -> crate::compile::select::Engine {
168 $engine
169 }
170 fn set_inputs(&mut self, coords: &[u64]) {
171 self.core.drive.coords.clear();
172 self.core.drive.coords.extend_from_slice(coords);
173 self.core.drive.stale = true;
174 }
175 fn set_input(&mut self, name: &str, value: crate::ast::Value) -> Result<(), String> {
176 self.core.drive.stale = true;
177 $ty::set_input(self, name, value)
178 }
179 fn set_cursor(
180 &mut self,
181 name: &str,
182 partition: &crate::iteration::cursor_partition::Partition,
183 ) -> Result<(), String> {
184 self.core.drive.stale = true;
185 $ty::set_cursor(self, name, partition)
186 }
187 fn eval(&mut self) {
188 self.eval_pending();
189 self.core.drive.stale = false;
190 }
191 fn pull(&mut self, name: &str) -> crate::ast::Value {
192 self.pull_value(name)
193 }
194 fn input_names(&self) -> Vec<String> {
195 self.core.externs.input_names().to_vec()
196 }
197 fn output_names(&self) -> Vec<String> {
200 self.core.externs.output_names().to_vec()
201 }
202 fn output_type(&self, name: &str) -> Option<crate::ast::PortType> {
203 self.core.output_types.get(name).copied()
204 }
205 fn externs(&self) -> Vec<(String, crate::ast::PortType)> {
206 self.core
207 .externs
208 .names()
209 .into_iter()
210 .map(|(n, t)| (n.to_string(), t))
211 .collect()
212 }
213 fn cursor_schemas(&self) -> &[crate::iteration::source::SourceSchema] {
214 self.core.externs.cursor_schemas()
215 }
216 fn input_value(&self, name: &str) -> Option<crate::ast::Value> {
217 self.core.externs.value(name).or_else(|| {
218 let i = self
219 .core
220 .externs
221 .input_names()
222 .iter()
223 .position(|n| n == name)?;
224 if i < self.core.coord_count {
225 let pending = self.core.drive.coords.get(i).copied();
226 Some(crate::ast::Value::U64(
227 pending.unwrap_or(self.core.buffer[i]),
228 ))
229 } else {
230 None
231 }
232 })
233 }
234 fn traversals(&self) -> &[crate::dsl::traversal::Traversal] {
235 &self.core.traversals
236 }
237 fn plan(&self) -> crate::EnginePlan {
238 self.core.plan()
239 }
240 fn input_index(&self, name: &str) -> Option<usize> {
241 self.core
242 .externs
243 .input_names()
244 .iter()
245 .position(|n| n == name)
246 }
247 fn set_input_at(
248 &mut self,
249 index: usize,
250 value: crate::ast::Value,
251 ) -> Result<(), String> {
252 self.core.drive.stale = true;
253 $ty::set_input_at(self, index, value)
254 }
255 fn output_index(&self, name: &str) -> Option<usize> {
256 self.core
257 .externs
258 .output_names()
259 .iter()
260 .position(|n| n == name)
261 }
262 fn pull_at(&mut self, index: usize) -> crate::ast::Value {
263 self.pull_value_at(index)
264 }
265 fn traverse(&mut self, index: usize) -> Result<crate::kernel::TraversalStream, String> {
266 let traversal = self.core.traversals.get(index).cloned().ok_or_else(|| {
267 format!(
268 "no traversal at index {index}; the program declares {}",
269 self.core.traversals.len()
270 )
271 })?;
272 crate::kernel::activation::open_traversal(self, traversal)
273 }
274 fn invalidate_all(&mut self) {
275 self.mark_all_dirty();
276 self.core.invalidate_all();
277 }
278 fn shared_cells(&self) -> Vec<crate::kernel::SharedCellEntry> {
279 self.core.externs.shared_cells()
280 }
281 fn attach_shared_cell(
282 &mut self,
283 name: &str,
284 cell: crate::kernel::SharedCell,
285 ) -> Result<(), String> {
286 self.core.attach_cell(name, cell)
287 }
288 fn into_program(
289 mut self: Box<Self>,
290 ) -> std::sync::Arc<dyn crate::kernel::KernelProgram> {
291 self.mark_all_dirty();
292 self.core.drive.stale = true;
293 std::sync::Arc::new(crate::kernel::SharedKernel(*self))
294 }
295 }
296
297 impl crate::kernel::KernelInternals for $ty {
298 fn set_traversals(
301 &mut self,
302 traversals: Vec<crate::dsl::traversal::Traversal>,
303 _producers: Vec<crate::dsl::traversal::Producer>,
304 ) {
305 self.core.traversals = traversals.into();
306 }
307 fn slot_value(&self, slot: usize, ty: crate::ast::PortType) -> crate::ast::Value {
308 self.core.slot_value(slot, ty)
309 }
310 fn folded_value(&self, name: &str) -> Option<crate::ast::Value> {
311 let slot = *self.core.output_map.get(name)?;
312 let ty = *self.core.output_types.get(name)?;
313 Some(self.core.slot_value(slot, ty))
314 }
315 fn set_cursor_extent(&mut self, index: usize, extent: u64) {
316 self.core.externs.set_cursor_extent(index, extent);
317 }
318 fn reset_to_program(&mut self) {
319 self.core.externs.reset_to_program(&mut self.core.buffer);
320 self.mark_all_dirty();
321 }
322 }
323 };
324}
325pub(crate) use impl_kernel_trait;
326
327pub(crate) struct Invalidation {
334 pub(crate) input_dependents: Vec<Vec<usize>>,
337 pub(crate) cones: std::collections::HashMap<String, Vec<usize>>,
339}
340
341impl Invalidation {
342 pub(crate) fn from_provenance(
347 input_dependents: Vec<Vec<usize>>,
348 step_inputs: &[&[usize]],
349 step_outputs: &[&[usize]],
350 output_slots: &std::collections::HashMap<String, usize>,
351 total_slots: usize,
352 ) -> Self {
353 let step_count = step_inputs.len();
354 let mut slot_step: Vec<Option<usize>> = vec![None; total_slots];
355 for (i, outs) in step_outputs.iter().enumerate() {
356 for &s in outs.iter() {
357 slot_step[s] = Some(i);
358 }
359 }
360 let cones = output_slots
361 .iter()
362 .map(|(name, &slot)| {
363 let mut wanted = vec![false; step_count];
364 let mut stack: Vec<usize> = slot_step[slot].into_iter().collect();
365 while let Some(i) = stack.pop() {
366 if wanted[i] {
367 continue;
368 }
369 wanted[i] = true;
370 stack.extend(step_inputs[i].iter().filter_map(|&s| slot_step[s]));
371 }
372 (
373 name.clone(),
374 (0..step_count).filter(|&i| wanted[i]).collect(),
375 )
376 })
377 .collect();
378 Self {
379 input_dependents,
380 cones,
381 }
382 }
383}
384
385#[derive(Default)]
393pub(crate) struct Attribution {
394 pub(crate) sites: Vec<NodeSite>,
395 pub(crate) context: String,
397}
398
399pub(crate) struct NodeSite {
401 pub(crate) name: String,
402 pub(crate) outputs: Vec<String>,
404 pub(crate) inputs: Vec<(usize, crate::ast::PortType)>,
406}
407
408impl Attribution {
409 fn inputs_of(&self, step: usize, buffer: &[u64], none: Option<&[bool]>) -> Vec<String> {
414 let Some(site) = self.sites.get(step) else {
415 return Vec::new();
416 };
417 let _quiet = crate::kernel::engines::EvalPanicCaptureGuard::arm();
418 site.inputs
419 .iter()
420 .map(|&(slot, ty)| {
421 if none.is_some_and(|m| m.get(slot).copied().unwrap_or(false)) {
422 return "None".to_string();
423 }
424 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
425 crate::kernel::engines::format_value_for_diag(&marshal::decode_output(
426 buffer, slot, ty,
427 ))
428 }))
429 .unwrap_or_else(|_| format!("{ty:?}"))
430 })
431 .collect()
432 }
433
434 pub(crate) fn reraise(
439 &self,
440 payload: Box<dyn std::any::Any + Send>,
441 step: usize,
442 buffer: &[u64],
443 none: Option<&[bool]>,
444 ) -> ! {
445 let site = self.sites.get(step);
446 let name = site
447 .map(|s| s.name.clone())
448 .unwrap_or_else(|| format!("<unknown node #{step}>"));
449 let outputs: Vec<&str> = site
450 .map(|s| s.outputs.iter().map(String::as_str).collect())
451 .unwrap_or_default();
452 let inputs = self.inputs_of(step, buffer, none);
453 let enriched =
454 crate::kernel::engines::enrich_panic(payload, &name, &outputs, &self.context, &inputs);
455 crate::kernel::engines::reraise_enriched(enriched)
456 }
457}