Skip to content

Commit

Permalink
Garbage collector
Browse files Browse the repository at this point in the history
  • Loading branch information
dils2k committed Jun 9, 2023
1 parent 2ada75a commit 483e076
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 4 deletions.
3 changes: 3 additions & 0 deletions chunk.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "chunk.h"
#include "memory.h"
#include "vm.h"

void initChunk(Chunk* chunk) {
chunk->count = 0;
Expand Down Expand Up @@ -31,6 +32,8 @@ void freeChunk(Chunk* chunk) {
}

int addConstant(Chunk* chunk, Value value) {
push(value);
writeValueArray(&chunk->constants, value);
pop();
return chunk->constants.count - 1;
}
7 changes: 5 additions & 2 deletions common.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
#include <stddef.h>
#include <stdint.h>

// #define DEBUG_PRINT_CODE
// #define DEBUG_TRACE_EXECUTION
#define DEBUG_PRINT_CODE
#define DEBUG_TRACE_EXECUTION

#define DEBUG_STRESS_GC
#define DEBUG_LOG_GC

#define UINT8_COUNT (UINT8_MAX + 1)

Expand Down
9 changes: 9 additions & 0 deletions compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "common.h"
#include "compiler.h"
#include "memory.h"
#include "scanner.h"

#ifdef DEBUG_PRINT_CODE
Expand Down Expand Up @@ -807,3 +808,11 @@ ObjFunction* compile(const char* source) {
ObjFunction* function = endCompiler();
return parser.hadError ? NULL : function;
}

void markCompilerRoots() {
Compiler* compiler = current;
while (compiler != NULL) {
markObject((Obj*)compiler->function);
compiler = compiler->enclosing;
}
}
1 change: 1 addition & 0 deletions compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
#include "vm.h"

ObjFunction* compile(const char* source);
void markCompilerRoots();

#endif
157 changes: 157 additions & 0 deletions memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,27 @@
#include "common.h"
#include "vm.h"
#include "memory.h"
#include "compiler.h"

#ifdef DEBUG_LOG_GC
#include <stdio.h>
#include "debug.h"
#endif

#define GC_HEAP_GROW_FACTOR 2

void* reallocate(void* pointer, size_t oldSize, size_t newSize) {
vm.bytesAllocated += newSize - oldSize;
if (newSize > oldSize) {
#ifdef DEBUG_STRESS_GC
collectGarbage();
#endif
}

if (vm.bytesAllocated > vm.nextGC) {
collectGarbage();
}

if (newSize == 0) {
free(pointer);
return NULL;
Expand All @@ -14,7 +33,74 @@ void* reallocate(void* pointer, size_t oldSize, size_t newSize) {
return result;
}

void markObject(Obj* object) {
if (object == NULL) return;
if (object->isMarked) return;

#ifdef DEBUG_LOG_GC
printf("%p mark ", (void*)object);
printValue(OBJ_VAL(object));
printf("\n");
#endif
object->isMarked = true;

if (vm.grayCapacity < vm.grayCount + 1) {
vm.grayCapacity = GROW_CAPACITY(vm.grayCapacity);
vm.grayStack = (Obj**)realloc(vm.grayStack,
sizeof(Obj*) * vm.grayCapacity);
// if we fail to allocate space for grayStack, there is nothing we
// can do here.
if (vm.grayStack == NULL) exit(1);
}
vm.grayStack[vm.grayCount++] = object;
}

void markValue(Value value) {
if (IS_OBJ(value)) markObject(AS_OBJ(value));
}

static void markArray(ValueArray* array) {
for (int i = 0; i < array->count; i++) {
markValue(array->values[i]);
}
}

static void blackenObject(Obj* object) {
#ifdef DEBUG_LOG_GC
printf("%p blacken ", (void*)object);
printValue(OBJ_VAL(object));
printf("\n");
#endif

switch (object->type) {
case OBJ_CLOSURE: {
ObjClosure* closure = (ObjClosure*)object;
markObject((Obj*)closure->function);
for (int i = 0; i < closure->upvalueCount; i++) {
markObject((Obj*)closure->upvalues[i]);
}
break;
}
case OBJ_FUNCTION: {
ObjFunction* function = (ObjFunction*)object;
markObject((Obj*)function->name);
markArray(&function->chunk.constants);
break;
}
case OBJ_UPVALUE:
markValue(((ObjUpvalue*)object)->closed);
break;
case OBJ_NATIVE:
case OBJ_STRING:
break;
}
}

static void freeObject(Obj* object) {
#ifdef DEBUG_LOG_GC
printf("%p free type %d\n", (void*)object, object->type);
#endif

switch (object->type) {
case OBJ_CLOSURE: {
ObjClosure* closure = (ObjClosure*)object;
Expand Down Expand Up @@ -51,5 +137,76 @@ void freeObjects() {
freeObject(object);
object = next;
}

free(vm.grayStack);
}

static void markRoots() {
for (Value* slot = vm.stack; slot < vm.stackTop; slot++) {
markValue(*slot);
}

for (int i = 0; i < vm.frameCount; i++) {
markObject((Obj*)vm.frames[i].closure);
}

for (ObjUpvalue* upvalue = vm.openUpvalues;
upvalue != NULL;
upvalue = upvalue->next) {
markObject((Obj*)upvalue);
}

markTable(&vm.globals);
markCompilerRoots();
}

static void traceReferences() {
while (vm.grayCount > 0) {
Obj* object = vm.grayStack[--vm.grayCount];
blackenObject(object);
}
}

static void sweep() {
Obj* previous = NULL;
Obj* object = vm.objects;

while (object != NULL) {
if (object->isMarked) {
object->isMarked = false;
previous = object;
object = object->next;
} else {
Obj* unreached = object;
object = object->next;
if (previous != NULL) {
previous->next = object;
} else {
vm.objects = object;
}

freeObject(unreached);
}
}
}

void collectGarbage() {
#ifdef DEBUG_LOG_GC
printf("-- gc begin\n");
size_t before = vm.bytesAllocated;
#endif

markRoots();
traceReferences();
tableRemoveWhite(&vm.strings);
sweep();

vm.nextGC = vm.bytesAllocated * GC_HEAP_GROW_FACTOR;

#ifdef DEBUG_LOG_GC
printf("-- gc end\n");
printf(" collected %zu bytes (from %zu to %zu) next at %zu\n",
before - vm.bytesAllocated, before, vm.bytesAllocated,
vm.nextGC);
#endif
}
3 changes: 3 additions & 0 deletions memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)

void* reallocate(void* pointer, size_t oldSize, size_t newSize);
void markObject(Obj* object);
void markValue(Value value);
void collectGarbage();
void freeObjects();


Expand Down
10 changes: 10 additions & 0 deletions object.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@
static Obj* allocateObject(size_t size, ObjType type) {
Obj* object = (Obj*)reallocate(NULL, 0, size);
object->type = type;
object->isMarked = false;
object->next = vm.objects;
vm.objects = object;

#ifdef DEBUG_LOG_GC
printf("%p allocate %zu for %d\n", (void*)object, size, type);
#endif

return object;
}

Expand Down Expand Up @@ -51,7 +57,11 @@ static ObjString* allocateString(char* chars, int length, uint32_t hash) {
string->length = length;
string->chars = chars;
string->hash = hash;

push(OBJ_VAL(string));
tableSet(&vm.strings, string, NIL_VAL);
pop();

return string;
}

Expand Down
1 change: 1 addition & 0 deletions object.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ typedef enum {

struct Obj {
ObjType type;
bool isMarked;
struct Obj* next;
};

Expand Down
17 changes: 17 additions & 0 deletions table.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,23 @@ ObjString* tableFindString(Table* table, const char* chars,
}
}

void tableRemoveWhite(Table* table) {
for (int i = 0; i < table->capacity; i++) {
Entry* entry = &table->entries[i];
if (entry->key != NULL && !entry->key->obj.isMarked) {
tableDelete(table, entry->key);
}
}
}

void markTable(Table* table) {
for (int i = 0; i < table->capacity; i++) {
Entry* entry = &table->entries[i];
markObject((Obj*)entry->key);
markValue(entry->value);
}
}

bool tableGet(Table* table, ObjString* key, Value* value) {
if (table->count == 0) return false;

Expand Down
2 changes: 2 additions & 0 deletions table.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ bool tableDelete(Table* table, ObjString* key);
void tableAddAll(Table* from, Table* to);
ObjString* tableFindString(Table* table, const char* chars,
int length, uint32_t hash);
void tableRemoveWhite(Table* table);
void markTable(Table* table);

#endif
13 changes: 11 additions & 2 deletions vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ static void defineNative(const char* name, NativeFn function) {
void initVM() {
resetStack();
vm.objects = NULL;
vm.bytesAllocated = 0;
vm.nextGC = 1024 * 1024;

vm.grayCount = 0;
vm.grayCapacity = 0;
vm.grayStack = NULL;

initTable(&vm.globals);
initTable(&vm.strings);

Expand Down Expand Up @@ -160,8 +167,8 @@ static bool isFalsey(Value value) {
}

static void concatenate() {
ObjString* b = AS_STRING(pop());
ObjString* a = AS_STRING(pop());
ObjString* b = AS_STRING(peek(0));
ObjString* a = AS_STRING(peek(1));

int length = a->length + b->length;
char* chars = ALLOCATE(char, length + 1);
Expand All @@ -170,6 +177,8 @@ static void concatenate() {
chars[length] = '\0';

ObjString* result = takeString(chars, length);
pop();
pop();
push(OBJ_VAL(result));
}

Expand Down
8 changes: 8 additions & 0 deletions vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,17 @@ typedef struct {

Value stack[STACK_MAX];
Value* stackTop;

size_t bytesAllocated;
size_t nextGC;
Obj* objects;
int grayCount;
int grayCapacity;
Obj** grayStack;

Table strings;
Table globals;

ObjUpvalue* openUpvalues;
} VM;

Expand Down

0 comments on commit 483e076

Please sign in to comment.