Skip to main content

cljrs_runtime/
mode.rs

1//! Execution mode and tier state.
2//!
3//! [`ExecutionMode`] is chosen once, when a runtime is built, and never
4//! changes: it selects which function-call path a runtime uses.  Before the
5//! merge each mode was expressed by storing a different `fn` pointer in
6//! `GlobalEnv`; the pointers existed only to let `cljrs-interp` reach
7//! `cljrs-eval` without a dependency cycle.  Now that both live in this
8//! package the mode is data and the dispatch is a direct call.
9//!
10//! [`TierState`] is the *current* state of that mode, and it does change: a
11//! tiered runtime tree-walks its own bootstrap (nothing can be lowered before
12//! `clojure.core` exists) and is promoted to [`TierState::Ir`] or
13//! [`TierState::Jit`] once the bootstrap finishes.  It replaces the old
14//! `GlobalEnv::compiler_ready` flag, which said only "not tree-walk" and could
15//! not distinguish the IR interpreter from native JIT dispatch.
16
17/// How a runtime executes Clojure function calls.
18///
19/// Selected with [`crate::RuntimeBuilder::execution_mode`] and fixed for the
20/// life of the runtime.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
22pub enum ExecutionMode {
23    /// Tree-walking interpreter only.  No IR is lowered, cached, or executed.
24    ///
25    /// The cheapest mode to construct and the one to use for short-lived
26    /// environments (tests, one-shot evaluation, the AOT test harness) where
27    /// populating the IR cache would be pure overhead.
28    TreeWalk,
29
30    /// Tree walk, tier-1 IR interpreter, and native JIT promotion.
31    ///
32    /// The default, and what the `cljrs` CLI uses.  Native dispatch is only
33    /// reached once a JIT backend is attached to the runtime
34    /// (`cljrs_compiler::jit::install`); without one this behaves like
35    /// [`ExecutionMode::TieredNoJit`].
36    #[default]
37    Tiered,
38
39    /// Tree walk and tier-1 IR interpreter, with native JIT promotion
40    /// suppressed even when a JIT backend is linked in.
41    TieredNoJit,
42
43    /// Tree walk inside a no-GC transaction arena, with a call-depth cap.
44    ///
45    /// Used by `cljrs-tx`, where interpreted recursion runs on the host's
46    /// Rust stack and must be bounded.  Install the cap for the dynamic
47    /// extent of one transaction with [`crate::env::depth::DepthGuard`].
48    NoGcTransaction,
49}
50
51impl ExecutionMode {
52    /// The tier this mode is promoted to once bootstrap finishes.
53    pub fn target_tier(self) -> TierState {
54        match self {
55            ExecutionMode::TreeWalk | ExecutionMode::NoGcTransaction => TierState::TreeWalk,
56            ExecutionMode::Tiered => TierState::Jit,
57            ExecutionMode::TieredNoJit => TierState::Ir,
58        }
59    }
60
61    /// True when function calls go through the IR-aware dispatch path
62    /// (`crate::tiered::apply::call_cljrs_fn`) rather than straight to the
63    /// tree walker.
64    pub fn is_tiered(self) -> bool {
65        matches!(self, ExecutionMode::Tiered | ExecutionMode::TieredNoJit)
66    }
67}
68
69/// Which execution tiers are live right now.
70///
71/// Monotonic within a runtime's life: it starts at [`TierState::TreeWalk`]
72/// while `clojure.core` bootstraps and is raised once to
73/// [`ExecutionMode::target_tier`] when the runtime is ready.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
75#[repr(u8)]
76pub enum TierState {
77    /// Only the tree walker runs.  No lowering is attempted.
78    TreeWalk = 0,
79    /// Tree walk plus IR lowering and the tier-1 IR interpreter.
80    Ir = 1,
81    /// Tree walk, IR, and native code published by a JIT backend.
82    Jit = 2,
83}
84
85impl TierState {
86    /// Decode from the `AtomicU8` representation held by `GlobalEnv`.
87    /// Unknown values decode as [`TierState::TreeWalk`].
88    pub(crate) fn from_u8(raw: u8) -> Self {
89        match raw {
90            1 => TierState::Ir,
91            2 => TierState::Jit,
92            _ => TierState::TreeWalk,
93        }
94    }
95
96    /// True when IR may be lowered, cached, and interpreted.
97    pub fn ir_enabled(self) -> bool {
98        self >= TierState::Ir
99    }
100
101    /// True when dispatch may jump to native code published by a JIT backend.
102    pub fn jit_enabled(self) -> bool {
103        self == TierState::Jit
104    }
105}