Skip to main content

beadie_backend/
lib.rs

1// beadie-backend — JIT backend abstraction layer.
2//
3// Depends on beadie-core for Bead, Chain, Broker, Policy, Deopt.
4// Provides the trait + adapters; does not link any JIT compiler itself.
5
6pub mod tiered;
7pub use tiered::{TieredAdapter, TieredBound};
8
9use std::sync::Arc;
10
11use beadie_core::{
12    Bead, Beadie, CoreHandle, HotnessPolicy, OsrCompileResult, OsrEntry, ReloadOutcome, SwapResult,
13    ThresholdPolicy,
14};
15
16// ─────────────────────────────────────────────────────────────────────────────
17// CompileError
18// ─────────────────────────────────────────────────────────────────────────────
19
20#[derive(Debug)]
21pub struct CompileError {
22    pub message: String,
23}
24impl std::fmt::Display for CompileError {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        f.write_str(&self.message)
27    }
28}
29impl std::error::Error for CompileError {}
30impl CompileError {
31    pub fn new(msg: impl Into<String>) -> Self {
32        Self {
33            message: msg.into(),
34        }
35    }
36    pub fn from_err<E: std::error::Error>(e: E) -> Self {
37        Self {
38            message: e.to_string(),
39        }
40    }
41}
42
43// ─────────────────────────────────────────────────────────────────────────────
44// JitBackend trait
45// ─────────────────────────────────────────────────────────────────────────────
46
47/// An IR-agnostic compilation backend.
48///
49/// Implement this to plug any JIT compiler into beadie.
50///
51/// - `FunctionDef` — the vendor IR container; built by the caller using the
52///   native builder API, then passed to [`compile`](JitBackend::compile).
53/// - `Error` — compilation failure type.
54pub trait JitBackend: Send + Sync + 'static {
55    type FunctionDef: Send + 'static;
56    type Error: std::error::Error + Send + Sync + 'static;
57
58    /// Compile `def` to native code and return the entry-point pointer.
59    /// Return `null` or `Err` on failure — the bead will be deopt'd.
60    fn compile(&self, bead: &Arc<Bead>, def: Self::FunctionDef) -> Result<*mut (), Self::Error>;
61}
62
63// ─────────────────────────────────────────────────────────────────────────────
64// OsrBuild — output of an OSR-aware factory
65// ─────────────────────────────────────────────────────────────────────────────
66
67/// Output of an OSR-aware factory closure passed to [`BackendAdapter::on_invoke_osr`].
68///
69/// Carries the backend-specific [`JitBackend::FunctionDef`] that compiles to
70/// the main entry point, plus a vector of [`OsrEntry`]s — one per hot loop
71/// header the JIT plans to emit. The adapter compiles the `def` via the
72/// backend and hands both the resulting code pointer and `osr` entries to
73/// the bead atomically.
74pub struct OsrBuild<D> {
75    pub def: D,
76    pub osr: Vec<OsrEntry>,
77}
78
79// ─────────────────────────────────────────────────────────────────────────────
80// BoundBead
81// ─────────────────────────────────────────────────────────────────────────────
82
83/// A bead pre-wired to a specific JIT backend.
84#[derive(Clone)]
85pub struct BoundBead<B: JitBackend> {
86    pub(crate) bead: Arc<Bead>,
87    pub(crate) backend: Arc<B>,
88}
89
90impl<B: JitBackend> BoundBead<B> {
91    pub fn bead(&self) -> &Arc<Bead> {
92        &self.bead
93    }
94    pub fn backend(&self) -> &Arc<B> {
95        &self.backend
96    }
97
98    /// Eagerly compile and install. Skips the broker — use for pre-compilation.
99    pub fn compile(&self, def: B::FunctionDef) -> Result<Arc<Bead>, B::Error> {
100        let ptr = self.backend.compile(&self.bead, def)?;
101        self.bead.eager_install(ptr);
102        Ok(Arc::clone(&self.bead))
103    }
104
105    pub fn reload(&self) -> ReloadOutcome {
106        self.bead.reload()
107    }
108
109    pub fn swap_compiled(&self, new_code: *mut ()) -> Option<SwapResult> {
110        self.bead.swap_compiled(new_code)
111    }
112
113    /// Tier-up swap: replace the code pointer and the OSR table together.
114    /// See [`Bead::swap_compiled_with_osr`].
115    pub fn swap_compiled_with_osr(
116        &self,
117        new_code: *mut (),
118        osr: Vec<OsrEntry>,
119    ) -> Option<SwapResult> {
120        self.bead.swap_compiled_with_osr(new_code, osr)
121    }
122}
123
124// ─────────────────────────────────────────────────────────────────────────────
125// BackendAdapter
126// ─────────────────────────────────────────────────────────────────────────────
127
128/// Beadie wired to a single JIT backend.
129pub struct BackendAdapter<B: JitBackend, P: HotnessPolicy = ThresholdPolicy> {
130    beadie: Beadie<P>,
131    backend: Arc<B>,
132}
133
134impl<B: JitBackend> BackendAdapter<B> {
135    pub fn new(backend: B) -> Self {
136        Self::with_policy(backend, ThresholdPolicy::default())
137    }
138}
139
140impl<B: JitBackend, P: HotnessPolicy> BackendAdapter<B, P> {
141    pub fn with_policy(backend: B, policy: P) -> Self {
142        Self {
143            beadie: Beadie::with_policy(policy),
144            backend: Arc::new(backend),
145        }
146    }
147
148    pub fn register(
149        &self,
150        core: CoreHandle,
151        on_invalidate: Option<Box<dyn Fn() + Send + Sync>>,
152    ) -> BoundBead<B> {
153        let bead = self.beadie.register(core, on_invalidate);
154        BoundBead {
155            bead,
156            backend: Arc::clone(&self.backend),
157        }
158    }
159
160    #[inline]
161    pub fn on_invoke<F>(&self, bound: &BoundBead<B>, factory: F) -> Option<*mut ()>
162    where
163        F: FnOnce(&Arc<Bead>) -> B::FunctionDef + Send + 'static,
164    {
165        let backend = Arc::clone(&bound.backend);
166        self.beadie.on_invoke(&bound.bead, move |bead| {
167            let def = factory(bead);
168            match backend.compile(bead, def) {
169                Ok(ptr) => ptr,
170                Err(e) => {
171                    eprintln!("beadie: compile error: {e}");
172                    core::ptr::null_mut()
173                }
174            }
175        })
176    }
177
178    /// OSR-aware dispatch.
179    ///
180    /// The factory returns an [`OsrBuild`] — the backend function def plus
181    /// one [`OsrEntry`] per hot loop header the compiled code will expose.
182    /// The adapter compiles `def` via the backend and publishes both the
183    /// main entry and the OSR table atomically on the bead.
184    ///
185    /// Back-edge probes in the runtime use [`Bead::osr_entry`] to look up
186    /// a resume point and transfer a live interpreter frame into native code.
187    #[inline]
188    pub fn on_invoke_osr<F>(&self, bound: &BoundBead<B>, factory: F) -> Option<*mut ()>
189    where
190        F: FnOnce(&Arc<Bead>) -> OsrBuild<B::FunctionDef> + Send + 'static,
191    {
192        let backend = Arc::clone(&bound.backend);
193        self.beadie.on_invoke_osr(&bound.bead, move |bead| {
194            let build = factory(bead);
195            let entry = match backend.compile(bead, build.def) {
196                Ok(ptr) => ptr,
197                Err(e) => {
198                    eprintln!("beadie: compile error: {e}");
199                    core::ptr::null_mut()
200                }
201            };
202            OsrCompileResult {
203                entry,
204                osr: build.osr,
205            }
206        })
207    }
208
209    pub fn prune(&self) {
210        self.beadie.prune();
211    }
212    pub fn reload_all(&self) -> usize {
213        self.beadie.reload_all()
214    }
215    pub fn reload_matching(&self, pred: impl Fn(&Arc<Bead>) -> bool) -> usize {
216        self.beadie.reload_matching(pred)
217    }
218    pub fn chain_len(&self) -> usize {
219        self.beadie.chain_len()
220    }
221    pub fn beadie(&self) -> &Beadie<P> {
222        &self.beadie
223    }
224    pub fn backend(&self) -> &Arc<B> {
225        &self.backend
226    }
227}