From 50013028557b6a590c74c41dccc3e11704a702f9 Mon Sep 17 00:00:00 2001 From: Andre Wagner Date: Sun, 17 May 2026 10:03:02 -0500 Subject: [PATCH] . --- TODO.md | 6 ++-- lib/code.c | 12 +++++++ lib/compiler/compiler.lua | 2 +- lib/priv.h | 1 + lib/tyche.h | 2 ++ lib/vm.c | 56 ++++++++++++++++++++++-------- test/code-tests.lua | 36 +++++++++++++++++-- test/tests.c | 73 ++++++++++++++++++++++----------------- 8 files changed, 137 insertions(+), 51 deletions(-) diff --git a/TODO.md b/TODO.md index 63e690d..5e61e5d 100644 --- a/TODO.md +++ b/TODO.md @@ -26,9 +26,9 @@ Decisions: - [x] Local variables - [x] Functions - [x] With parameters - - [ ] Debug VM execution - - [ ] Control flow - - [ ] Recursion + - [x] Debug VM execution + - [x] Control flow + - [x] Recursion - [ ] Strings - [ ] From constants - [ ] Garbage collection diff --git a/lib/code.c b/lib/code.c index 1495012..ba1f690 100644 --- a/lib/code.c +++ b/lib/code.c @@ -176,6 +176,18 @@ Instruction code_next_instruction(Code const* code, uint32_t function_id, uint32 #ifdef DEBUG_ASSEMBLY +void code_debug_bytecode(Code const* code) +{ + for (int i = 0; i < code->bytecode_sz; ++i) { + if (i % 16 == 0) + printf("%04X : ", i); + printf("%02X ", code->bytecode[i]); + if (i % 16 == 15) + printf("\n"); + } + printf("\n"); +} + void code_decompile(Code const* code) { if (code_n_consts(code) > 0) diff --git a/lib/compiler/compiler.lua b/lib/compiler/compiler.lua index 813002e..b7041c3 100644 --- a/lib/compiler/compiler.lua +++ b/lib/compiler/compiler.lua @@ -279,7 +279,7 @@ local function assemble(proto) if type(bin[i]) == 'string' then local label_addr = labels[bin[i]] if label_addr == nil then error("Label not found: " .. bin[i]) end - replace32(i, label_addr) + replace16(i, label_addr) end end diff --git a/lib/priv.h b/lib/priv.h index 3d43bd6..ea44cb3 100644 --- a/lib/priv.h +++ b/lib/priv.h @@ -227,6 +227,7 @@ uint32_t code_n_functions(Code const* code); uint32_t code_function_sz(Code const* code, uint32_t f_id); Instruction code_next_instruction(Code const* code, uint32_t function_id, uint32_t pc); +void code_debug_bytecode(Code const* code); void code_decompile(Code const* code); void code_parse_instruction(Instruction inst, char* outbuf, size_t sz); diff --git a/lib/tyche.h b/lib/tyche.h index 7b0c29f..84f40ba 100644 --- a/lib/tyche.h +++ b/lib/tyche.h @@ -37,6 +37,7 @@ void tyc_destroy(TycheVM* t); // debugging (DEBUG_ASSEMBLY needs to be setup in compilation options) void tyc_debug_to_console(TycheVM* T, bool activate); void tyc_assembly_decompile(TycheVM* T); +void tyc_print_bytecode(TycheVM* T); // code loading and execution TYC_RESULT tyc_load_bytecode(TycheVM* T, uint8_t const* bytecode, size_t bytecode_sz); @@ -48,6 +49,7 @@ void tyc_pushnil(TycheVM* T); void tyc_pushinteger(TycheVM* T, int32_t value); TYC_RESULT tyc_type(TycheVM* T, int idx, TYC_TYPE* type); TYC_RESULT tyc_tointeger(TycheVM* T, int idx, int32_t* value); +TYC_RESULT tyc_tostring(TycheVM* T, int idx, const char** str); TYC_RESULT tyc_expr(TycheVM* T, TYC_EXPR expr); #endif //TYCHE_TYCHE_H diff --git a/lib/vm.c b/lib/vm.c index c08dc1c..f1fb02d 100644 --- a/lib/vm.c +++ b/lib/vm.c @@ -144,6 +144,11 @@ void tyc_assembly_decompile(TycheVM* T) code_decompile(T->code); } +void tyc_print_bytecode(TycheVM* T) +{ + code_debug_bytecode(T->code); +} + #endif // @@ -269,13 +274,26 @@ TYC_RESULT tyc_type(TycheVM* T, int idx, TYC_TYPE* type) TYC_RESULT tyc_tointeger(TycheVM* T, int idx, int32_t* value) { VALUE v; - TYC_RESULT r = stack_at(T->stack, idx, &v); - if (r == T_OK) { - if (v.type != TT_INTEGER) - return T_ERR_TYPE_UNEXPECTED; - *value = value_integer(v); - } - return r; + TYC_RESULT r; + TRY(stack_at(T->stack, idx, &v)) + if (v.type != TT_INTEGER) + return T_ERR_TYPE_UNEXPECTED; + *value = value_integer(v); + return T_OK; +} + +TYC_RESULT tyc_tostring(TycheVM* T, int idx, const char** str) +{ + VALUE v; + TYC_RESULT r; + TRY(stack_at(T->stack, idx, &v)) + if (v.type == TT_STRING) + return heap_get_string(T->heap, value_idx(v), str); + else if (v.type == TT_STRING_CONST) + *str = code_const_string(T->code, value_idx(v)); + else + return T_ERR_TYPE_UNEXPECTED; + return T_OK; } TYC_RESULT tyc_expr(TycheVM* T, TYC_EXPR op) @@ -322,11 +340,21 @@ static TYC_RESULT step(TycheVM* T) break; case TO_PUSHF: - if (inst.operand < 0 || inst.operand > (int) code_n_functions(T->code)) + if (inst.operand < 0 || inst.operand >= (int) code_n_functions(T->code)) return T_ERR_VALUE_OUT_OF_RANGE; TRY(stack_push(T->stack, create_value_idx(TT_FUNCTION, (uint32_t) inst.operand))) break; + case TO_PUSHC: + if (inst.operand < 0 || inst.operand >= (int) code_n_consts(T->code)) + return T_ERR_VALUE_OUT_OF_RANGE; + if (code_const_type(T->code, (size_t) inst.operand) == TC_STRING) { + TRY(stack_push(T->stack, create_value_idx(TT_STRING_CONST, inst.operand))) + } else { + abort(); // REAL consts not supported for now + } + break; + case TO_POP: TRY(stack_pop(T->stack, NULL)) break; @@ -400,14 +428,14 @@ static TYC_RESULT step(TycheVM* T) // case TO_JMP: - if (inst.operand < 0) - return T_ERR_VALUE_OUT_OF_RANGE; // TODO - also check function size + if (inst.operand < 0 || inst.operand >= code_function_sz(T->code, loc->function_id)) + return T_ERR_VALUE_OUT_OF_RANGE; loc->pc = (uint32_t) inst.operand; goto dont_update_pc; case TO_BZ: - if (inst.operand < 0) - return T_ERR_VALUE_OUT_OF_RANGE; // TODO - also check function size + if (inst.operand < 0 || inst.operand >= code_function_sz(T->code, loc->function_id)) + return T_ERR_VALUE_OUT_OF_RANGE; TRY(stack_pop(T->stack, &a)) if (value_is_zero(a)) { loc->pc = (uint32_t) inst.operand; @@ -416,8 +444,8 @@ static TYC_RESULT step(TycheVM* T) break; case TO_BNZ: - if (inst.operand < 0) - return T_ERR_VALUE_OUT_OF_RANGE; // TODO - also check function size + if (inst.operand < 0 || inst.operand >= code_function_sz(T->code, loc->function_id)) + return T_ERR_VALUE_OUT_OF_RANGE; TRY(stack_pop(T->stack, &a)) if (!value_is_zero(a)) { loc->pc = (uint32_t) inst.operand; diff --git a/test/code-tests.lua b/test/code-tests.lua index 234c2e6..22fe4a5 100644 --- a/test/code-tests.lua +++ b/test/code-tests.lua @@ -93,8 +93,40 @@ return { pushi 7 ; 18 ret ; 20 ]], - decompile = true, - debug = true, + --debug_bytecode = true, + --decompile = true, + --debug = true, expected_stack_top = 6, }, + { + name = "VM: jumps (bz)", + code = [[ + .func 0 + jmp @x1 + pushi 5 + @x1: + pushi 0 + bnz @x2 + pushi 0 + bz @x3 + @x2: + pushi 6 + ret + @x3: + pushi 7 + ret + ]], + expected_stack_top = 7, + }, + { + name = "VM: string from const", + code = [[ + .const + 0: "Hello" + .func 0 + pushc 0 + ret + ]], + expected_stack_top = "Hello" + } } \ No newline at end of file diff --git a/test/tests.c b/test/tests.c index 266342d..7946d32 100644 --- a/test/tests.c +++ b/test/tests.c @@ -14,8 +14,8 @@ static void run_assembly_tests(void); static void run_assembly_test(lua_State* L); -static void run_assembly_test_code(lua_State* L, bool debug, bool decompile); -static void run_assembly_test_template(lua_State* L, bool debug, bool decompile); +static void run_assembly_test_code(lua_State* L, bool debug, bool decompile, bool debug_bytecode); +static void run_assembly_test_template(lua_State* L, bool debug, bool decompile, bool debug_bytecode); int main(void) { @@ -357,16 +357,21 @@ static void run_assembly_test(lua_State* L) bool debug = lua_isboolean(L, -1) && lua_toboolean(L, -1); lua_pop(L, 1); - // decompile + // decompile? lua_getfield(L, -1, "decompile"); bool decompile = lua_isboolean(L, -1) && lua_toboolean(L, -1); lua_pop(L, 1); + // decompile? + lua_getfield(L, -1, "debug_bytecode"); + bool debug_bytecode = lua_isboolean(L, -1) && lua_toboolean(L, -1); + lua_pop(L, 1); + // has code? lua_getfield(L, -1, "code"); if (!lua_isnil(L, -1)) { lua_pop(L, 1); - run_assembly_test_code(L, debug, decompile); + run_assembly_test_code(L, debug, decompile, debug_bytecode); return; } else { lua_pop(L, 1); @@ -376,13 +381,35 @@ static void run_assembly_test(lua_State* L) lua_getfield(L, -1, "template"); if (!lua_isnil(L, -1)) { lua_pop(L, 1); - run_assembly_test_template(L, debug, decompile); + run_assembly_test_template(L, debug, decompile, debug_bytecode); } else { lua_pop(L, 1); } } -static void run_assembly_test_code(lua_State* L, bool debug, bool decompile) +static void check_expected_top(lua_State* L, TycheVM* T) +{ + // check stack size + lua_getfield(L, -1, "expected_stack_size"); + if (!lua_isnil(L, -1)) + assert(tyc_stack_size(T) == (size_t) lua_tointeger(L, -1)); + lua_pop(L, 1); + + // check stack top + lua_getfield(L, -1, "expected_stack_top"); + if (lua_isinteger(L, -1)) { + TYC_TYPE type; assert(tyc_type(T, -1, &type) == T_OK); assert(type == TT_INTEGER); + int32_t v; assert(tyc_tointeger(T, -1, &v) == T_OK); assert(v == lua_tointeger(L, -1)); + } else if (lua_isstring(L, -1)) { + TYC_TYPE type; assert(tyc_type(T, -1, &type) == T_OK); assert(type == TT_STRING || type == TT_STRING_CONST); + const char* str; assert(tyc_tostring(T, -1, &str) == T_OK); assert(strcmp(str, lua_tostring(L, -1)) == 0); + } else if (!lua_isnil(L, -1)) { + abort(); + } + lua_pop(L, 1); +} + +static void run_assembly_test_code(lua_State* L, bool debug, bool decompile, bool debug_bytecode) { TycheVM* T = tyc_new(); tyc_debug_to_console(T, debug); @@ -395,32 +422,21 @@ static void run_assembly_test_code(lua_State* L, bool debug, bool decompile) // run code assert(tyc_load_bytecode(T, bytecode, bytecode_sz) == T_OK); + if (debug_bytecode) + tyc_print_bytecode(T); if (decompile) tyc_assembly_decompile(T); assert(tyc_call(T, 0) == T_OK); - // check stack size - lua_getfield(L, -1, "expected_stack_size"); - if (!lua_isnil(L, -1)) - assert(tyc_stack_size(T) == (size_t) lua_tointeger(L, -1)); - lua_pop(L, 1); - - // check stack top - lua_getfield(L, -1, "expected_stack_top"); - if (lua_isinteger(L, -1)) { - TYC_TYPE type; assert(tyc_type(T, -1, &type) == T_OK); assert(type == TT_INTEGER); - int32_t v; assert(tyc_tointeger(T, -1, &v) == T_OK); assert(v == lua_tointeger(L, -1)); - } else if (!lua_isnil(L, -1)) { - abort(); - } - lua_pop(L, 1); + // assert + check_expected_top(L, T); // cleanup free(bytecode); tyc_destroy(T); } -static void run_assembly_test_template(lua_State* L, bool debug, bool decompile) +static void run_assembly_test_template(lua_State* L, bool debug, bool decompile, bool debug_bytecode) { lua_getfield(L, -1, "template"); char* template = strdup(lua_tostring(L, -1)); @@ -459,19 +475,14 @@ static void run_assembly_test_template(lua_State* L, bool debug, bool decompile) uint8_t* bytecode; size_t bytecode_sz; assert(code_assemble(formatted_code, &bytecode, &bytecode_sz) == T_OK); assert(tyc_load_bytecode(T, bytecode, bytecode_sz) == T_OK); + if (debug_bytecode) + tyc_print_bytecode(T); if (decompile) tyc_assembly_decompile(T); assert(tyc_call(T, 0) == T_OK); - // check stack top - lua_getfield(L, -1, "expected_stack_top"); - if (lua_isinteger(L, -1)) { - TYC_TYPE type; assert(tyc_type(T, -1, &type) == T_OK); assert(type == TT_INTEGER); - int32_t v; assert(tyc_tointeger(T, -1, &v) == T_OK); assert(v == lua_tointeger(L, -1)); - } else if (!lua_isnil(L, -1)) { - abort(); - } - lua_pop(L, 1); + // assert + check_expected_top(L, T); // cleanup free(bytecode);