From 4500427e4ae28031cac79b4fd13ba3e17311ff3b Mon Sep 17 00:00:00 2001 From: Edward Amsden Date: Fri, 10 Nov 2023 10:09:54 -0600 Subject: [PATCH 1/4] jets: rewrite zing in destination passing style --- rust/ares/src/jets/text.rs | 51 +++++++++++--------------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/rust/ares/src/jets/text.rs b/rust/ares/src/jets/text.rs index c3cb345..20ad3e0 100644 --- a/rust/ares/src/jets/text.rs +++ b/rust/ares/src/jets/text.rs @@ -48,48 +48,27 @@ pub mod util { pub fn zing(stack: &mut NockStack, mut list: Noun) -> jets::Result { unsafe { - let (mut new_cell, mut new_memory) = Cell::new_raw_mut(stack); - #[allow(unused_assignments)] - let (mut cell, mut memory) = (new_cell, new_memory); let mut res: Noun = D(0); - let mut flag = false; - - loop { - if list.raw_equals(D(0)) { - break; - } + let mut dest = &mut res as *mut Noun; + while !list.raw_equals(D(0)) { let pair = list.as_cell()?; - let mut sub_list = pair.head(); - - loop { - if sub_list.raw_equals(D(0)) { - break; - } - - let elem = sub_list.as_cell()?; - let head = elem.head(); - - if flag { - (new_cell, new_memory) = Cell::new_raw_mut(stack); - (*memory).tail = new_cell.as_noun(); - memory = new_memory; - } else { - (cell, memory) = Cell::new_raw_mut(stack); - res = cell.as_noun(); - flag = true; - } - (*memory).head = head; - - sub_list = elem.tail(); - } - + let mut sublist = pair.head(); list = pair.tail(); - } - if flag { - (*memory).tail = D(0); + while !sublist.raw_equals(D(0)) { + let it = sublist.as_cell()?; + let i = it.head(); + sublist = it.tail(); + + let (new_cell, new_memory) = Cell::new_raw_mut(stack); + (*new_memory).head = i; + *dest = new_cell.as_noun(); + dest = &mut (*new_memory).tail; + } } + + *dest = D(0); Ok(res) } } From 4177ae6fa3a6f02a797559623f0cf8458d46ea2f Mon Sep 17 00:00:00 2001 From: Edward Amsden Date: Fri, 10 Nov 2023 10:39:55 -0600 Subject: [PATCH 2/4] jets: peg and mas implemented using bitslices --- rust/ares/src/jets/tree.rs | 60 ++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/rust/ares/src/jets/tree.rs b/rust/ares/src/jets/tree.rs index eadf78e..5584228 100644 --- a/rust/ares/src/jets/tree.rs +++ b/rust/ares/src/jets/tree.rs @@ -1,11 +1,9 @@ /** Tree jets */ use crate::interpreter::{Context, Error}; -use crate::jets; -use crate::jets::bits; use crate::jets::util::*; use crate::jets::{JetErr, Result}; -use crate::noun::{Noun, D}; +use crate::noun::{IndirectAtom, Noun, D}; crate::gdb!(); @@ -27,43 +25,55 @@ pub fn jet_cap(_context: &mut Context, subject: Noun) -> Result { pub fn jet_mas(context: &mut Context, subject: Noun) -> Result { let stack = &mut context.stack; - let arg = slot(subject, 6)?; - let tom = arg.as_atom()?; + let tom = slot(subject, 6)?.as_atom()?; let met = met(0, tom); if met < 2 { Err(JetErr::Fail(Error::Deterministic(D(0)))) } else { - let c = bex(stack, met - 1); - let d = bex(stack, met - 2); - let e = sub(stack, tom, c)?; - - Ok(con(stack, e, d).as_noun()) + let out_bits = met - 1; + let out_words = (out_bits + 63) << 6; + let (mut indirect_out, out_bs) = unsafe { IndirectAtom::new_raw_mut_bitslice(stack, out_words) }; + out_bs.set(met - 2, true); // Set MSB + if met > 2 { + out_bs[0..met - 2].copy_from_bitslice(&tom.as_bitslice()[0..met - 2]); + }; + unsafe { + Ok(indirect_out.normalize_as_atom().as_noun()) + } } } pub fn jet_peg(context: &mut Context, subject: Noun) -> Result { let stack = &mut context.stack; let arg = slot(subject, 6)?; - let a = slot(arg, 2)?; - let b = slot(arg, 3)?; + let a = slot(arg, 2)?.as_atom()?; + let b = slot(arg, 3)?.as_atom()?; unsafe { - if a.raw_equals(D(0)) { + if a.as_noun().raw_equals(D(0)) { return Err(JetErr::Fail(Error::Deterministic(D(0)))); - } - // XX: Import jet mistmatch from Vere - if b.raw_equals(D(0)) { - return Err(JetErr::Fail(Error::Deterministic(D(0)))); - } - }; + }; - let c = met(0, b.as_atom()?); - let d = c - 1; - let e = bits::util::lsh(stack, 0, d, D(1).as_atom()?)?; - let f = jets::util::sub(stack, b.as_atom()?, e)?; - let g = bits::util::lsh(stack, 0, d, a.as_atom()?)?; - Ok(jets::util::add(stack, f, g).as_noun()) + if b.as_noun().raw_equals(D(0)) { + return Err(JetErr::Fail(Error::Deterministic(D(0)))); + }; + } + + let a_bits = met(0, a); + let b_bits = met(0, b); + let out_bits = a_bits + b_bits - 1; + + let out_words = (out_bits + 63) << 6; // bits to 8-byte words + + let (mut indirect_out, out_bs) = unsafe { IndirectAtom::new_raw_mut_bitslice(stack, out_words) }; + + out_bs[0..b_bits - 1].copy_from_bitslice(&b.as_bitslice()[0..b_bits - 1]); + out_bs[b_bits - 1..out_bits].copy_from_bitslice(&a.as_bitslice()[0..a_bits]); + + unsafe { + Ok(indirect_out.normalize_as_atom().as_noun()) + } } #[cfg(test)] From 214b745a435fd25743b866ce6e76f35035293207 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 15 Nov 2023 11:08:48 +0100 Subject: [PATCH 3/4] jets: fix allocation bug and add tests for it --- rust/ares/Cargo.toml | 4 ++-- rust/ares/src/jets.rs | 19 +++++++++++++++ rust/ares/src/jets/tree.rs | 49 +++++++++++++++++++++++++++++--------- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 96ffd8b..22c28c3 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -12,9 +12,9 @@ edition = "2018" # Please keep these alphabetized [dependencies] ares_macros = { path = "../ares_macros" } -assert_no_alloc = "1.1.2" +# assert_no_alloc = "1.1.2" # use this when debugging requires allocation (e.g. eprintln) -# assert_no_alloc = {version="1.1.2", features=["warn_debug"]} +assert_no_alloc = {version="1.1.2", features=["warn_debug"]} bitvec = "1.0.0" criterion = "0.4" either = "1.9.0" diff --git a/rust/ares/src/jets.rs b/rust/ares/src/jets.rs index 9e57b4d..2137a8f 100644 --- a/rust/ares/src/jets.rs +++ b/rust/ares/src/jets.rs @@ -421,6 +421,14 @@ pub mod util { } } + pub fn assert_jet_size(context: &mut Context, jet: Jet, sam: Noun, siz: usize) { + let sam = T(&mut context.stack, &[D(0), sam, D(0)]); + let res = assert_no_alloc(|| jet(context, sam).unwrap()); + assert!(res.is_atom(), "jet result not atom"); + let res_siz = res.atom().unwrap().size(); + assert!(siz == res_siz, "got: {}, need: {}", res_siz, siz); + } + pub fn assert_common_jet( context: &mut Context, jet: Jet, @@ -452,6 +460,17 @@ pub mod util { let sam = T(&mut context.stack, &sam); assert_jet_err(context, jet, sam, err); } + + pub fn assert_common_jet_size( + context: &mut Context, + jet: Jet, + sam: &[fn(&mut NockStack) -> Noun], + siz: usize, + ) { + let sam: Vec = sam.iter().map(|f| f(&mut context.stack)).collect(); + let sam = T(&mut context.stack, &sam); + assert_jet_size(context, jet, sam, siz) + } } #[cfg(test)] diff --git a/rust/ares/src/jets/tree.rs b/rust/ares/src/jets/tree.rs index 5584228..d69be46 100644 --- a/rust/ares/src/jets/tree.rs +++ b/rust/ares/src/jets/tree.rs @@ -32,15 +32,14 @@ pub fn jet_mas(context: &mut Context, subject: Noun) -> Result { Err(JetErr::Fail(Error::Deterministic(D(0)))) } else { let out_bits = met - 1; - let out_words = (out_bits + 63) << 6; - let (mut indirect_out, out_bs) = unsafe { IndirectAtom::new_raw_mut_bitslice(stack, out_words) }; + let out_words = (out_bits + 63) >> 6; + let (mut indirect_out, out_bs) = + unsafe { IndirectAtom::new_raw_mut_bitslice(stack, out_words) }; out_bs.set(met - 2, true); // Set MSB if met > 2 { - out_bs[0..met - 2].copy_from_bitslice(&tom.as_bitslice()[0..met - 2]); + out_bs[0..(met - 2)].copy_from_bitslice(&tom.as_bitslice()[0..(met - 2)]); }; - unsafe { - Ok(indirect_out.normalize_as_atom().as_noun()) - } + unsafe { Ok(indirect_out.normalize_as_atom().as_noun()) } } } @@ -64,16 +63,15 @@ pub fn jet_peg(context: &mut Context, subject: Noun) -> Result { let b_bits = met(0, b); let out_bits = a_bits + b_bits - 1; - let out_words = (out_bits + 63) << 6; // bits to 8-byte words + let out_words = (out_bits + 63) >> 6; // bits to 8-byte words - let (mut indirect_out, out_bs) = unsafe { IndirectAtom::new_raw_mut_bitslice(stack, out_words) }; + let (mut indirect_out, out_bs) = + unsafe { IndirectAtom::new_raw_mut_bitslice(stack, out_words) }; out_bs[0..b_bits - 1].copy_from_bitslice(&b.as_bitslice()[0..b_bits - 1]); out_bs[b_bits - 1..out_bits].copy_from_bitslice(&a.as_bitslice()[0..a_bits]); - unsafe { - Ok(indirect_out.normalize_as_atom().as_noun()) - } + unsafe { Ok(indirect_out.normalize_as_atom().as_noun()) } } #[cfg(test)] @@ -102,10 +100,18 @@ mod tests { D(0x4000000000000000) } + fn atom_64(stack: &mut NockStack) -> Noun { + A(stack, &ubig!(_0x8000000000000000)) + } + fn atom_65(stack: &mut NockStack) -> Noun { A(stack, &ubig!(_0x10000000000000000)) } + fn atom_66(stack: &mut NockStack) -> Noun { + A(stack, &ubig!(_0x20000000000000000)) + } + fn pos_2(_stack: &mut NockStack) -> Noun { D(2) } @@ -137,10 +143,16 @@ mod tests { #[test] fn test_mas() { let c = &mut init_context(); + let a63 = atom_63(&mut c.stack); + let a64 = atom_64(&mut c.stack); + let a65 = atom_65(&mut c.stack); + let a66 = atom_66(&mut c.stack); + // Test invalid input assert_jet_err(c, jet_mas, D(0), JetErr::Fail(Error::Deterministic(D(0)))); assert_jet_err(c, jet_mas, D(1), JetErr::Fail(Error::Deterministic(D(0)))); + // Test direct assert_jet(c, jet_mas, D(2), D(1)); assert_jet(c, jet_mas, D(3), D(1)); assert_jet(c, jet_mas, D(4), D(2)); @@ -148,6 +160,16 @@ mod tests { assert_jet(c, jet_mas, D(6), D(2)); assert_jet(c, jet_mas, D(7), D(3)); assert_jet(c, jet_mas, D(8), D(4)); + + // Test indirect + assert_jet(c, jet_mas, a64, a63); + assert_jet(c, jet_mas, a65, a64); + assert_jet(c, jet_mas, a66, a65); + + // Test allocation + assert_jet_size(c, jet_mas, D(2), 1_usize); + assert_jet_size(c, jet_mas, a65, 1_usize); + assert_jet_size(c, jet_mas, a66, 2_usize); } #[test] @@ -184,5 +206,10 @@ mod tests { &[atom_65, atom_65], ubig!(_0x100000000000000000000000000000000), ); + + // Test allocation + assert_common_jet_size(c, jet_peg, &[atom_65, atom_1], 2_usize); + assert_common_jet_size(c, jet_peg, &[atom_1, atom_65], 2_usize); + assert_common_jet_size(c, jet_peg, &[atom_65, atom_65], 3_usize); } } From 3d65516be68e4e5432b718cd9b86a9c8b824ea9b Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 15 Nov 2023 11:41:06 +0100 Subject: [PATCH 4/4] jets: re-enable assert_no_alloc --- rust/ares/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 22c28c3..96ffd8b 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -12,9 +12,9 @@ edition = "2018" # Please keep these alphabetized [dependencies] ares_macros = { path = "../ares_macros" } -# assert_no_alloc = "1.1.2" +assert_no_alloc = "1.1.2" # use this when debugging requires allocation (e.g. eprintln) -assert_no_alloc = {version="1.1.2", features=["warn_debug"]} +# assert_no_alloc = {version="1.1.2", features=["warn_debug"]} bitvec = "1.0.0" criterion = "0.4" either = "1.9.0"