WIP functions working, untested, memory issues, read more

I've ripped out the deep copying, and flattened the bucket usage, but
this results in no memory being freed or reused for the lifetime of the
program.

This is shown most clearly with this script:

```toy
fn makeCounter() {
	var counter: int = 0;

	fn increment() {
		return ++counter;
	}

	return increment;
}

var tally = makeCounter();

while (true) {
	var result = tally();

	if (result >= 10_000_000) {
		break;
	}
}
```

The number of calls vs amount of memory consumed is:

```
function calls -> memory used in megabytes
1_000 -> 0.138128
10_000 -> 2.235536
100_000 -> 21.7021
1_000_000 -> 216.1712
10_000_000 -> 1520.823
```

Obviously this needs to be fixed, as ballooning to gigabytes of memory
in only a moment isn't practical. That will be the next task - to find
some way to free memory that isn't needed anymore.

See #163, #160
This commit is contained in:
2025-02-21 10:48:24 +11:00
parent 9fe6d6b218
commit d3b59eb0da
8 changed files with 42 additions and 167 deletions

View File

@@ -73,12 +73,12 @@ static void processRead(Toy_VM* vm) {
//build a string from the data section
if (stringType == TOY_STRING_LEAF) {
value = TOY_VALUE_FROM_STRING(Toy_createString(&vm->literalBucket, cstring));
value = TOY_VALUE_FROM_STRING(Toy_createString(&vm->memoryBucket, cstring));
}
else if (stringType == TOY_STRING_NAME) {
Toy_ValueType valueType = TOY_VALUE_UNKNOWN;
value = TOY_VALUE_FROM_STRING(Toy_createNameStringLength(&vm->literalBucket, cstring, len, valueType, false));
value = TOY_VALUE_FROM_STRING(Toy_createNameStringLength(&vm->memoryBucket, cstring, len, valueType, false));
}
else {
Toy_error("Invalid string type found in opcode read");
@@ -162,10 +162,10 @@ static void processRead(Toy_VM* vm) {
unsigned int addr = (unsigned int)READ_INT(vm);
Toy_Module module = Toy_parseModule(vm->code + vm->subsAddr + addr);
module.parentScope = Toy_private_pushDummyScope(&vm->scopeBucket, vm->scope);
module.parentScope = Toy_private_pushDummyScope(&vm->memoryBucket, vm->scope);
//create and push the function value
Toy_Function* function = Toy_createModuleFunction(&vm->literalBucket, module);
Toy_Function* function = Toy_createModuleFunction(&vm->memoryBucket, module);
value = TOY_VALUE_FROM_FUNCTION(function);
break;
@@ -210,7 +210,7 @@ static void processDeclare(Toy_VM* vm) {
char* cstring = (char*)(vm->code + vm->dataAddr + jump);
//build the name string
Toy_String* name = Toy_createNameStringLength(&vm->literalBucket, cstring, len, type, constant);
Toy_String* name = Toy_createNameStringLength(&vm->memoryBucket, cstring, len, type, constant);
//get the value
Toy_Value value = Toy_popStack(&vm->stack);
@@ -430,7 +430,7 @@ static void processInvoke(Toy_VM* vm) {
const char* cstr = ((char*)(module.code + module.dataAddr)) + paramAddr;
//as a name string
Toy_String* name = Toy_createNameStringLength(&subVM.literalBucket, cstr, strlen(cstr), paramType, true);
Toy_String* name = Toy_createNameStringLength(&subVM.memoryBucket, cstr, strlen(cstr), paramType, true);
Toy_declareScope(subVM.scope, name, argValue);
}
@@ -440,7 +440,7 @@ static void processInvoke(Toy_VM* vm) {
//extract and store any results
if (resultCount > 0) {
Toy_Array* results = Toy_extractResultsFromVM(&vm->scopeBucket, &vm->literalBucket, &subVM, resultCount);
Toy_Array* results = Toy_extractResultsFromVM(&subVM, resultCount);
for (unsigned int i = 0; i < results->count; i++) {
//NOTE: since the results array is being immediately freed, just push each element without a call to copy
@@ -723,7 +723,7 @@ static void processAssert(Toy_VM* vm) {
//determine the args
if (count == 1) {
message = TOY_VALUE_FROM_STRING(Toy_createString(&vm->literalBucket, "assertion failed")); //TODO: needs a better default message
message = TOY_VALUE_FROM_STRING(Toy_createString(&vm->memoryBucket, "assertion failed")); //TODO: needs a better default message
value = Toy_popStack(&vm->stack);
}
else if (count == 2) {
@@ -738,7 +738,7 @@ static void processAssert(Toy_VM* vm) {
//do the check
if (TOY_VALUE_IS_NULL(value) || Toy_checkValueIsTruthy(value) != true) {
//on a failure, print the message
Toy_String* string = Toy_stringifyValue(&vm->literalBucket, message);
Toy_String* string = Toy_stringifyValue(&vm->memoryBucket, message);
char* buffer = Toy_getStringRawBuffer(string);
Toy_assertFailure(buffer);
@@ -756,7 +756,7 @@ static void processAssert(Toy_VM* vm) {
static void processPrint(Toy_VM* vm) {
//print the value on top of the stack, popping it
Toy_Value value = Toy_popStack(&vm->stack);
Toy_String* string = Toy_stringifyValue(&vm->literalBucket, value);
Toy_String* string = Toy_stringifyValue(&vm->memoryBucket, value);
char* buffer = Toy_getStringRawBuffer(string); //TODO: check string type to skip this call
Toy_print(buffer);
@@ -779,7 +779,7 @@ static void processConcat(Toy_VM* vm) {
}
//all good
Toy_String* result = Toy_concatStrings(&vm->literalBucket, TOY_VALUE_AS_STRING(left), TOY_VALUE_AS_STRING(right));
Toy_String* result = Toy_concatStrings(&vm->memoryBucket, TOY_VALUE_AS_STRING(left), TOY_VALUE_AS_STRING(right));
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_STRING(result));
}
@@ -847,11 +847,11 @@ static void processIndex(Toy_VM* vm) {
//extract cstring, based on type
if (str->info.type == TOY_STRING_LEAF) {
const char* cstr = str->leaf.data;
result = Toy_createStringLength(&vm->literalBucket, cstr + i, l);
result = Toy_createStringLength(&vm->memoryBucket, cstr + i, l);
}
else if (str->info.type == TOY_STRING_NODE) {
char* cstr = Toy_getStringRawBuffer(str);
result = Toy_createStringLength(&vm->literalBucket, cstr + i, l);
result = Toy_createStringLength(&vm->memoryBucket, cstr + i, l);
free(cstr);
}
else {
@@ -1031,7 +1031,7 @@ static unsigned int process(Toy_VM* vm) {
break;
case TOY_OPCODE_SCOPE_PUSH:
vm->scope = Toy_pushScope(&vm->scopeBucket, vm->scope);
vm->scope = Toy_pushScope(&vm->memoryBucket, vm->scope);
break;
case TOY_OPCODE_SCOPE_POP:
@@ -1095,10 +1095,9 @@ void Toy_initVM(Toy_VM* vm) {
//create persistent memory
vm->scope = NULL;
vm->stack = Toy_allocateStack();
vm->literalBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
vm->scopeBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
vm->memoryBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
vm->scopeBucketHandle = NULL; //not used
vm->parentBucketHandle = NULL;
Toy_resetVM(vm, true);
}
@@ -1107,10 +1106,9 @@ void Toy_inheritVM(Toy_VM* vm, Toy_VM* parent) {
//inherent persistent memory
vm->scope = NULL;
vm->stack = Toy_allocateStack();
vm->literalBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
vm->scopeBucket = parent->scopeBucket;
vm->memoryBucket = parent->memoryBucket;
vm->scopeBucketHandle = &parent->scopeBucket; //track this to update it later
vm->parentBucketHandle = &parent->memoryBucket; //track this to update it later
Toy_resetVM(vm, true);
}
@@ -1130,7 +1128,7 @@ void Toy_bindVM(Toy_VM* vm, Toy_Module* module, bool preserveScope) {
vm->subsAddr = module->subsAddr;
if (preserveScope == false) {
vm->scope = Toy_pushScope(&vm->scopeBucket, module->parentScope);
vm->scope = Toy_pushScope(&vm->memoryBucket, module->parentScope);
}
}
@@ -1152,17 +1150,16 @@ void Toy_freeVM(Toy_VM* vm) {
//clear the persistent memory
Toy_freeStack(vm->stack);
Toy_freeBucket(&vm->literalBucket);
if (vm->scopeBucketHandle != NULL) {
*(vm->scopeBucketHandle) = vm->scopeBucket; //re-adjust the parent's scopeBucket pointer, in case it was expanded
if (vm->parentBucketHandle != NULL) {
*(vm->parentBucketHandle) = vm->memoryBucket; //update the outter VM, if there is one
}
else {
Toy_freeBucket(&vm->scopeBucket);
Toy_freeBucket(&vm->memoryBucket);
}
}
Toy_Array* Toy_extractResultsFromVM(Toy_Bucket** scopeBucketHandle, Toy_Bucket** literalBucketHandle, Toy_VM* subVM, unsigned int resultCount) {
Toy_Array* Toy_extractResultsFromVM(Toy_VM* subVM, unsigned int resultCount) {
if (subVM->stack->count < resultCount) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Too many results requested from VM, exiting\n" TOY_CC_RESET);
exit(-1);
@@ -1173,7 +1170,7 @@ Toy_Array* Toy_extractResultsFromVM(Toy_Bucket** scopeBucketHandle, Toy_Bucket**
const unsigned int offset = subVM->stack->count - resultCount; //first element to extract
for (/* EMPTY */; results->count < resultCount; results->count++) {
results->data[results->count] = Toy_private_deepCopyValue(scopeBucketHandle, literalBucketHandle, subVM->stack->data[offset + results->count]);
results->data[results->count] = Toy_copyValue(subVM->stack->data[offset + results->count]);
}
return results;