diff --git a/hoon/scaffolding/playpen.hoon b/hoon/scaffolding/playpen.hoon index 0eb9e78d..1939da6b 100644 --- a/hoon/scaffolding/playpen.hoon +++ b/hoon/scaffolding/playpen.hoon @@ -226,6 +226,7 @@ ++ lent :: length ~/ %lent |= a=(list) + ~> %sham.%lent ^- @ =+ b=0 |- @@ -579,7 +580,6 @@ ++ dor :: tree order ~/ %dor |= [a=* b=*] - ~> %sham.%dor ^- ? ?: =(a b) & ?. ?=(@ a) @@ -593,7 +593,6 @@ ++ gor :: mug order ~/ %gor |= [a=* b=*] - ~> %sham.%gor ^- ? =+ [c=(mug a) d=(mug b)] ?: =(c d) @@ -603,7 +602,6 @@ ++ mor :: more mug order ~/ %mor |= [a=* b=*] - ~> %sham.%mor ^- ? =+ [c=(mug (mug a)) d=(mug (mug b))] ?: =(c d) @@ -626,13 +624,11 @@ ++ in ~/ %in =| a=(tree) :: (set) - ~> %sham.%in |@ ++ apt =< $ ~/ %apt =| [l=(unit) r=(unit)] - ~> %sham.%apt |. ^- ? ?~ a & ?& ?~(l & (gor n.a u.l)) @@ -644,7 +640,6 @@ ++ del ~/ %del |* b=* - ~> %sham.%del |- ^+ a ?~ a ~ @@ -662,7 +657,6 @@ ++ put ~/ %put |* b=* - ~> %sham.%put |- ^+ a ?~ a [b ~ ~] @@ -686,13 +680,11 @@ ++ by ~/ %by =| a=(tree (pair)) :: (map) - ~> %sham.%by =* node ?>(?=(^ a) n.a) |@ ++ del ~/ %del |* b=* - ~> %sham.%del |- ^+ a ?~ a ~ @@ -711,7 +703,6 @@ =< $ ~/ %apt =| [l=(unit) r=(unit)] - ~> %sham.%apt |. ^- ? ?~ a & ?& ?~(l & &((gor p.n.a u.l) !=(p.n.a u.l))) @@ -725,7 +716,6 @@ ++ get ~/ %get |* b=* - ~> %sham.%get => .(b `_?>(?=(^ a) p.n.a)`b) |- ^- (unit _?>(?=(^ a) q.n.a)) ?~ a @@ -739,7 +729,6 @@ ++ put ~/ %put |* [b=* c=*] - ~> %sham.%put |- ^+ a ?~ a [[b c] ~ ~] diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 8988f911..96ffd8b1 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -48,3 +48,4 @@ check_all = [ "check_acyclic", "check_forwarding", "check_junior" ] check_acyclic = [] check_forwarding = [] check_junior = [] +sham_hints = [] diff --git a/rust/ares/src/hamt.rs b/rust/ares/src/hamt.rs index 2f2fb28e..5c631730 100644 --- a/rust/ares/src/hamt.rs +++ b/rust/ares/src/hamt.rs @@ -255,11 +255,12 @@ assert_eq_size!(&[(Noun, ())], Leaf<()>); // Our custom stem type is the same size as a fat pointer to `Entry`s assert_eq_size!(&[Entry<()>], Stem<()>); +#[derive(Copy, Clone)] pub struct Hamt(Stem); -impl Hamt { +impl Hamt { // Make a new, empty HAMT - pub fn new() -> Hamt { + pub fn new() -> Self { Hamt(Stem { bitmap: 0, typemap: 0, @@ -425,13 +426,57 @@ impl Hamt { } } -impl Default for Hamt { +impl Default for Hamt { fn default() -> Self { Self::new() } } impl Preserve for Hamt { + unsafe fn assert_in_stack(&self, stack: &NockStack) { + stack.assert_struct_is_in(self.0.buffer, self.0.size()); + let mut traversal_stack: [Option<(Stem, u32)>; 6] = [None; 6]; + traversal_stack[0] = Some((self.0, 0)); + let mut traversal_depth = 1; + 'check: loop { + if traversal_depth == 0 { + break; + } + let (stem, mut position) = traversal_stack[traversal_depth - 1] + .expect("Attempted to access uninitialized array element"); + // can we loop over the size and count leading 0s remaining in the bitmap? + 'check_stem: loop { + if position >= 32 { + traversal_depth -= 1; + continue 'check; + } + match stem.entry(position) { + None => { + position += 1; + continue 'check_stem; + } + Some((Left(next_stem), _idx)) => { + stack.assert_struct_is_in(next_stem.buffer, next_stem.size()); + assert!(traversal_depth <= 5); // will increment + traversal_stack[traversal_depth - 1] = Some((stem, position + 1)); + traversal_stack[traversal_depth] = Some((next_stem, 0)); + traversal_depth += 1; + continue 'check; + } + Some((Right(leaf), _idx)) => { + stack.assert_struct_is_in(leaf.buffer, leaf.len); + for pair in leaf.to_mut_slice().iter() { + pair.0.assert_in_stack(stack); + pair.1.assert_in_stack(stack); + } + position += 1; + continue 'check_stem; + } + } + } + } + } + unsafe fn preserve(&mut self, stack: &mut NockStack) { if stack.is_in_frame(self.0.buffer) { let dest_buffer = stack.struct_alloc_in_previous_frame(self.0.size()); diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index aa70d36f..c10fb847 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -2,6 +2,10 @@ use crate::assert_acyclic; use crate::assert_no_forwarding_pointers; use crate::assert_no_junior_pointers; use crate::hamt::Hamt; +use crate::jets::cold; +use crate::jets::cold::Cold; +use crate::jets::hot::Hot; +use crate::jets::warm::Warm; use crate::jets::JetErr; use crate::mem::unifying_equality; use crate::mem::NockStack; @@ -231,13 +235,16 @@ enum NockWork { Work11S(Nock11S), } -pub struct Context<'a, 'b, 'c> { +pub struct Context<'a> { pub stack: &'a mut NockStack, // For printing slogs; if None, print to stdout; Option slated to be removed - pub newt: Option<&'b mut Newt>, + pub newt: Option<&'a mut Newt>, // Per-event cache; option to share cache with virtualized events - pub cache: &'c mut Hamt, + pub cache: &'a mut Hamt, // XX: persistent memo cache + pub cold: &'a mut Cold, + pub warm: &'a mut Warm, + pub hot: &'a Hot, } #[derive(Debug)] @@ -262,6 +269,12 @@ impl From for NockErr { } } +impl From for NockErr { + fn from(_: cold::Error) -> Self { + NockErr::Deterministic + } +} + impl From for NockErr { fn from(e: JetErr) -> Self { match e { @@ -317,6 +330,8 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res debug_assertions(context.stack, res); context.stack.preserve(context.cache); + context.stack.preserve(context.cold); + context.stack.preserve(context.warm); context.stack.preserve(&mut res); context.stack.frame_pop(); @@ -331,6 +346,8 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res debug_assertions(context.stack, res); context.stack.preserve(context.cache); + context.stack.preserve(context.cold); + context.stack.preserve(context.warm); context.stack.preserve(&mut res); context.stack.frame_pop(); @@ -385,6 +402,24 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res push_formula(context.stack, vale.formula, false)?; } Todo2::ComputeResult => { + if let Some(jet) = + context + .warm + .find_jet(context.stack, &mut vale.subject, &mut res) + { + match jet(context, vale.subject) { + Ok(jet_res) => { + res = jet_res; + context.stack.pop::(); + continue; + } + Err(JetErr::Punt) => {} + Err(err) => { + break Err(err.into()); + } + } + }; + if vale.tail { context.stack.pop::(); subject = vale.subject; @@ -545,7 +580,23 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res push_formula(context.stack, kale.core, false)?; } Todo9::ComputeResult => { - if let Ok(formula) = res.slot_atom(kale.axis) { + if let Ok(mut formula) = res.slot_atom(kale.axis) { + if let Some(jet) = + context.warm.find_jet(context.stack, &mut res, &mut formula) + { + match jet(context, res) { + Ok(jet_res) => { + res = jet_res; + context.stack.pop::(); + continue; + } + Err(JetErr::Punt) => {} + Err(err) => { + break Err(err.into()); + } + } + }; + if kale.tail { context.stack.pop::(); subject = res; @@ -629,10 +680,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res context.stack.pop::(); } Ok(None) => { - dint.todo = Todo11D::Done; if dint.tail { context.stack.pop::(); } else { + dint.todo = Todo11D::Done; + dint.hint = res; *context.stack.top() = NockWork::Work11D(dint); } push_formula(context.stack, dint.body, dint.tail)?; @@ -643,10 +695,17 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } } Todo11D::Done => { - if let Some(found) = - hint::match_post_nock(context, subject, dint.tag, dint.body, res) - { - res = found; + match hint::match_post_nock( + context, + subject, + dint.tag, + Some(dint.hint), + dint.body, + res, + ) { + Ok(Some(found)) => res = found, + Err(err) => break Err(err), + _ => {} } context.stack.pop::(); } @@ -659,10 +718,10 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res context.stack.pop::(); } Ok(None) => { - sint.todo = Todo11S::Done; if sint.tail { context.stack.pop::(); } else { + sint.todo = Todo11S::Done; *context.stack.top() = NockWork::Work11S(sint); } push_formula(context.stack, sint.body, sint.tail)?; @@ -673,10 +732,12 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } } Todo11S::Done => { - if let Some(found) = - hint::match_post_nock(context, subject, sint.tag, sint.body, res) - { - res = found; + match hint::match_post_nock( + context, subject, sint.tag, None, sint.body, res, + ) { + Ok(Some(found)) => res = found, + Err(err) => break Err(err), + _ => {} } context.stack.pop::(); } @@ -1013,7 +1074,8 @@ pub fn inc(stack: &mut NockStack, atom: Atom) -> Atom { mod hint { use super::*; use crate::jets; - use crate::jets::nock::util::mook; + use crate::jets::cold; + use crate::jets::nock::util::{mook, LEAF}; use crate::mem::unifying_equality; use crate::noun::{tape, Atom, Cell, Noun, D, T}; use crate::serf::TERMINATOR; @@ -1044,71 +1106,78 @@ mod hint { ) -> Result, NockErr> { // XX: handle IndirectAtom tags match tag.as_direct()?.data() { - // %sham hints are scaffolding until we have a real jet dashboard tas!(b"sham") => { - let jet_formula = hint.as_cell()?; - // XX: what is the head here? - let jet_name = jet_formula.tail(); - - if let Some(jet) = jets::get_jet(jet_name) { - match jet(context, subject) { - Ok(mut jet_res) => { - // XX: simplify this by moving jet test mode into the 11 code in interpret, or into its own function? - // if in test mode, check that the jet returns the same result as the raw nock - if jets::get_jet_test_mode(jet_name) { - // XX: we throw away trace, which might matter for non-deterministic errors - // maybe mook and slog it? - match interpret(context, subject, body) { - Ok(mut nock_res) => { - let stack = &mut context.stack; - if unsafe { - !unifying_equality(stack, &mut nock_res, &mut jet_res) - } { + if cfg!(feature = "sham_hints") { + let jet_formula = hint.as_cell()?; + // XX: what is the head here? + let jet_name = jet_formula.tail(); + + if let Some(jet) = jets::get_jet(jet_name) { + match jet(context, subject) { + Ok(mut jet_res) => { + // XX: simplify this by moving jet test mode into the 11 code in interpret, or into its own function? + // if in test mode, check that the jet returns the same result as the raw nock + if jets::get_jet_test_mode(jet_name) { + // XX: we throw away trace, which might matter for non-deterministic errors + // maybe mook and slog it? + match interpret(context, subject, body) { + Ok(mut nock_res) => { + let stack = &mut context.stack; + if unsafe { + !unifying_equality( + stack, + &mut nock_res, + &mut jet_res, + ) + } { + // XX: need string interpolation without allocation, then delete eprintln + // let tape = tape(stack, "jet mismatch in {}, raw: {}, jetted: {}", jet_name, nock_res, jet_res); + eprintln!( + "\rjet {} failed, raw: {:?}, jetted: {}", + jet_name, nock_res, jet_res + ); + let tape = tape(*stack, "jet mismatch"); + let mean = T(*stack, &[D(tas!(b"mean")), tape]); + mean_push(stack, mean); + Err(NockErr::Deterministic) + } else { + Ok(Some(nock_res)) + } + } + Err(Tone::Error(err, _)) => { + let stack = &mut context.stack; // XX: need string interpolation without allocation, then delete eprintln - // let tape = tape(stack, "jet mismatch in {}, raw: {}, jetted: {}", jet_name, nock_res, jet_res); + // let tape = tape(stack, "jet mismatch in {}, raw: {}, jetted: {}", jet_name, err, jet_res); eprintln!( "\rjet {} failed, raw: {:?}, jetted: {}", - jet_name, nock_res, jet_res + jet_name, err, jet_res ); let tape = tape(*stack, "jet mismatch"); let mean = T(*stack, &[D(tas!(b"mean")), tape]); mean_push(stack, mean); - Err(NockErr::Deterministic) - } else { - Ok(Some(nock_res)) + Err(err) + } + Err(Tone::Blocked(_)) => { + panic!("jet test mode: no scry handling") } } - Err(Tone::Error(err, _)) => { - let stack = &mut context.stack; - // XX: need string interpolation without allocation, then delete eprintln - // let tape = tape(stack, "jet mismatch in {}, raw: {}, jetted: {}", jet_name, err, jet_res); - eprintln!( - "\rjet {} failed, raw: {:?}, jetted: {}", - jet_name, err, jet_res - ); - let tape = tape(*stack, "jet mismatch"); - let mean = T(*stack, &[D(tas!(b"mean")), tape]); - mean_push(stack, mean); - Err(err) - } - Err(Tone::Blocked(_)) => { - panic!("jet test mode: no scry handling") - } + } else { + Ok(Some(jet_res)) } - } else { - Ok(Some(jet_res)) + } + Err(JetErr::Punt) => Ok(None), + Err(err) => { + let stack = &mut context.stack; + // XX: need string interpolation without allocation + // let tape = tape(stack, "{} jet error in {}", err, jet_name); + let tape = tape(*stack, "jet error"); + let mean = T(*stack, &[D(tas!(b"mean")), tape]); + mean_push(stack, mean); + Err(err.into()) } } - Err(JetErr::Punt) => Ok(None), - Err(err) => { - let stack = &mut context.stack; - // XX: need string interpolation without allocation - // let tape = tape(stack, "{} jet error in {}", err, jet_name); - let tape = tape(*stack, "jet error"); - let mean = T(*stack, &[D(tas!(b"mean")), tape]); - mean_push(stack, mean); - Err(err.into()) - } + } else { + Ok(None) } } else { Ok(None) @@ -1134,19 +1203,12 @@ mod hint { // XX: handle IndirectAtom tags match tag.as_direct()?.data() { tas!(b"slog") => { - let (_, hint_res) = hint.ok_or(NockErr::Deterministic)?; - let slog_cell = hint_res.as_cell()?; + let (_form, clue) = hint.ok_or(NockErr::Deterministic)?; + let slog_cell = clue.as_cell()?; let pri = slog_cell.head().as_direct()?.data(); let tank = slog_cell.tail(); - if context.newt.is_none() { - eprintln!("raw slog: {} {}", pri, tank); - } else { - context - .newt - .as_mut() - .unwrap() - .slog(context.stack, pri, tank); - } + + slog(context.stack, &mut context.newt, pri, tank); Ok(None) } tas!(b"hand") | tas!(b"hunk") | tas!(b"lose") | tas!(b"mean") | tas!(b"spot") => { @@ -1156,8 +1218,8 @@ mod hint { } let stack = &mut context.stack; - let (_, hint_res) = hint.ok_or(NockErr::Deterministic)?; - let noun = T(*stack, &[tag.as_noun(), hint_res]); + let (_form, clue) = hint.ok_or(NockErr::Deterministic)?; + let noun = T(*stack, &[tag.as_noun(), clue]); mean_push(stack, noun); Ok(None) } @@ -1185,11 +1247,7 @@ mod hint { } let cell = list.as_cell().unwrap(); - if context.newt.is_none() { - eprintln!("raw slog: {} {}", 0, cell.head()); - } else { - context.newt.as_mut().unwrap().slog(stack, 0, cell.head()); - } + slog(stack, &mut context.newt, 0, cell.head()); list = cell.tail(); } @@ -1214,14 +1272,16 @@ mod hint { context: &mut Context, subject: Noun, tag: Atom, + hint: Option, body: Noun, res: Noun, - ) -> Option { + ) -> Result, NockErr> { let stack = &mut context.stack; let cache = &mut context.cache; + let newt = &mut context.newt; // XX: handle IndirectAtom tags - match tag.direct()?.data() { + match tag.as_direct()?.data() { tas!(b"memo") => { let mut key = Cell::new(*stack, subject, body).as_noun(); **cache = (*cache).insert(stack, &mut key, res); @@ -1229,9 +1289,69 @@ mod hint { tas!(b"hand") | tas!(b"hunk") | tas!(b"lose") | tas!(b"mean") | tas!(b"spot") => { mean_pop(stack); } + tas!(b"fast") => { + if let Some(clue) = hint { + let cold_res: cold::Result = { + let chum = clue.slot(2)?; + let parent_formula_op = clue.slot(12)?.as_atom()?.as_direct()?; + let parent_formula_ax = clue.slot(13)?.as_atom()?; + + if parent_formula_op.data() == 1 { + if parent_formula_ax.as_direct()?.data() == 0 { + context.cold.register(stack, res, parent_formula_ax, chum) + } else { + // XX: Need better message in slog; need better slogging tools + // format!("invalid root parent axis: {} {}", chum, parent_formula_ax) + let tape = + tape(*stack, "serf: cold: register: invalid root parent axis"); + slog_leaf(stack, newt, tape); + Ok(false) + } + } else { + context.cold.register(stack, res, parent_formula_ax, chum) + } + }; + + match cold_res { + Ok(true) => *context.warm = Warm::init(stack, context.cold, context.hot), + Err(cold::Error::NoParent) => { + // XX: Need better message in slog; need better slogging tools + // format!("could not find parent battery at given axis: {} {}", chum, parent_formula_ax) + let tape = tape( + *stack, + "serf: cold: register: could not find parent battery at given axis", + ); + slog_leaf(stack, newt, tape); + } + Err(cold::Error::BadNock) => { + // XX: Need better message in slog; need better slogging tools + // format!("bad clue formula: {}", clue) + let tape = tape(*stack, "serf: cold: register: bad clue formula"); + slog_leaf(stack, newt, tape); + } + _ => {} + } + } else { + let tape = tape(*stack, "serf: cold: register: no clue for %fast"); + slog_leaf(stack, newt, tape); + } + } _ => {} } - None + Ok(None) + } + + fn slog_leaf(stack: &mut NockStack, newt: &mut Option<&mut Newt>, tape: Noun) { + let tank = T(stack, &[LEAF, tape]); + slog(stack, newt, 0u64, tank); + } + + fn slog(stack: &mut NockStack, newt: &mut Option<&mut Newt>, pri: u64, tank: Noun) { + if newt.is_none() { + eprintln!("raw slog: {} {}", pri, tank); + } else { + newt.as_mut().unwrap().slog(stack, pri, tank); + } } } diff --git a/rust/ares/src/jets.rs b/rust/ares/src/jets.rs index 56e60817..53a349f0 100644 --- a/rust/ares/src/jets.rs +++ b/rust/ares/src/jets.rs @@ -1,3 +1,7 @@ +pub mod cold; +pub mod hot; +pub mod warm; + pub mod bits; pub mod form; pub mod hash; @@ -8,12 +12,15 @@ pub mod tree; use crate::interpreter::Context; use crate::jets::bits::*; +use crate::jets::cold::Cold; use crate::jets::form::*; use crate::jets::hash::*; +use crate::jets::hot::Hot; use crate::jets::math::*; use crate::jets::nock::*; use crate::jets::text::*; use crate::jets::tree::*; +use crate::jets::warm::Warm; use crate::mem::NockStack; use crate::noun::{self, Noun, Slots}; use ares_macros::tas; @@ -309,10 +316,18 @@ pub mod util { } pub fn assert_jet(stack: &mut NockStack, jet: Jet, sam: Noun, res: Noun) { + // XX: consider making a mock context singleton that tests can use + let mut cache = Hamt::::new(); + let mut cold = Cold::new(stack); + let mut warm = Warm::new(); + let hot = Hot::init(stack); let mut context = Context { stack, newt: None, - cache: &mut Hamt::::new(), + cache: &mut cache, + cold: &mut cold, + warm: &mut warm, + hot: &hot, }; let sam = T(context.stack, &[D(0), sam, D(0)]); let jet_res = assert_no_alloc(|| jet(&mut context, sam).unwrap()); @@ -330,10 +345,18 @@ pub mod util { } pub fn assert_jet_err(stack: &mut NockStack, jet: Jet, sam: Noun, err: JetErr) { + // XX: consider making a mock context singleton that tests can use + let mut cache = Hamt::::new(); + let mut cold = Cold::new(stack); + let mut warm = Warm::new(); + let hot = Hot::init(stack); let mut context = Context { stack, newt: None, - cache: &mut Hamt::::new(), + cache: &mut cache, + cold: &mut cold, + warm: &mut warm, + hot: &hot, }; let sam = T(context.stack, &[D(0), sam, D(0)]); let jet_res = jet(&mut context, sam); diff --git a/rust/ares/src/jets/cold.rs b/rust/ares/src/jets/cold.rs new file mode 100644 index 00000000..c3bff84f --- /dev/null +++ b/rust/ares/src/jets/cold.rs @@ -0,0 +1,532 @@ +use crate::hamt::Hamt; +use crate::mem::{unifying_equality, NockStack, Preserve}; +use crate::noun; +use crate::noun::{Atom, DirectAtom, Noun, Slots, D, T}; +use std::ptr::copy_nonoverlapping; +use std::ptr::null_mut; + +pub enum Error { + NoParent, + BadNock, +} + +impl From for Error { + fn from(_: noun::Error) -> Self { + Error::BadNock + } +} + +pub type Result = std::result::Result; + +#[derive(Copy, Clone)] +pub struct Batteries(*mut BatteriesMem); + +const NO_BATTERIES: Batteries = Batteries(null_mut()); + +#[derive(Copy, Clone)] +struct BatteriesMem { + battery: Noun, + parent_axis: Atom, + parent_batteries: Batteries, +} + +impl Preserve for Batteries { + unsafe fn assert_in_stack(&self, stack: &NockStack) { + if self.0.is_null() { + return; + }; + let mut cursor = *self; + loop { + stack.assert_struct_is_in(cursor.0, 1); + (*cursor.0).battery.assert_in_stack(stack); + (*cursor.0).parent_axis.assert_in_stack(stack); + if (*cursor.0).parent_batteries.0.is_null() { + break; + }; + cursor = (*cursor.0).parent_batteries; + } + } + unsafe fn preserve(&mut self, stack: &mut NockStack) { + if self.0.is_null() { + return; + }; + let mut ptr: *mut *mut BatteriesMem = &mut self.0; + loop { + if stack.is_in_frame(*ptr) { + (**ptr).battery.preserve(stack); + (**ptr).parent_axis.preserve(stack); + let dest_mem: *mut BatteriesMem = stack.struct_alloc_in_previous_frame(1); + copy_nonoverlapping(*ptr, dest_mem, 1); + *ptr = dest_mem; + ptr = &mut ((**ptr).parent_batteries.0); + if (*dest_mem).parent_batteries.0.is_null() { + break; + }; + } else { + break; + } + } + } +} + +impl Iterator for Batteries { + type Item = (*mut Noun, Atom); + fn next(&mut self) -> Option { + if self.0.is_null() { + None + } else { + unsafe { + let res = ( + &mut (*(self.0)).battery as *mut Noun, + (*(self.0)).parent_axis, + ); + *self = (*(self.0)).parent_batteries; + Some(res) + } + } + } +} + +impl Batteries { + pub fn matches(self, stack: &mut NockStack, mut core: Noun) -> bool { + let mut root_found: bool = false; + + for (battery, parent_axis) in self { + if root_found { + panic!("cold: core matched to root, but more data remains in path"); + } + + if let Ok(d) = parent_axis.as_direct() { + if d.data() == 0 { + if unsafe { unifying_equality(stack, &mut core, battery) } { + root_found = true; + continue; + } else { + return false; + }; + }; + }; + if let Ok(mut core_battery) = core.slot(2) { + if unsafe { !unifying_equality(stack, &mut core_battery, battery) } { + return false; + }; + if let Ok(core_parent) = core.slot_atom(parent_axis) { + core = core_parent; + continue; + } else { + return false; + } + } else { + return false; + } + } + + if !root_found { + panic!("cold: core matched exactly, but never matched root"); + } + + true + } +} + +#[derive(Copy, Clone)] +pub struct BatteriesList(*mut BatteriesListMem); + +const BATTERIES_LIST_NIL: BatteriesList = BatteriesList(null_mut()); + +#[derive(Copy, Clone)] +struct BatteriesListMem { + batteries: Batteries, + next: BatteriesList, +} + +impl Preserve for BatteriesList { + unsafe fn assert_in_stack(&self, stack: &NockStack) { + if self.0.is_null() { + return; + } + let mut cursor = *self; + loop { + stack.assert_struct_is_in(cursor.0, 1); + (*cursor.0).batteries.assert_in_stack(stack); + if (*cursor.0).next.0.is_null() { + break; + }; + cursor = (*cursor.0).next; + } + } + unsafe fn preserve(&mut self, stack: &mut NockStack) { + if self.0.is_null() { + return; + }; + let mut ptr: *mut *mut BatteriesListMem = &mut self.0; + loop { + if stack.is_in_frame(*ptr) { + (**ptr).batteries.preserve(stack); + let dest_mem: *mut BatteriesListMem = stack.struct_alloc_in_previous_frame(1); + copy_nonoverlapping(*ptr, dest_mem, 1); + *ptr = dest_mem; + ptr = &mut ((**ptr).next.0); + if (*dest_mem).next.0.is_null() { + break; + }; + } else { + break; + } + } + } +} + +impl Iterator for BatteriesList { + type Item = Batteries; + fn next(&mut self) -> Option { + if self.0.is_null() { + None + } else { + unsafe { + let mem = *(self.0); + let res = mem.batteries; + *self = mem.next; + Some(res) + } + } + } +} + +impl BatteriesList { + fn matches(mut self, stack: &mut NockStack, core: Noun) -> Option { + self.find(|&batteries| batteries.matches(stack, core)) + } +} + +#[derive(Copy, Clone)] +struct NounList(*mut NounListMem); + +const NOUN_LIST_NIL: NounList = NounList(null_mut()); + +#[derive(Copy, Clone)] +struct NounListMem { + element: Noun, + next: NounList, +} + +impl Preserve for NounList { + unsafe fn assert_in_stack(&self, stack: &NockStack) { + if self.0.is_null() { + return; + }; + let mut cursor = *self; + loop { + stack.assert_struct_is_in(cursor.0, 1); + (*cursor.0).element.assert_in_stack(stack); + if (*cursor.0).next.0.is_null() { + break; + }; + cursor = (*cursor.0).next; + } + } + unsafe fn preserve(&mut self, stack: &mut NockStack) { + if self.0.is_null() { + return; + }; + let mut ptr: *mut NounList = self; + loop { + if stack.is_in_frame((*ptr).0) { + (*(*ptr).0).element.preserve(stack); + let dest_mem: *mut NounListMem = stack.struct_alloc_in_previous_frame(1); + copy_nonoverlapping((*ptr).0, dest_mem, 1); + *ptr = NounList(dest_mem); + ptr = &mut ((*(*ptr).0).next); + if (*dest_mem).next.0.is_null() { + break; + }; + } else { + break; + } + } + } +} + +impl Iterator for NounList { + type Item = *mut Noun; + fn next(&mut self) -> Option { + if self.0.is_null() { + None + } else { + unsafe { + let res = &mut (*(self.0)).element; + *self = (*(self.0)).next; + Some(res) + } + } + } +} + +pub struct Cold(*mut ColdMem); + +struct ColdMem { + /// key: outermost battery + /// value: possible registered paths for core + battery_to_paths: Hamt, + /// Roots + /// key: root noun + /// value: root path + root_to_paths: Hamt, + /// key: registered path to core + /// value: linked list of a sequence of nested batteries + path_to_batteries: Hamt, +} + +impl Preserve for Cold { + unsafe fn assert_in_stack(&self, stack: &NockStack) { + stack.assert_struct_is_in(self.0, 1); + (*self.0).battery_to_paths.assert_in_stack(stack); + (*self.0).root_to_paths.assert_in_stack(stack); + (*self.0).path_to_batteries.assert_in_stack(stack); + } + unsafe fn preserve(&mut self, stack: &mut NockStack) { + (*(self.0)).battery_to_paths.preserve(stack); + (*(self.0)).root_to_paths.preserve(stack); + (*(self.0)).path_to_batteries.preserve(stack); + let new_dest: *mut ColdMem = stack.struct_alloc_in_previous_frame(1); + copy_nonoverlapping(self.0, new_dest, 1); + self.0 = new_dest; + } +} + +impl Cold { + pub fn new(stack: &mut NockStack) -> Self { + let battery_to_paths = Hamt::new(); + let root_to_paths = Hamt::new(); + let path_to_batteries = Hamt::new(); + unsafe { + let cold_mem_ptr: *mut ColdMem = stack.struct_alloc(1); + *cold_mem_ptr = ColdMem { + battery_to_paths, + root_to_paths, + path_to_batteries, + }; + Cold(cold_mem_ptr) + } + } + + pub fn find(&mut self, stack: &mut NockStack, path: &mut Noun) -> BatteriesList { + unsafe { + (*(self.0)) + .path_to_batteries + .lookup(stack, path) + .unwrap_or(BATTERIES_LIST_NIL) + } + } + + /// register a core, return a boolean of whether we actually needed to register (false -> + /// already registered) + /// + /// XX: validate chum Noun as $chum + #[allow(clippy::result_unit_err)] + pub fn register( + &mut self, + stack: &mut NockStack, + mut core: Noun, + parent_axis: Atom, + mut chum: Noun, + ) -> Result { + unsafe { + // Are we registering a root? + if let Ok(parent_axis_direct) = parent_axis.as_direct() { + if parent_axis_direct.data() == 0 { + let mut root_path = T(stack, &[chum, D(0)]); + if let Some(paths) = (*(self.0)).root_to_paths.lookup(stack, &mut core) { + for a_path in paths { + if unifying_equality(stack, &mut root_path, a_path) { + return Ok(false); // it's already in here + } + } + } + let batteries_mem_ptr: *mut BatteriesMem = stack.struct_alloc(1); + *batteries_mem_ptr = BatteriesMem { + battery: core, + parent_axis: DirectAtom::new_unchecked(0).as_atom(), + parent_batteries: NO_BATTERIES, + }; + + let current_batteries_list: BatteriesList = (*(self.0)) + .path_to_batteries + .lookup(stack, &mut root_path) + .unwrap_or(BATTERIES_LIST_NIL); + + let batteries_list_mem_ptr: *mut BatteriesListMem = stack.struct_alloc(1); + *batteries_list_mem_ptr = BatteriesListMem { + batteries: Batteries(batteries_mem_ptr), + next: current_batteries_list, + }; + + let current_paths_list: NounList = (*(self.0)) + .root_to_paths + .lookup(stack, &mut core) + .unwrap_or(NOUN_LIST_NIL); + + let paths_list_mem_ptr: *mut NounListMem = stack.struct_alloc(1); + *paths_list_mem_ptr = NounListMem { + element: root_path, + next: current_paths_list, + }; + + let cold_mem_ptr: *mut ColdMem = stack.struct_alloc(1); + *cold_mem_ptr = ColdMem { + battery_to_paths: (*(self.0)).battery_to_paths, + root_to_paths: (*(self.0)).root_to_paths.insert( + stack, + &mut core, + NounList(paths_list_mem_ptr), + ), + path_to_batteries: (*(self.0)).path_to_batteries.insert( + stack, + &mut root_path, + BatteriesList(batteries_list_mem_ptr), + ), + }; + + *self = Cold(cold_mem_ptr); + return Ok(true); + } + } + + let mut battery = core.slot(2)?; + let mut parent = core.slot_atom(parent_axis)?; + // Check if we already registered this core + if let Some(paths) = (*(self.0)).battery_to_paths.lookup(stack, &mut battery) { + for path in paths { + if let Ok(path_cell) = (*path).as_cell() { + if unifying_equality(stack, &mut path_cell.head(), &mut chum) { + if let Some(batteries_list) = + (*(self.0)).path_to_batteries.lookup(stack, &mut *path) + { + if let Some(_batteries) = batteries_list.matches(stack, core) { + return Ok(false); + } + } + } + } + } + } + + let mut parent_battery = parent.slot(2)?; + + // err until we actually found a parent + let mut ret: Result = Err(Error::NoParent); + + let mut path_to_batteries = (*(self.0)).path_to_batteries; + let mut battery_to_paths = (*(self.0)).battery_to_paths; + let root_to_paths = (*(self.0)).root_to_paths; + + if let Some(paths) = battery_to_paths.lookup(stack, &mut parent_battery) { + for a_path in paths { + // path is a reserved word lol + let battery_list = path_to_batteries + .lookup(stack, &mut *a_path) + .unwrap_or(BATTERIES_LIST_NIL); + if let Some(parent_batteries) = battery_list.matches(stack, parent) { + let mut my_path = T(stack, &[chum, *a_path]); + + let batteries_mem_ptr: *mut BatteriesMem = stack.struct_alloc(1); + *batteries_mem_ptr = BatteriesMem { + battery, + parent_axis, + parent_batteries, + }; + + let current_batteries_list = path_to_batteries + .lookup(stack, &mut my_path) + .unwrap_or(BATTERIES_LIST_NIL); + let batteries_list_mem_ptr: *mut BatteriesListMem = stack.struct_alloc(1); + *batteries_list_mem_ptr = BatteriesListMem { + batteries: Batteries(batteries_mem_ptr), + next: current_batteries_list, + }; + + let current_paths_list = battery_to_paths + .lookup(stack, &mut battery) + .unwrap_or(NOUN_LIST_NIL); + let paths_list_mem_ptr: *mut NounListMem = stack.struct_alloc(1); + *paths_list_mem_ptr = NounListMem { + element: my_path, + next: current_paths_list, + }; + + path_to_batteries = path_to_batteries.insert( + stack, + &mut my_path, + BatteriesList(batteries_list_mem_ptr), + ); + battery_to_paths = battery_to_paths.insert( + stack, + &mut battery, + NounList(paths_list_mem_ptr), + ); + ret = Ok(true); + } + } + }; + + if let Some(paths) = root_to_paths.lookup(stack, &mut parent) { + for a_path in paths { + // path is a reserved word lol + let battery_list = path_to_batteries + .lookup(stack, &mut *a_path) + .unwrap_or(BATTERIES_LIST_NIL); + if let Some(parent_batteries) = battery_list.matches(stack, parent) { + let mut my_path = T(stack, &[chum, *a_path]); + + let batteries_mem_ptr: *mut BatteriesMem = stack.struct_alloc(1); + *batteries_mem_ptr = BatteriesMem { + battery, + parent_axis, + parent_batteries, + }; + + let current_batteries_list = path_to_batteries + .lookup(stack, &mut my_path) + .unwrap_or(BATTERIES_LIST_NIL); + let batteries_list_mem_ptr: *mut BatteriesListMem = stack.struct_alloc(1); + *batteries_list_mem_ptr = BatteriesListMem { + batteries: Batteries(batteries_mem_ptr), + next: current_batteries_list, + }; + + let current_paths_list = battery_to_paths + .lookup(stack, &mut battery) + .unwrap_or(NOUN_LIST_NIL); + let paths_list_mem_ptr: *mut NounListMem = stack.struct_alloc(1); + *paths_list_mem_ptr = NounListMem { + element: my_path, + next: current_paths_list, + }; + + path_to_batteries = path_to_batteries.insert( + stack, + &mut my_path, + BatteriesList(batteries_list_mem_ptr), + ); + battery_to_paths = battery_to_paths.insert( + stack, + &mut battery, + NounList(paths_list_mem_ptr), + ); + ret = Ok(true); + } + } + }; + + let cold_mem_ptr: *mut ColdMem = stack.struct_alloc(1); + *cold_mem_ptr = ColdMem { + battery_to_paths, + root_to_paths, + path_to_batteries, + }; + + *self = Cold(cold_mem_ptr); + ret + } + } +} diff --git a/rust/ares/src/jets/hot.rs b/rust/ares/src/jets/hot.rs new file mode 100644 index 00000000..78ca9edc --- /dev/null +++ b/rust/ares/src/jets/hot.rs @@ -0,0 +1,116 @@ +use crate::jets::*; +use crate::noun::{Atom, DirectAtom, Noun, D, T}; +use ares_macros::tas; +use either::Either::{self, Left, Right}; +use std::ptr::null_mut; + +const A_50: Either = Right((tas!(b"a"), 50)); + +// This is the const state all in one spot as literals +#[allow(clippy::complexity)] +const HOT_STATE: &[(&[Either], u64, Jet)] = &[ + (&[A_50, Left(tas!(b"add"))], 1, jet_add), + (&[A_50, Left(tas!(b"dec"))], 1, jet_dec), + (&[A_50, Left(tas!(b"div"))], 1, jet_div), + (&[A_50, Left(tas!(b"dvr"))], 1, jet_dvr), + (&[A_50, Left(tas!(b"gth"))], 1, jet_gth), + (&[A_50, Left(tas!(b"gte"))], 1, jet_gte), + (&[A_50, Left(tas!(b"lte"))], 1, jet_lte), + (&[A_50, Left(tas!(b"lth"))], 1, jet_lth), + (&[A_50, Left(tas!(b"mod"))], 1, jet_mod), + (&[A_50, Left(tas!(b"mul"))], 1, jet_mul), + (&[A_50, Left(tas!(b"sub"))], 1, jet_sub), + // + (&[A_50, Left(tas!(b"cap"))], 1, jet_cap), + (&[A_50, Left(tas!(b"mas"))], 1, jet_mas), + // + (&[A_50, Left(tas!(b"lent"))], 1, jet_lent), + // + (&[A_50, Left(tas!(b"bex"))], 1, jet_bex), + (&[A_50, Left(tas!(b"can"))], 1, jet_can), + (&[A_50, Left(tas!(b"cat"))], 1, jet_cat), + (&[A_50, Left(tas!(b"cut"))], 1, jet_cut), + (&[A_50, Left(tas!(b"end"))], 1, jet_end), + (&[A_50, Left(tas!(b"lsh"))], 1, jet_lsh), + (&[A_50, Left(tas!(b"met"))], 1, jet_met), + (&[A_50, Left(tas!(b"rap"))], 1, jet_rap), + (&[A_50, Left(tas!(b"rep"))], 1, jet_rep), + (&[A_50, Left(tas!(b"rev"))], 1, jet_rev), + (&[A_50, Left(tas!(b"rip"))], 1, jet_rip), + (&[A_50, Left(tas!(b"rsh"))], 1, jet_rsh), + // + (&[A_50, Left(tas!(b"con"))], 1, jet_con), + (&[A_50, Left(tas!(b"dis"))], 1, jet_dis), + (&[A_50, Left(tas!(b"mix"))], 1, jet_mix), + // + (&[A_50, Left(tas!(b"mug"))], 1, jet_mug), + // + (&[A_50, Left(tas!(b"scow"))], 1, jet_scow), + // + (&[A_50, Left(tas!(b"mink"))], 1, jet_mink), +]; + +#[derive(Copy, Clone)] +pub struct Hot(*mut HotMem); + +impl Hot { + pub fn init(stack: &mut NockStack) -> Self { + unsafe { + let mut next = Hot(null_mut()); + for (htap, axe, jet) in HOT_STATE { + let mut a_path = D(0); + for i in *htap { + match i { + Left(tas) => { + a_path = T( + stack, + &[DirectAtom::new_panic(*tas).as_atom().as_noun(), a_path], + ); + } + Right((tas, ver)) => { + let chum = T( + stack, + &[ + DirectAtom::new_panic(*tas).as_atom().as_noun(), + DirectAtom::new_panic(*ver).as_atom().as_noun(), + ], + ); + a_path = T(stack, &[chum, a_path]); + } + }; + } + let axis = DirectAtom::new_panic(*axe).as_atom(); + let hot_mem_ptr: *mut HotMem = stack.struct_alloc(1); + *hot_mem_ptr = HotMem { + a_path, + axis, + jet: *jet, + next, + }; + next = Hot(hot_mem_ptr); + } + next + } + } +} + +impl Iterator for Hot { + type Item = (Noun, Atom, Jet); // path,axis,jet + fn next(&mut self) -> Option { + if self.0.is_null() { + return None; + } + unsafe { + let res = ((*(self.0)).a_path, (*(self.0)).axis, (*(self.0)).jet); + *self = (*(self.0)).next; + Some(res) + } + } +} + +struct HotMem { + a_path: Noun, + axis: Atom, // Axis of jetted formula in *battery*; + jet: Jet, + next: Hot, +} diff --git a/rust/ares/src/jets/nock.rs b/rust/ares/src/jets/nock.rs index ed22fc81..a3dc4609 100644 --- a/rust/ares/src/jets/nock.rs +++ b/rust/ares/src/jets/nock.rs @@ -29,8 +29,8 @@ pub mod util { use ares_macros::tas; use std::result; - const LEAF: Noun = D(tas!(b"leaf")); - const ROSE: Noun = D(tas!(b"rose")); + pub const LEAF: Noun = D(tas!(b"leaf")); + pub const ROSE: Noun = D(tas!(b"rose")); pub fn mink(context: &mut Context, subject: Noun, formula: Noun) -> jets::Result { // XX: no partial traces; all of our traces go down to the "home road" diff --git a/rust/ares/src/jets/warm.rs b/rust/ares/src/jets/warm.rs new file mode 100644 index 00000000..4a3b52d1 --- /dev/null +++ b/rust/ares/src/jets/warm.rs @@ -0,0 +1,141 @@ +use crate::hamt::Hamt; +use crate::jets::cold::{Batteries, Cold}; +use crate::jets::hot::Hot; +use crate::jets::Jet; +use crate::mem::{NockStack, Preserve}; +use crate::noun::{Noun, Slots}; +use std::ptr::{copy_nonoverlapping, null_mut}; + +pub struct Warm(Hamt); + +impl Preserve for Warm { + unsafe fn assert_in_stack(&self, stack: &NockStack) { + self.0.assert_in_stack(stack); + } + unsafe fn preserve(&mut self, stack: &mut NockStack) { + self.0.preserve(stack); + } +} + +#[derive(Copy, Clone)] +struct WarmEntry(*mut WarmEntryMem); + +const WARM_ENTRY_NIL: WarmEntry = WarmEntry(null_mut()); + +struct WarmEntryMem { + batteries: Batteries, + jet: Jet, + path: Noun, // useful for profiling/debugging + next: WarmEntry, +} + +impl Preserve for WarmEntry { + unsafe fn assert_in_stack(&self, stack: &NockStack) { + if self.0.is_null() { + return; + }; + let mut cursor = *self; + loop { + stack.assert_struct_is_in(cursor.0, 1); + (*cursor.0).batteries.assert_in_stack(stack); + (*cursor.0).path.assert_in_stack(stack); + if (*cursor.0).next.0.is_null() { + break; + }; + cursor = (*cursor.0).next; + } + } + unsafe fn preserve(&mut self, stack: &mut NockStack) { + if self.0.is_null() { + return; + } + let mut ptr: *mut *mut WarmEntryMem = &mut self.0; + loop { + if stack.is_in_frame(*ptr) { + (**ptr).batteries.preserve(stack); + (**ptr).path.preserve(stack); + let dest_mem: *mut WarmEntryMem = stack.struct_alloc_in_previous_frame(1); + copy_nonoverlapping(*ptr, dest_mem, 1); + *ptr = dest_mem; + ptr = &mut ((*dest_mem).next.0); + if (*dest_mem).next.0.is_null() { + break; + }; + } else { + break; + } + } + } +} + +impl Iterator for WarmEntry { + type Item = (Noun, Batteries, Jet); + fn next(&mut self) -> Option { + if self.0.is_null() { + return None; + } + unsafe { + let res = ((*(self.0)).path, (*(self.0)).batteries, (*(self.0)).jet); + *self = (*(self.0)).next; + Some(res) + } + } +} + +impl Warm { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Warm(Hamt::new()) + } + + fn insert( + &mut self, + stack: &mut NockStack, + formula: &mut Noun, + path: Noun, + batteries: Batteries, + jet: Jet, + ) { + let current_warm_entry = self.0.lookup(stack, formula).unwrap_or(WARM_ENTRY_NIL); + unsafe { + let warm_entry_mem_ptr: *mut WarmEntryMem = stack.struct_alloc(1); + *warm_entry_mem_ptr = WarmEntryMem { + batteries, + jet, + path, + next: current_warm_entry, + }; + self.0 = self.0.insert(stack, formula, WarmEntry(warm_entry_mem_ptr)); + } + } + + pub fn init(stack: &mut NockStack, cold: &mut Cold, hot: &Hot) -> Self { + let mut warm = Self::new(); + for (mut path, axis, jet) in *hot { + let batteries_list = cold.find(stack, &mut path); + for batteries in batteries_list { + let mut batteries_tmp = batteries; + let (battery, _parent_axis) = batteries_tmp + .next() + .expect("IMPOSSIBLE: empty battery entry in cold state"); + if let Ok(mut formula) = unsafe { (*battery).slot_atom(axis) } { + warm.insert(stack, &mut formula, path, batteries, jet); + } else { + eprintln!("Bad axis {} into formula {:?}", axis, battery); + continue; + } + } + } + warm + } + + pub fn find_jet(&mut self, stack: &mut NockStack, s: &mut Noun, f: &mut Noun) -> Option { + let warm_it = self.0.lookup(stack, f)?; + for (_path, batteries, jet) in warm_it { + if batteries.matches(stack, *s) { + return Some(jet); + } + } + None + } +} diff --git a/rust/ares/src/main.rs b/rust/ares/src/main.rs index 75afefb5..73ea4f52 100644 --- a/rust/ares/src/main.rs +++ b/rust/ares/src/main.rs @@ -1,5 +1,8 @@ use ares::hamt::Hamt; use ares::interpreter::{interpret, Context}; +use ares::jets::cold::Cold; +use ares::jets::hot::Hot; +use ares::jets::warm::Warm; use ares::mem::NockStack; use ares::noun::{IndirectAtom, Noun}; use ares::serf::serf; @@ -62,10 +65,17 @@ fn main() -> io::Result<()> { let input_cell = input .as_cell() .expect("Input must be jam of subject/formula pair"); + let mut cache = Hamt::::new(); + let mut cold = Cold::new(&mut stack); + let mut warm = Warm::new(); + let hot = Hot::init(&mut stack); let mut context = Context { stack: &mut stack, newt: None, - cache: &mut Hamt::::new(), + cache: &mut cache, + cold: &mut cold, + warm: &mut warm, + hot: &hot, }; let result = interpret(&mut context, input_cell.head(), input_cell.tail()).expect("nock failed"); diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index c8c80c13..90cb32e7 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -468,6 +468,64 @@ impl NockStack { assert_no_junior_pointers!(self, *noun); } + pub unsafe fn assert_struct_is_in(&self, ptr: *const T, count: usize) { + let ap = (if self.pc { + *(self.prev_alloc_pointer_pointer()) + } else { + self.alloc_pointer + }) as usize; + let sp = (if self.pc { + *(self.prev_stack_pointer_pointer()) + } else { + self.stack_pointer + }) as usize; + let (low, hi) = if ap > sp { (sp, ap) } else { (ap, sp) }; + if ((ptr as usize) < low && (ptr.add(count) as usize) <= low) + || ((ptr as usize) >= hi && (ptr.add(count) as usize) > hi) + { + return; + } + panic!( + "Use after free: allocation from {:#x} to {:#x}, free space from {:#x} to {:#x}", + ptr as usize, + ptr.add(count) as usize, + low, + hi + ); + } + + unsafe fn assert_noun_in(&self, noun: Noun) { + let mut dbg_stack = Vec::new(); + dbg_stack.push(noun); + let ap = (if self.pc { + *(self.prev_alloc_pointer_pointer()) + } else { + self.alloc_pointer + }) as usize; + let sp = (if self.pc { + *(self.prev_stack_pointer_pointer()) + } else { + self.stack_pointer + }) as usize; + let (low, hi) = if ap > sp { (sp, ap) } else { (ap, sp) }; + loop { + if let Some(subnoun) = dbg_stack.pop() { + if let Ok(a) = subnoun.as_allocated() { + let np = a.to_raw_pointer() as usize; + if np >= low && np < hi { + panic!("noun not in {:?}: {:?}", (low, hi), subnoun); + } + if let Right(c) = a.as_either() { + dbg_stack.push(c.tail()); + dbg_stack.push(c.head()); + } + } + } else { + return; + } + } + } + pub unsafe fn copy_pma(&mut self, noun: &mut Noun) { // copy_pma() should only be called when there is a single stack // frame; these asserts assure that. @@ -1016,6 +1074,7 @@ impl NounAllocator for NockStack { pub trait Preserve { /// Ensure an object will not be invalidated by popping the NockStack unsafe fn preserve(&mut self, stack: &mut NockStack); + unsafe fn assert_in_stack(&self, stack: &NockStack); } impl Preserve for IndirectAtom { @@ -1025,6 +1084,9 @@ impl Preserve for IndirectAtom { copy_nonoverlapping(self.to_raw_pointer(), buf, size); *self = IndirectAtom::from_raw_pointer(buf); } + unsafe fn assert_in_stack(&self, stack: &NockStack) { + stack.assert_noun_in(self.as_atom().as_noun()); + } } impl Preserve for Atom { @@ -1037,12 +1099,18 @@ impl Preserve for Atom { } } } + unsafe fn assert_in_stack(&self, stack: &NockStack) { + stack.assert_noun_in(self.as_noun()); + } } impl Preserve for Noun { unsafe fn preserve(&mut self, stack: &mut NockStack) { stack.copy(self); } + unsafe fn assert_in_stack(&self, stack: &NockStack) { + stack.assert_noun_in(*self); + } } impl Stack for NockStack { diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 2442890e..fdddfe75 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -1,8 +1,11 @@ use crate::hamt::Hamt; use crate::interpreter; use crate::interpreter::{inc, interpret, Tone}; +use crate::jets::cold::Cold; +use crate::jets::hot::Hot; use crate::jets::nock::util::mook; use crate::jets::text::util::lent; +use crate::jets::warm::Warm; use crate::mem::NockStack; use crate::mug::mug_u32; use crate::newt::Newt; @@ -31,6 +34,9 @@ struct Context { newt: Newt, cache: Hamt, // XX: persistent memo cache + cold: Cold, + warm: Warm, + hot: Hot, } impl Context { @@ -42,6 +48,10 @@ impl Context { let newt = Newt::new(); let cache = Hamt::::new(); + let cold = Cold::new(&mut stack); + let warm = Warm::new(); + let hot = Hot::init(&mut stack); + let (epoch, event_num, arvo) = snapshot.load(&mut stack).unwrap_or((0, 0, D(0))); let mug = mug_u32(&mut stack, arvo); @@ -54,6 +64,9 @@ impl Context { stack, newt, cache, + cold, + warm, + hot, } } @@ -84,6 +97,9 @@ impl Context { stack: &mut self.stack, newt: Some(&mut self.newt), cache: &mut self.cache, + cold: &mut self.cold, + warm: &mut self.warm, + hot: &self.hot, } } diff --git a/rust/ares/test_data/decfast.jam b/rust/ares/test_data/decfast.jam new file mode 100644 index 00000000..97072fd2 Binary files /dev/null and b/rust/ares/test_data/decfast.jam differ diff --git a/rust/ares/test_data/decflow.jam b/rust/ares/test_data/decflow.jam new file mode 100644 index 00000000..7738787a Binary files /dev/null and b/rust/ares/test_data/decflow.jam differ diff --git a/rust/ares/test_data/decslow.jam b/rust/ares/test_data/decslow.jam new file mode 100644 index 00000000..0dc72b79 --- /dev/null +++ b/rust/ares/test_data/decslow.jam @@ -0,0 +1 @@ +~1j -p'vil|.B!Td ZdL!Y< [7 ^9{0l >.x*7>wAa&ɐA/1(k \ No newline at end of file