Initial commit
This commit is contained in:
commit
b2edca2cb4
25 changed files with 3590 additions and 0 deletions
269
memory.c
Normal file
269
memory.c
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "compiler.h"
|
||||
#include "memory.h"
|
||||
#include "vm.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 nullptr;
|
||||
}
|
||||
|
||||
void* result = realloc(pointer, newSize);
|
||||
if (result == nullptr) {
|
||||
exit(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void markObject(Obj* object)
|
||||
{
|
||||
if (object == nullptr) {
|
||||
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 (vm.grayStack == nullptr) {
|
||||
fprintf(stderr, "failed to realloc grayStack\n");
|
||||
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 (auto 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_BOUND_METHOD:
|
||||
{
|
||||
ObjBoundMethod* bound = (ObjBoundMethod*)object;
|
||||
markValue(bound->receiver);
|
||||
markObject((Obj*)bound->method);
|
||||
}
|
||||
break;
|
||||
case OBJ_CLASS:
|
||||
{
|
||||
ObjClass* klass = (ObjClass*)object;
|
||||
markObject((Obj*)klass->name);
|
||||
markTable(&klass->methods);
|
||||
}
|
||||
break;
|
||||
case OBJ_CLOSURE:
|
||||
{
|
||||
ObjClosure* closure = (ObjClosure*)object;
|
||||
markObject((Obj*)closure->function);
|
||||
for (auto 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_INSTANCE:
|
||||
{
|
||||
ObjInstance* instance = (ObjInstance*)object;
|
||||
markObject((Obj*)instance->klass);
|
||||
markTable(&instance->fields);
|
||||
}
|
||||
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_BOUND_METHOD:
|
||||
FREE(ObjBoundMethod, object);
|
||||
break;
|
||||
case OBJ_CLASS:
|
||||
{
|
||||
ObjClass* klass = (ObjClass*)object;
|
||||
freeTable(&klass->methods);
|
||||
FREE(ObjClass, object);
|
||||
}
|
||||
break;
|
||||
case OBJ_CLOSURE:
|
||||
FREE(ObjClosure, object);
|
||||
break;
|
||||
case OBJ_FUNCTION:
|
||||
{
|
||||
ObjFunction* function = (ObjFunction*)object;
|
||||
freeChunk(&function->chunk);
|
||||
FREE(ObjFunction, object);
|
||||
}
|
||||
break;
|
||||
case OBJ_INSTANCE:
|
||||
{
|
||||
ObjInstance* instance = (ObjInstance*)object;
|
||||
freeTable(&instance->fields);
|
||||
FREE(ObjInstance, object);
|
||||
}
|
||||
break;
|
||||
case OBJ_NATIVE:
|
||||
FREE(ObjNative, object);
|
||||
break;
|
||||
case OBJ_STRING:
|
||||
{
|
||||
ObjString* string = (ObjString*)object;
|
||||
FREE_ARRAY(char, string->chars, string->length + 1);
|
||||
FREE(ObjString, object);
|
||||
}
|
||||
break;
|
||||
case OBJ_UPVALUE:
|
||||
FREE(ObjUpvalue, object);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void markRoots()
|
||||
{
|
||||
for (Value* slot = vm.stack; slot < vm.stackTop; slot++) {
|
||||
markValue(*slot);
|
||||
}
|
||||
|
||||
for (auto i = 0; i < vm.frameCount; i++) {
|
||||
markObject((Obj*)vm.frames[i].closure);
|
||||
}
|
||||
|
||||
for (ObjUpvalue* upvalue = vm.openUpvalues; upvalue != nullptr; upvalue = upvalue->next) {
|
||||
markObject((Obj*)upvalue);
|
||||
}
|
||||
|
||||
markTable(&vm.globals);
|
||||
markCompilerRoots();
|
||||
markObject((Obj*)vm.initString);
|
||||
}
|
||||
|
||||
static void traceReferences()
|
||||
{
|
||||
while (vm.grayCount > 0) {
|
||||
Obj* object = vm.grayStack[--vm.grayCount];
|
||||
blackenObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
static void sweep()
|
||||
{
|
||||
Obj* previous = nullptr;
|
||||
Obj* object = vm.objects;
|
||||
while (object != nullptr) {
|
||||
if (object->isMarked) {
|
||||
object->isMarked = false;
|
||||
previous = object;
|
||||
object = object->next;
|
||||
} else {
|
||||
Obj* unreached = object;
|
||||
object = object->next;
|
||||
if (previous != nullptr) {
|
||||
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
|
||||
}
|
||||
|
||||
void freeObjects()
|
||||
{
|
||||
Obj* object = vm.objects;
|
||||
while (object != nullptr) {
|
||||
Obj* next = object->next;
|
||||
freeObject(object);
|
||||
object = next;
|
||||
}
|
||||
|
||||
free(vm.grayStack);
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue