This commit is contained in:
bQUARKz 2026-02-18 16:28:52 +00:00
parent 46146993aa
commit 13f025e448
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
3 changed files with 73 additions and 0 deletions

View File

@ -5,8 +5,10 @@ mod virtual_machine;
pub mod vm_init_error; pub mod vm_init_error;
pub mod object; pub mod object;
pub mod heap; pub mod heap;
pub mod roots;
pub use prometeu_hal::{HostContext, HostReturn, NativeInterface, SyscallId}; pub use prometeu_hal::{HostContext, HostReturn, NativeInterface, SyscallId};
pub use virtual_machine::{BudgetReport, LogicalFrameEndingReason, VirtualMachine}; pub use virtual_machine::{BudgetReport, LogicalFrameEndingReason, VirtualMachine};
pub use object::{object_flags, ObjectHeader, ObjectKind}; pub use object::{object_flags, ObjectHeader, ObjectKind};
pub use heap::{Heap, StoredObject}; pub use heap::{Heap, StoredObject};
pub use roots::{RootVisitor, visit_value_for_roots};

View File

@ -0,0 +1,37 @@
use prometeu_bytecode::{HeapRef, Value};
/// Visitor for GC roots. Implementors receive every `HeapRef` discovered
/// during root traversal. No marking/sweeping semantics here.
pub trait RootVisitor {
fn visit_heap_ref(&mut self, r: HeapRef);
}
/// Helper: if `val` is a `Value::HeapRef`, call the visitor.
pub fn visit_value_for_roots<V: RootVisitor + ?Sized>(val: &Value, visitor: &mut V) {
if let Value::HeapRef(r) = val {
visitor.visit_heap_ref(*r);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::VirtualMachine;
struct CollectVisitor { pub seen: Vec<HeapRef> }
impl RootVisitor for CollectVisitor {
fn visit_heap_ref(&mut self, r: HeapRef) { self.seen.push(r); }
}
#[test]
fn visits_heapref_on_operand_stack() {
let mut vm = VirtualMachine::default();
// Place a HeapRef on the operand stack
vm.operand_stack.push(Value::HeapRef(HeapRef(123)));
let mut v = CollectVisitor { seen: vec![] };
vm.visit_roots(&mut v);
assert_eq!(v.seen, vec![HeapRef(123)]);
}
}

View File

@ -5,6 +5,7 @@ use crate::{HostContext, NativeInterface};
use prometeu_bytecode::isa::core::CoreOpCode as OpCode; use prometeu_bytecode::isa::core::CoreOpCode as OpCode;
use prometeu_bytecode::ProgramImage; use prometeu_bytecode::ProgramImage;
use prometeu_bytecode::Value; use prometeu_bytecode::Value;
use crate::roots::{RootVisitor, visit_value_for_roots};
use crate::heap::Heap; use crate::heap::Heap;
use prometeu_bytecode::{ use prometeu_bytecode::{
TRAP_BAD_RET_SLOTS, TRAP_DIV_ZERO, TRAP_INVALID_FUNC, TRAP_INVALID_SYSCALL, TRAP_OOB, TRAP_BAD_RET_SLOTS, TRAP_DIV_ZERO, TRAP_INVALID_FUNC, TRAP_INVALID_SYSCALL, TRAP_OOB,
@ -1030,6 +1031,39 @@ impl VirtualMachine {
Err(OpError::Panic(msg)) => Err(LogicalFrameEndingReason::Panic(msg)), Err(OpError::Panic(msg)) => Err(LogicalFrameEndingReason::Panic(msg)),
} }
} }
/// Visit all GC roots reachable from the VM state.
/// This includes:
/// - Entire operand stack values
/// - Locals/args in each call frame (derived from `stack_base` and function layout)
/// - Global variables
pub fn visit_roots<V: RootVisitor + ?Sized>(&self, visitor: &mut V) {
// 1) Operand stack (all values are roots)
for v in &self.operand_stack {
visit_value_for_roots(v, visitor);
}
// 2) Call frames: iterate locals/args range for each frame
for frame in &self.call_stack {
if let Some(func_meta) = self.program.functions.get(frame.func_idx) {
let start = frame.stack_base;
let frame_slots = (func_meta.param_slots as usize) + (func_meta.local_slots as usize);
let mut end = start.saturating_add(frame_slots);
// Clamp to current stack height just in case
if end > self.operand_stack.len() { end = self.operand_stack.len(); }
for i in start..end {
if let Some(v) = self.operand_stack.get(i) {
visit_value_for_roots(v, visitor);
}
}
}
}
// 3) Globals
for g in &self.globals {
visit_value_for_roots(g, visitor);
}
}
} }
#[cfg(test)] #[cfg(test)]