Merge pull request #178 from urbit/revert-177-revert-175-eamsden/gc-top-frame

2stackz: gc top frame by flipping polarity (take 2)
This commit is contained in:
Edward Amsden 2023-12-13 22:42:14 -06:00 committed by GitHub
commit 4ff2f1e161
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 80 additions and 8 deletions

View File

@ -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;
}
}
}

View File

@ -75,7 +75,7 @@ impl NockStack {
unsafe {
*frame_pointer.sub(FRAME + 1) = ptr::null::<u64>() as u64; // "frame pointer" from "previous" frame
*frame_pointer.sub(STACK + 1) = ptr::null::<u64>() as u64; // "stack pointer" from "previous" frame
*frame_pointer.sub(ALLOC + 1) = ptr::null::<u64>() as u64; // "alloc pointer" from "previous" frame
*frame_pointer.sub(ALLOC + 1) = start as u64; // "alloc pointer" from "previous" frame
};
NockStack {
start,
@ -88,7 +88,43 @@ impl NockStack {
}
}
/** Resets the 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::<u64>() as u64;
*new_frame_pointer.add(STACK) = ptr::null::<u64>() as u64;
*new_frame_pointer.add(ALLOC) = self.start.add(self.size) 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::<u64>() as u64;
*new_frame_pointer.sub(STACK + 1) = ptr::null::<u64>() 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. The top frame is west as in the initial creation of the NockStack. */
pub fn reset(&mut self, top_slots: usize) {
self.frame_pointer = unsafe { self.start.add(RESERVED + top_slots) } as *mut u64;
self.stack_pointer = self.frame_pointer;
@ -97,7 +133,8 @@ impl NockStack {
unsafe {
*self.frame_pointer.sub(FRAME + 1) = ptr::null::<u64>() as u64; // "frame pointer" from "previous" frame
*self.frame_pointer.sub(STACK + 1) = ptr::null::<u64>() as u64; // "stack pointer" from "previous" frame
*self.frame_pointer.sub(ALLOC + 1) = ptr::null::<u64>() as u64; // "alloc pointer" from "previous" frame
*self.frame_pointer.sub(ALLOC + 1) = self.start as u64; // "alloc pointer" from "previous" frame
assert!(self.is_west());
};
}
@ -123,7 +160,17 @@ impl NockStack {
let ptr_u64 = ptr as *const u64;
let prev = *self.prev_stack_pointer_pointer();
if self.is_west() {
ptr_u64 >= self.alloc_pointer && ptr_u64 < prev
// If we are in a top/west frame, the stack pointer will be null, so our allocation
// arena was the alloc pointer to the top of the NockStack arena
if prev.is_null() {
ptr_u64 >= self.alloc_pointer && ptr_u64 < self.start.add(self.size)
} else {
ptr_u64 >= self.alloc_pointer && ptr_u64 < prev
}
// If we are in a top/east frame, the stack pointer will be null, so our allocation arena
// was the alloc pointer to the bottom of the NockStack arena
} else if prev.is_null() {
ptr_u64 >= self.start && ptr_u64 < self.alloc_pointer
} else {
ptr_u64 >= prev && ptr_u64 < self.alloc_pointer
}

View File

@ -224,7 +224,6 @@ pub fn serf(constant_hot_state: &[HotEntry]) -> io::Result<()> {
// Reset the local cache and scry handler stack
context.nock_context.cache = Hamt::<Noun>::new();
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() {
@ -269,13 +268,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);
}
}