idris-1.3.0: rts/idris_gc.c
#include "idris_heap.h"
#include "idris_rts.h"
#include "idris_gc.h"
#include "idris_bitstring.h"
#include <assert.h>
static inline VAL copy_plain(VM* vm, VAL x, size_t sz) {
VAL cl = iallocate(vm, sz, 1);
memcpy(cl, x, sz);
return cl;
}
VAL copy(VM* vm, VAL x) {
int ar;
VAL cl;
if (x==NULL) {
return x;
}
switch(GETTY(x)) {
case CT_INT: return x;
case CT_BITS32: return copy_plain(vm, x, sizeof(Bits32));
case CT_BITS64: return copy_plain(vm, x, sizeof(Bits64));
case CT_FLOAT: return copy_plain(vm, x, sizeof(Float));
case CT_FWD:
return GETPTR(x);
case CT_CDATA:
cl = copy_plain(vm, x, sizeof(CDataC));
c_heap_mark_item(GETCDATA(x));
break;
case CT_BIGINT:
cl = MKBIGMc(vm, GETMPZ(x));
break;
case CT_CON:
ar = CARITY(x);
if (ar == 0 && CTAG(x) < 256) {
return x;
}
// FALLTHROUGH
case CT_ARRAY:
case CT_STRING:
case CT_REF:
case CT_STROFFSET:
case CT_PTR:
case CT_MANAGEDPTR:
case CT_RAWDATA:
cl = copy_plain(vm, x, x->hdr.sz);
break;
default:
cl = NULL;
assert(0);
break;
}
assert(x->hdr.sz >= sizeof(Fwd));
SETTY(x, CT_FWD);
((Fwd*)x)->fwd = cl;
return cl;
}
void cheney(VM *vm) {
char* scan = aligned_heap_pointer(vm->heap.heap);
while(scan < vm->heap.next) {
VAL heap_item = (VAL)scan;
// If it's a CT_CON, CT_REF or CT_STROFFSET, copy its arguments
switch(GETTY(heap_item)) {
case CT_CON:
{
Con * c = (Con*)heap_item;
size_t len = CARITY(c);
for(size_t i = 0; i < len; ++i)
c->args[i] = copy(vm, c->args[i]);
}
break;
case CT_ARRAY:
{
Array * a = (Array*)heap_item;
size_t len = CELEM(a);
for(size_t i = 0; i < len; ++i)
a->array[i] = copy(vm, a->array[i]);
}
break;
case CT_REF:
{
Ref * r = (Ref*)heap_item;
r->ref = copy(vm, r->ref);
}
break;
case CT_STROFFSET:
{
StrOffset * s = (StrOffset*)heap_item;
s->base = (String*)copy(vm, (VAL)s->base);
}
break;
default: // Nothing to copy
break;
}
scan += aligned(valSize(heap_item));
}
assert(scan == vm->heap.next);
}
void idris_gc(VM* vm) {
HEAP_CHECK(vm)
STATS_ENTER_GC(vm->stats, vm->heap.size)
if (vm->heap.old != NULL)
free(vm->heap.old);
/* Allocate swap heap. */
alloc_heap(&vm->heap, vm->heap.size, vm->heap.growth, vm->heap.heap);
VAL* root;
for(root = vm->valstack; root < vm->valstack_top; ++root) {
*root = copy(vm, *root);
}
#ifdef HAS_PTHREAD
Msg* msg;
for(msg = vm->inbox; msg < vm->inbox_write; ++msg) {
msg->msg = copy(vm, msg->msg);
}
#endif
vm->ret = copy(vm, vm->ret);
vm->reg1 = copy(vm, vm->reg1);
cheney(vm);
// After reallocation, if we've still more than half filled the new heap, grow the heap
// for next time.
if ((vm->heap.next - vm->heap.heap) > vm->heap.size >> 1) {
vm->heap.size += vm->heap.growth;
}
// finally, sweep the C heap
c_heap_sweep(&vm->c_heap);
STATS_LEAVE_GC(vm->stats, vm->heap.size, vm->heap.next - vm->heap.heap)
HEAP_CHECK(vm)
}
void idris_gcInfo(VM* vm, int doGC) {
printf("Stack: <BOT %p> <TOP %p>\n", vm->valstack, vm->valstack_top);
printf("Final heap size %zd\n", vm->heap.size);
printf("Final heap use %zd\n", vm->heap.next - vm->heap.heap);
if (doGC) { idris_gc(vm); }
printf("Final heap use after GC %zd\n", vm->heap.next - vm->heap.heap);
#ifdef IDRIS_ENABLE_STATS
printf("Total allocations %" PRIu64 "\n", vm->stats.allocations);
#endif
printf("Number of collections %" PRIu32 "\n", vm->stats.collections);
}