Initial commit

This commit is contained in:
ktkk 2026-01-13 19:52:23 +00:00
commit b2edca2cb4
25 changed files with 3590 additions and 0 deletions

269
memory.c Normal file
View 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);
}