Skip to main content

cljrs_eval/
ir_cache.rs

1//! Thread-safe IR cache for compiled function arities.
2//!
3//! Each `CljxFnArity` is assigned a unique `ir_arity_id` at creation time.
4//! When a function is called, the cache is consulted:
5//! - `NotAttempted` → try lowering via the Clojure compiler
6//! - `Cached(ir)` → execute via IR interpreter
7//! - `Unsupported` → fall back to tree-walking (don't retry)
8//!
9//! The hot path (`get_cached`) uses `RwLock` so concurrent reads don't
10//! contend.  Writes (store) are infrequent (only during lowering).
11
12use std::collections::HashMap;
13use std::sync::{Arc, RwLock};
14
15use cljrs_ir::IrFunction;
16
17// ── Cache entries ────────────────────────────────────────────────────────────
18
19/// State of an IR cache entry for one function arity.
20pub enum IrCacheEntry {
21    /// Lowering has not been attempted yet.
22    NotAttempted,
23    /// Lowering was attempted but failed (unsupported form); don't retry.
24    Unsupported,
25    /// Successfully lowered IR function.
26    Cached(Arc<IrFunction>),
27}
28
29// ── Global cache ─────────────────────────────────────────────────────────────
30
31static IR_CACHE: RwLock<Option<HashMap<u64, IrCacheEntry>>> = RwLock::new(None);
32
33/// Look up a cached IR function by arity ID.
34/// Returns `None` if not cached or if lowering previously failed.
35/// Returns `Some(ir)` if cached.
36///
37/// This is the hot path — uses a read lock so concurrent callers don't block.
38pub fn get_cached(id: u64) -> Option<Arc<IrFunction>> {
39    let guard = IR_CACHE.read().unwrap();
40    let cache = guard.as_ref()?;
41    match cache.get(&id) {
42        Some(IrCacheEntry::Cached(ir)) => Some(ir.clone()),
43        _ => None,
44    }
45}
46
47/// Check if lowering should be attempted for this arity.
48/// Returns `true` if the entry is `NotAttempted` (or absent).
49pub fn should_attempt(id: u64) -> bool {
50    let guard = IR_CACHE.read().unwrap();
51    match guard.as_ref() {
52        Some(cache) => !cache.contains_key(&id),
53        None => true,
54    }
55}
56
57/// Store a successful IR compilation result.
58pub fn store_cached(id: u64, ir: Arc<IrFunction>) {
59    let mut guard = IR_CACHE.write().unwrap();
60    let cache = guard.get_or_insert_with(HashMap::new);
61    cache.insert(id, IrCacheEntry::Cached(ir));
62}
63
64/// Mark an arity as unsupported (lowering failed; don't retry).
65pub fn store_unsupported(id: u64) {
66    let mut guard = IR_CACHE.write().unwrap();
67    let cache = guard.get_or_insert_with(HashMap::new);
68    cache.insert(id, IrCacheEntry::Unsupported);
69}