Skip to main content

polydat_core/kernel/
opt.rs

1// Copyright 2024-2026 Jonathan Shook
2// SPDX-License-Identifier: Apache-2.0
3
4//! `KernelOptLevel` — session-wide optimization knob for op-template
5//! kernel synthesis.
6//!
7//! Today's closure-binding economy (Rule 5) drops input slots for
8//! names nothing in the body references — magic externs `body` /
9//! `count` / `ok` and result-binding LHSs whose values nothing
10//! downstream reads get DCE'd at slot-allocation time. Runtime
11//! writes to those names land on a kernel that has no slot for them
12//! and silently no-op.
13//!
14//! That's fine in production: the workload didn't ask for the value,
15//! we don't pay to track it. It's actively harmful for step-debug /
16//! cycle-replay / "show me what the adapter actually wrote this
17//! cycle even though nothing read it" inspection.
18//!
19//! `Diagnostic` mode relaxes the DCE: every magic extern referenced
20//! or not is allocated, every result-binding LHS gets a kernel slot
21//! whether or not anything reads it. The writes land, `wires.get`
22//! answers, the step-debugger sees the real values. Compute path
23//! unchanged — the extra slots have no eval cone hanging off them.
24//!
25//! The knob threads through [`crate::kernel::subcontext::CompileOptions`]
26//! and is consulted by `SubcontextBuilder::add_result_bindings`. The
27//! polydat binary does not expose it; a host may map a CLI flag onto it.
28
29/// Optimization level for op-template kernel synthesis.
30///
31/// `Release` is the production default — closure-binding economy
32/// elides slots for names nothing references. `Diagnostic` keeps
33/// every magic-extern and result-binding-LHS slot allocated so
34/// step-debug / cycle-replay introspection can see the values the
35/// runtime would otherwise drop.
36///
37/// Naming: matches the rustc convention (`opt-level=0..3`) but
38/// collapsed to two semantically-distinct positions; there's no
39/// useful middle ground between "DCE on" and "keep everything for
40/// inspection."
41#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
42pub enum KernelOptLevel {
43    /// Production default. Closure-binding economy elides slots
44    /// for unreferenced magic externs and result-binding LHSs.
45    /// Writes to elided names silently no-op.
46    #[default]
47    Release,
48    /// Step-debug / cycle-replay mode. Force-allocate every magic
49    /// extern (`body` / `count` / `ok`) and every result-binding
50    /// LHS slot regardless of downstream reference. Runtime writes
51    /// always land; `wires.get` always answers.
52    Diagnostic,
53}
54
55impl KernelOptLevel {
56    /// True when slot allocation should ignore the "is this name
57    /// referenced?" check and force-allocate every candidate slot.
58    pub fn keep_unreferenced_slots(self) -> bool {
59        matches!(self, Self::Diagnostic)
60    }
61
62    /// Parse from a CLI-style string. Returns `Err(input)` on an
63    /// unrecognised value so the caller can format its own
64    /// diagnostic.
65    pub fn parse(s: &str) -> Result<Self, &str> {
66        match s {
67            "release" => Ok(Self::Release),
68            "diagnostic" => Ok(Self::Diagnostic),
69            _ => Err(s),
70        }
71    }
72
73    /// Canonical CLI spelling for this level. Inverse of
74    /// [`Self::parse`].
75    pub fn as_str(self) -> &'static str {
76        match self {
77            Self::Release => "release",
78            Self::Diagnostic => "diagnostic",
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn default_is_release() {
89        assert_eq!(KernelOptLevel::default(), KernelOptLevel::Release);
90    }
91
92    #[test]
93    fn keep_unreferenced_slots_release() {
94        assert!(!KernelOptLevel::Release.keep_unreferenced_slots());
95    }
96
97    #[test]
98    fn keep_unreferenced_slots_diagnostic() {
99        assert!(KernelOptLevel::Diagnostic.keep_unreferenced_slots());
100    }
101
102    #[test]
103    fn parse_roundtrip() {
104        for lvl in [KernelOptLevel::Release, KernelOptLevel::Diagnostic] {
105            assert_eq!(KernelOptLevel::parse(lvl.as_str()), Ok(lvl));
106        }
107    }
108
109    #[test]
110    fn parse_unknown_returns_err() {
111        assert_eq!(KernelOptLevel::parse("none"), Err("none"));
112        assert_eq!(KernelOptLevel::parse(""), Err(""));
113    }
114}