Merge pull request #3490 from urbit/jb/moar-pile

u3: use new road-stack api in noun traversals
This commit is contained in:
Joe Bryan 2020-10-02 15:25:01 -07:00 committed by GitHub
commit 4415c1c099
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 658 additions and 716 deletions

View File

@ -81,9 +81,9 @@
u3_noun c,
u3_noun d);
/* u3r_mug(): MurmurHash3 on a noun.
/* u3r_mug(): statefully mug a noun with 31-bit murmur3.
*/
c3_w
c3_l
u3r_mug(u3_noun veb);
/* u3r_fing():

View File

@ -1048,170 +1048,27 @@ _me_gain_use(u3_noun dog)
}
}
#define TAKE_ROOT 0
#define TAKE_HEAD 1
#define TAKE_TAIL 2
#undef VERBOSE_TAKE
// stack frame for recording head vs tail iteration
//
// In Hoon, this structure would be as follows:
//
// $% [%root ~]
// [%head old=* new=*]
// [%tail old=* new=*]
// ==
//
typedef struct takeframe
{
c3_y tag_y;
u3a_cell* old_u;
u3a_cell* new_u;
} takeframe;
static inline void
_ca_take_push(c3_ys mov,
c3_ys off,
c3_y tag_y,
u3a_cell* old_u,
u3a_cell* new_u)
{
u3R->cap_p += mov;
// ensure we haven't overflowed the stack
// (off==0 means we're on a north road)
//
if ( 0 == off ) {
if( !(u3R->cap_p > u3R->hat_p) ) {
u3m_bail(c3__meme);
}
}
else {
if( !(u3R->cap_p < u3R->hat_p) ) {
u3m_bail(c3__meme);
}
}
takeframe* fam_u = u3to(takeframe, u3R->cap_p + off);
fam_u->tag_y = tag_y;
fam_u->old_u = old_u;
fam_u->new_u = new_u;
}
static inline takeframe
_ca_take_pop(c3_ys mov, c3_ys off)
{
takeframe* fam_u = u3to(takeframe, u3R->cap_p + off);
u3R->cap_p -= mov;
return *fam_u;
}
/* u3a_take(): gain, copying juniors.
/* _ca_take_atom(): reallocate an indirect atom off the stack.
*/
u3_noun
u3a_take(u3_noun veb)
static inline u3_atom
_ca_take_atom(u3a_atom* old_u)
{
c3_assert(u3_none != veb);
u3t_on(coy_o);
// initialize signed stack offsets (relative to north/south road)
//
c3_o nor_o = u3a_is_north(u3R);
c3_ys mov, off;
{
c3_y wis_y = c3_wiseof(takeframe);
mov = ( c3y == nor_o ? -wis_y : wis_y );
off = ( c3y == nor_o ? 0 : -wis_y );
}
// stash the current stack post
//
u3p(takeframe) cap_p = u3R->cap_p;
// push the (only) ROOT stack frame (our termination condition)
//
_ca_take_push(mov, off, TAKE_ROOT, 0, 0);
// the finished copy of our current noun .veb
//
u3_noun pro;
// read from the current noun .veb
//
advance: {
if ( c3y == u3a_is_cat(veb) ) {
pro = veb;
goto retreat;
}
// senior pointers are not refcounted
//
else if ( c3y == (( c3y == nor_o )
? u3a_north_is_senior(u3R, veb)
: u3a_south_is_senior(u3R, veb)) )
{
pro = veb;
goto retreat;
}
// not junior; a normal pointer in our road -- refcounted
//
else if ( c3n == (( c3y == nor_o )
? u3a_north_is_junior(u3R, veb)
: u3a_south_is_junior(u3R, veb)) )
{
// bypass normal road checks in u3k
//
_me_gain_use(veb);
pro = veb;
goto retreat;
}
// junior pointers are copied
//
else {
u3a_noun* veb_u = u3a_to_ptr(veb);
// 32-bit mug_w: already copied .veb and .mug_w is the pointer
//
if ( veb_u->mug_w >> 31 ) {
u3_noun nov = (u3_noun)veb_u->mug_w;
c3_assert( c3y == (( c3y == nor_o)
? u3a_north_is_normal(u3R, nov)
: u3a_south_is_normal(u3R, nov)) );
#ifdef VERBOSE_TAKE
u3l_log("%s: %p is already %p\r\n", ( c3y == nor_o )
? "north"
: "south",
veb_u,
u3a_to_ptr(nov));
#endif
// bypass normal road checks in u3k
//
_me_gain_use(nov);
pro = nov;
goto retreat;
}
else {
if ( c3y == u3a_is_atom(veb) ) {
u3a_atom* old_u = u3a_to_ptr(veb);
c3_w* new_w = u3a_walloc(old_u->len_w + c3_wiseof(u3a_atom));
u3a_atom* new_u = (u3a_atom*)(void *)new_w;
u3_noun new = u3a_to_pug(u3a_outa(new_u));
#ifdef VERBOSE_TAKE
u3l_log("%s: atom %p to %p\r\n", ( c3y == nor_o )
u3l_log("%s: atom %p to %p\r\n", ( c3y == u3a_is_north(u3R) )
? "north"
: "south",
old_u,
new_u);
#endif
// XX use memcpy?
//
new_u->mug_w = old_u->mug_w;
new_u->len_w = old_u->len_w;
{
@ -1222,22 +1079,26 @@ u3a_take(u3_noun veb)
}
}
// Borrow mug slot to record new destination in .old_u.
// borrow mug slot to record new destination in [old_u]
//
old_u->mug_w = new;
pro = new;
goto retreat;
}
else {
u3a_cell* old_u = u3a_to_ptr(veb);
return new;
}
/* _ca_take_cell(): reallocate a cell off the stack.
*/
static inline u3_cell
_ca_take_cell(u3a_cell* old_u, u3_noun hed, u3_noun tel)
{
// XX use u3a_celloc?
//
c3_w* new_w = u3a_walloc(c3_wiseof(u3a_cell));
u3a_cell* new_u = (u3a_cell*)(void *)new_w;
u3_cell new = u3a_to_pom(u3a_outa(new_u));
#ifdef VERBOSE_TAKE
u3l_log("%s: cell %p to %p\r\n", ( c3y == nor_o )
u3l_log("%s: cell %p to %p\r\n", ( c3y == u3a_is_north(u3R) )
? "north"
: "south",
old_u,
@ -1245,64 +1106,233 @@ u3a_take(u3_noun veb)
#endif
new_u->mug_w = old_u->mug_w;
new_u->hed = hed;
new_u->tel = tel;
// borrow mug slot to record new destination in [old_u]
//
old_u->mug_w = new;
return new;
}
/* _ca_take: stack frame for recording cell travesal
** (u3_none == hed) == head-frame
*/
typedef struct _ca_take
{
u3_weak hed; // taken head
u3_cell old; // old cell
} _ca_take;
/* _ca_take_next_south: take next noun, pushing cells on stack.
*/
static inline u3_noun
_ca_take_next_north(u3a_pile* pil_u, u3_noun veb)
{
while ( 1 ) {
// direct atoms and senior refs are not counted.
//
if ( (c3y == u3a_is_cat(veb))
|| (c3y == u3a_north_is_senior(u3R, veb)) )
{
return veb;
}
// not junior; normal (heap) refs on our road are counted.
//
else if ( c3n == u3a_north_is_junior(u3R, veb) ) {
_me_gain_use(veb); // bypass branches in u3k()
return veb;
}
// junior (stack) refs are copied.
//
else {
u3a_noun* veb_u = u3a_to_ptr(veb);
// 32-bit mug_w: already copied [veb] and [mug_w] is the new ref.
//
if ( veb_u->mug_w >> 31 ) {
u3_noun nov = (u3_noun)veb_u->mug_w;
c3_assert( c3y == u3a_north_is_normal(u3R, nov) );
#ifdef VERBOSE_TAKE
u3l_log("north: %p is already %p\r\n", veb_u, u3a_to_ptr(nov));
#endif
_me_gain_use(nov); // bypass branches in u3k()
return nov;
}
else if ( c3y == u3a_is_atom(veb) ) {
return _ca_take_atom((u3a_atom*)veb_u);
}
else {
u3a_cell* old_u = (u3a_cell*)veb_u;
_ca_take* fam_u = u3a_push(pil_u);
u3a_pile_sane(pil_u);
fam_u->hed = u3_none;
fam_u->old = veb;
veb = old_u->hed;
_ca_take_push(mov, off, TAKE_HEAD, old_u, new_u);
goto advance;
}
continue;
}
}
}
}
// consume: popped stack frame, and .pro from above
/* _ca_take_next_south: take next noun, pushing cells on stack.
*/
static inline u3_noun
_ca_take_next_south(u3a_pile* pil_u, u3_noun veb)
{
while ( 1 ) {
// direct atoms and senior refs are not counted.
//
retreat: {
takeframe fam_u = _ca_take_pop(mov, off);
switch ( fam_u.tag_y ) {
default: {
c3_assert(0);
if ( (c3y == u3a_is_cat(veb))
|| (c3y == u3a_south_is_senior(u3R, veb)) )
{
return veb;
}
// .fam_u is our stack root, we're done.
// not junior; a normal pointer in our road -- refcounted
//
case TAKE_ROOT: {
break;
else if ( c3n == u3a_south_is_junior(u3R, veb) ) {
_me_gain_use(veb); // bypass branches in u3k()
return veb;
}
// .pro is the copied head of a cell; save a pointer to it in .new_u
// and advance to copy the tail
// junior (stack) refs are copied.
//
case TAKE_HEAD: {
fam_u.new_u->hed = pro;
else {
u3a_noun* veb_u = u3a_to_ptr(veb);
veb = fam_u.old_u->tel;
_ca_take_push(mov, off, TAKE_TAIL, fam_u.old_u, fam_u.new_u);
goto advance;
}
// .pro is the copied tail of a cell; save a pointer to it in .new_u,
// and produce the whole copied cell (as if it were a read from above).
// 32-bit mug_w: already copied [veb] and [mug_w] is the new ref.
//
case TAKE_TAIL: {
fam_u.new_u->tel = pro;
pro = u3a_to_pom(u3a_outa(fam_u.new_u));
if ( veb_u->mug_w >> 31 ) {
u3_noun nov = (u3_noun)veb_u->mug_w;
// Borrow mug slot to record new destination in old_u.
c3_assert( c3y == u3a_south_is_normal(u3R, nov) );
#ifdef VERBOSE_TAKE
u3l_log("south: %p is already %p\r\n", veb_u, u3a_to_ptr(nov));
#endif
_me_gain_use(nov); // bypass branches in u3k()
return nov;
}
else if ( c3y == u3a_is_atom(veb) ) {
return _ca_take_atom((u3a_atom*)veb_u);
}
else {
u3a_cell* old_u = (u3a_cell*)veb_u;
_ca_take* fam_u = u3a_push(pil_u);
u3a_pile_sane(pil_u);
fam_u->hed = u3_none;
fam_u->old = veb;
veb = old_u->hed;
continue;
}
}
}
}
/* _ca_take_north(): in a north road, gain, copying juniors (from stack).
*/
static u3_noun
_ca_take_north(u3_noun veb)
{
u3_noun pro;
_ca_take* fam_u;
u3a_pile pil_u;
u3a_pile_prep(&pil_u, sizeof(*fam_u));
// commence taking
//
fam_u.old_u->mug_w = pro;
pro = _ca_take_next_north(&pil_u, veb);
goto retreat;
}
}
}
// sanity check
// process cell results
//
c3_assert( u3R->cap_p == cap_p );
if ( c3n == u3a_pile_done(&pil_u) ) {
fam_u = u3a_peek(&pil_u);
do {
// head-frame: stash copy and continue into the tail
//
if ( u3_none == fam_u->hed ) {
u3a_cell* old_u = u3a_to_ptr(fam_u->old);
fam_u->hed = pro;
pro = _ca_take_next_north(&pil_u, old_u->tel);
fam_u = u3a_peek(&pil_u);
}
// tail-frame: copy cell and pop the stack
//
else {
u3a_cell* old_u = u3a_to_ptr(fam_u->old);
pro = _ca_take_cell(old_u, fam_u->hed, pro);
fam_u = u3a_pop(&pil_u);
}
} while ( c3n == u3a_pile_done(&pil_u) );
}
return pro;
}
/* _ca_take_south(): in a south road, gain, copying juniors (from stack).
*/
static u3_noun
_ca_take_south(u3_noun veb)
{
u3_noun pro;
_ca_take* fam_u;
u3a_pile pil_u;
u3a_pile_prep(&pil_u, sizeof(*fam_u));
// commence taking
//
pro = _ca_take_next_south(&pil_u, veb);
// process cell results
//
if ( c3n == u3a_pile_done(&pil_u) ) {
fam_u = u3a_peek(&pil_u);
do {
// head-frame: stash copy and continue into the tail
//
if ( u3_none == fam_u->hed ) {
u3a_cell* old_u = u3a_to_ptr(fam_u->old);
fam_u->hed = pro;
pro = _ca_take_next_south(&pil_u, old_u->tel);
fam_u = u3a_peek(&pil_u);
}
// tail-frame: copy cell and pop the stack
//
else {
u3a_cell* old_u = u3a_to_ptr(fam_u->old);
pro = _ca_take_cell(old_u, fam_u->hed, pro);
fam_u = u3a_pop(&pil_u);
}
} while ( c3n == u3a_pile_done(&pil_u) );
}
return pro;
}
/* u3a_take(): gain, copying juniors.
*/
u3_noun
u3a_take(u3_noun veb)
{
u3_noun pro;
u3t_on(coy_o);
c3_assert(u3_none != veb);
pro = ( c3y == u3a_is_north(u3R) )
? _ca_take_north(veb)
: _ca_take_south(veb);
u3t_off(coy_o);
return pro;
}
@ -2572,59 +2602,33 @@ u3a_walk_fore(u3_noun a,
void (*pat_f)(u3_atom, void*),
c3_o (*cel_f)(u3_noun, void*))
{
// initialize signed stack offsets (relative to N or S road)
//
c3_o nor_o = u3a_is_north(u3R);
c3_ys mov_ys, off_ys;
{
c3_y wis_y = c3_wiseof(u3_noun);
mov_ys = ( c3y == nor_o ? -wis_y : wis_y );
off_ys = ( c3y == nor_o ? 0 : -wis_y );
}
u3_noun* top;
u3a_pile pil_u;
// set stack root, push argument
// initialize stack control; push argument
//
u3_noun *top, *don;
{
don = u3to(u3_noun, u3R->cap_p + off_ys);
u3R->cap_p += mov_ys;
top = u3to(u3_noun, u3R->cap_p + off_ys);
u3a_pile_prep(&pil_u, sizeof(u3_noun));
top = u3a_push(&pil_u);
*top = a;
}
while ( top != don ) {
while ( c3n == u3a_pile_done(&pil_u) ) {
// visit an atom, then pop the stack
//
if ( c3y == u3a_is_atom(a) ) {
pat_f(a, ptr_v);
u3R->cap_p -= mov_ys;
top = u3to(u3_noun, u3R->cap_p + off_ys);
top = u3a_pop(&pil_u);
}
// vist a cell, if c3n, pop the stack
//
else if ( c3n == cel_f(a, ptr_v) ) {
u3R->cap_p -= mov_ys;
top = u3to(u3_noun, u3R->cap_p + off_ys);
top = u3a_pop(&pil_u);
}
// otherwise, push the tail and continue into the head
//
else {
*top = u3t(a);
u3R->cap_p += mov_ys;
if ( c3y == nor_o ) {
if( !(u3R->cap_p > u3R->hat_p) ) {
u3m_bail(c3__meme);
}
}
else {
if( !(u3R->cap_p < u3R->hat_p) ) {
u3m_bail(c3__meme);
}
}
top = u3to(u3_noun, u3R->cap_p + off_ys);
top = u3a_push(&pil_u);
u3a_pile_sane(&pil_u);
*top = u3h(a);
}
@ -2640,47 +2644,34 @@ u3a_walk_fore_unsafe(u3_noun a,
void (*pat_f)(u3_atom, void*),
c3_o (*cel_f)(u3_noun, void*))
{
// initialize signed stack offsets (relative to N or S road)
//
c3_ys mov_ys, off_ys;
{
c3_y wis_y = c3_wiseof(u3_noun);
c3_o nor_o = u3a_is_north(u3R);
mov_ys = ( c3y == nor_o ? -wis_y : wis_y );
off_ys = ( c3y == nor_o ? 0 : -wis_y );
}
u3_noun* top;
u3a_pile pil_u;
// set stack root, push argument
// initialize stack control; push argument
//
u3_noun *top, *don;
{
don = u3to(u3_noun, u3R->cap_p + off_ys);
u3R->cap_p += mov_ys;
top = u3to(u3_noun, u3R->cap_p + off_ys);
u3a_pile_prep(&pil_u, sizeof(u3_noun));
top = u3a_push(&pil_u);
*top = a;
}
while ( top != don ) {
while ( c3n == u3a_pile_done(&pil_u) ) {
// visit an atom, then pop the stack
//
if ( c3y == u3a_is_atom(a) ) {
pat_f(a, ptr_v);
u3R->cap_p -= mov_ys;
top = u3to(u3_noun, u3R->cap_p + off_ys);
top = u3a_pop(&pil_u);
}
// vist a cell, if c3n, pop the stack
//
else if ( c3n == cel_f(a, ptr_v) ) {
u3R->cap_p -= mov_ys;
top = u3to(u3_noun, u3R->cap_p + off_ys);
top = u3a_pop(&pil_u);
}
// otherwise, push the tail and continue into the head
//
else {
*top = u3t(a);
u3R->cap_p += mov_ys;
top = u3to(u3_noun, u3R->cap_p + off_ys);
// NB: overflow check elided here
//
top = u3a_push(&pil_u);
*top = u3h(a);
}

View File

@ -219,9 +219,25 @@ u3r_mean(u3_noun som, ...)
return ret_o;
}
#define SONG_NONE 0
#define SONG_HEAD 1
#define SONG_TAIL 2
// stack frame for tracking noun comparison and unification
//
// we always compare arbitrary nouns in a none-frame.
// when we compare two cells, we change the none-frame to a head-frame
// and push a new none-frame for their heads. if the heads are equal,
// we get the cells from the head-frame and unify their head pointers.
// then, we convert the head-frame to a tail-frame and repeat with
// the tails, mutatis mutandis.
//
// in Hoon, this structure would be:
//
// $% [%none a=* b=*]
// [%head a=^ b=^]
// [%tail a=^ b=^]
// ==
//
#define SING_NONE 0
#define SING_HEAD 1
#define SING_TAIL 2
typedef struct {
c3_y sat_y;
@ -229,34 +245,43 @@ typedef struct {
u3_noun b;
} eqframe;
/* _cr_sing_push(): push a new stack frame, initialized as SING_NONE.
*/
static inline eqframe*
_eq_push(c3_ys mov, c3_ys off, u3_noun a, u3_noun b)
_cr_sing_push(u3a_pile* pil_u, u3_noun a, u3_noun b)
{
u3R->cap_p += mov;
eqframe* cur = u3to(eqframe, u3R->cap_p + off);
cur->sat_y = SONG_NONE;
cur->a = a;
cur->b = b;
return cur;
eqframe* fam_u = u3a_push(pil_u);
fam_u->sat_y = SING_NONE;
fam_u->a = a;
fam_u->b = b;
return fam_u;
}
static inline eqframe*
_eq_pop(c3_ys mov, c3_ys off)
{
u3R->cap_p -= mov;
return u3to(eqframe, u3R->cap_p + off);
}
/* _song_atom(): check if atom [a] is indirect and equal to noun [b]
/* _cr_sing_mug(): short-circuit comparison if mugs are present and not equal.
*/
static inline c3_o
_song_atom(u3_atom a, u3_noun b)
_cr_sing_mug(u3a_noun* a_u, u3a_noun* b_u)
{
// XX add debug assertions that both mugs are 31-bit
// (ie, not u3a_take() relocation references)
//
if ( a_u->mug_w && b_u->mug_w && (a_u->mug_w != b_u->mug_w) ) {
return c3n;
}
return c3y;
}
/* _cr_sing_atom(): check if atom [a] is indirect and equal to noun [b]
*/
static inline c3_o
_cr_sing_atom(u3_atom a, u3_noun b)
{
// [a] is an atom, not pointer-equal to noun [b].
// if they're not both indirect atoms, they can't be equal.
//
if ( (c3n == u3a_is_pug(a)) ||
(c3n == u3a_is_pug(b)) )
if ( (c3n == u3a_is_pug(a))
|| (c3n == u3a_is_pug(b)) )
{
return c3n;
}
@ -264,23 +289,26 @@ _song_atom(u3_atom a, u3_noun b)
u3a_atom* a_u = u3a_to_ptr(a);
u3a_atom* b_u = u3a_to_ptr(b);
if ( (0 != a_u->mug_w) &&
(0 != b_u->mug_w) &&
(a_u->mug_w != b_u->mug_w) )
{
// [a] and [b] are not equal if their mugs are present and not equal.
//
if ( c3n == _cr_sing_mug((u3a_noun*)a_u, (u3a_noun*)b_u) ) {
return c3n;
}
else {
c3_w w_rez = a_u->len_w;
c3_w w_mox = b_u->len_w;
c3_w a_w = a_u->len_w;
c3_w b_w = b_u->len_w;
if ( w_rez != w_mox ) {
// [a] and [b] are not equal if their lengths are not equal
//
if ( a_w != b_w ) {
return c3n;
}
else {
c3_w i_w;
for ( i_w = 0; i_w < w_rez; i_w++ ) {
// XX memcmp
//
for ( i_w = 0; i_w < a_w; i_w++ ) {
if ( a_u->buf_w[i_w] != b_u->buf_w[i_w] ) {
return c3n;
}
@ -292,129 +320,68 @@ _song_atom(u3_atom a, u3_noun b)
return c3y;
}
/* _song_x_cape(): unifying equality with comparison deduplication
* (tightly coupled to _song_x)
*/
static c3_o
_song_x_cape(c3_ys mov, c3_ys off,
eqframe* fam, eqframe* don,
u3p(u3h_root) har_p)
/* _cr_sing_cape_test(): check for previous comparison of [a] and [b].
*/
static inline c3_o
_cr_sing_cape_test(u3p(u3h_root) har_p, u3_noun a, u3_noun b)
{
u3_noun a, b, key;
u3_weak got;
u3a_cell* a_u;
u3a_cell* b_u;
u3_noun key = u3nc(u3a_to_off(a), u3a_to_off(b));
u3_noun val;
while ( don != fam ) {
a = fam->a;
b = fam->b;
switch ( fam->sat_y ) {
case SONG_NONE:
if ( a == b ) {
break;
}
else if ( c3y == u3a_is_atom(a) ) {
if ( c3n == _song_atom(a, b) ) {
return c3n;
}
else {
break;
}
}
else if ( c3y == u3a_is_atom(b) ) {
return c3n;
}
else {
a_u = u3a_to_ptr(a);
b_u = u3a_to_ptr(b);
if ( (0 != a_u->mug_w) &&
(0 != b_u->mug_w) &&
(a_u->mug_w != b_u->mug_w) )
{
return c3n;
}
else {
key = u3nc(u3a_to_off(a), u3a_to_off(b));
u3t_off(euq_o);
got = u3h_get(har_p, key);
val = u3h_git(har_p, key);
u3t_on(euq_o);
u3z(key);
if ( u3_none != got ) {
fam = _eq_pop(mov, off);
continue;
}
fam->sat_y = SONG_HEAD;
fam = _eq_push(mov, off, a_u->hed, b_u->hed);
continue;
}
}
return ( u3_none == val ) ? c3y : c3n;
}
case SONG_HEAD:
a_u = u3a_to_ptr(a);
b_u = u3a_to_ptr(b);
u3a_wed(&(a_u->hed), &(b_u->hed));
fam->sat_y = SONG_TAIL;
fam = _eq_push(mov, off, a_u->tel, b_u->tel);
continue;
case SONG_TAIL:
a_u = u3a_to_ptr(a);
b_u = u3a_to_ptr(b);
u3a_wed(&(a_u->tel), &(b_u->tel));
break;
default:
c3_assert(0);
break;
}
// [har_p] is effectively a set of equal pairs.
// we cons [a] and [b] as posts so that we don't
// touch their reference counts.
/* _cr_sing_cape_keep(): store [a] and [b] to short-circuit subsequent tests.
** NB: [a] and [b] (which MUST be equal nouns)
** are cons'd as offsets (direct atoms) to avoid refcount churn.
*/
static inline void
_cr_sing_cape_keep(u3p(u3h_root) har_p, u3_noun a, u3_noun b)
{
// only store if [a] and [b] are copies of each other
//
if ( a != b ) {
key = u3nc(u3a_to_off(a), u3a_to_off(b));
u3_noun key = u3nc(u3a_to_off(a), u3a_to_off(b));
u3t_off(euq_o);
u3h_put(har_p, key, c3y);
u3t_on(euq_o);
u3z(key);
}
fam = _eq_pop(mov, off);
}
return c3y;
}
/* _song_x(): yes if a and b are the same noun, use uni to unify
*/
/* _cr_sing_cape(): unifying equality with comparison deduplication
* (tightly coupled to _cr_sing)
*/
static c3_o
_song_x(u3_noun a, u3_noun b)
_cr_sing_cape(u3a_pile* pil_u, u3p(u3h_root) har_p)
{
u3p(eqframe) empty = u3R->cap_p;
c3_y wis_y = c3_wiseof(eqframe);
c3_o nor_o = u3a_is_north(u3R);
c3_ys mov = ( c3y == nor_o ? -wis_y : wis_y );
c3_ys off = ( c3y == nor_o ? 0 : -wis_y );
c3_s ovr_s = 0;
eqframe* fam = _eq_push(mov, off, a, b);
eqframe* don = u3to(eqframe, empty + off);
eqframe* fam_u = u3a_peek(pil_u);
u3_noun a, b, key;
u3_weak got;
u3a_cell* a_u;
u3a_cell* b_u;
while ( don != fam ) {
a = fam->a;
b = fam->b;
switch ( fam->sat_y ) {
case SONG_NONE:
// loop while arguments remain on the stack
//
do {
a = fam_u->a;
b = fam_u->b;
switch ( fam_u->sat_y ) {
// [a] and [b] are arbitrary nouns
//
case SING_NONE: {
if ( a == b ) {
break;
}
else if ( c3y == u3a_is_atom(a) ) {
if ( c3n == _song_atom(a, b) ) {
u3R->cap_p = empty;
if ( c3n == _cr_sing_atom(a, b) ) {
return c3n;
}
else {
@ -422,57 +389,178 @@ _song_x(u3_noun a, u3_noun b)
}
}
else if ( c3y == u3a_is_atom(b) ) {
u3R->cap_p = empty;
return c3n;
}
// [a] and [b] are cells
//
else {
a_u = u3a_to_ptr(a);
b_u = u3a_to_ptr(b);
if ( (0 != a_u->mug_w) &&
(0 != b_u->mug_w) &&
(a_u->mug_w != b_u->mug_w) )
{
u3R->cap_p = empty;
// short-circuiting mug check
//
if ( c3n == _cr_sing_mug((u3a_noun*)a_u, (u3a_noun*)b_u) ) {
return c3n;
}
// short-circuiting re-comparison check
//
else if ( c3y == _cr_sing_cape_test(har_p, a, b) ) {
fam_u = u3a_pop(pil_u);
continue;
}
// upgrade none-frame to head-frame, check heads
//
else {
fam->sat_y = SONG_HEAD;
fam = _eq_push(mov, off, a_u->hed, b_u->hed);
fam_u->sat_y = SING_HEAD;
fam_u = _cr_sing_push(pil_u, a_u->hed, b_u->hed);
continue;
}
}
} break;
case SONG_HEAD:
// cells [a] and [b] have equal heads
//
case SING_HEAD: {
a_u = u3a_to_ptr(a);
b_u = u3a_to_ptr(b);
u3a_wed(&(a_u->hed), &(b_u->hed));
fam->sat_y = SONG_TAIL;
fam = _eq_push(mov, off, a_u->tel, b_u->tel);
continue;
case SONG_TAIL:
// upgrade head-frame to tail-frame, check tails
//
fam_u->sat_y = SING_TAIL;
fam_u = _cr_sing_push(pil_u, a_u->tel, b_u->tel);
continue;
}
// cells [a] and [b] are equal
//
case SING_TAIL: {
a_u = u3a_to_ptr(a);
b_u = u3a_to_ptr(b);
u3a_wed(&(a_u->tel), &(b_u->tel));
break;
} break;
default:
default: {
c3_assert(0);
break;
} break;
}
// [ovr_s] counts iterations. when it overflows, we know we've hit a
// pathological case and MUST start de-duplicating comparisons.
// track equal pairs to short-circuit possible (re-)comparison
//
_cr_sing_cape_keep(har_p, a, b);
fam_u = u3a_pop(pil_u);
}
while ( c3n == u3a_pile_done(pil_u) );
return c3y;
}
/* _cr_sing(): unifying equality.
*/
static c3_o
_cr_sing(u3_noun a, u3_noun b)
{
c3_s ovr_s = 0;
u3a_cell* a_u;
u3a_cell* b_u;
eqframe* fam_u;
u3a_pile pil_u;
// initialize stack control, push arguments onto the stack (none-frame)
//
u3a_pile_prep(&pil_u, sizeof(eqframe));
fam_u = _cr_sing_push(&pil_u, a, b);
// loop while arguments are on the stack
//
while ( c3n == u3a_pile_done(&pil_u) ) {
a = fam_u->a;
b = fam_u->b;
switch ( fam_u->sat_y ) {
// [a] and [b] are arbitrary nouns
//
case SING_NONE: {
if ( a == b ) {
break;
}
else if ( c3y == u3a_is_atom(a) ) {
if ( c3n == _cr_sing_atom(a, b) ) {
u3R->cap_p = pil_u.top_p;
return c3n;
}
else {
break;
}
}
else if ( c3y == u3a_is_atom(b) ) {
u3R->cap_p = pil_u.top_p;
return c3n;
}
// [a] and [b] are cells
//
else {
a_u = u3a_to_ptr(a);
b_u = u3a_to_ptr(b);
// short-circuiting mug check
//
if ( c3n == _cr_sing_mug((u3a_noun*)a_u, (u3a_noun*)b_u) ) {
u3R->cap_p = pil_u.top_p;
return c3n;
}
// upgrade none-frame to head-frame, check heads
//
else {
fam_u->sat_y = SING_HEAD;
fam_u = _cr_sing_push(&pil_u, a_u->hed, b_u->hed);
continue;
}
}
} break;
// cells [a] and [b] have equal heads
//
case SING_HEAD: {
a_u = u3a_to_ptr(a);
b_u = u3a_to_ptr(b);
u3a_wed(&(a_u->hed), &(b_u->hed));
// upgrade head-frame to tail-frame, check tails
//
fam_u->sat_y = SING_TAIL;
fam_u = _cr_sing_push(&pil_u, a_u->tel, b_u->tel);
continue;
}
// cells [a] and [b] are equal
//
case SING_TAIL: {
a_u = u3a_to_ptr(a);
b_u = u3a_to_ptr(b);
u3a_wed(&(a_u->tel), &(b_u->tel));
} break;
default: {
c3_assert(0);
} break;
}
// [ovr_s] counts comparisons, if it overflows, we've likely hit
// a pathological case (highly duplicated tree), so we de-duplicate
// subsequent comparisons by maintaining a set of equal pairs.
//
if ( 0 == ++ovr_s ) {
u3p(u3h_root) har_p = u3h_new();
c3_o ret_o = _song_x_cape(mov, off, fam, don, har_p);
c3_o ret_o = _cr_sing_cape(&pil_u, har_p);
u3h_free(har_p);
u3R->cap_p = empty;
u3R->cap_p = pil_u.top_p;
return ret_o;
}
fam = _eq_pop(mov, off);
fam_u = u3a_pop(&pil_u);
}
return c3y;
@ -485,7 +573,7 @@ u3r_sing(u3_noun a, u3_noun b)
{
c3_o ret_o;
u3t_on(euq_o);
ret_o = _song_x(a, b);
ret_o = _cr_sing(a, b);
u3t_off(euq_o);
return ret_o;
}
@ -1455,101 +1543,24 @@ u3r_mug_cell(u3_noun hed,
return u3r_mug_both(lus_w, biq_w);
}
#define MUG_ROOT 0
#define MUG_HEAD 1
#define MUG_TAIL 2
/* _cr_mug: stack frame for recording cell traversal
** !mug == head-frame
*/
typedef struct {
c3_l mug_l;
u3_cell cel;
} _cr_mugf;
// stack frame for recording head vs tail iteration
//
// In Hoon, this structure would be as follows:
//
// $% [%root ~]
// [%head cell=^]
// [%tail cell=^ hed-mug=@]
// ==
//
typedef struct mugframe
/* _cr_mug_next(): advance mug calculation, pushing cells onto the stack.
*/
static inline c3_l
_cr_mug_next(u3a_pile* pil_u, u3_noun veb)
{
c3_y tag_y;
u3a_cell* cel_u;
c3_w mug_w;
} mugframe;
static inline void
_mug_push(c3_ys mov,
c3_ys off,
c3_y tag_y,
u3a_cell* cel_u,
c3_w mug_w)
{
u3R->cap_p += mov;
// ensure we haven't overflowed the stack
// (off==0 means we're on a north road)
//
if ( 0 == off ) {
if( !(u3R->cap_p > u3R->hat_p) ) {
u3m_bail(c3__meme);
}
}
else {
if( !(u3R->cap_p < u3R->hat_p) ) {
u3m_bail(c3__meme);
}
}
mugframe* fam_u = u3to(mugframe, u3R->cap_p + off);
fam_u->tag_y = tag_y;
fam_u->cel_u = cel_u;
fam_u->mug_w = mug_w;
}
static inline mugframe
_mug_pop(c3_ys mov, c3_ys off)
{
mugframe* fam_u = u3to(mugframe, u3R->cap_p + off);
u3R->cap_p -= mov;
return *fam_u;
}
// u3r_mug(): statefully mug a noun using a 31-bit MurmurHash3
//
c3_w
u3r_mug(u3_noun veb)
{
// sanity check (makes a clear error message)
//
c3_assert( u3_none != veb );
// initialize signed stack offsets (relative to north/south road)
//
c3_ys mov, off;
{
c3_y wis_y = c3_wiseof(mugframe);
c3_o nor_o = u3a_is_north(u3R);
mov = ( c3y == nor_o ? -wis_y : wis_y );
off = ( c3y == nor_o ? 0 : -wis_y );
}
// stash the current stack post
//
u3p(mugframe) cap_p = u3R->cap_p;
// push the (only) ROOT stack frame (our termination condition)
//
_mug_push(mov, off, MUG_ROOT, 0, 0);
c3_w mug_w;
// read from the current noun .veb
//
advance: {
while ( 1 ) {
// veb is a direct atom, mug is not memoized
//
if ( _(u3a_is_cat(veb)) ) {
mug_w = u3r_mug_words(&veb, 1);
goto retreat;
if ( c3y == u3a_is_cat(veb) ) {
return (c3_l)u3r_mug_words(&veb, 1);
}
// veb is indirect, a pointer into the loom
//
@ -1558,72 +1569,86 @@ u3r_mug(u3_noun veb)
// veb has already been mugged, return memoized value
//
if ( 0 != veb_u->mug_w ) {
mug_w = veb_u->mug_w;
goto retreat;
// XX add debug assertion that mug is 31-bit?
//
if ( veb_u->mug_w ) {
return (c3_l)veb_u->mug_w;
}
// veb is an indirect atom, mug its bytes and memoize
//
else if ( _(u3a_is_atom(veb)) ) {
else if ( c3y == u3a_is_atom(veb) ) {
u3a_atom* vat_u = (u3a_atom*)veb_u;
mug_w = u3r_mug_words(vat_u->buf_w, vat_u->len_w);
vat_u->mug_w = mug_w;
goto retreat;
c3_l mug_l = u3r_mug_words(vat_u->buf_w, vat_u->len_w);
vat_u->mug_w = mug_l;
return mug_l;
}
// veb is a cell, push a stack frame to mark head-recursion
// and read the head
//
else {
u3a_cell* cel_u = (u3a_cell*)veb_u;
_mug_push(mov, off, MUG_HEAD, cel_u, 0);
_cr_mugf* fam_u = u3a_push(pil_u);
// check for overflow
//
u3a_pile_sane(pil_u);
fam_u->mug_l = 0;
fam_u->cel = veb;
veb = cel_u->hed;
goto advance;
continue;
}
}
}
}
// consume the popped stack frame and mug from above
//
retreat: {
mugframe fam_u = _mug_pop(mov, off);
switch ( fam_u.tag_y ) {
default: {
c3_assert(0);
}
// we done
//
case MUG_ROOT: {
break;
}
// mug_w is the mug of the head of cel_u
// push a stack frame to mark tail recursion,
// record the mug of the head, and read the tail
//
case MUG_HEAD: {
_mug_push(mov, off, MUG_TAIL, fam_u.cel_u, mug_w);
veb = fam_u.cel_u->tel;
goto advance;
}
// mug_w is the mug of the tail of cel_u
// combine the mugs, memoize the value, and recur
//
case MUG_TAIL: {
u3a_cell* cel_u = fam_u.cel_u;
mug_w = u3r_mug_both(fam_u.mug_w, mug_w);
cel_u->mug_w = mug_w;
goto retreat;
}
}
}
/* u3r_mug(): statefully mug a noun with 31-bit murmur3.
*/
c3_l
u3r_mug(u3_noun veb)
{
u3a_pile pil_u;
_cr_mugf* fam_u;
c3_l mug_l;
// sanity check
//
c3_assert( u3R->cap_p == cap_p );
c3_assert( u3_none != veb );
return mug_w;
u3a_pile_prep(&pil_u, sizeof(*fam_u));
// commence mugging
//
mug_l = _cr_mug_next(&pil_u, veb);
// process cell results
//
if ( c3n == u3a_pile_done(&pil_u) ) {
fam_u = u3a_peek(&pil_u);
do {
// head-frame: stash mug and continue into the tail
//
if ( !fam_u->mug_l ) {
u3a_cell* cel_u = u3a_to_ptr(fam_u->cel);
fam_u->mug_l = mug_l;
mug_l = _cr_mug_next(&pil_u, cel_u->tel);
fam_u = u3a_peek(&pil_u);
}
// tail-frame: calculate/memoize cell mug and pop the stack
//
else {
u3a_cell* cel_u = u3a_to_ptr(fam_u->cel);
mug_l = u3r_mug_both(fam_u->mug_l, mug_l);
cel_u->mug_w = mug_l;
fam_u = u3a_pop(&pil_u);
}
}
while ( c3n == u3a_pile_done(&pil_u) );
}
return mug_l;
}

View File

@ -286,111 +286,35 @@ u3s_jam_xeno(u3_noun a, c3_d* len_d, c3_y** byt_y)
return ur_bsw_done(&jam_u.rit_u, len_d, byt_y);
}
#define CUE_ROOT 0
#define CUE_HEAD 1
#define CUE_TAIL 2
// stack frame for recording head vs tail iteration
//
// In Hoon, this structure would be as follows:
//
// $% [%root ~]
// [%head cell-cursor=@]
// [%tail cell-cursor=@ hed-width=@ hed-value=*]
// ==
//
typedef struct _cs_cue_frame
{
c3_y tag_y;
u3_atom cur;
u3_atom wid;
u3_noun hed;
} cueframe;
/* _cs_cue_push(): construct a cueframe and push it onto the road stack.
/* _cs_cue: stack frame for tracking intermediate cell results
*/
static inline void
_cs_cue_push(c3_ys mov,
c3_ys off,
c3_y tag_y,
typedef struct _cs_cue {
u3_weak hed; // head of a cell or u3_none
u3_atom wid; // bitwidth of [hed] or 0
u3_atom cur; // bit-cursor position
} _cs_cue;
/* _cs_rub: rub, TRANSFER [cur], RETAIN [a]
*/
static inline u3_noun
_cs_rub(u3_atom cur, u3_atom a)
{
u3_noun pro = u3qe_rub(cur, a);
u3z(cur);
return pro;
}
/* _cs_cue_next(): advance into [a], reading next value
** TRANSFER [cur], RETAIN [a]
*/
static inline u3_noun
_cs_cue_next(u3a_pile* pil_u,
u3p(u3h_root) har_p,
u3_atom cur,
u3_atom wid,
u3_noun hed)
u3_atom a,
u3_atom* wid)
{
u3R->cap_p += mov;
// ensure we haven't overflowed the stack
// (off==0 means we're on a north road)
//
if ( 0 == off ) {
if( !(u3R->cap_p > u3R->hat_p) ) {
u3m_bail(c3__meme);
}
}
else {
if( !(u3R->cap_p < u3R->hat_p) ) {
u3m_bail(c3__meme);
}
}
cueframe* fam_u = u3to(cueframe, u3R->cap_p + off);
fam_u->tag_y = tag_y;
fam_u->cur = cur;
fam_u->wid = wid;
fam_u->hed = hed;
}
/* _cs_cue_pop(): pop a cueframe off the road stack and return it.
*/
static inline cueframe
_cs_cue_pop(c3_ys mov, c3_ys off)
{
cueframe* fam_u = u3to(cueframe, u3R->cap_p + off);
u3R->cap_p -= mov;
return *fam_u;
}
/* u3s_cue(): cue [a]
*/
u3_noun
u3s_cue(u3_atom a)
{
// initialize signed stack offsets (relative to north/south road)
//
c3_ys mov, off;
{
c3_y wis_y = c3_wiseof(cueframe);
c3_o nor_o = u3a_is_north(u3R);
mov = ( c3y == nor_o ? -wis_y : wis_y );
off = ( c3y == nor_o ? 0 : -wis_y );
}
// initialize a hash table for dereferencing backrefs
//
u3p(u3h_root) har_p = u3h_new();
// stash the current stack post
//
u3p(cueframe) cap_p = u3R->cap_p;
// push the (only) ROOT stack frame (our termination condition)
//
_cs_cue_push(mov, off, CUE_ROOT, 0, 0, 0);
// initialize cursor to bit-position 0
//
u3_atom cur = 0;
// the bitwidth and product from reading at cursor
//
u3_atom wid, pro;
// read from atom at cursor
//
// TRANSFER .cur
//
advance: {
while ( 1 ) {
// read tag bit at cur
//
c3_y tag_y = u3qc_cut(0, cur, 1, a);
@ -400,19 +324,14 @@ u3s_cue(u3_atom a)
// produce atom and the width we read
//
if ( 0 == tag_y ) {
u3_noun bur;
{
u3_noun x = u3qa_inc(cur);
bur = u3qe_rub(x, a);
u3z(x);
}
u3_noun bur = _cs_rub(u3i_vint(cur), a);
u3_noun pro = u3k(u3t(bur));
pro = u3k(u3t(bur));
u3h_put(har_p, cur, u3k(pro));
wid = u3qa_inc(u3h(bur));
*wid = u3qa_inc(u3h(bur));
u3z(bur);
goto retreat;
return pro;
}
else {
// read tag bit at (1 + cur)
@ -428,87 +347,94 @@ u3s_cue(u3_atom a)
// produce referenced value and the width we read
//
if ( 1 == tag_y ) {
u3_noun bur;
{
u3_noun x = u3ka_add(2, cur);
bur = u3qe_rub(x, a);
u3z(x);
}
u3_noun bur = _cs_rub(u3ka_add(2, cur), a);
u3_noun pro = u3x_good(u3h_get(har_p, u3t(bur)));
pro = u3h_get(har_p, u3k(u3t(bur)));
if ( u3_none == pro ) {
return u3m_bail(c3__exit);
}
wid = u3qa_add(2, u3h(bur));
*wid = u3qa_add(2, u3h(bur));
u3z(bur);
goto retreat;
return pro;
}
// next bit unset, (2 + cur) points to the head of a cell
//
// push a frame to mark HEAD recursion and read the head
// push a head-frame onto the road stack and read the head
//
else {
_cs_cue_push(mov, off, CUE_HEAD, cur, 0, 0);
_cs_cue* fam_u = u3a_push(pil_u);
u3a_pile_sane(pil_u);
// NB: fam_u->wid unused in head-frame
//
fam_u->hed = u3_none;
fam_u->cur = cur;
cur = u3qa_add(2, cur);
goto advance;
continue;
}
}
}
}
// consume: popped stack frame, .wid and .pro from above.
u3_noun
u3s_cue(u3_atom a)
{
// pro: cue'd noun product
// wid: bitwidth read to produce [pro]
// fam_u: stack frame
// har_p: backreference table
// pil_u: stack control structure
//
// TRANSFER .wid, .pro, and contents of .fam_u
// (.cur is in scope, but we have already lost our reference to it)
u3_noun pro;
u3_atom wid, cur = 0;
_cs_cue* fam_u;
u3p(u3h_root) har_p = u3h_new();
u3a_pile pil_u;
// initialize stack control
//
retreat: {
cueframe fam_u = _cs_cue_pop(mov, off);
u3a_pile_prep(&pil_u, sizeof(*fam_u));
switch ( fam_u.tag_y ) {
default: {
c3_assert(0);
}
// fam_u is our stack root, we're done.
// commence cueing at bit-position 0
//
case CUE_ROOT: {
break;
}
pro = _cs_cue_next(&pil_u, har_p, 0, a, &wid);
// .wid and .pro are the head of the cell at fam_u.cur.
// save them (and the cell cursor) in a TAIL frame,
// set the cursor to the tail and read there.
// process cell results
//
case CUE_HEAD: {
_cs_cue_push(mov, off, CUE_TAIL, fam_u.cur, wid, pro);
if ( c3n == u3a_pile_done(&pil_u) ) {
fam_u = u3a_peek(&pil_u);
cur = u3ka_add(2, u3qa_add(wid, fam_u.cur));
goto advance;
}
// .wid and .pro are the tail of the cell at fam_u.cur,
// construct the cell, memoize it, and produce it along with
// its total width (as if it were a read from above).
do {
// head-frame: stash [pro] and [wid]; continue into the tail
//
case CUE_TAIL: {
pro = u3nc(fam_u.hed, pro);
u3h_put(har_p, fam_u.cur, u3k(pro));
wid = u3ka_add(2, u3ka_add(wid, fam_u.wid));
goto retreat;
if ( u3_none == fam_u->hed ) {
// NB: fam_u->wid unused in head-frame
//
fam_u->hed = pro;
fam_u->wid = wid;
// continue reading at the bit-position after [pro]
{
u3_noun cur = u3ka_add(2, u3qa_add(wid, fam_u->cur));
pro = _cs_cue_next(&pil_u, har_p, cur, a, &wid);
}
fam_u = u3a_peek(&pil_u);
}
// tail-frame: cons cell, recalculate [wid], and pop the stack
//
else {
pro = u3nc(fam_u->hed, pro);
u3h_put(har_p, fam_u->cur, u3k(pro));
u3z(fam_u->cur);
wid = u3ka_add(2, u3ka_add(wid, fam_u->wid));
fam_u = u3a_pop(&pil_u);
}
} while ( c3n == u3a_pile_done(&pil_u) );
}
u3z(wid);
u3h_free(har_p);
// sanity check
//
c3_assert( u3R->cap_p == cap_p );
return pro;
}