garbage collection implementations.

This commit is contained in:
Thakee Nathees 2021-04-25 20:49:39 +05:30
parent c601e5cf90
commit 34716b4f6b
8 changed files with 98 additions and 12 deletions

View File

@ -40,6 +40,18 @@ typedef struct MSVM MSVM;
typedef struct Var Var;
#endif
// A function that'll be called for all the allocation calls by MSVM.
//
// - To allocate new memory it'll pass NULL to parameter [memory] and the
// required size to [new_size]. On failure the return value would be NULL.
//
// - When reallocating an existing memory if it's grow in place the return
// address would be the same as [memory] otherwise a new address.
//
// - To free an allocated memory pass [memory] and 0 to [new_size]. The
// function will return NULL.
typedef void* (*MiniScriptReallocFn)(void* memory, size_t new_size, void* user_data);
// C function pointer which is callable from MiniScript.
typedef void (*MiniScriptNativeFn)(MSVM* vm);
@ -84,6 +96,10 @@ typedef void (*MiniScriptLoadScriptDoneFn) (MSVM* vm, const char* path,
typedef struct {
// The callback used to allocate, reallocate, and free. If the function
// pointer is NULL it defaults to the VM's realloc(), free() wrappers.
MiniScriptReallocFn realloc_fn;
MiniScriptErrorFn error_fn;
MiniScriptWriteFn write_fn;
@ -95,6 +111,10 @@ typedef struct {
} MSConfiguration;
// Initialize the configuration and set ALL of it's values to the defaults.
// Call this before setting any particular field of it.
void MSInitConfiguration(MSConfiguration* config);
typedef enum {
RESULT_SUCCESS = 0,
RESULT_COMPILE_ERROR,

View File

@ -40,6 +40,7 @@ def generate_files():
import buffergen
ec = buffergen.gen()
## .bat files are just for quick rebuild of the buffer templates in windows.
if sys.platform == 'win32':
with open('src/types/gen.bat', 'w') as f:
f.write('python buffergen.py')

View File

@ -268,7 +268,6 @@ void initializeCore(MSVM* vm) {
STD_NEW_SCRIPT("std:list");
STD_ADD_FUNCTION("sort", stdListSort, 1);
// std:os script.
STD_NEW_SCRIPT("std:os");
STD_ADD_FUNCTION("clock", stdOsClock, 0);
@ -408,7 +407,7 @@ Var varGetAttrib(MSVM* vm, Var on, String* attrib) {
int index = nameTableFind(&scr->function_names, attrib->data,
attrib->length);
if (index != -1) {
// TODO: Assert index (not a runtime error).
ASSERT_INDEX(index, scr->functions.count);
return VAR_OBJ(scr->functions.data[index]);
}
@ -492,7 +491,7 @@ do { \
return;
case OBJ_USER:
ERR_NO_ATTRIB();
TODO; //ERR_NO_ATTRIB();
return;
default:

View File

@ -9,6 +9,14 @@
#include "var.h"
#include "common.h"
// Initialize core language, builtin function and "std" scripts.
// Note (TODO: refactore required):
// Since the builtin function doesn't require any allocation they're
// elements of a static `builtins` array but the "std" scripts are `Script`
// objects they required memory management and they're bound with the VM.
// It contradicts `initializeCore()` to be called for each VM or only once.
// 1. Make the scripts share between VMs.
// 2. Destroy scripts buffer only when the last VM die.
void initializeCore(MSVM* vm);
// Find the builtin function name and returns it's index in the builtins array

View File

@ -17,6 +17,8 @@ void nameTableClear(NameTable* self, MSVM* vm) {
int nameTableAdd(NameTable* self, MSVM* vm, const char* name, size_t length,
String** ptr) {
// Note: Since stringBuffer won't copy the string we don't have to free the
// below string, it'll be managed by the string buffer.
String* string = newString(vm, name, (uint32_t)length);
vmPushTempRef(vm, &string->_super);

View File

@ -17,6 +17,14 @@
// Minimum size of the stack.
#define MIN_STACK_SIZE 128
static void* defaultRealloc(void* memory, size_t new_size, void* user_data) {
if (new_size == 0) {
free(memory);
return NULL;
}
return realloc(memory, new_size);
}
Fiber* newFiber(MSVM* vm) {
Fiber* fiber = ALLOCATE(vm, Fiber);
memset(fiber, 0, sizeof(Fiber));
@ -26,33 +34,43 @@ Fiber* newFiber(MSVM* vm) {
void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) {
// TODO: Debug trace allocations here.
// Track the total allocated memory of the VM to trigger the GC.
// if vmRealloc is called for freeing the old_size would be 0 since
// deallocated bytes are traced by garbage collector.
self->bytes_allocated += new_size - old_size;
// TODO: If vm->bytes_allocated > some_value -> GC();
if (new_size > 0 && self->bytes_allocated > self->next_gc) {
vmCollectGarbage(self);
}
if (new_size == 0) {
free(memory);
return NULL;
}
return realloc(memory, new_size);
return self->config.realloc_fn(memory, new_size, self->config.user_data);
}
void vmInit(MSVM* self, MSConfiguration* config) {
memset(self, 0, sizeof(MSVM));
self->config = *config;
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.
initializeCore(self);
}
void vmPushTempRef(MSVM* self, Object* obj) {
ASSERT(obj != NULL, "Cannot reference to NULL.");
ASSERT(self->temp_reference_count < MAX_TEMP_REFERENCE,
"Too many temp references");
ASSERT(self->temp_reference_count < MAX_TEMP_REFERENCE,
"Too many temp references");
self->temp_reference[self->temp_reference_count++] = obj;
}
@ -61,6 +79,15 @@ void vmPopTempRef(MSVM* self) {
self->temp_reference_count--;
}
void vmCollectGarbage(MSVM* self) {
// Reset VM's bytes_allocated value and count it again so that we don't
// required to know the size of each object that'll be freeing.
self->bytes_allocated = 0;
TODO;
}
void vmAddStdScript(MSVM* self, Script* script) {
ASSERT(self->std_count < MAX_SCRIPT_CACHE, OOPS);
self->std_scripts[self->std_count++] = script;
@ -127,6 +154,18 @@ void vmReportError(MSVM* vm) {
ASSERT(false, "TODO: create debug.h");
}
void MSInitConfiguration(MSConfiguration* config) {
config->realloc_fn = defaultRealloc;
// TODO: Handle Null functions before calling them.
config->error_fn = NULL;
config->write_fn = NULL;
config->load_script_fn = NULL;
config->load_script_done_fn = NULL;
config->user_data = NULL;
}
MSVM* msNewVM(MSConfiguration* config) {
MSVM* vm = (MSVM*)malloc(sizeof(MSVM));
vmInit(vm, config);
@ -137,7 +176,8 @@ MSInterpretResult msInterpret(MSVM* vm, const char* file) {
Script* script = compileSource(vm, file);
if (script == NULL) return RESULT_COMPILE_ERROR;
// TODO: Check if scripts size is enough.
// TODO: The below assertion should be an error report.
ASSERT(vm->script_count + 1 < MAX_SCRIPT_CACHE, "Scripts cache out of bound.");
vm->scripts[vm->script_count++] = script;
return vmRunScript(vm, script);
}

View File

@ -36,15 +36,15 @@ struct Fiber {
// body function).
Function* func;
// The stack of the execution holding locals and temps. A heap allocated
// Will and grow as needed.
// The stack of the execution holding locals and temps. A heap will be
// allocated and grow as needed.
Var* stack;
// The stack pointer (%rsp) pointing to the stack top.
Var* sp;
// The stack base pointer of the current frame. It'll be updated before
// calling a native function.
// calling a native function. (`fiber->ret` === `curr_call_frame->rbp`).
Var* ret;
// Size of the allocated stack.
@ -68,8 +68,18 @@ struct MSVM {
// The first object in the link list of all heap allocated objects.
Object* first;
// The number of bytes allocated by the vm and not (yet) garbage collected.
size_t bytes_allocated;
// The number of bytes that'll trigger the next GC.
size_t next_gc;
// 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** 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.
Object* temp_reference[MAX_TEMP_REFERENCE];
@ -81,7 +91,7 @@ struct MSVM {
// Current compiler reference to mark it's heap allocated objects.
Compiler* compiler;
// Std scripts array.
// Std scripts array. (TODO: assert "std" scripts doesn't have global vars).
Script* std_scripts[MAX_SCRIPT_CACHE];
// Std scripts count.
@ -122,6 +132,9 @@ void vmPushTempRef(MSVM* self, Object* obj);
// Pop the top most object from temporary reference stack.
void vmPopTempRef(MSVM* self);
// Trigger garbage collection manually.
void vmCollectGarbage(MSVM* self);
// Add a std script to vm when initializing core.
void vmAddStdScript(MSVM* self, Script* script);

View File

@ -18,6 +18,8 @@ void writeFunction(MSVM* vm, const char* text) {
}
void loadScriptDone(MSVM* vm, const char* path, void* user_data) {
// User data is the allocated source code buffer and it has to be freed
// manually since it wasn't allocated by the VM.
free(user_data);
}
@ -66,6 +68,7 @@ int main(int argc, char** argv) {
const char* source_path = argv[1];
MSConfiguration config;
MSInitConfiguration(&config);
config.error_fn = errorPrint;
config.write_fn = writeFunction;
config.load_script_fn = loadScript;