Skip to content

Commit

Permalink
ES: Make tagging the main protection method
Browse files Browse the repository at this point in the history
  • Loading branch information
l4haie committed Feb 15, 2025
1 parent 11f055f commit 06d281b
Showing 1 changed file with 31 additions and 97 deletions.
128 changes: 31 additions & 97 deletions src/host/c/es.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@
* will occur when the stack pointer will point to something else (might
* be different in another system when we return from a primitive and we
* need to force a drop)
* - Full support for tagging (inc. roots and protected ribs, maybe newly
* allocated ribs if necessary)
* - Crash on overflow (keep the basic negative rank approach for now,
* optimizing this is not a priority)
* - Tag Ribbit's 3 roots for faster collectable check?
* - Benchmarks for the new adoption scheme and dirtiness check
* - Ref count (make sure all non-cyclic ribs are collected, adapt io and sys
* primitives, apply, ... you know, make it work)
Expand Down Expand Up @@ -65,10 +64,6 @@
#define REF_COUNT
// )@@

// @@(feature tagging
#define TAGGING
// )@@

// @@(feature exp-adoption
#define EXPERIMENTAL_ADOPTION
// )@@
Expand Down Expand Up @@ -202,7 +197,15 @@ typedef struct {
#define IS_MARKED2(x) ((x)&4)
#endif

#define is_protected(o, i) (IS_RIB(o) && IS_MARKED(get_field(o,i)))
#define protect(o) get_field(o,7) = MARK(RANK(o))
#define is_protected(o) (IS_RIB(o) && IS_MARKED(RANK(o)))
#define unprotect(o) \
do { \
if (IS_RIB(o)) { \
get_field(o,7) = UNMARK(RANK(o)); \
remove_root(o); \
} \
} while (0)

#define INSTR_AP 0
#define INSTR_SET 1
Expand Down Expand Up @@ -728,11 +731,9 @@ void pq_remove(obj o) {
// ordering invariant AND `from` is NOT `to`'s parent
#define is_dirty(from, to) ((get_rank(from) < get_rank(to)-1) && (get_rank(get_parent(to)) < get_rank(from)))

#ifdef TAGGING
#define is_root(x) (x == pc || x == stack || x == FALSE || is_protected(x, 7))
#else
#define is_collectable(x) (!is_root(x) && !is_protected(x))

#define is_root(x) (x == pc || x == stack || x == FALSE)
#endif

#define is_parent(x, p) (CFR(x) == p)
#define get_parent(x) CFR(x)
Expand Down Expand Up @@ -821,7 +822,7 @@ void add_cofriend(obj x, obj cfr, int i) {
// root's only co-friend, in practice this means that this operation should
// have no impact on the root's rank (see the paper for a counter-example
// where a cycle is created and an unsafe adoption occurs because of that)
if (!is_root(x)) {
if (is_collectable(x)) {
set_rank(x, get_rank(cfr)+1);
}
return;
Expand Down Expand Up @@ -1009,7 +1010,7 @@ void drop() {

// making x's children "fall" along with him
for (int i = 0; i < 3; i++) {
if (IS_RIB(_x[i]) && is_parent(_x[i], x) && (!is_root(_x[i]))) {
if (IS_RIB(_x[i]) && is_parent(_x[i], x) && (is_collectable(_x[i]))) {
if (!is_falling(_x[i])) {
loosen(_x[i]);
q_enqueue(_x[i]);
Expand All @@ -1033,7 +1034,7 @@ void drop() {

// making x's children "fall" along with him
for (int i = 0; i < 3; i++) {
if (IS_RIB(_x[i]) && is_parent(_x[i], x) && (!is_root(_x[i]))) {
if (IS_RIB(_x[i]) && is_parent(_x[i], x) && (is_collectable(_x[i]))) {
if (!is_falling(_x[i]) && !adopt(_x[i])) {
// if we loosen here instead of when we dequeue, we can reuse the
// queue field for the priority queue
Expand Down Expand Up @@ -1142,7 +1143,7 @@ void remove_edge(obj from, obj to, int i) {
// disconnected and so a drop phase must ensue UNLESS `to` is protected or
// if `to` can be adopted right away
remove_parent(to, from, i);
if (!is_root(to) && !is_parent(to, from) && !_adopt(to)) {
if (is_collectable(to) && !is_parent(to, from) && !_adopt(to)) {
// @@(location gc-start)@@
q_enqueue(to);
fall(to);
Expand Down Expand Up @@ -1252,16 +1253,6 @@ void set_pc(obj new_pc) {

#else

#define protect(o, i) get_field(o,i) = MARK(get_field(o,i))

#define unprotect(o, i) \
do { \
if (IS_RIB(o)) { \
get_field(o,i) = UNMARK(get_field(o,i)); \
remove_root(o); \
} \
} while (0)


void set_field(obj src, int i, obj dest) { // write barrier
// The order differs a bit from the ref count version of the write barrier...
Expand All @@ -1272,31 +1263,19 @@ void set_field(obj src, int i, obj dest) { // write barrier
if (ref[i] == dest) return; // src's i-th field already points to dest

if (IS_RIB(ref[i])) { // no need to dereference _NULL or a num
if (!is_root(ref[i]) && is_parent(ref[i], src) && next_cofriend(ref[i], src) == _NULL) {
if (is_collectable(ref[i]) && is_parent(ref[i], src) && next_cofriend(ref[i], src) == _NULL) {
// We need to be more careful here since simply removing the edge
// between src and ref[i] will deallocate ref[i] and potentially
// some other ribs refered by ref[i]. This is problematic if dest
// contains a reference to one of ref[i]'s children (or more) since
// we'll deallocate a rib (or more) that shouldn't be deallocated.
// We can get around that by using a temporary rib to point to ref[i]
obj tmp = ref[i];
#ifdef TAGGING
protect(tmp, 7);
#else
set_rank(NIL, get_rank(src));
TEMP3 = ref[i]; // protect old dest
add_edge(NIL, ref[i], 0);
#endif
protect(tmp);
remove_ref(src, ref[i], i); // new dest
ref[i] = dest;
add_ref(src, dest, i);
#ifdef TAGGING
unprotect(tmp, 7);
#else
remove_ref(NIL, tmp, 0); // unprotect old dest
TEMP3 = _NULL;
set_rank(NIL, 1);
#endif
unprotect(tmp);
return;
}
remove_ref(src, ref[i], i);
Expand Down Expand Up @@ -1429,77 +1408,32 @@ obj pop() {
obj tos = CAR(stack);
if (IS_RIB(tos) && M_CAR(stack) == _NULL) {
// protect TOS only if it gets deallocated otherwise
#ifdef TAGGING
protect(tos, 7);
#else
TEMP5 = tos;
add_edge(null_rib, tos, 0);
#endif
protect(tos);
}
set_stack(CDR(stack));
return tos;
}

// to avoid too many preprocessor instructions in the RVM code
#ifdef TAGGING
#define DEC_POP(o) if (IS_RIB(o) && is_protected(o, 7)) unprotect(o, 7)
#else
#define DEC_POP(o) if (TEMP5 == o) remove_ref_nr(o, 0)
#endif

#define DEC_POP(o) if (IS_RIB(o) && is_protected(o)) unprotect(o)

#ifdef TAGGING
#define _protect(var) if (IS_RIB(var) && M_CAR(stack) == _NULL) protect(var)
#define _unprotect(var) if (IS_RIB(var) && is_protected(var)) unprotect(var)

#define _protect(var, i) if (IS_RIB(var) && M_CAR(stack) == _NULL) protect(var, i)
#define _unprotect(var, i) if (IS_RIB(var) && is_protected(var, i)) unprotect(var, i)

#define _pop(var, i) \
#define _pop(var) \
obj var = CAR(stack); \
_protect(var, 7); \
_protect(var); \
set_stack(CDR(stack))

#define PRIM1() _pop(x, 0)
#define PRIM2() _pop(y, 1); PRIM1()
#define PRIM3() _pop(z, 2); PRIM2()

#define DEC_PRIM1() _unprotect(x, 7);
#define DEC_PRIM2() _unprotect(y, 7); DEC_PRIM1()
#define DEC_PRIM3() _unprotect(z, 7); DEC_PRIM2()

#else
// FIXME!!! only protect a popped object if said object would get deallocated
// otherwise (can easily be checked since M_CAR(stack) would be _NULL)

#define _pop(var, i) \
obj var = CAR(stack); \
add_ref(null_rib, var, i); \
set_stack(CDR(stack));
#define PRIM1() _pop(x)
#define PRIM2() _pop(y); PRIM1()
#define PRIM3() _pop(z); PRIM2()

#define PRIM1() TEMP5 = CAR(stack); _pop(x, 0)
#define PRIM2() TEMP6 = CAR(stack); _pop(y, 1); PRIM1()
#define PRIM3() TEMP7 = CAR(stack); _pop(z, 2); PRIM2()

#define CLEAR_NR() \
obj *nr_ptr = RIB(null_rib)->fields; \
nr_ptr[3] = _NULL; \
nr_ptr[4] = _NULL; \
nr_ptr[5] = _NULL;

#define DEC_PRIM1() \
remove_ref(null_rib, x, 0); \
TEMP5 = _NULL; \
CLEAR_NR()
#define DEC_PRIM2() \
remove_ref(null_rib, y, 1); \
TEMP6 = _NULL; \
DEC_PRIM1()
#define DEC_PRIM3() \
remove_ref(null_rib, z, 2); \
TEMP7 = _NULL; \
DEC_PRIM2()
#define DEC_PRIM1() _unprotect(x);
#define DEC_PRIM2() _unprotect(y); DEC_PRIM1()
#define DEC_PRIM3() _unprotect(z); DEC_PRIM2()

#endif
#endif

#ifdef REF_COUNT

Expand Down

0 comments on commit 06d281b

Please sign in to comment.