From 447499dedefd8cf62abd0d6bc1d7f93377a06d19 Mon Sep 17 00:00:00 2001 From: flameyosflow Date: Tue, 16 Dec 2025 22:08:29 +0200 Subject: [PATCH] Add GC TODO: add pointer bitmaps --- libsuicmez/suicmez_gc.c | 216 ++++++++++++++++++++++++++++++++++++++++ libsuicmez/suicmez_gc.h | 55 ++++++++++ 2 files changed, 271 insertions(+) create mode 100644 libsuicmez/suicmez_gc.c create mode 100644 libsuicmez/suicmez_gc.h diff --git a/libsuicmez/suicmez_gc.c b/libsuicmez/suicmez_gc.c new file mode 100644 index 0000000..d6e3e02 --- /dev/null +++ b/libsuicmez/suicmez_gc.c @@ -0,0 +1,216 @@ +#include "suicmez_gc.h" +#include +#include +#include +#include + +typedef struct { + /* Young generation */ + uint8_t *from_space; + uint8_t *from_ptr; + + uint8_t *to_space; + uint8_t *to_ptr; + + size_t young_size; + + /* Old generation */ + uint8_t *old; + uint8_t *old_ptr; + size_t old_size; + + /* Card table */ + uint8_t *card_table; + size_t card_count; + + /* Roots = pointer slots */ + void **roots[MAX_ROOTS]; + size_t root_count; +} SuicmezGC; + +static SuicmezGC gc; + +static int is_young(void *p) { + return (uint8_t*)p >= gc.from_space && + (uint8_t*)p < gc.from_ptr; +} + +static int is_old(void *p) { + return (uint8_t*)p >= gc.old && + (uint8_t*)p < gc.old_ptr; +} + +static size_t card_index(void *p) { + return ((uint8_t*)p - gc.old) / CARD_SIZE; +} + +void gc_add_root(void **slot) { + gc.roots[gc.root_count++] = slot; +} + +void gc_remove_root(void **slot) { + for (size_t i = 0; i < gc.root_count; i++) { + if (gc.roots[i] == slot) { + gc.roots[i] = gc.roots[--gc.root_count]; + return; + } + } +} + +void gc_write_barrier(void *owner, void *value) { + if (is_old(owner) && is_young(value)) { + size_t idx = card_index(owner); + gc.card_table[idx] = 1; + } +} + +static Obj *promote(Obj *obj) { + size_t sz = obj->header.size; + if (gc.old_ptr + sz > gc.old + gc.old_size) { + fprintf(stderr, "GC: old gen overflow\n"); + abort(); + } + + Obj *new_obj = (Obj*)gc.old_ptr; + memcpy(new_obj, obj, sz); + gc.old_ptr += sz; + + return new_obj; +} + +static Obj *forward(Obj *obj) { + if (!obj) return NULL; + + if (is_old(obj)) + return obj; + + if (!is_young(obj)) + return obj; + + if (obj->header.forwarding) + return obj->header.forwarding; + + if (obj->header.age >= PROMOTION_AGE) { + Obj *p = promote(obj); + obj->header.forwarding = p; + return p; + } + + size_t sz = obj->header.size; + Obj *new_obj = (Obj*)gc.to_ptr; + memcpy(new_obj, obj, sz); + + gc.to_ptr += sz; + + new_obj->header.age++; + new_obj->header.forwarding = NULL; + obj->header.forwarding = new_obj; + + return new_obj; +} + +static void gc_minor_collect(void) { + gc.to_ptr = gc.to_space; + + /* 1. Roots */ + for (size_t i = 0; i < gc.root_count; i++) { + void **slot = gc.roots[i]; + *slot = forward((Obj*)*slot); + } + + /* 2. Remembered set */ + for (size_t c = 0; c < gc.card_count; c++) { + if (!gc.card_table[c]) continue; + gc.card_table[c] = 0; + + uint8_t *start = gc.old + c * CARD_SIZE; + uint8_t *end = start + CARD_SIZE; + if (end > gc.old_ptr) end = gc.old_ptr; + + for (uint8_t *p = start; p < end;) { + Obj *obj = (Obj*)p; + size_t fields = + (obj->header.size - sizeof(ObjHeader)) / sizeof(void*); + + for (size_t i = 0; i < fields; i++) { + obj->fields[i] = forward((Obj*)obj->fields[i]); + } + p += obj->header.size; + } + } + + /* 3. Cheney scan */ + uint8_t *scan = gc.to_space; + while (scan < gc.to_ptr) { + Obj *obj = (Obj*)scan; + size_t fields = + (obj->header.size - sizeof(ObjHeader)) / sizeof(void*); + + for (size_t i = 0; i < fields; i++) { + obj->fields[i] = forward((Obj*)obj->fields[i]); + } + + scan += obj->header.size; + } + + /* 4. Swap spaces */ + uint8_t *tmp = gc.from_space; + gc.from_space = gc.to_space; + gc.to_space = tmp; + gc.from_ptr = gc.to_ptr; + + /* 5. Clear forwarding pointers */ + scan = gc.from_space; + while (scan < gc.from_ptr) { + Obj *obj = (Obj*)scan; + obj->header.forwarding = NULL; + scan += obj->header.size; + } +} + +void *gc_alloc_young(size_t payload_size) { + size_t size = payload_size + sizeof(ObjHeader); + + if (gc.from_ptr + size > gc.from_space + gc.young_size) { + gc_minor_collect(); + if (gc.from_ptr + size > gc.from_space + gc.young_size) { + fprintf(stderr, "GC: out of memory\n"); + abort(); + } + } + + Obj *obj = (Obj*)gc.from_ptr; + obj->header.size = size; + obj->header.age = 0; + obj->header.flags = 0; + obj->header.forwarding = NULL; + + gc.from_ptr += size; + return obj; +} + +void gc_init(void) { + gc.young_size = YOUNG_SIZE; + gc.old_size = OLD_SIZE; + + gc.from_space = malloc(gc.young_size); + gc.to_space = malloc(gc.young_size); + gc.old = malloc(gc.old_size); + + gc.from_ptr = gc.from_space; + gc.to_ptr = gc.to_space; + gc.old_ptr = gc.old; + + gc.card_count = gc.old_size / CARD_SIZE; + gc.card_table = calloc(gc.card_count, 1); + + gc.root_count = 0; +} + + +void gc_shutdown(void) { + free(gc.from_space); + free(gc.to_space); + free(gc.old); + free(gc.card_table); +} \ No newline at end of file diff --git a/libsuicmez/suicmez_gc.h b/libsuicmez/suicmez_gc.h new file mode 100644 index 0000000..c5f4557 --- /dev/null +++ b/libsuicmez/suicmez_gc.h @@ -0,0 +1,55 @@ +#ifndef SUICMEZ_GC_H +#define SUICMEZ_GC_H + +#include +#include + +#define YOUNG_SIZE (1024 * 1024 * 2) +#define SURVIVOR_SIZE (512 * 1024) +#define OLD_SIZE (16 * 1024 * 1024) +#define CARD_SIZE 512 +#define PROMOTION_AGE 2 +#define MAX_ROOTS 1024 +#define MARKED 0x1 + +/* Forward declare Obj so ObjHeader can reference it */ +typedef struct Obj Obj; + +/* Header must come first */ +typedef struct ObjHeader { + uint32_t size; // total object size incl header + uint16_t age; + uint16_t flags; + Obj *forwarding; // non-NULL if moved +} ObjHeader; + +/* Full object definition */ +struct Obj { + ObjHeader header; + void *fields[]; +}; + +// --- API --- +// Initialize the GC +void gc_init(void); + +// Shutdown GC +void gc_shutdown(void); + +// Allocate object in young generation +void* gc_alloc_young(size_t size); + +// Root management +void gc_add_root(void **ptr); +void gc_remove_root(void **ptr); + +// Write barrier for old->young references +void gc_write_barrier(void *src, void *dst); + +// Minor GC slice (incremental) +void gc_minor_collect_slice(size_t max_objects); + +// Trigger major GC (async background) +void gc_major_collect(void); + +#endif // SUICMEZ_GC_H \ No newline at end of file