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}