Skip to content
Merged
14 changes: 14 additions & 0 deletions majit/majit-backend-cranelift/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ fn register_active_hooks(supports_guard_gc_type: bool) {
Some(gc_remove_root_via_active_runtime),
);
majit_gc::set_active_gc_owns_object(Some(gc_owns_object_via_active_runtime));
majit_gc::set_active_gc_is_nursery_object(Some(gc_is_nursery_object_via_active_runtime));
majit_gc::set_active_gc_id_or_identityhash(Some(id_or_identityhash_via_active_runtime));
majit_gc::set_active_write_barrier(Some(gc_write_barrier_via_active_runtime));
majit_gc::set_active_finalizer_hooks(
Expand Down Expand Up @@ -1672,6 +1673,19 @@ fn gc_owns_object_via_active_runtime(addr: usize) -> bool {
}
}

fn gc_is_nursery_object_via_active_runtime(addr: usize) -> bool {
CRANELIFT_ACTIVE_GC.with(|cell| match cell.try_borrow_mut() {
Ok(mut guard) => guard
.as_deref_mut()
.map(|gc| gc.is_nursery_object(addr))
.unwrap_or(false),
Err(_) => CRANELIFT_ACTIVE_GC_RAW.with(|raw| match raw.get() {
Some(ptr) => unsafe { (&*ptr).is_nursery_object(addr) },
None => false,
}),
})
}

/// Returns true when the active GC was present and roots were
/// registered; false when no GC is active and registration was a
/// no-op. Callers pair the bool with `unregister_gc_roots` on Drop.
Expand Down
14 changes: 14 additions & 0 deletions majit/majit-backend-dynasm/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ fn register_active_hooks(supports_guard_gc_type: bool) {
majit_gc::set_active_heap_stats(Some(dynasm_heap_stats));
majit_gc::set_active_root_hooks(Some(dynasm_gc_add_root), Some(dynasm_gc_remove_root));
majit_gc::set_active_gc_owns_object(Some(dynasm_gc_owns_object));
majit_gc::set_active_gc_is_nursery_object(Some(dynasm_gc_is_nursery_object));
majit_gc::set_active_gc_id_or_identityhash(Some(dynasm_id_or_identityhash));
majit_gc::set_active_write_barrier(Some(dynasm_gc_write_barrier));
majit_gc::set_active_finalizer_hooks(
Expand Down Expand Up @@ -616,6 +617,19 @@ fn dynasm_gc_owns_object(addr: usize) -> bool {
}
}

fn dynasm_gc_is_nursery_object(addr: usize) -> bool {
DYNASM_ACTIVE_GC.with(|cell| match cell.try_borrow() {
Ok(guard) => guard
.as_deref()
.map(|gc| gc.is_nursery_object(addr))
.unwrap_or(false),
Err(_) => DYNASM_ACTIVE_GC_RAW.with(|raw| match raw.get() {
Some(ptr) => unsafe { (&*ptr).is_nursery_object(addr) },
None => false,
}),
})
}

/// `gc.py:51` malloc-helper OOM signaling.
///
/// `do_malloc_fixedsize_clear` raises `MemoryError` on failure;
Expand Down
374 changes: 316 additions & 58 deletions majit/majit-backend-wasm/src/codegen.rs

Large diffs are not rendered by default.

60 changes: 60 additions & 0 deletions majit/majit-backend-wasm/src/failguard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ pub struct CompiledWasmLoop {
/// Geometry frozen when this token was first compiled. Every bridge
/// chained onto it is emitted against this exact layout.
pub frame: crate::codegen::FrameGeometry,
/// True when this loop or any successfully chained bridge uses the host
/// residual-call trampoline. A CA callee frame is movable, but that
/// trampoline retains the pre-call frame pointer, so `compile_bridge` must
/// not enable the CA arm for this source token.
pub has_trampoline_calls: Cell<bool>,
/// Base address (shared linear memory) of this loop's per-guard bridge-slot
/// cell array — one i32 per `fail_index`, `0` = no bridge. The trace's
/// epilogue reads `cells[fail_index]` and `compile_bridge` writes a bridge's
Expand Down Expand Up @@ -286,6 +291,17 @@ pub struct CompiledWasmLoop {
pub ca_active: Cell<bool>,
}

impl CompiledWasmLoop {
/// Incorporate the normal (non-CA unless this bridge is the candidate)
/// codegen census for a bridge after it has been chained onto this token.
/// Every earlier bridge remains reachable from a later CA recursion's
/// guard exits, so its host trampoline use also rules out CA.
pub fn record_chained_bridge_trampoline_calls(&self, bridge_has_trampoline_calls: bool) {
self.has_trampoline_calls
.set(self.has_trampoline_calls.get() || bridge_has_trampoline_calls);
}
}

impl Drop for CompiledWasmLoop {
fn drop(&mut self) {
// Retract this loop's published label targets so a later bridge
Expand All @@ -307,3 +323,47 @@ impl Drop for CompiledWasmLoop {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

fn token_with_trampoline_census(has_trampoline_calls: bool) -> CompiledWasmLoop {
CompiledWasmLoop {
trace_id: 0,
input_types: Vec::new(),
func_handle: 0,
fail_descrs: RefCell::new(Vec::new()),
num_inputs: 0,
max_output_slots: 0,
num_ref_homes: 0,
frame: crate::codegen::FrameGeometry::fixed(),
has_trampoline_calls: Cell::new(has_trampoline_calls),
bridge_cells_base: 0,
num_guard_cells: 0,
has_preamble: false,
label_descrs: Vec::new(),
guard_fail_arg_advanced: Vec::new(),
bridge_descr_ranges: RefCell::new(Vec::new()),
chained_trace_meta: RefCell::new(std::collections::HashMap::new()),
_bridge_cells_owner: None,
_bridge_owned_cells: RefCell::new(Vec::new()),
ca_active: Cell::new(false),
}
}

#[test]
fn chained_bridge_trampoline_census_is_orred_into_token() {
let token = token_with_trampoline_census(false);
token.record_chained_bridge_trampoline_calls(false);
assert!(!token.has_trampoline_calls.get());

token.record_chained_bridge_trampoline_calls(true);
assert!(token.has_trampoline_calls.get());

// A later clean bridge cannot erase an earlier chained bridge's
// trampoline census before a CA bridge is considered.
token.record_chained_bridge_trampoline_calls(false);
assert!(token.has_trampoline_calls.get());
}
}
Loading
Loading