TODO: add pointer bitmaps
This commit is contained in:
flameyosflow 2025-12-16 22:08:29 +02:00
commit 447499dede
2 changed files with 271 additions and 0 deletions

216
libsuicmez/suicmez_gc.c Normal file
View file

@ -0,0 +1,216 @@
#include "suicmez_gc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
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);
}

55
libsuicmez/suicmez_gc.h Normal file
View file

@ -0,0 +1,55 @@
#ifndef SUICMEZ_GC_H
#define SUICMEZ_GC_H
#include <stdint.h>
#include <stddef.h>
#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