diff --git a/crates/oxide_vm/src/builtins/error.rs b/crates/oxide_vm/src/builtins/error.rs index 77f5fac2..27024dd3 100644 --- a/crates/oxide_vm/src/builtins/error.rs +++ b/crates/oxide_vm/src/builtins/error.rs @@ -177,7 +177,7 @@ mod tests { return_addr: 0, function_name: name_si, caller_reg_limit: 1, - saved_regs: vec![JsValue::undefined()].into_boxed_slice(), + saved_reg_offset: 0, saved_this: JsValue::undefined(), saved_new_target: JsValue::undefined(), callee: JsValue::undefined(), diff --git a/crates/oxide_vm/src/session_gc.rs b/crates/oxide_vm/src/session_gc.rs index 77fe1fed..93a804da 100644 --- a/crates/oxide_vm/src/session_gc.rs +++ b/crates/oxide_vm/src/session_gc.rs @@ -460,10 +460,10 @@ fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObje for value in &mut vm.regs { *value = rewrite_forwarded_value(*value, forwarding); } + for value in &mut vm.save_stack { + *value = rewrite_forwarded_value(*value, forwarding); + } for frame in &mut vm.frames { - for value in frame.saved_regs.iter_mut() { - *value = rewrite_forwarded_value(*value, forwarding); - } frame.saved_this = rewrite_forwarded_value(frame.saved_this, forwarding); frame.saved_new_target = rewrite_forwarded_value(frame.saved_new_target, forwarding); frame.callee = rewrite_forwarded_value(frame.callee, forwarding); @@ -553,8 +553,8 @@ mod tests { vm.frames.push(CallFrame { return_addr: 0, function_name: 0, - caller_reg_limit: 0, - saved_regs: vec![JsValue::from_js_object(frame_session)].into_boxed_slice(), + caller_reg_limit: 1, + saved_reg_offset: 0, saved_this: JsValue::from_js_object(this_session), saved_new_target: JsValue::from_js_object(child_session), callee: JsValue::from_js_object(child_session), @@ -563,6 +563,7 @@ mod tests { is_derived_constructor: false, continuation: FrameContinuation::None, }); + vm.save_stack.push(JsValue::from_js_object(frame_session)); vm.regs[1] = JsValue::from_js_object(child_session); vm.exception_value = Some(JsValue::from_js_object(root_session)); diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 6ee8f9f1..6a9a67e7 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -99,7 +99,7 @@ pub struct CallFrame { pub return_addr: usize, pub function_name: u32, pub caller_reg_limit: u8, - pub saved_regs: Box<[JsValue]>, + pub saved_reg_offset: u32, pub saved_this: JsValue, pub saved_new_target: JsValue, pub callee: JsValue, @@ -140,6 +140,7 @@ pub(crate) struct InlineSyncState { pub(crate) last_for_of_result: JsValue, pub(crate) saved_bytecode_stack: Vec>, pub(crate) saved_constants_stack: Vec>, + pub(crate) save_stack: Vec, } pub struct Vm { @@ -169,6 +170,10 @@ pub struct Vm { pub(crate) sub_module_constants: Vec>, pub(crate) saved_bytecode_stack: Vec>, pub(crate) saved_constants_stack: Vec>, + /// Shared register save-stack. Each active `CallFrame` saved its caller's live + /// registers (`regs[..caller_reg_limit]`) here at `saved_reg_offset`; restore copies + /// them back and truncates. Capacity is retained across calls — zero per-call heap alloc. + pub(crate) save_stack: Vec, pub(crate) try_stack: Vec, pub(crate) exception_value: Option, pub(crate) pending_exception: Option, @@ -302,14 +307,14 @@ impl Vm { } } for frame in &self.frames { - for &v in &frame.saved_regs { - f(v); - } f(frame.saved_this); f(frame.saved_new_target); f(frame.callee); f(frame.constructed_this.unwrap_or(JsValue::undefined())); } + for &v in &self.save_stack { + f(v); + } f(JsValue::from_js_object(self.session.global_object().as_ptr() as *mut JsObject)); f(self.exception_value.unwrap_or(JsValue::undefined())); f(self.pending_exception.unwrap_or(JsValue::undefined())); @@ -650,7 +655,8 @@ impl Vm { let sub_param_base = self.sub_modules[sub_idx].param_base as usize; let sub_is_arrow = self.sub_modules[sub_idx].is_arrow; let caller_reg_limit = self.active_reg_limit.max(1); - let saved_regs = self.regs[..caller_reg_limit as usize].to_vec().into_boxed_slice(); + let saved_reg_offset = self.save_stack.len() as u32; + self.save_stack.extend_from_slice(&self.regs[..caller_reg_limit as usize]); let saved_this = self.regs[254]; let saved_new_target = self.regs[255]; @@ -674,7 +680,7 @@ impl Vm { return_addr: self.pc, function_name, caller_reg_limit, - saved_regs, + saved_reg_offset, saved_this, saved_new_target, callee, @@ -971,7 +977,8 @@ impl Vm { let sub_constants = self.sub_modules[sub_idx].constants.clone(); let sub_param_base = self.sub_modules[sub_idx].param_base as usize; let caller_reg_limit = self.active_reg_limit.max(1); - let saved_regs = self.regs[..caller_reg_limit as usize].to_vec().into_boxed_slice(); + let saved_reg_offset = self.save_stack.len() as u32; + self.save_stack.extend_from_slice(&self.regs[..caller_reg_limit as usize]); let saved_this = self.regs[254]; let saved_new_target = self.regs[255]; @@ -993,7 +1000,7 @@ impl Vm { .map(|name| self.kernel_core.perm_interner().intern(name).0) .unwrap_or(0), caller_reg_limit, - saved_regs, + saved_reg_offset, saved_this, saved_new_target, callee: super_ctor, @@ -1426,7 +1433,7 @@ mod tests { return_addr: 1, function_name: 0, caller_reg_limit: 2, - saved_regs: vec![JsValue::undefined()].into_boxed_slice(), + saved_reg_offset: 0, saved_this: JsValue::undefined(), saved_new_target: JsValue::undefined(), callee: JsValue::undefined(), @@ -1435,6 +1442,7 @@ mod tests { is_derived_constructor: false, continuation: super::FrameContinuation::None, }); + vm.save_stack.push(JsValue::undefined()); vm.for_in_iters.push(std::ptr::dangling_mut::>()); vm.for_of_iters.push(JsValue::undefined()); vm.saved_bytecode_stack @@ -1453,6 +1461,7 @@ mod tests { assert_eq!(vm.pc, 0); assert!(vm.frames.is_empty()); + assert!(vm.save_stack.is_empty()); assert!(vm.for_in_iters.is_empty()); assert!(vm.for_of_iters.is_empty()); assert!(vm.saved_bytecode_stack.is_empty()); diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index 1e1c9c48..14163038 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -99,7 +99,8 @@ impl Vm { let sub_constants = self.sub_modules[sub_idx].constants.clone(); let sub_param_base = self.sub_modules[sub_idx].param_base as usize; let caller_reg_limit = self.active_reg_limit.max(1); - let saved_regs = self.regs[..caller_reg_limit as usize].to_vec().into_boxed_slice(); + let saved_reg_offset = self.save_stack.len() as u32; + self.save_stack.extend_from_slice(&self.regs[..caller_reg_limit as usize]); let saved_this = self.regs[254]; let saved_new_target = self.regs[255]; @@ -126,7 +127,7 @@ impl Vm { .map(|name| self.kernel_core.perm_interner().intern(name).0) .unwrap_or(0), caller_reg_limit, - saved_regs, + saved_reg_offset, saved_this, saved_new_target, callee: constructor, diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index e8cf273f..01742e96 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -50,6 +50,7 @@ impl Vm { last_for_of_result: self.last_for_of_result, saved_bytecode_stack: std::mem::take(&mut self.saved_bytecode_stack), saved_constants_stack: std::mem::take(&mut self.saved_constants_stack), + save_stack: std::mem::take(&mut self.save_stack), }); self.regs = [JsValue::undefined(); 256]; @@ -91,6 +92,7 @@ impl Vm { self.last_for_of_result = saved.last_for_of_result; self.saved_bytecode_stack = saved.saved_bytecode_stack; self.saved_constants_stack = saved.saved_constants_stack; + self.save_stack = saved.save_stack; result } @@ -102,8 +104,10 @@ impl Vm { if let Some(saved_consts) = self.saved_constants_stack.pop() { self.constants = saved_consts; } - let restore_len = frame.saved_regs.len(); - self.regs[..restore_len].copy_from_slice(&frame.saved_regs); + let offset = frame.saved_reg_offset as usize; + let len = frame.caller_reg_limit as usize; + self.regs[..len].copy_from_slice(&self.save_stack[offset..offset + len]); + self.save_stack.truncate(offset); self.regs[254] = frame.saved_this; self.regs[255] = frame.saved_new_target; self.active_reg_limit = frame.caller_reg_limit; diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index c79efd31..64f8a72a 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -42,6 +42,7 @@ impl Vm { sub_module_constants: Vec::new(), saved_bytecode_stack: Vec::new(), saved_constants_stack: Vec::new(), + save_stack: Vec::new(), try_stack: Vec::new(), exception_value: None, pending_exception: None, @@ -88,6 +89,7 @@ impl Vm { sub_module_constants: Vec::new(), saved_bytecode_stack: Vec::new(), saved_constants_stack: Vec::new(), + save_stack: Vec::new(), try_stack: Vec::new(), exception_value: None, pending_exception: None, @@ -174,6 +176,7 @@ impl Vm { self.last_for_of_result = JsValue::undefined(); self.saved_bytecode_stack.clear(); self.saved_constants_stack.clear(); + self.save_stack.clear(); self.try_stack.clear(); self.exception_value = None; self.pending_exception = None;