feat: optimize runtime (2x+ faster)

This commit is contained in:
samsonium
2026-10-29 05:21:00 +03:00
parent eeca5e653d
commit 7b804b8809
9 changed files with 189 additions and 25 deletions
+2
View File
@@ -14,6 +14,8 @@ typedef enum
FZL_OPCODE_STORE = 0xF10003, // Push variable to environment table
FZL_OPCODE_LOOKUP_ENV = 0xF10004, // Lookup name in environment table
FZL_OPCODE_WRITE = 0xF10005, // Wirte into variable
FZL_OPCODE_LOAD_LOCAL = 0xF10006, // Load local variable
FZL_OPCODE_STORE_LOCAL = 0xF10007, // Store local variable
FZL_OPCODE_JMP = 0xF20001, // Jump to specified IP address
FZL_OPCODE_JZ = 0xF20002, // Jump if top value in dstack is zero
+8
View File
@@ -23,6 +23,9 @@ typedef struct
fzl_node_t *node;
size_t ip;
bool slot_set;
size_t slot;
char *classname;
} fzl_sym_t;
@@ -32,6 +35,8 @@ typedef struct
size_t size;
size_t remainder;
size_t free_slot;
fzl_sym_t *symbols;
} fzl_symtable_t;
@@ -46,6 +51,9 @@ fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, fzl_node_t *node,
fzl_symtable_res_t fzl_symt_setobjclass(fzl_symtable_t *symt, char *name,
char *classname);
// Assign a local slot to a variable
int fzl_symt_setslot(fzl_symtable_t *symt, char *name);
// Get entry from specified symtable
bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res);
+2
View File
@@ -32,6 +32,8 @@ int op_rm_envtable(fzl_vm_t *vm);
int op_store(fzl_vm_t *vm);
int op_lookup_env(fzl_vm_t *vm);
int op_write(fzl_vm_t *vm);
int op_load_local(fzl_vm_t *vm);
int op_store_local(fzl_vm_t *vm);
// --- flow ---
+35 -18
View File
@@ -855,14 +855,10 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
case FZL_NODETYPE_IDENTIFIER:
{
size_t n = add_const(
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING,
.as.string.chars = node->token->value,
.as.string.length = node->token->value_sz});
fzl_sym_t sym;
bool exists = false;
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
ssize_t i = walker->sym_top - 1;
for (; i >= 0; i--)
if ((exists =
fzl_symt_get(walker->syms[i], node->token->value, &sym)))
break;
@@ -876,9 +872,17 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
}
push_op(walker, FZL_OPCODE_LOADCONST);
push_word(walker, n);
push_op(walker, FZL_OPCODE_LOOKUP_ENV);
if (!sym.slot_set)
{
snprintf(wlast_err, sizeof(wlast_err),
"internal error: local '%s' has no slot", sym.name);
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
}
size_t depth = walker->sym_top - 1 - i;
push_op(walker, FZL_OPCODE_LOAD_LOCAL);
push_word(walker, depth);
push_word(walker, sym.slot);
current_res = (fzl_walk_res_t){
.type = sym.type, .error = false, .classname = sym.classname};
@@ -1239,6 +1243,15 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
}
if (fzl_symt_setslot(walker->syms[walker->sym_top - 1],
node->var.identifier->token->value) < 0)
{
snprintf(wlast_err, sizeof(wlast_err),
"failed to assign a local slot to '%s'",
(char *)node->var.identifier->token->value);
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
}
fzl_token_t *id_tok = node->var.identifier->token;
size_t const_n = add_const(
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING,
@@ -1283,7 +1296,8 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
{
fzl_sym_t sym;
bool found = false;
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
ssize_t i = walker->sym_top - 1;
for (; i >= 0; i--)
if ((found =
fzl_symt_get(walker->syms[i],
node->binary_op.left->token->value, &sym)))
@@ -1308,14 +1322,17 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
}
size_t const_n = add_const(
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING,
.as.string.chars = sym.name,
.as.string.length =
node->binary_op.left->token->value_sz});
push_op(walker, FZL_OPCODE_LOADCONST);
push_word(walker, const_n);
push_op(walker, FZL_OPCODE_WRITE);
if (!sym.slot_set)
{
snprintf(wlast_err, sizeof(wlast_err),
"internal error: local '%s' has no slot", sym.name);
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
}
size_t depth = walker->sym_top - 1 - i;
push_op(walker, FZL_OPCODE_STORE_LOCAL);
push_word(walker, depth);
push_word(walker, sym.slot);
}
else if (node->binary_op.left->type == FZL_NODETYPE_FIELD)
{
+30 -2
View File
@@ -13,6 +13,7 @@ fzl_symtable_t *fzl_mksymt()
if (!tbl)
return NULL;
tbl->free_slot = 0;
tbl->remainder = 2;
tbl->size = 0;
tbl->symbols = malloc(tbl->remainder * sizeof(fzl_sym_t));
@@ -38,8 +39,13 @@ fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, fzl_node_t *node,
symt->symbols = realloc(symt->symbols, symt->remainder * sizeof(fzl_sym_t));
}
symt->symbols[symt->size++] = (fzl_sym_t){
.name = name, .node = node, .type = type, .ip = ip, .classname = NULL};
symt->symbols[symt->size++] = (fzl_sym_t){.name = name,
.node = node,
.type = type,
.ip = ip,
.classname = NULL,
.slot_set = false,
.slot = 0};
return FZL_SYMTABLE_RESULT_OK;
}
@@ -64,6 +70,28 @@ fzl_symtable_res_t fzl_symt_setobjclass(fzl_symtable_t *symt, char *name,
return FZL_SYMTABLE_RESULT_ERR;
}
int fzl_symt_setslot(fzl_symtable_t *symt, char *name)
{
if (!symt)
return -1;
for (size_t i = 0; i < symt->size; i++)
{
if (strcmp(symt->symbols[i].name, name) == 0)
{
if (symt->symbols[i].slot_set)
return symt->symbols[i].slot;
symt->symbols[i].slot = symt->free_slot++;
symt->symbols[i].slot_set = true;
return symt->symbols[i].slot;
}
}
return -1;
}
bool fzl_symt_get(fzl_symtable_t *symt, char *name, fzl_sym_t *res)
{
if (!symt)
+17 -1
View File
@@ -1,4 +1,4 @@
#include "common/fzl_opcodes.h"
#include <common/fzl_opcodes.h>
#include <stdbool.h>
#include <stdio.h>
#include <vm/fzl_vm.h>
@@ -20,6 +20,22 @@ void fzl_vm_print_ops(fzl_vm_t *vm)
switch (vm->ops[i])
{
case FZL_OPCODE_STORE_LOCAL:
{
size_t depth = vm->ops[++i];
size_t slot = vm->ops[++i];
printf("STORE_LOCAL D=%zu S=%zu\n", depth, slot);
break;
}
case FZL_OPCODE_LOAD_LOCAL:
{
size_t depth = vm->ops[++i];
size_t slot = vm->ops[++i];
printf("LOAD_LOCAL D=%zu S=%zu\n", depth, slot);
break;
}
case FZL_OPCODE_BUILD_STR:
printf("BUILD_STR %zu\n", vm->ops[++i]);
break;
+61
View File
@@ -1,4 +1,7 @@
#include <common/fzl_value.h>
#include <stdio.h>
#include <vm/fzl_vm.h>
#include <vm/fzl_vm_env.h>
#include <vm/internals/opcode.h>
// Allocate environment table and increment scope level
@@ -125,3 +128,61 @@ int op_write(fzl_vm_t *vm)
return 0;
}
int op_load_local(fzl_vm_t *vm)
{
size_t depth = vm->ops[++vm->ip];
size_t slot = vm->ops[++vm->ip];
if (depth >= vm->etop)
{
snprintf(last_err, sizeof(last_err), "runtime error - local depth out of range");
print_err_data(vm);
return -1;
}
fzl_vm_env_t *env = vm->estack[vm->etop - 1 - depth];
if (env->size <= slot)
{
snprintf(last_err, sizeof(last_err), "runtime error - local slot out of range");
print_err_data(vm);
return -1;
}
vm->dstack[vm->dtop++] = env->table[slot].value;
return 0;
}
int op_store_local(fzl_vm_t *vm)
{
if (vm->dtop < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - expected value in data stack");
print_err_data(vm);
return -1;
}
fzl_value_t value = vm->dstack[--vm->dtop];
size_t depth = vm->ops[++vm->ip];
size_t slot = vm->ops[++vm->ip];
if (depth >= vm->etop)
{
snprintf(last_err, sizeof(last_err), "runtime error - local depth out of range");
print_err_data(vm);
return -1;
}
fzl_vm_env_t *env = vm->estack[vm->etop - 1 - depth];
if (env->size <= slot)
{
snprintf(last_err, sizeof(last_err), "runtime error - local slot out of range");
print_err_data(vm);
return -1;
}
env->table[slot].value = value;
return 0;
}
+4 -4
View File
@@ -9,10 +9,10 @@ const opcode_handler group_stack[] = {
};
// Context-relative opcodes group
const opcode_handler group_ctx[] = {
[1] = op_mk_envtable, [2] = op_rm_envtable, [3] = op_store,
[4] = op_lookup_env, [5] = op_write,
};
const opcode_handler group_ctx[] = {[1] = op_mk_envtable, [2] = op_rm_envtable,
[3] = op_store, [4] = op_lookup_env,
[5] = op_write, [6] = op_load_local,
[7] = op_store_local};
// Flow-relative opcodes group
const opcode_handler group_flow[] = {
+30
View File
@@ -0,0 +1,30 @@
// Slot indices follow declaration order, not first-use order.
uint first = 1;
uint second = 2;
second = second + 1;
first = first + second;
if (first != 4)
exit(1);
// Shadowed locals use distinct slots in distinct environments.
uint value = 10;
{
uint value = 20;
value = value + 1;
if (value != 21)
exit(1);
}
if (value != 10)
exit(1);
// Access an outer local from nested loop/body scopes.
uint total = 0;
for (uint i = 0; i < 4; i = i + 1)
{
{
uint nested = i;
total = total + nested;
}
}
if (total != 6)
exit(1);