126 lines
2.6 KiB
C
126 lines
2.6 KiB
C
#include <assert.h>
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "gc.h"
|
|
#include "vendor/yar.h"
|
|
|
|
#define ALIGN(n) (((n) + 7) & ~7)
|
|
static inline int infrom(Gc *gc, V *ptr) {
|
|
const U8 *x = (const U8 *)ptr;
|
|
return (x >= gc->from.start && x < gc->from.end);
|
|
}
|
|
|
|
V gc_addroot(Gc *gc, O *ptr) { *yar_append(&gc->roots) = ptr; }
|
|
I gc_mark(Gc *gc) { return gc->roots.count; }
|
|
V gc_reset(Gc *gc, I mark) { gc->roots.count = mark; }
|
|
|
|
static O copy(Gc *gc, Hd *hdr) {
|
|
assert(infrom(gc, hdr));
|
|
assert(hdr->type != TYPE_FWD);
|
|
|
|
Z sz = ALIGN(hdr->size);
|
|
Hd *new = (Hd *)gc->to.free;
|
|
gc->to.free += sz;
|
|
memcpy(new, hdr, sz);
|
|
|
|
hdr->type = TYPE_FWD;
|
|
O *obj = (O *)(hdr + 1);
|
|
*obj = BOX(new);
|
|
return *obj;
|
|
}
|
|
|
|
static O forward(Gc *gc, O obj) {
|
|
if (obj == 0)
|
|
return 0;
|
|
if (IMM(obj))
|
|
return obj;
|
|
if (!infrom(gc, (V *)obj))
|
|
return obj;
|
|
|
|
Hd *hdr = UNBOX(obj);
|
|
if (hdr->type == TYPE_FWD) {
|
|
O *o = (O *)(hdr + 1);
|
|
return *o;
|
|
} else {
|
|
return copy(gc, hdr);
|
|
}
|
|
}
|
|
|
|
#if GC_DEBUG
|
|
static V printstats(Gc *gc, const char *label) {
|
|
Z used = (Z)(gc->from.free - gc->from.start);
|
|
fprintf(stderr, "[%s] used=%zu/%zu bytes (%.1f%%)\n", label, used,
|
|
(Z)HEAP_BYTES, (F)used / (F)HEAP_BYTES * 100.0);
|
|
}
|
|
#endif
|
|
|
|
V gc_collect(Gc *gc) {
|
|
uint8_t *scan = gc->to.free;
|
|
|
|
#if GC_DEBUG
|
|
printstats(gc, "before GC");
|
|
#endif
|
|
|
|
for (Z i = 0; i < gc->roots.count; i++) {
|
|
O *o = gc->roots.items[i];
|
|
*o = forward(gc, *o);
|
|
}
|
|
|
|
while (scan < gc->to.free) {
|
|
if (scan >= gc->to.end) {
|
|
fprintf(stderr, "fatal GC error: out of memory\n");
|
|
abort();
|
|
}
|
|
Hd *hdr = (Hd *)scan;
|
|
switch (hdr->type) {
|
|
// TODO: the rest of the owl
|
|
case TYPE_FWD:
|
|
fprintf(stderr, "fatal GC error: forwarding pointer in to-space\n");
|
|
abort();
|
|
default:
|
|
fprintf(stderr, "GC warning: junk object type %" PRId32 "\n", hdr->type);
|
|
}
|
|
scan += ALIGN(hdr->size);
|
|
}
|
|
|
|
Gs tmp = gc->from;
|
|
gc->from = gc->to;
|
|
gc->to = tmp;
|
|
gc->to.free = gc->to.start;
|
|
|
|
#if GC_DEBUG
|
|
printstats(gc, "after GC");
|
|
#endif
|
|
}
|
|
|
|
void gc_init(Gc *gc) {
|
|
gc->from.start = malloc(HEAP_BYTES);
|
|
if (!gc->from.start)
|
|
goto fatal;
|
|
gc->from.end = gc->from.start + HEAP_BYTES;
|
|
gc->from.free = gc->from.start;
|
|
|
|
gc->to.start = malloc(HEAP_BYTES);
|
|
if (!gc->to.start)
|
|
goto fatal;
|
|
gc->to.end = gc->to.start + HEAP_BYTES;
|
|
gc->to.free = gc->to.start;
|
|
|
|
gc->roots.capacity = 0;
|
|
gc->roots.count = 0;
|
|
gc->roots.items = NULL;
|
|
return;
|
|
|
|
fatal:
|
|
fprintf(stderr, "failed to allocate heap space\n");
|
|
abort();
|
|
}
|
|
|
|
void gc_deinit(Gc *gc) {
|
|
gc_collect(gc);
|
|
free(gc->from.start);
|
|
free(gc->to.start);
|
|
yar_free(&gc->roots);
|
|
}
|