Skip to main content

nmbrs_runtime/
fiber_engine.rs

1// Copyright 2024-2026 Jonathan Shook
2// SPDX-License-Identifier: Apache-2.0
3
4//! The engine nmbrs runs its kernels on.
5//!
6//! Scope synthesis reads program structure on the interpreter
7//! (`PolydatProgram`), and pull plans resolve their indices against the
8//! interpreter's program. Every kernel that runs — each scope of the
9//! tree ([`crate::scope_kernel::ScopeKernel`]), each fiber's main kernel,
10//! its per-op kernels, the kernels adapters hold — runs on a compiled
11//! engine instead, bound under its parent through the engine-neutral
12//! `Kernel` surface (polydat's native_scope_trees.md).
13//!
14//! A compiled image stands in for an interpreter program only where it
15//! reports the same inputs and outputs in the same order, which is what
16//! lets an index resolved on the interpreter's program drive the image.
17//! polydat guarantees that for a scope module's images; an image
18//! compiled separately from the same source is checked here, once, and
19//! an image that disagrees is not used.
20
21use std::sync::Arc;
22
23use polydat::kernel::subcontext::{Child, RootMarker, ScopeModule};
24use polydat::kernel::{KernelProgram, PolydatProgram};
25
26/// The module an op-template scope finalizes to: its interpreter
27/// program plus the settings and write-throughs every other engine's
28/// image is built from.
29pub type OpTemplateModule = ScopeModule<Child<RootMarker>>;
30
31/// The engine scope, fiber and per-op kernels run on: polydat's default, the
32/// most native form the build has.
33pub fn fiber_engine() -> polydat::Engine {
34    polydat::Engine::default()
35}
36
37/// Whether `image` has `program`'s inputs and outputs in the same
38/// order, coordinates first — the condition for driving it with indices
39/// resolved on `program`.
40pub fn agrees(program: &PolydatProgram, image: &Arc<dyn KernelProgram>) -> bool {
41    let kernel = image.clone().create_kernel();
42    let outputs: Vec<String> = program
43        .output_names()
44        .iter()
45        .map(|n| n.to_string())
46        .collect();
47    kernel.input_names() == program.input_names()
48        && kernel.output_names() == outputs
49        && kernel.coord_count() == program.coord_count()
50}
51
52/// The fiber-engine image of a scope module, or `None` when the
53/// engine refuses it or it disagrees with the module's program; the
54/// module's kernels then stay on the interpreter.
55pub fn module_image(module: &OpTemplateModule, context: &str) -> Option<Arc<dyn KernelProgram>> {
56    match module.program_on(fiber_engine()) {
57        Ok(image) if agrees(module.program(), &image) => Some(image),
58        Ok(_) => {
59            warn_interpreter(
60                context,
61                "its compiled image lists inputs or outputs differently",
62            );
63            None
64        }
65        Err(e) => {
66            warn_interpreter(context, &e.to_string());
67            None
68        }
69    }
70}
71
72/// The fiber-engine image of a scope compiled from `source` under
73/// `options`, the same inputs that produced `program`; `None`, with a
74/// warning, when it cannot stand in for `program`.
75pub fn source_image(
76    program: &PolydatProgram,
77    source: &str,
78    options: &polydat::dsl::compile::CompileOptions,
79    context: &str,
80) -> Option<Arc<dyn KernelProgram>> {
81    let options = polydat::dsl::compile::CompileOptions {
82        engine: fiber_engine(),
83        ..options.clone()
84    };
85    match polydat::dsl::compile::compile_polydat_kernel_with_options(source, &options, None) {
86        Ok(kernel) => {
87            let image = kernel.into_program();
88            if agrees(program, &image) {
89                Some(image)
90            } else {
91                warn_interpreter(
92                    context,
93                    "its compiled image lists inputs or outputs differently",
94                );
95                None
96            }
97        }
98        Err(e) => {
99            warn_interpreter(context, &e.to_string());
100            None
101        }
102    }
103}
104
105fn warn_interpreter(context: &str, reason: &str) {
106    crate::diag!(
107        crate::observer::LogLevel::Warn,
108        "{context}: kernels stay on the interpreter ({} unavailable: {reason})",
109        fiber_engine()
110    );
111}