Basic garbage collection; had to fix some stuff for stack preallocation

This commit is contained in:
K. Lange 2020-12-27 15:58:32 +09:00
parent 78022fb701
commit 1d0fac5640
9 changed files with 172 additions and 8 deletions

View File

@ -1,5 +1,6 @@
#include "chunk.h"
#include "memory.h"
#include "vm.h"
void krk_initChunk(KrkChunk * chunk) {
chunk->count = 0;
@ -30,7 +31,9 @@ void krk_freeChunk(KrkChunk * chunk) {
}
size_t krk_addConstant(KrkChunk * chunk, KrkValue value) {
krk_push(value);
krk_writeValueArray(&chunk->constants, value);
krk_pop();
return chunk->constants.count - 1;
}

View File

@ -4,6 +4,7 @@
#include "kuroko.h"
#include "compiler.h"
#include "memory.h"
#include "scanner.h"
#include "object.h"
#include "debug.h"
@ -197,8 +198,8 @@ static void endOfLine() {
}
}
static void emitConstant(KrkValue value) {
krk_writeConstant(currentChunk(), value, parser.previous.line);
static size_t emitConstant(KrkValue value) {
return krk_writeConstant(currentChunk(), value, parser.previous.line);
}
static void number(int canAssign) {
@ -604,7 +605,7 @@ static void unary(int canAssign) {
static void string(int canAssign) {
/* TODO: needs to handle escape sequences */
emitConstant(OBJECT_VAL(copyString(parser.previous.start + 1, parser.previous.length - 2)));
size_t r = emitConstant(OBJECT_VAL(copyString(parser.previous.start + 1, parser.previous.length - 2)));
}
/* TODO
@ -855,3 +856,11 @@ KrkFunction * krk_compile(const char * src) {
KrkFunction * function = endCompiler();
return parser.hadError ? NULL : function;
}
void krk_markCompilerRoots() {
Compiler * compiler = current;
while (compiler != NULL) {
krk_markObject((KrkObj*)compiler->function);
compiler = compiler->enclosing;
}
}

View File

@ -2,4 +2,5 @@
#include "object.h"
KrkFunction * krk_compile(const char * src);
extern KrkFunction * krk_compile(const char * src);
extern void krk_markCompilerRoots(void);

124
memory.c
View File

@ -1,8 +1,18 @@
#include "vm.h"
#include "memory.h"
#include "object.h"
#include "compiler.h"
void * krk_reallocate(void * ptr, size_t old, size_t new) {
vm.bytesAllocated += new - old;
if (new > old && ptr != vm.stack) {
//krk_collectGarbage();
if (vm.bytesAllocated > vm.nextGC) {
krk_collectGarbage();
}
}
if (new == 0) {
free(ptr);
return NULL;
@ -49,4 +59,118 @@ void krk_freeObjects() {
freeObject(object);
object = next;
}
free(vm.grayStack);
}
void krk_markObject(KrkObj * object) {
if (!object) return;
if (object->isMarked) return;
object->isMarked = 1;
if (vm.grayCapacity < vm.grayCount + 1) {
vm.grayCapacity = GROW_CAPACITY(vm.grayCapacity);
vm.grayStack = realloc(vm.grayStack, sizeof(KrkObj*) * vm.grayCapacity);
if (!vm.grayStack) exit(1);
}
vm.grayStack[vm.grayCount++] = object;
}
void krk_markValue(KrkValue value) {
if (!IS_OBJECT(value)) return;
krk_markObject(AS_OBJECT(value));
}
static void markArray(KrkValueArray * array) {
for (size_t i = 0; i < array->count; ++i) {
krk_markValue(array->values[i]);
}
}
static void blackenObject(KrkObj * object) {
switch (object->type) {
case OBJ_CLOSURE: {
KrkClosure * closure = (KrkClosure *)object;
krk_markObject((KrkObj*)closure->function);
for (size_t i = 0; i < closure->upvalueCount; ++i) {
krk_markObject((KrkObj*)closure->upvalues[i]);
}
break;
}
case OBJ_FUNCTION: {
KrkFunction * function = (KrkFunction *)object;
krk_markObject((KrkObj*)function->name);
markArray(&function->chunk.constants);
break;
}
case OBJ_UPVALUE:
krk_markValue(((KrkUpvalue*)object)->closed);
break;
case OBJ_NATIVE:
case OBJ_STRING:
break;
}
}
static void traceReferences() {
while (vm.grayCount > 0) {
KrkObj * object = vm.grayStack[--vm.grayCount];
blackenObject(object);
}
}
static void sweep() {
KrkObj * previous = NULL;
KrkObj * object = vm.objects;
while (object) {
if (object->isMarked) {
object->isMarked = 0;
previous = object;
object = object->next;
} else {
KrkObj * unreached = object;
object = object->next;
if (previous != NULL) {
previous->next = object;
} else {
vm.objects = object;
}
freeObject(unreached);
}
}
}
void krk_markTable(KrkTable * table) {
for (size_t i = 0; i < table->capacity; ++i) {
KrkTableEntry * entry = &table->entries[i];
krk_markValue(entry->key);
krk_markValue(entry->value);
}
}
void krk_tableRemoveWhite(KrkTable * table) {
for (size_t i = 0; i < table->capacity; ++i) {
KrkTableEntry * entry = &table->entries[i];
if (IS_OBJECT(entry->key) && !(AS_OBJECT(entry->key))->isMarked) {
krk_tableDelete(table, entry->key);
}
}
}
static void markRoots() {
for (KrkValue * slot = vm.stack; slot < vm.stackTop; ++slot) {
krk_markValue(*slot);
}
for (KrkUpvalue * upvalue = vm.openUpvalues; upvalue; upvalue = upvalue->next) {
krk_markObject((KrkObj*)upvalue);
}
krk_markTable(&vm.globals);
krk_markCompilerRoots();
}
void krk_collectGarbage(void) {
markRoots();
traceReferences();
krk_tableRemoveWhite(&vm.strings);
sweep();
vm.nextGC = vm.bytesAllocated * 2;
}

View File

@ -1,6 +1,8 @@
#pragma once
#include "kuroko.h"
#include "object.h"
#include "table.h"
#define GROW_CAPACITY(c) ((c) < 8 ? 8 : (c) * 2)
#define GROW_ARRAY(t,p,o,n) (t*)krk_reallocate(p,sizeof(t)*o,sizeof(t)*n)
@ -12,3 +14,8 @@
extern void * krk_reallocate(void *, size_t, size_t);
extern void krk_freeObjects(void);
extern void krk_collectGarbage(void);
extern void krk_markValue(KrkValue value);
extern void krk_markObject(KrkObj * object);
extern void krk_markTable(KrkTable * table);
extern void krk_tableRemoveWhite(KrkTable * table);

View File

@ -14,6 +14,7 @@
static KrkObj * allocateObject(size_t size, ObjType type) {
KrkObj * object = (KrkObj*)krk_reallocate(NULL, 0, size);
object->type = type;
object->isMarked = 0;
object->next = vm.objects;
vm.objects = object;
return object;
@ -24,7 +25,9 @@ static KrkString * allocateString(char * chars, size_t length, uint32_t hash) {
string->length = length;
string->chars = chars;
string->hash = hash;
krk_push(OBJECT_VAL(string));
krk_tableSet(&vm.strings, OBJECT_VAL(string), NONE_VAL());
krk_pop();
return string;
}
@ -51,7 +54,7 @@ KrkString * takeString(char * chars, size_t length) {
KrkString * copyString(const char * chars, size_t length) {
uint32_t hash = hashString(chars, length);
KrkString * interned = tableFindString(&vm.strings, chars, length, hash);
if (interned != NULL) return interned;
if (interned) return interned;
char * heapChars = ALLOCATE(char, length + 1);
memcpy(heapChars, chars, length);
heapChars[length] = '\0';

View File

@ -27,6 +27,7 @@ typedef enum {
struct Obj {
ObjType type;
char isMarked;
struct Obj * next;
};

17
vm.c
View File

@ -155,6 +155,11 @@ static void closeUpvalues(KrkValue * last) {
void krk_initVM() {
resetStack();
vm.objects = NULL;
vm.bytesAllocated = 0;
vm.nextGC = 1024 * 1024;
vm.grayCount = 0;
vm.grayCapacity = 0;
vm.grayStack = NULL;
krk_initTable(&vm.globals);
krk_initTable(&vm.strings);
defineNative("sleep", krk_sleep);
@ -231,12 +236,14 @@ static void concatenate(const char * a, const char * b, size_t al, size_t bl) {
chars[length] = '\0';
KrkString * result = takeString(chars, length);
krk_pop();
krk_pop();
krk_push(OBJECT_VAL(result));
}
static void addObjects() {
KrkValue _b = krk_pop();
KrkValue _a = krk_pop();
KrkValue _b = krk_peek(0);
KrkValue _a = krk_peek(1);
if (IS_STRING(_a)) {
KrkString * a = AS_STRING(_a);
@ -260,6 +267,8 @@ static void addObjects() {
concatenate(a->chars,tmp,a->length,strlen(tmp));
} else {
runtimeError("Can not concatenate types %s and %s", typeName(_a), typeName(_b)); \
krk_pop();
krk_pop();
krk_push(NONE_VAL());
}
}
@ -281,7 +290,9 @@ static size_t readBytes(CallFrame * frame, int num) {
unsigned int bot = READ_BYTE();
return (top << 16) | (mid << 8) | (bot);
}
return 0;
runtimeError("Invalid byte read?");
return (size_t)-1;
}
static KrkValue run() {

5
vm.h
View File

@ -23,6 +23,11 @@ typedef struct {
KrkTable strings;
KrkUpvalue * openUpvalues;
KrkObj * objects;
size_t bytesAllocated;
size_t nextGC;
size_t grayCount;
size_t grayCapacity;
KrkObj** grayStack;
} KrkVM;
extern KrkVM vm;