Skip to main content

logicaffeine_compile/vm/
bg_aot.rs

1//! Background AOT-native compilation (HOTSWAP §Axis-3 / P18).
2//!
3//! A very-hot function's OPTIMIZED form is compiled to a cdylib and loaded on a WORKER
4//! thread — `rustc` is seconds, so it must never block the interpreter — then the loaded
5//! function is installed via `Vm::install_aot_native` at a drain point and runs at
6//! compiled-binary speed from then on. This is the AOT path to the coarse-tiered
7//! "optimize hot functions during the run" goal: it reuses the proven on-demand build
8//! ([`crate::compile::aot_build_function`]) off-thread, the same mpsc worker shape as
9//! the forge background compiler (§6), and the existing `NativeSlot::Ready` dispatch —
10//! no new VM seam. Because an AOT function is all-or-nothing per call (it bails to the
11//! lower tier on a signature mismatch rather than precise-deopting), it sidesteps the
12//! warm-bytecode run-loop change the forge coarse path would need.
13//!
14//! Native-only: the browser uses pre-bundled wasm, and there is no `rustc` there.
15
16#![cfg(not(target_arch = "wasm32"))]
17
18use std::path::PathBuf;
19use std::sync::mpsc::{Receiver, Sender};
20use std::thread::JoinHandle;
21
22use super::native_tier::NativeFn;
23
24/// A request to background-compile function `fi` (named `fn_name`) from `source`,
25/// caching the artifact under `cache_dir`. All fields owned so the request crosses to
26/// the worker thread.
27pub struct AotRequest {
28    pub fi: usize,
29    pub source: String,
30    pub fn_name: String,
31    pub cache_dir: PathBuf,
32}
33
34/// The worker's reply: the function index and the loaded AOT function (or `None` if the
35/// function was outside the scalar subset or the build/load failed — the interpreter
36/// then keeps it on VM+JIT).
37pub struct AotResult {
38    pub fi: usize,
39    pub nf: Option<Box<dyn NativeFn>>,
40}
41
42/// The interpreter-side handle to the background AOT compiler. `submit` is
43/// non-blocking; `try_drain` is polled at profiling points and `drain_blocking` is the
44/// determinism hook tests use. The worker detaches on drop.
45pub struct BgAotCompiler {
46    req_tx: Sender<AotRequest>,
47    res_rx: Receiver<AotResult>,
48    inflight: usize,
49    _worker: JoinHandle<()>,
50}
51
52impl BgAotCompiler {
53    /// Spawn the AOT build worker.
54    pub fn new() -> Self {
55        let (req_tx, req_rx) = std::sync::mpsc::channel::<AotRequest>();
56        let (res_tx, res_rx) = std::sync::mpsc::channel::<AotResult>();
57        let worker = std::thread::spawn(move || {
58            while let Ok(req) = req_rx.recv() {
59                let nf = crate::compile::aot_build_function(&req.source, &req.fn_name, &req.cache_dir);
60                if res_tx.send(AotResult { fi: req.fi, nf }).is_err() {
61                    break; // interpreter gone — stop quietly
62                }
63            }
64        });
65        BgAotCompiler { req_tx, res_rx, inflight: 0, _worker: worker }
66    }
67
68    /// Queue an AOT build. Non-blocking; the interpreter keeps running on the lower
69    /// tier (bytecode / forge) while `rustc` works.
70    pub fn submit(&mut self, req: AotRequest) {
71        if self.req_tx.send(req).is_ok() {
72            self.inflight += 1;
73        }
74    }
75
76    /// Take one finished AOT build if ready — polled at the profiling points.
77    pub fn try_drain(&mut self) -> Option<AotResult> {
78        match self.res_rx.try_recv() {
79            Ok(r) => {
80                self.inflight = self.inflight.saturating_sub(1);
81                Some(r)
82            }
83            Err(_) => None,
84        }
85    }
86
87    /// Whether any submitted build is still outstanding.
88    pub fn is_idle(&self) -> bool {
89        self.inflight == 0
90    }
91
92    /// Block until every outstanding build has come back — the determinism hook for
93    /// the differential gates (a single AOT build is seconds, so tests opt in).
94    pub fn drain_blocking(&mut self) -> Vec<AotResult> {
95        let mut out = Vec::with_capacity(self.inflight);
96        while self.inflight > 0 {
97            match self.res_rx.recv() {
98                Ok(r) => {
99                    self.inflight -= 1;
100                    out.push(r);
101                }
102                Err(_) => break,
103            }
104        }
105        out
106    }
107}
108
109impl Default for BgAotCompiler {
110    fn default() -> Self {
111        Self::new()
112    }
113}