108 lines
2.6 KiB
C
108 lines
2.6 KiB
C
#pragma once
|
|
#include "h.h"
|
|
|
|
|
|
u64 gc_depth = 1;
|
|
void gc_disable() { gc_depth++; }
|
|
void gc_enable() { gc_depth--; }
|
|
|
|
vfn gc_roots[16];
|
|
u32 gc_rootSz;
|
|
void gc_addFn(vfn f) {
|
|
if (gc_rootSz>=16) err("Too many GC roots");
|
|
gc_roots[gc_rootSz++] = f;
|
|
}
|
|
B gc_rootObjs[256];
|
|
u32 gc_rootObjSz;
|
|
void gc_add(B x) {
|
|
if (gc_rootObjSz>=256) err("Too many GC root objects");
|
|
gc_rootObjs[gc_rootObjSz++] = x;
|
|
}
|
|
|
|
#ifdef LOG_GC
|
|
u64 gc_visitBytes, gc_visitCount, gc_freedBytes, gc_freedCount;
|
|
#endif
|
|
|
|
u8 gc_tagCurr = 0x80; // if no gc is running, this is what all objects will have
|
|
u8 gc_tagNew = 0x00;
|
|
void mm_visit(B x) {
|
|
#ifdef HEAP_VERIFY
|
|
if(heapVerify_visit(x)) return;
|
|
#endif
|
|
|
|
if (!isVal(x)) return;
|
|
u8 p = v(x)->mmInfo;
|
|
if ((p&0x80)==gc_tagNew) return;
|
|
v(x)->mmInfo = p^0x80;
|
|
#ifdef LOG_GC
|
|
gc_visitBytes+= mm_size(v(x)); gc_visitCount++;
|
|
#endif
|
|
TI(x).visit(x);
|
|
}
|
|
void mm_visitP(void* xp) {
|
|
#ifdef HEAP_VERIFY
|
|
if(heapVerify_visitP(xp)) return;
|
|
#endif
|
|
|
|
Value* x = (Value*)xp;
|
|
u8 p = x->mmInfo;
|
|
if ((p&0x80)==gc_tagNew) return;
|
|
x->mmInfo = p^0x80;
|
|
#ifdef LOG_GC
|
|
gc_visitBytes+= mm_size(x); gc_visitCount++;
|
|
#endif
|
|
ti[x->type].visit(tag(x, OBJ_TAG));
|
|
}
|
|
void gc_tryFree(Value* v) {
|
|
u8 t = v->type;
|
|
#if defined(DEBUG) && !defined(CATCH_ERRORS)
|
|
if (t==t_freed) err("GC found t_freed\n");
|
|
#endif
|
|
if (t!=t_empty & (v->mmInfo&0x80)==gc_tagCurr) {
|
|
if (t==t_shape) return;
|
|
#ifdef DONT_FREE
|
|
v->flags = t;
|
|
#else
|
|
#ifdef CATCH_ERRORS
|
|
if (t==t_freed) { mm_free(v); return; }
|
|
#endif
|
|
#endif
|
|
#ifdef LOG_GC
|
|
gc_freedBytes+= mm_size(v); gc_freedCount++;
|
|
#endif
|
|
v->type = t_freed;
|
|
ti[t].free(tag(v, OBJ_TAG));
|
|
// Object may not be immediately freed if it's not a part of a cycle, but instead a descendant of one.
|
|
// It will be freed when the cycle is freed, and the t_freed type ensures it doesn't double-free itself
|
|
}
|
|
}
|
|
|
|
|
|
void gc_visitRoots() {
|
|
for (u32 i = 0; i < gc_rootSz; i++) gc_roots[i]();
|
|
for (u32 i = 0; i < gc_rootObjSz; i++) mm_visit(gc_rootObjs[i]);
|
|
}
|
|
u64 gc_lastAlloc;
|
|
void gc_forceGC() {
|
|
#ifdef LOG_GC
|
|
u64 start = nsTime();
|
|
gc_visitBytes = 0; gc_freedBytes = 0;
|
|
gc_visitCount = 0; gc_freedCount = 0;
|
|
#endif
|
|
#ifdef ENABLE_GC
|
|
gc_visitRoots();
|
|
mm_forHeap(gc_tryFree);
|
|
gc_tagNew = gc_tagCurr;
|
|
gc_tagCurr^= 0x80;
|
|
#ifdef LOG_GC
|
|
fprintf(stderr, "GC kept %ldB from %ld objects, freed %ldB from %ld objects; took %.3fms\n", gc_visitBytes, gc_visitCount, gc_freedBytes, gc_freedCount, (nsTime()-start)/1e6);
|
|
#endif
|
|
gc_lastAlloc = allocB;
|
|
#endif
|
|
}
|
|
|
|
|
|
void gc_maybeGC() {
|
|
if (!gc_depth && allocB > gc_lastAlloc*2) gc_forceGC();
|
|
}
|