mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
reachability analysis implemented
This commit is contained in:
parent
8f52c6d54e
commit
d901b7fc7e
@ -1804,11 +1804,11 @@ Script* compileSource(MSVM* vm, const char* path) {
|
||||
void compilerMarkObjects(Compiler* compiler, MSVM* vm) {
|
||||
|
||||
// Mark the script which is currently being compiled.
|
||||
markObject(&compiler->script->_super, vm);
|
||||
grayObject(&compiler->script->_super, vm);
|
||||
|
||||
// Mark the string literals (they haven't added to the script's literal
|
||||
// buffer yet).
|
||||
markValue(compiler->parser.current.value, vm);
|
||||
markValue(compiler->parser.previous.value, vm);
|
||||
markValue(compiler->parser.next.value, vm);
|
||||
grayValue(compiler->parser.current.value, vm);
|
||||
grayValue(compiler->parser.previous.value, vm);
|
||||
grayValue(compiler->parser.next.value, vm);
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ void initializeCore(MSVM* vm) {
|
||||
|
||||
void markCoreObjects(MSVM* vm) {
|
||||
for (int i = 0; i < builtins_count; i++) {
|
||||
markObject(&builtins[i].fn->_super, vm);
|
||||
grayObject(&builtins[i].fn->_super, vm);
|
||||
}
|
||||
}
|
||||
|
||||
|
151
src/var.c
151
src/var.c
@ -45,26 +45,155 @@ void varInitObject(Object* self, MSVM* vm, ObjectType type) {
|
||||
vm->first = self;
|
||||
}
|
||||
|
||||
void markObject(Object* self, MSVM* vm) {
|
||||
void grayObject(Object* self, MSVM* vm) {
|
||||
if (self == NULL || self->is_marked) return;
|
||||
self->is_marked = true;
|
||||
|
||||
// Add the object to the VM's gray_list so that we can recursively mark
|
||||
// it's referenced objects later.
|
||||
if (vm->marked_list_count >= vm->marked_list_capacity) {
|
||||
vm->marked_list_capacity *= 2;
|
||||
vm->marked_list = (Object**)vm->config.realloc_fn(
|
||||
vm->marked_list,
|
||||
vm->marked_list_capacity * sizeof(Object*),
|
||||
if (vm->gray_list_count >= vm->gray_list_capacity) {
|
||||
vm->gray_list_capacity *= 2;
|
||||
vm->gray_list = (Object**)vm->config.realloc_fn(
|
||||
vm->gray_list,
|
||||
vm->gray_list_capacity * sizeof(Object*),
|
||||
vm->config.user_data);
|
||||
}
|
||||
|
||||
vm->marked_list[vm->marked_list_count++] = self;
|
||||
vm->gray_list[vm->gray_list_count++] = self;
|
||||
}
|
||||
|
||||
void markValue(Var self, MSVM* vm) {
|
||||
void grayValue(Var self, MSVM* vm) {
|
||||
if (!IS_OBJ(self)) return;
|
||||
markObject(AS_OBJ(self), vm);
|
||||
grayObject(AS_OBJ(self), vm);
|
||||
}
|
||||
|
||||
void grayVarBuffer(VarBuffer* self, MSVM* vm) {
|
||||
for (int i = 0; i < self->count; i++) {
|
||||
grayValue(self->data[i], vm);
|
||||
}
|
||||
}
|
||||
|
||||
void grayStringBuffer(StringBuffer* self, MSVM* vm) {
|
||||
for (int i = 0; i < self->count; i++) {
|
||||
grayObject((Object*)self->data[i], vm);
|
||||
}
|
||||
}
|
||||
|
||||
void grayFunctionBuffer(FunctionBuffer* self, MSVM* vm) {
|
||||
for (int i = 0; i < self->count; i++) {
|
||||
grayObject((Object*)self->data[i], vm);
|
||||
}
|
||||
}
|
||||
|
||||
void grayNameTable(NameTable* self, MSVM* vm) {
|
||||
for (int i = 0; i < self->count; i++) {
|
||||
grayObject((Object*)self->data[i], vm);
|
||||
}
|
||||
}
|
||||
|
||||
static void blackenObject(Object* obj, MSVM* vm) {
|
||||
// TODO: trace here.
|
||||
|
||||
switch (obj->type) {
|
||||
case OBJ_STRING: {
|
||||
vm->bytes_allocated += sizeof(String);
|
||||
vm->bytes_allocated += (size_t)(((String*)obj)->length + 1);
|
||||
} break;
|
||||
|
||||
case OBJ_LIST: {
|
||||
List* list = (List*)obj;
|
||||
grayVarBuffer(&list->elements, vm);
|
||||
vm->bytes_allocated += sizeof(List);
|
||||
vm->bytes_allocated += sizeof(Var) * list->elements.capacity;
|
||||
} break;
|
||||
|
||||
case OBJ_MAP: {
|
||||
TODO;
|
||||
} break;
|
||||
|
||||
case OBJ_RANGE: {
|
||||
vm->bytes_allocated += sizeof(Range);
|
||||
} break;
|
||||
|
||||
case OBJ_SCRIPT:
|
||||
{
|
||||
Script* script = (Script*)obj;
|
||||
vm->bytes_allocated += sizeof(Script);
|
||||
|
||||
const int NT_ELEM_SIZE = sizeof(*script->global_names.data);
|
||||
|
||||
grayVarBuffer(&script->globals, vm);
|
||||
vm->bytes_allocated += sizeof(Var) * script->globals.capacity;
|
||||
|
||||
grayNameTable(&script->global_names, vm);
|
||||
vm->bytes_allocated += NT_ELEM_SIZE * script->global_names.capacity;
|
||||
|
||||
grayVarBuffer(&script->literals, vm);
|
||||
vm->bytes_allocated += sizeof(Var) * script->literals.capacity;
|
||||
|
||||
grayFunctionBuffer(&script->functions, vm);
|
||||
vm->bytes_allocated += sizeof(Function*) * script->functions.capacity;
|
||||
|
||||
grayNameTable(&script->function_names, vm);
|
||||
vm->bytes_allocated += NT_ELEM_SIZE * script->function_names.capacity;
|
||||
|
||||
grayStringBuffer(&script->names, vm);
|
||||
vm->bytes_allocated += sizeof(String*) * script->names.capacity;
|
||||
|
||||
grayObject((Object*)script->body, vm);
|
||||
} break;
|
||||
|
||||
case OBJ_FUNC:
|
||||
{
|
||||
Function* func = (Function*)obj;
|
||||
vm->bytes_allocated += sizeof(Function);
|
||||
|
||||
grayObject((Object*)func->owner, vm);
|
||||
|
||||
if (!func->is_native) {
|
||||
Fn* fn = func->fn;
|
||||
vm->bytes_allocated += sizeof(uint8_t)* fn->opcodes.capacity;
|
||||
vm->bytes_allocated += sizeof(int) * fn->oplines.capacity;
|
||||
}
|
||||
} break;
|
||||
|
||||
case OBJ_FIBER:
|
||||
{
|
||||
Fiber* fiber = (Fiber*)obj;
|
||||
vm->bytes_allocated += sizeof(Fiber);
|
||||
|
||||
grayObject((Object*)fiber->func, vm);
|
||||
|
||||
// Blacken the stack.
|
||||
for (Var* local = fiber->stack; local < fiber->sp; local++) {
|
||||
grayValue(*local, vm);
|
||||
}
|
||||
vm->bytes_allocated += sizeof(Var) * fiber->stack_size;
|
||||
|
||||
// Blacken call frames.
|
||||
for (int i = 0; i < fiber->frame_count; i++) {
|
||||
grayObject((Object*)fiber->frames[i].fn, vm);
|
||||
grayObject((Object*)fiber->frames[i].fn->owner, vm);
|
||||
}
|
||||
vm->bytes_allocated += sizeof(CallFrame) * fiber->frame_capacity;
|
||||
|
||||
grayObject((Object*)fiber->error, vm);
|
||||
|
||||
} break;
|
||||
|
||||
case OBJ_USER:
|
||||
TODO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void blackenObjects(MSVM* vm) {
|
||||
while (vm->gray_list_count > 0) {
|
||||
// Pop the gray object from the list.
|
||||
Object* gray = vm->gray_list[--vm->gray_list_count];
|
||||
blackenObject(gray, vm);
|
||||
}
|
||||
}
|
||||
|
||||
#if VAR_NAN_TAGGING
|
||||
@ -99,7 +228,7 @@ double varToDouble(Var value) {
|
||||
static String* allocateString(MSVM* vm, size_t length) {
|
||||
String* string = ALLOCATE_DYNAMIC(vm, String, length + 1, char);
|
||||
varInitObject(&string->_super, vm, OBJ_STRING);
|
||||
string->length = length;
|
||||
string->length = (uint32_t)length;
|
||||
string->data[length] = '\0';
|
||||
return string;
|
||||
}
|
||||
@ -138,7 +267,7 @@ Script* newScript(MSVM* vm) {
|
||||
varInitObject(&script->_super, vm, OBJ_SCRIPT);
|
||||
|
||||
script->name = NULL;
|
||||
script->path = NULL;
|
||||
// TODO: script->path = NULL;
|
||||
|
||||
varBufferInit(&script->globals);
|
||||
nameTableInit(&script->global_names);
|
||||
|
34
src/var.h
34
src/var.h
@ -195,6 +195,7 @@ typedef enum /* ObjectType */ {
|
||||
|
||||
OBJ_FIBER,
|
||||
|
||||
// TODO: remove OBJ_USER and implement handlers for that.
|
||||
OBJ_USER,
|
||||
} ObjectType;
|
||||
|
||||
@ -245,10 +246,13 @@ struct Script {
|
||||
// One of the below is null and other one is not. Since "std" script names
|
||||
// are hardcoded and user script names are constructed.
|
||||
const char* name; //< Std script's name. Null for user script.
|
||||
String* path; //< Absolute path of the script. Null for std scripts.
|
||||
|
||||
ID imports[MAX_IMPORT_SCRIPTS]; //< Imported script IDs.
|
||||
int import_count; //< Number of import in imports.
|
||||
// TODO:
|
||||
// String* path; //< Absolute path of the script. Null for std scripts.
|
||||
|
||||
// TODO: maybe imported scripts are stored in var buffer and not here.
|
||||
// ID imports[MAX_IMPORT_SCRIPTS]; //< Imported script IDs.
|
||||
// int import_count; //< Number of import in imports.
|
||||
|
||||
VarBuffer globals; //< Script level global variables.
|
||||
NameTable global_names; //< Name map to index in globals.
|
||||
@ -328,11 +332,31 @@ void varInitObject(Object* self, MSVM* vm, ObjectType type);
|
||||
|
||||
// Mark the reachable objects at the mark-and-sweep phase of the garbage
|
||||
// collection.
|
||||
void markObject(Object* self, MSVM* vm);
|
||||
void grayObject(Object* self, MSVM* vm);
|
||||
|
||||
// Mark the reachable values at the mark-and-sweep phase of the garbage
|
||||
// collection.
|
||||
void markValue(Var self, MSVM* vm);
|
||||
void grayValue(Var self, MSVM* vm);
|
||||
|
||||
// Mark the elements of the buffer as reachable at the mark-and-sweep pahse of
|
||||
// the garbage collection.
|
||||
void grayVarBuffer(VarBuffer* self, MSVM* vm);
|
||||
|
||||
// Mark the elements of the buffer as reachable at the mark-and-sweep pahse of
|
||||
// the garbage collection.
|
||||
void grayStringBuffer(StringBuffer* self, MSVM* vm);
|
||||
|
||||
// Mark the elements of the buffer as reachable at the mark-and-sweep pahse of
|
||||
// the garbage collection.
|
||||
void grayFunctionBuffer(FunctionBuffer* self, MSVM* vm);
|
||||
|
||||
// Mark the elements of the name table as reachable at the mark-and-sweep pahse
|
||||
// of the garbage collection.
|
||||
void grayNameTable(NameTable* self, MSVM* vm);
|
||||
|
||||
// Pop objects from the gray list and add it's referenced objects to the
|
||||
// working list to traverse and update the vm's [bytes_allocated] value.
|
||||
void blackenObjects(MSVM* vm);
|
||||
|
||||
// Instead use VAR_NUM(value) and AS_NUM(value)
|
||||
Var doubleToVar(double value);
|
||||
|
24
src/vm.c
24
src/vm.c
@ -74,8 +74,8 @@ void msFreeVM(MSVM* self) {
|
||||
obj = next;
|
||||
}
|
||||
|
||||
self->marked_list = (Object**)self->config.realloc_fn(
|
||||
self->marked_list, 0, self->config.user_data);
|
||||
self->gray_list = (Object**)self->config.realloc_fn(
|
||||
self->gray_list, 0, self->config.user_data);
|
||||
self->config.realloc_fn(self, 0, self->config.user_data);
|
||||
}
|
||||
|
||||
@ -83,10 +83,10 @@ void vmInit(MSVM* self, MSConfiguration* config) {
|
||||
memset(self, 0, sizeof(MSVM));
|
||||
self->config = *config;
|
||||
|
||||
self->marked_list_count = 0;
|
||||
self->marked_list_capacity = 8; // TODO: refactor the magic '8' here.
|
||||
self->marked_list = (Object**)self->config.realloc_fn(
|
||||
NULL, sizeof(Object*) * self->marked_list_capacity, NULL);
|
||||
self->gray_list_count = 0;
|
||||
self->gray_list_capacity = 8; // TODO: refactor the magic '8' here.
|
||||
self->gray_list = (Object**)self->config.realloc_fn(
|
||||
NULL, sizeof(Object*) * self->gray_list_capacity, NULL);
|
||||
self->next_gc = 1024 * 1024 * 10; // TODO:
|
||||
|
||||
// TODO: no need to initialize if already done by another vm.
|
||||
@ -116,12 +116,12 @@ void vmCollectGarbage(MSVM* self) {
|
||||
|
||||
// Mark all the 'std' scripts.
|
||||
for (int i = 0; i < self->std_count; i++) {
|
||||
markObject(&(self->std_scripts[i]->_super), self);
|
||||
grayObject(&(self->std_scripts[i]->_super), self);
|
||||
}
|
||||
|
||||
// Mark temp references.
|
||||
for (int i = 0; i < self->temp_reference_count; i++) {
|
||||
markObject(self->temp_reference[i], self);
|
||||
grayObject(self->temp_reference[i], self);
|
||||
}
|
||||
|
||||
// Garbage collection triggered at the middle of a compilation.
|
||||
@ -131,14 +131,16 @@ void vmCollectGarbage(MSVM* self) {
|
||||
|
||||
// Garbage collection triggered at the middle of runtime.
|
||||
if (self->script != NULL) {
|
||||
markObject(&self->script->_super, self);
|
||||
grayObject(&self->script->_super, self);
|
||||
}
|
||||
|
||||
if (self->fiber != NULL) {
|
||||
markObject(&self->fiber->_super, self);
|
||||
grayObject(&self->fiber->_super, self);
|
||||
}
|
||||
|
||||
TODO;
|
||||
blackenObjects(self);
|
||||
|
||||
TODO; // Sweep.
|
||||
}
|
||||
|
||||
void vmAddStdScript(MSVM* self, Script* script) {
|
||||
|
6
src/vm.h
6
src/vm.h
@ -36,9 +36,9 @@ struct MSVM {
|
||||
|
||||
// In the tri coloring scheme gray is the working list. We recursively pop
|
||||
// from the list color it balck and add it's referenced objects to gray_list.
|
||||
Object** marked_list;
|
||||
int marked_list_count;
|
||||
int marked_list_capacity;
|
||||
Object** gray_list;
|
||||
int gray_list_count;
|
||||
int gray_list_capacity;
|
||||
|
||||
// A stack of temporary object references to ensure that the object
|
||||
// doesn't garbage collected.
|
||||
|
Loading…
Reference in New Issue
Block a user