mirror of
https://github.com/urbit/ares.git
synced 2024-12-23 05:12:15 +03:00
snag and roll jets (#269)
* snag * roll and call site caching logic * site: propagate errors * interpreter: use flog for string interpolation * yak shaving, reorder jets alphabetically * jets: flip sample in snag tests
This commit is contained in:
parent
74fad40bdc
commit
299292a876
@ -12,7 +12,7 @@ sword_guard = { path = "../sword_guard" }
|
|||||||
sword_crypto = { path = "../sword_crypto" }
|
sword_crypto = { path = "../sword_crypto" }
|
||||||
sword_macros = { path = "../sword_macros" }
|
sword_macros = { path = "../sword_macros" }
|
||||||
sword_pma = { path = "../sword_pma" }
|
sword_pma = { path = "../sword_pma" }
|
||||||
assert_no_alloc = { path = "../assert_no_alloc" }
|
assert_no_alloc = { path = "../assert_no_alloc", features=["warn_debug"]}
|
||||||
murmur3 = { path = "../murmur3" }
|
murmur3 = { path = "../murmur3" }
|
||||||
bitvec = "1.0.0"
|
bitvec = "1.0.0"
|
||||||
criterion = "0.4"
|
criterion = "0.4"
|
||||||
|
@ -1563,9 +1563,7 @@ mod hint {
|
|||||||
slogger.slog(stack, 0, cell.head());
|
slogger.slog(stack, 0, cell.head());
|
||||||
list = cell.tail();
|
list = cell.tail();
|
||||||
} else {
|
} else {
|
||||||
let stack = &mut context.stack;
|
flog!(context, "serf: %hela: list ends without ~");
|
||||||
let tape = tape(stack, "serf: %hela: list ends without ~");
|
|
||||||
slog_leaf(stack, slogger, tape);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1609,37 +1607,36 @@ mod hint {
|
|||||||
tas!(b"fast") => {
|
tas!(b"fast") => {
|
||||||
if !cfg!(feature = "sham_hints") {
|
if !cfg!(feature = "sham_hints") {
|
||||||
if let Some(clue) = hint {
|
if let Some(clue) = hint {
|
||||||
let cold_res: cold::Result = {
|
let chum = clue.slot(2).ok()?;
|
||||||
let chum = clue.slot(2).ok()?;
|
let mut parent = clue.slot(6).ok()?;
|
||||||
|
loop {
|
||||||
let mut parent = clue.slot(6).ok()?;
|
if let Ok(parent_cell) = parent.as_cell() {
|
||||||
loop {
|
if unsafe { parent_cell.head().raw_equals(D(11)) } {
|
||||||
if let Ok(parent_cell) = parent.as_cell() {
|
match parent.slot(7) {
|
||||||
if unsafe { parent_cell.head().raw_equals(D(11)) } {
|
Ok(noun) => {
|
||||||
match parent.slot(7) {
|
parent = noun;
|
||||||
Ok(noun) => {
|
}
|
||||||
parent = noun;
|
Err(_) => {
|
||||||
}
|
return None;
|
||||||
Err(_) => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return None;
|
break;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
let parent_formula_op = parent.slot(2).ok()?.atom()?.direct()?;
|
}
|
||||||
let parent_formula_ax = parent.slot(3).ok()?.atom()?;
|
let parent_formula_op = parent.slot(2).ok()?.atom()?.direct()?;
|
||||||
|
let parent_formula_ax = parent.slot(3).ok()?.atom()?;
|
||||||
|
|
||||||
|
let cold_res: cold::Result = {
|
||||||
if parent_formula_op.data() == 1 {
|
if parent_formula_op.data() == 1 {
|
||||||
if parent_formula_ax.direct()?.data() == 0 {
|
if parent_formula_ax.direct()?.data() == 0 {
|
||||||
cold.register(stack, res, parent_formula_ax, chum)
|
cold.register(stack, res, parent_formula_ax, chum)
|
||||||
} else {
|
} else {
|
||||||
// XX: Need better message in slog; need better slogging tools
|
// XX: flog! is ideal, but it runs afoul of the borrow checker
|
||||||
// format!("invalid root parent axis: {} {}", chum, parent_formula_ax)
|
// flog!(context, "invalid root parent formula: {} {}", chum, parent);
|
||||||
let tape = tape(
|
let tape = tape(
|
||||||
stack,
|
stack,
|
||||||
"serf: cold: register: invalid root parent axis",
|
"serf: cold: register: invalid root parent axis",
|
||||||
@ -1655,25 +1652,15 @@ mod hint {
|
|||||||
match cold_res {
|
match cold_res {
|
||||||
Ok(true) => context.warm = Warm::init(stack, cold, hot),
|
Ok(true) => context.warm = Warm::init(stack, cold, hot),
|
||||||
Err(cold::Error::NoParent) => {
|
Err(cold::Error::NoParent) => {
|
||||||
// XX: Need better message in slog; need better slogging tools
|
flog!(context, "serf: cold: register: could not match parent battery at given axis: {} {}", chum, parent_formula_ax);
|
||||||
// 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, slogger, tape);
|
|
||||||
}
|
}
|
||||||
Err(cold::Error::BadNock) => {
|
Err(cold::Error::BadNock) => {
|
||||||
// XX: Need better message in slog; need better slogging tools
|
flog!(context, "serf: cold: register: bad clue formula: {}", clue);
|
||||||
// format!("bad clue formula: {}", clue)
|
|
||||||
let tape = tape(stack, "serf: cold: register: bad clue formula");
|
|
||||||
slog_leaf(stack, slogger, tape);
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let tape = tape(stack, "serf: cold: register: no clue for %fast");
|
flog!(context, "serf: cold: register: no clue for %fast");
|
||||||
slog_leaf(stack, slogger, tape);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,16 @@ pub const URBIT_HOT_STATE: &[HotEntry] = &[
|
|||||||
1,
|
1,
|
||||||
jet_lent,
|
jet_lent,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
&[K_139, Left(b"one"), Left(b"two"), Left(b"roll")],
|
||||||
|
1,
|
||||||
|
jet_roll,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
&[K_139, Left(b"one"), Left(b"two"), Left(b"snag")],
|
||||||
|
1,
|
||||||
|
jet_snag,
|
||||||
|
),
|
||||||
(
|
(
|
||||||
&[K_139, Left(b"one"), Left(b"two"), Left(b"snip")],
|
&[K_139, Left(b"one"), Left(b"two"), Left(b"snip")],
|
||||||
1,
|
1,
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
/** Text processing jets
|
/** Text processing jets
|
||||||
*/
|
*/
|
||||||
use crate::interpreter::{interpret, Context};
|
use crate::interpreter::Context;
|
||||||
use crate::jets::util::{slot, BAIL_FAIL};
|
use crate::jets::util::{slot, BAIL_FAIL};
|
||||||
use crate::jets::Result;
|
use crate::jets::Result;
|
||||||
use crate::noun::{Cell, Noun, D, T};
|
use crate::noun::{Cell, Noun, D, T};
|
||||||
use bitvec::order::Lsb0;
|
use crate::site::{site_slam, Site};
|
||||||
use bitvec::slice::BitSlice;
|
|
||||||
|
|
||||||
crate::gdb!();
|
crate::gdb!();
|
||||||
|
|
||||||
@ -19,11 +18,71 @@ pub fn jet_lent(_context: &mut Context, subject: Noun) -> Result {
|
|||||||
util::lent(list).map(|x| D(x as u64))
|
util::lent(list).map(|x| D(x as u64))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn jet_roll(context: &mut Context, subject: Noun) -> Result {
|
||||||
|
let sample = slot(subject, 6)?;
|
||||||
|
let mut list = slot(sample, 2)?;
|
||||||
|
let mut gate = slot(sample, 3)?;
|
||||||
|
let mut prod = slot(gate, 13)?;
|
||||||
|
|
||||||
|
let site = Site::new(context, &mut gate);
|
||||||
|
loop {
|
||||||
|
if let Ok(list_cell) = list.as_cell() {
|
||||||
|
list = list_cell.tail();
|
||||||
|
let sam = T(&mut context.stack, &[list_cell.head(), prod]);
|
||||||
|
prod = site_slam(context, &site, sam)?;
|
||||||
|
} else {
|
||||||
|
if unsafe { !list.raw_equals(D(0)) } {
|
||||||
|
return Err(BAIL_FAIL);
|
||||||
|
}
|
||||||
|
return Ok(prod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jet_snag(_context: &mut Context, subject: Noun) -> Result {
|
||||||
|
let sam = slot(subject, 6)?;
|
||||||
|
let index = slot(sam, 2)?;
|
||||||
|
let list = slot(sam, 3)?;
|
||||||
|
|
||||||
|
util::snag(list, index)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn jet_snip(context: &mut Context, subject: Noun) -> Result {
|
pub fn jet_snip(context: &mut Context, subject: Noun) -> Result {
|
||||||
let list = slot(subject, 6)?;
|
let list = slot(subject, 6)?;
|
||||||
util::snip(&mut context.stack, list)
|
util::snip(&mut context.stack, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn jet_turn(context: &mut Context, subject: Noun) -> Result {
|
||||||
|
let sample = slot(subject, 6)?;
|
||||||
|
let mut list = slot(sample, 2)?;
|
||||||
|
let mut gate = slot(sample, 3)?;
|
||||||
|
let mut res = D(0);
|
||||||
|
let mut dest: *mut Noun = &mut res; // Mutable pointer because we cannot guarantee initialized
|
||||||
|
|
||||||
|
// Since the gate doesn't change, we can do a single jet check and use that through the whole
|
||||||
|
// loop
|
||||||
|
let site = Site::new(context, &mut gate);
|
||||||
|
loop {
|
||||||
|
if let Ok(list_cell) = list.as_cell() {
|
||||||
|
list = list_cell.tail();
|
||||||
|
unsafe {
|
||||||
|
let (new_cell, new_mem) = Cell::new_raw_mut(&mut context.stack);
|
||||||
|
(*new_mem).head = site_slam(context, &site, list_cell.head())?;
|
||||||
|
*dest = new_cell.as_noun();
|
||||||
|
dest = &mut (*new_mem).tail;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if unsafe { !list.raw_equals(D(0)) } {
|
||||||
|
return Err(BAIL_FAIL);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
*dest = D(0);
|
||||||
|
};
|
||||||
|
return Ok(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn jet_zing(context: &mut Context, subject: Noun) -> Result {
|
pub fn jet_zing(context: &mut Context, subject: Noun) -> Result {
|
||||||
let list = slot(subject, 6)?;
|
let list = slot(subject, 6)?;
|
||||||
let stack = &mut context.stack;
|
let stack = &mut context.stack;
|
||||||
@ -31,97 +90,6 @@ pub fn jet_zing(context: &mut Context, subject: Noun) -> Result {
|
|||||||
util::zing(stack, list)
|
util::zing(stack, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn jet_turn(context: &mut Context, subject: Noun) -> Result {
|
|
||||||
let sample = slot(subject, 6)?;
|
|
||||||
let mut list = slot(sample, 2)?;
|
|
||||||
let mut gate = slot(sample, 3)?;
|
|
||||||
let mut gate_battery = slot(gate, 2)?;
|
|
||||||
let gate_context = slot(gate, 7)?;
|
|
||||||
let mut res = D(0);
|
|
||||||
let mut dest: *mut Noun = &mut res; // Mutable pointer because we cannot guarantee initialized
|
|
||||||
|
|
||||||
// Since the gate doesn't change, we can do a single jet check and use that through the whole
|
|
||||||
// loop
|
|
||||||
if let Some((jet, _path)) = context
|
|
||||||
.warm
|
|
||||||
.find_jet(&mut context.stack, &mut gate, &mut gate_battery)
|
|
||||||
.filter(|(_jet, mut path)| {
|
|
||||||
// check that 7 is a prefix of the parent battery axis,
|
|
||||||
// to ensure that the sample (axis 6) is not part of the jet match.
|
|
||||||
//
|
|
||||||
// XX TODO this check is pessimized since there could be multiple ways to match the
|
|
||||||
// jet and we only actually match one of them, but we check all of them and run
|
|
||||||
// unjetted if any have an axis outside 7.
|
|
||||||
let axis_7_bits: &BitSlice<u64, Lsb0> = BitSlice::from_element(&7u64);
|
|
||||||
let batteries_list = context.cold.find(&mut context.stack, &mut path);
|
|
||||||
let mut ret = true;
|
|
||||||
for mut batteries in batteries_list {
|
|
||||||
if let Some((_battery, parent_axis)) = batteries.next() {
|
|
||||||
let parent_axis_prefix_bits = &parent_axis.as_bitslice()[0..3];
|
|
||||||
if parent_axis_prefix_bits == axis_7_bits {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
ret = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
})
|
|
||||||
{
|
|
||||||
loop {
|
|
||||||
if let Ok(list_cell) = list.as_cell() {
|
|
||||||
list = list_cell.tail();
|
|
||||||
let element_subject = T(
|
|
||||||
&mut context.stack,
|
|
||||||
&[gate_battery, list_cell.head(), gate_context],
|
|
||||||
);
|
|
||||||
unsafe {
|
|
||||||
let (new_cell, new_mem) = Cell::new_raw_mut(&mut context.stack);
|
|
||||||
(*new_mem).head = jet(context, element_subject)?;
|
|
||||||
*dest = new_cell.as_noun();
|
|
||||||
dest = &mut (*new_mem).tail;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if unsafe { !list.raw_equals(D(0)) } {
|
|
||||||
return Err(BAIL_FAIL);
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
*dest = D(0);
|
|
||||||
};
|
|
||||||
return Ok(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loop {
|
|
||||||
if let Ok(list_cell) = list.as_cell() {
|
|
||||||
list = list_cell.tail();
|
|
||||||
let element_subject = T(
|
|
||||||
&mut context.stack,
|
|
||||||
&[gate_battery, list_cell.head(), gate_context],
|
|
||||||
);
|
|
||||||
unsafe {
|
|
||||||
let (new_cell, new_mem) = Cell::new_raw_mut(&mut context.stack);
|
|
||||||
(*new_mem).head = interpret(context, element_subject, gate_battery)?;
|
|
||||||
*dest = new_cell.as_noun();
|
|
||||||
dest = &mut (*new_mem).tail;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if unsafe { !list.raw_equals(D(0)) } {
|
|
||||||
return Err(BAIL_FAIL);
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
*dest = D(0);
|
|
||||||
};
|
|
||||||
return Ok(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod util {
|
pub mod util {
|
||||||
use crate::jets::util::BAIL_EXIT;
|
use crate::jets::util::BAIL_EXIT;
|
||||||
use crate::jets::{JetErr, Result};
|
use crate::jets::{JetErr, Result};
|
||||||
@ -165,6 +133,22 @@ pub mod util {
|
|||||||
Ok(len)
|
Ok(len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn snag(tape: Noun, index: Noun) -> Result {
|
||||||
|
let mut list = tape;
|
||||||
|
let mut idx = index.as_atom()?.as_u64()? as usize;
|
||||||
|
loop {
|
||||||
|
if unsafe { list.raw_equals(D(0)) } {
|
||||||
|
return Err(BAIL_EXIT);
|
||||||
|
}
|
||||||
|
let cell = list.as_cell()?;
|
||||||
|
if idx == 0 {
|
||||||
|
return Ok(cell.head());
|
||||||
|
}
|
||||||
|
idx -= 1;
|
||||||
|
list = cell.tail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn snip(stack: &mut NockStack, tape: Noun) -> Result {
|
pub fn snip(stack: &mut NockStack, tape: Noun) -> Result {
|
||||||
let mut ret = D(0);
|
let mut ret = D(0);
|
||||||
let mut dest = &mut ret as *mut Noun;
|
let mut dest = &mut ret as *mut Noun;
|
||||||
@ -283,6 +267,24 @@ mod tests {
|
|||||||
assert_jet_err(c, jet_lent, sam, BAIL_EXIT);
|
assert_jet_err(c, jet_lent, sam, BAIL_EXIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snag() {
|
||||||
|
let c = &mut init_context();
|
||||||
|
let list1 = T(&mut c.stack, &[D(1), D(2), D(3), D(0)]);
|
||||||
|
let sam = T(&mut c.stack, &[D(1), list1]);
|
||||||
|
assert_jet(c, jet_snag, sam, D(2));
|
||||||
|
|
||||||
|
let list2 = T(&mut c.stack, &[D(1), D(0)]);
|
||||||
|
let sam = T(&mut c.stack, &[D(0), list2]);
|
||||||
|
assert_jet(c, jet_snag, sam, D(1));
|
||||||
|
|
||||||
|
let sam = T(&mut c.stack, &[D(3), list1]);
|
||||||
|
assert_jet_err(c, jet_snag, sam, BAIL_EXIT);
|
||||||
|
|
||||||
|
let sam = T(&mut c.stack, &[D(0), D(0)]);
|
||||||
|
assert_jet_err(c, jet_snag, sam, BAIL_EXIT);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_snip() {
|
fn test_snip() {
|
||||||
let c = &mut init_context();
|
let c = &mut init_context();
|
||||||
|
@ -13,6 +13,7 @@ pub mod mug;
|
|||||||
pub mod newt;
|
pub mod newt;
|
||||||
pub mod noun;
|
pub mod noun;
|
||||||
pub mod serf;
|
pub mod serf;
|
||||||
|
pub mod site;
|
||||||
//pub mod bytecode;
|
//pub mod bytecode;
|
||||||
pub mod persist;
|
pub mod persist;
|
||||||
pub mod serialization;
|
pub mod serialization;
|
||||||
|
73
rust/sword/src/site.rs
Normal file
73
rust/sword/src/site.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/** Call site of a kick (Nock 9), used to cache call targets. */
|
||||||
|
use bitvec::order::Lsb0;
|
||||||
|
use bitvec::slice::BitSlice;
|
||||||
|
|
||||||
|
use crate::interpreter::{interpret, Context};
|
||||||
|
use crate::jets::util::slot;
|
||||||
|
use crate::jets::{Jet, JetErr};
|
||||||
|
use crate::noun::{Noun, D, T};
|
||||||
|
|
||||||
|
/// Return Err if the computation crashed or should punt to Nock
|
||||||
|
pub type Result = std::result::Result<Noun, JetErr>;
|
||||||
|
|
||||||
|
pub struct Site {
|
||||||
|
pub battery: Noun, // battery
|
||||||
|
pub context: Noun, // context
|
||||||
|
pub jet: Option<Jet>, // jet driver
|
||||||
|
pub path: Noun, // label
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Site {
|
||||||
|
/// Prepare a locally cached gate to call repeatedly.
|
||||||
|
pub fn new(ctx: &mut Context, core: &mut Noun) -> Site {
|
||||||
|
let mut battery = slot(*core, 2).unwrap();
|
||||||
|
let context = slot(*core, 7).unwrap();
|
||||||
|
|
||||||
|
let warm_result = ctx
|
||||||
|
.warm
|
||||||
|
.find_jet(&mut ctx.stack, core, &mut battery)
|
||||||
|
.filter(|(_jet, mut path)| {
|
||||||
|
// check that 7 is a prefix of the parent battery axis,
|
||||||
|
// to ensure that the sample (axis 6) is not part of the jet match.
|
||||||
|
//
|
||||||
|
// XX TODO this check is pessimized since there could be multiple ways to match the
|
||||||
|
// jet and we only actually match one of them, but we check all of them and run
|
||||||
|
// unjetted if any have an axis outside 7.
|
||||||
|
let axis_7_bits: &BitSlice<u64, Lsb0> = BitSlice::from_element(&7u64);
|
||||||
|
let batteries_list = ctx.cold.find(&mut ctx.stack, &mut path);
|
||||||
|
let mut ret = true;
|
||||||
|
for mut batteries in batteries_list {
|
||||||
|
if let Some((_battery, parent_axis)) = batteries.next() {
|
||||||
|
let parent_axis_prefix_bits = &parent_axis.as_bitslice()[0..3];
|
||||||
|
if parent_axis_prefix_bits == axis_7_bits {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
});
|
||||||
|
Site {
|
||||||
|
battery,
|
||||||
|
context,
|
||||||
|
jet: warm_result.map(|(jet, _)| jet),
|
||||||
|
path: warm_result.map(|(_, path)| path).unwrap_or(D(0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Slam a cached call site.
|
||||||
|
pub fn site_slam(ctx: &mut Context, site: &Site, sample: Noun) -> Result {
|
||||||
|
let subject = T(&mut ctx.stack, &[site.battery, sample, site.context]);
|
||||||
|
if site.jet.is_some() {
|
||||||
|
let jet = site.jet.unwrap();
|
||||||
|
jet(ctx, subject)
|
||||||
|
} else {
|
||||||
|
Ok(interpret(ctx, subject, site.battery)?)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user