diff --git a/rust/ares/src/jets/hot.rs b/rust/ares/src/jets/hot.rs index fb366f6..4b95012 100644 --- a/rust/ares/src/jets/hot.rs +++ b/rust/ares/src/jets/hot.rs @@ -2,7 +2,7 @@ use crate::jets::*; use crate::noun::{Atom, DirectAtom, IndirectAtom, Noun, D, T}; use ares_macros::tas; use either::Either::{self, Left, Right}; -use std::ptr::null_mut; +use std::ptr::{copy_nonoverlapping, null_mut}; /** Root for Hoon %k.139 */ @@ -826,3 +826,27 @@ struct HotMem { jet: Jet, next: Hot, } + +impl Preserve for Hot { + unsafe fn preserve(&mut self, stack: &mut NockStack) { + let mut it = self; + while !it.0.is_null() && !stack.is_in_frame(it.0) { + let dest_mem = stack.struct_alloc_in_previous_frame(1); + copy_nonoverlapping(it.0, dest_mem, 1); + it.0 = dest_mem; + (*it.0).a_path.preserve(stack); + (*it.0).axis.preserve(stack); + it = &mut (*it.0).next; + } + } + + unsafe fn assert_in_stack(&self, stack: &NockStack) { + let mut it = self; + while !it.0.is_null() { + stack.assert_struct_is_in(it.0, 1); + (*it.0).a_path.assert_in_stack(stack); + (*it.0).axis.assert_in_stack(stack); + it = &mut (*it.0).next; + } + } +} diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index b33b8b6..9a6352a 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -74,7 +74,7 @@ impl NockStack { unsafe { *frame_pointer.sub(FRAME + 1) = ptr::null::() as u64; // "frame pointer" from "previous" frame *frame_pointer.sub(STACK + 1) = ptr::null::() as u64; // "stack pointer" from "previous" frame - *frame_pointer.sub(ALLOC + 1) = ptr::null::() as u64; // "alloc pointer" from "previous" frame + *frame_pointer.sub(ALLOC + 1) = start as u64; // "alloc pointer" from "previous" frame }; NockStack { start, @@ -87,6 +87,42 @@ impl NockStack { } } + /** Resets the NockStack but flipping the top-frame polarity and unsetting PC. Sets the alloc + * pointer to the "previous" alloc pointer stored in the top frame to keep things "preserved" + * from the top frame. This allows us to do a copying GC on the top frame without erroneously + * "popping" the top frame. + */ + pub unsafe fn flip_top_frame(&mut self, top_slots: usize) { + // Assert that we are at the top + assert!((*self.prev_frame_pointer_pointer()).is_null()); + assert!((*self.prev_stack_pointer_pointer()).is_null()); + let new_alloc_pointer = *(self.prev_alloc_pointer_pointer()); + + if self.is_west() { + // new top frame will be east + let new_frame_pointer = self.start.add(self.size).sub(RESERVED + top_slots) as *mut u64; + *new_frame_pointer.add(FRAME) = ptr::null::() as u64; + *new_frame_pointer.add(STACK) = ptr::null::() as u64; + *new_frame_pointer.add(ALLOC) = self.start as u64; + self.frame_pointer = new_frame_pointer; + self.stack_pointer = new_frame_pointer; + self.alloc_pointer = new_alloc_pointer; + self.pc = false; + assert!(!self.is_west()); + } else { + // new top frame will be west + let new_frame_pointer = self.start.add(RESERVED + top_slots) as *mut u64; + *new_frame_pointer.sub(FRAME + 1) = ptr::null::() as u64; + *new_frame_pointer.sub(STACK + 1) = ptr::null::() as u64; + *new_frame_pointer.sub(ALLOC + 1) = self.start as u64; + self.frame_pointer = new_frame_pointer; + self.stack_pointer = new_frame_pointer; + self.alloc_pointer = new_alloc_pointer; + self.pc = false; + assert!(self.is_west()); + } + } + /** Resets the NockStack. */ pub fn reset(&mut self, top_slots: usize) { self.frame_pointer = unsafe { self.start.add(RESERVED + top_slots) } as *mut u64; @@ -96,7 +132,7 @@ impl NockStack { unsafe { *self.frame_pointer.sub(FRAME + 1) = ptr::null::() as u64; // "frame pointer" from "previous" frame *self.frame_pointer.sub(STACK + 1) = ptr::null::() as u64; // "stack pointer" from "previous" frame - *self.frame_pointer.sub(ALLOC + 1) = ptr::null::() as u64; // "alloc pointer" from "previous" frame + *self.frame_pointer.sub(ALLOC + 1) = self.start as u64; // "alloc pointer" from "previous" frame }; } diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index bfe4e27..6fff0be 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -355,7 +355,6 @@ pub fn serf(constant_hot_state: &[HotEntry]) -> io::Result<()> { // Reset the local cache and scry handler stack context.nock_context.cache = Hamt::::new(&mut context.nock_context.stack); context.nock_context.scry_stack = D(0); - context.nock_context.stack.frame_push(0); let tag = slot(writ, 2)?.as_direct().unwrap(); match tag.data() { @@ -400,13 +399,15 @@ pub fn serf(constant_hot_state: &[HotEntry]) -> io::Result<()> { clear_interrupt(); // Persist data that should survive between events - // XX: Such data should go in the PMA once that's available + // XX: Such data should go in the PMA once that's available, except + // the warm and hot state which should survive between events but not interpreter runs unsafe { let stack = &mut context.nock_context.stack; stack.preserve(&mut context.arvo); stack.preserve(&mut context.nock_context.cold); stack.preserve(&mut context.nock_context.warm); - stack.frame_pop(); + stack.preserve(&mut context.nock_context.hot); + stack.flip_top_frame(0); } }