From 7b804b88094680bcfa00f11ceba0833dcb8f9654 Mon Sep 17 00:00:00 2001 From: samsonium Date: Thu, 29 Oct 2026 05:21:00 +0300 Subject: [PATCH] feat: optimize runtime (2x+ faster) --- include/common/fzl_opcodes.h | 2 + include/compiler/utils/fzl_symtable.h | 8 ++++ include/vm/internals/opcode.h | 2 + src/compiler/fzl_walker.c | 53 +++++++++++++++-------- src/compiler/utils/fzl_symtable.c | 32 +++++++++++++- src/vm/fzl_vm_debug.c | 18 +++++++- src/vm/handlers/group_ctx.c | 61 +++++++++++++++++++++++++++ src/vm/handlers/master.c | 8 ++-- tests/programs/pos_local_slots.fzs | 30 +++++++++++++ 9 files changed, 189 insertions(+), 25 deletions(-) create mode 100644 tests/programs/pos_local_slots.fzs diff --git a/include/common/fzl_opcodes.h b/include/common/fzl_opcodes.h index 93a3b07..8603d3f 100644 --- a/include/common/fzl_opcodes.h +++ b/include/common/fzl_opcodes.h @@ -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 diff --git a/include/compiler/utils/fzl_symtable.h b/include/compiler/utils/fzl_symtable.h index 949c5d1..7be68f0 100644 --- a/include/compiler/utils/fzl_symtable.h +++ b/include/compiler/utils/fzl_symtable.h @@ -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); diff --git a/include/vm/internals/opcode.h b/include/vm/internals/opcode.h index 00d9af7..4a8d801 100644 --- a/include/vm/internals/opcode.h +++ b/include/vm/internals/opcode.h @@ -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 --- diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index 47e17e3..d7f34d6 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -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) { diff --git a/src/compiler/utils/fzl_symtable.c b/src/compiler/utils/fzl_symtable.c index b425962..640298a 100644 --- a/src/compiler/utils/fzl_symtable.c +++ b/src/compiler/utils/fzl_symtable.c @@ -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) diff --git a/src/vm/fzl_vm_debug.c b/src/vm/fzl_vm_debug.c index ceb1788..40ca17f 100644 --- a/src/vm/fzl_vm_debug.c +++ b/src/vm/fzl_vm_debug.c @@ -1,4 +1,4 @@ -#include "common/fzl_opcodes.h" +#include #include #include #include @@ -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; diff --git a/src/vm/handlers/group_ctx.c b/src/vm/handlers/group_ctx.c index 877c067..edbdc46 100644 --- a/src/vm/handlers/group_ctx.c +++ b/src/vm/handlers/group_ctx.c @@ -1,4 +1,7 @@ +#include +#include #include +#include #include // 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; +} diff --git a/src/vm/handlers/master.c b/src/vm/handlers/master.c index 60d6f12..e73fc24 100644 --- a/src/vm/handlers/master.c +++ b/src/vm/handlers/master.c @@ -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[] = { diff --git a/tests/programs/pos_local_slots.fzs b/tests/programs/pos_local_slots.fzs new file mode 100644 index 0000000..69c40e2 --- /dev/null +++ b/tests/programs/pos_local_slots.fzs @@ -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);