Basic garbage collection; had to fix some stuff for stack preallocation
This commit is contained in:
parent
78022fb701
commit
1d0fac5640
3
chunk.c
3
chunk.c
@ -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;
|
||||
}
|
||||
|
||||
|
15
compiler.c
15
compiler.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
124
memory.c
@ -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;
|
||||
}
|
||||
|
7
memory.h
7
memory.h
@ -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);
|
||||
|
5
object.c
5
object.c
@ -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';
|
||||
|
1
object.h
1
object.h
@ -27,6 +27,7 @@ typedef enum {
|
||||
|
||||
struct Obj {
|
||||
ObjType type;
|
||||
char isMarked;
|
||||
struct Obj * next;
|
||||
};
|
||||
|
||||
|
17
vm.c
17
vm.c
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user