Add GC
TODO: add pointer bitmaps
This commit is contained in:
parent
a32db747e6
commit
447499dede
2 changed files with 271 additions and 0 deletions
216
libsuicmez/suicmez_gc.c
Normal file
216
libsuicmez/suicmez_gc.c
Normal 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
55
libsuicmez/suicmez_gc.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue