Merge branch 'status' into mink-scry

This commit is contained in:
Alex Shelkovnykov 2023-10-13 08:53:36 -06:00
commit 13f110241c
6 changed files with 212 additions and 3 deletions

View File

@ -36,7 +36,7 @@ jobs:
-A clippy::missing_safety_doc
- name: Build
run: cargo build --release --verbose
run: cargo build --release --verbose --features check_all
- name: Run tests
run: cargo test --verbose -- --test-threads=1

View File

@ -42,4 +42,9 @@ opt-level = 3
[profile.dev.package."*"]
opt-level = 3
# run with e.g. 'cargo build --features check_forwarding,check_acyclic'
[features]
check_all = [ "check_acyclic", "check_forwarding", "check_junior" ]
check_acyclic = []
check_forwarding = []
check_junior = []

View File

@ -1,3 +1,6 @@
use crate::assert_acyclic;
use crate::assert_no_forwarding_pointers;
use crate::assert_no_junior_pointers;
use crate::hamt::Hamt;
use crate::jets::JetErr;
use crate::mem::unifying_equality;
@ -290,9 +293,17 @@ impl From<JetErr> for NockErr {
}
}
#[allow(unused_variables)]
fn debug_assertions(stack: &mut NockStack, noun: Noun) {
assert_acyclic!(noun);
assert_no_forwarding_pointers!(noun);
assert_no_junior_pointers!(stack, noun);
}
/** Interpret nock */
pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Result<Noun, Tone> {
let terminator = Arc::clone(&TERMINATOR);
let orig_subject = subject; // for debugging
let virtual_frame: *const u64 = context.stack.get_frame_pointer();
let mut res: Noun = D(0);
@ -322,15 +333,30 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
let work: NockWork = *context.stack.top();
match work {
NockWork::Done => {
debug_assertions(context.stack, orig_subject);
debug_assertions(context.stack, subject);
debug_assertions(context.stack, res);
context.stack.preserve(context.cache);
context.stack.preserve(&mut res);
context.stack.frame_pop();
debug_assertions(context.stack, orig_subject);
debug_assertions(context.stack, res);
break Ok(res);
}
NockWork::Ret => {
debug_assertions(context.stack, orig_subject);
debug_assertions(context.stack, subject);
debug_assertions(context.stack, res);
context.stack.preserve(context.cache);
context.stack.preserve(&mut res);
context.stack.frame_pop();
debug_assertions(context.stack, orig_subject);
debug_assertions(context.stack, res);
}
NockWork::WorkCons(mut cons) => match cons.todo {
TodoCons::ComputeHead => {
@ -388,6 +414,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
vale.todo = Todo2::RestoreSubject;
std::mem::swap(&mut vale.subject, &mut subject);
*context.stack.top() = NockWork::Work2(vale);
debug_assertions(context.stack, orig_subject);
debug_assertions(context.stack, subject);
debug_assertions(context.stack, res);
mean_frame_push(context.stack, 0);
*context.stack.push() = NockWork::Ret;
push_formula(context.stack, res, true)?;
@ -396,6 +427,10 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
Todo2::RestoreSubject => {
subject = vale.subject;
context.stack.pop::<NockWork>();
debug_assertions(context.stack, orig_subject);
debug_assertions(context.stack, subject);
debug_assertions(context.stack, res);
}
}
}
@ -540,6 +575,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
kale.todo = Todo9::RestoreSubject;
kale.core = subject;
*context.stack.top() = NockWork::Work9(kale);
debug_assertions(context.stack, orig_subject);
debug_assertions(context.stack, subject);
debug_assertions(context.stack, res);
subject = res;
mean_frame_push(context.stack, 0);
*context.stack.push() = NockWork::Ret;
@ -553,6 +593,10 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
Todo9::RestoreSubject => {
subject = kale.core;
context.stack.pop::<NockWork>();
debug_assertions(context.stack, orig_subject);
debug_assertions(context.stack, subject);
debug_assertions(context.stack, res);
}
}
}

View File

@ -1,4 +1,6 @@
use crate::assert_acyclic;
use crate::assert_no_forwarding_pointers;
use crate::assert_no_junior_pointers;
use crate::noun::{Atom, Cell, CellMemory, IndirectAtom, Noun, NounAllocator};
use crate::snapshot::pma::{pma_in_arena, pma_malloc_w};
use assert_no_alloc::permit_alloc;
@ -356,6 +358,10 @@ impl NockStack {
}
unsafe fn copy(&mut self, noun: &mut Noun) {
assert_acyclic!(*noun);
assert_no_forwarding_pointers!(*noun);
assert_no_junior_pointers!(self, *noun);
self.pre_copy();
assert!(self.stack_is_empty());
let noun_ptr = noun as *mut Noun;
@ -443,7 +449,10 @@ impl NockStack {
}
}
// Set saved previous allocation pointer its new value after this allocation
assert_acyclic!(*noun);
assert_no_forwarding_pointers!(*noun);
assert_no_junior_pointers!(self, *noun);
}
pub unsafe fn copy_pma(&mut self, noun: &mut Noun) {
@ -517,7 +526,6 @@ impl NockStack {
},
}
}
assert_acyclic!(*noun);
}
pub unsafe fn frame_pop(&mut self) {
@ -679,6 +687,88 @@ impl NockStack {
unsafe { self.stack_pointer == self.alloc_pointer.add(RESERVED) }
}
}
pub fn no_junior_pointers(&self, noun: Noun) -> bool {
unsafe {
if let Ok(c) = noun.as_cell() {
let mut fp: *mut u64;
let mut sp = self.stack_pointer;
let mut ap = self.alloc_pointer;
let mut pfp = *(self.prev_frame_pointer_pointer());
let mut psp = *(self.prev_stack_pointer_pointer());
let mut pap = *(self.prev_alloc_pointer_pointer());
let mut dbg_stack = Vec::new();
// Detemine range
let (rlo, rhi) = loop {
if psp.is_null() {
psp = ((self.start as u64) + ((self.size << 3) as u64)) as *mut u64;
}
let (lo, hi) = if sp < ap { (ap, psp) } else { (psp, ap) };
let ptr = c.to_raw_pointer() as *mut u64;
if ptr >= lo && ptr < hi {
break if sp < ap { (sp, ap) } else { (ap, sp) };
} else {
fp = pfp;
sp = psp;
ap = pap;
if sp < ap {
pfp = *(fp.sub(FRAME + 1)) as *mut u64;
psp = *(fp.sub(STACK + 1)) as *mut u64;
pap = *(fp.sub(ALLOC + 1)) as *mut u64;
} else {
pfp = *(fp.add(FRAME)) as *mut u64;
psp = *(fp.add(STACK)) as *mut u64;
pap = *(fp.add(ALLOC)) as *mut u64;
}
}
};
dbg_stack.push(c.head());
dbg_stack.push(c.tail());
while let Some(n) = dbg_stack.pop() {
if let Ok(a) = n.as_allocated() {
let ptr = a.to_raw_pointer();
if ptr >= rlo && ptr < rhi {
eprintln!(
"\rserf: Noun {:x} has Noun {:x} in junior of range {:p}-{:p}",
(noun.raw << 3),
(n.raw << 3),
rlo,
rhi
);
return false;
}
if let Some(c) = a.cell() {
dbg_stack.push(c.tail());
dbg_stack.push(c.head());
}
}
}
true
} else {
true
}
}
}
}
#[cfg(feature = "check_junior")]
#[macro_export]
macro_rules! assert_no_junior_pointers {
( $x:expr, $y:expr ) => {
assert_no_alloc::permit_alloc(|| {
assert!($x.no_junior_pointers($y));
})
};
}
#[cfg(not(feature = "check_junior"))]
#[macro_export]
macro_rules! assert_no_junior_pointers {
( $x:expr, $y:expr ) => {};
}
pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Noun) -> bool {
@ -705,6 +795,13 @@ pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Nou
* senior noun, *never vice versa*, to avoid introducing references from more senior frames
* into more junior frames, which would result in incorrect operation of the copier.
*/
assert_acyclic!(*a);
assert_acyclic!(*b);
assert_no_forwarding_pointers!(*a);
assert_no_forwarding_pointers!(*b);
assert_no_junior_pointers!(stack, *a);
assert_no_junior_pointers!(stack, *b);
// If the nouns are already word-equal we have nothing to do
if (*a).raw_equals(*b) {
return true;
@ -800,8 +897,14 @@ pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Nou
}
}
stack.frame_pop();
assert_acyclic!(*a);
assert_acyclic!(*b);
assert_no_forwarding_pointers!(*a);
assert_no_forwarding_pointers!(*b);
assert_no_junior_pointers!(stack, *a);
assert_no_junior_pointers!(stack, *b);
(*a).raw_equals(*b)
}

View File

@ -1,4 +1,6 @@
use crate::assert_acyclic;
use crate::assert_no_forwarding_pointers;
use crate::assert_no_junior_pointers;
use crate::mem::*;
use crate::noun::{Allocated, Atom, DirectAtom, Noun};
use either::Either::*;
@ -119,7 +121,11 @@ pub fn mug_u32(stack: &mut NockStack, noun: Noun) -> u32 {
if let Some(mug) = get_mug(noun) {
return mug;
}
assert_acyclic!(noun);
assert_no_forwarding_pointers!(noun);
assert_no_junior_pointers!(stack, noun);
stack.frame_push(0);
unsafe {
*(stack.push()) = noun;
@ -171,6 +177,11 @@ pub fn mug_u32(stack: &mut NockStack, noun: Noun) -> u32 {
unsafe {
stack.frame_pop();
}
assert_acyclic!(noun);
assert_no_forwarding_pointers!(noun);
assert_no_junior_pointers!(stack, noun);
get_mug(noun).expect("Noun should have a mug once it is mugged.")
}

View File

@ -44,7 +44,9 @@ pub const NO: Noun = D(1);
#[macro_export]
macro_rules! assert_acyclic {
( $x:expr ) => {
assert!(crate::noun::acyclic_noun($x));
assert_no_alloc::permit_alloc(|| {
assert!(crate::noun::acyclic_noun($x));
})
};
}
@ -82,6 +84,42 @@ fn acyclic_noun_go(noun: Noun, seen: &mut IntMap<()>) -> bool {
}
}
#[cfg(feature = "check_forwarding")]
#[macro_export]
macro_rules! assert_no_forwarding_pointers {
( $x:expr ) => {
assert_no_alloc::permit_alloc(|| {
assert!(crate::noun::no_forwarding_pointers($x));
})
};
}
#[cfg(not(feature = "check_forwarding"))]
#[macro_export]
macro_rules! assert_no_forwarding_pointers {
( $x:expr ) => {};
}
pub fn no_forwarding_pointers(noun: Noun) -> bool {
let mut dbg_stack = Vec::new();
dbg_stack.push(noun);
while !dbg_stack.is_empty() {
if let Some(noun) = dbg_stack.pop() {
if unsafe { noun.raw & FORWARDING_MASK == FORWARDING_TAG } {
return false;
} else if let Ok(cell) = noun.as_cell() {
dbg_stack.push(cell.tail());
dbg_stack.push(cell.head());
}
} else {
break;
}
}
true
}
/** Test if a noun is a direct atom. */
fn is_direct_atom(noun: u64) -> bool {
noun & DIRECT_MASK == DIRECT_TAG
@ -835,6 +873,14 @@ impl Allocated {
}
}
pub fn cell(&self) -> Option<Cell> {
if self.is_cell() {
unsafe { Some(self.cell) }
} else {
None
}
}
pub fn as_noun(&self) -> Noun {
Noun { allocated: *self }
}