From: Brendan Hansen Date: Sat, 25 Jun 2022 04:35:04 +0000 (-0500) Subject: if statements are working! close to a testable version! X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=07af5df0683455daf2e53124c4f63ac9c5190cce;p=onyx-embedder.git if statements are working! close to a testable version! --- diff --git a/include/vm_codebuilder.h b/include/vm_codebuilder.h index c3e52f9..5d93b7a 100644 --- a/include/vm_codebuilder.h +++ b/include/vm_codebuilder.h @@ -22,6 +22,10 @@ struct ovm_code_builder_t { ovm_program_t *program; i32 start_instr; + + i32 func_table_arr_idx; + i32 next_external_func_idx; + i32 highest_value_number; }; enum label_kind_t { @@ -46,7 +50,7 @@ void ovm_code_builder_free(ovm_code_builder_t *builder); void ovm_code_builder_add_binop(ovm_code_builder_t *builder, u32 instr); void ovm_code_builder_add_unop(ovm_code_builder_t *builder, u32 instr); void ovm_code_builder_add_imm(ovm_code_builder_t *builder, u32 ovm_type, void *imm); -void ovm_code_builder_add_branch(ovm_code_builder_t *builder, i32 instr_delta); +void ovm_code_builder_add_branch(ovm_code_builder_t *builder, i32 label_idx); void ovm_code_builder_add_cond_branch(ovm_code_builder_t *builder, i32 label_idx); void ovm_code_builder_add_return(ovm_code_builder_t *builder); void ovm_code_builder_add_call(ovm_code_builder_t *builder, i32 func_idx, i32 param_count, bool has_return_value); diff --git a/src/vm/code_builder.c b/src/vm/code_builder.c index 467a35e..43ce2b9 100644 --- a/src/vm/code_builder.c +++ b/src/vm/code_builder.c @@ -3,7 +3,10 @@ #define PUSH_VALUE(b, r) (bh_arr_push((b)->execution_stack, r)) #define POP_VALUE(b) (bh_arr_length((b)->execution_stack) == 0 ? (assert(("invalid value pop", 0)), 0) : bh_arr_pop((b)->execution_stack)) -#define NEXT_VALUE(b) ((bh_arr_length((b)->execution_stack) == 0 ? (b)->param_count + (b)->local_count : bh_arr_last((b)->execution_stack)) + 1) +#define NEXT_VALUE(b) (bh_arr_length((b)->execution_stack) == 0 ? \ + (b)->param_count + (b)->local_count : \ + ((b)->highest_value_number = bh_max((b)->highest_value_number, bh_arr_last((b)->execution_stack)), \ + bh_arr_last((b)->execution_stack) + 1)) // #define POP_VALUE(b) bh_arr_pop((b)->execution_stack) @@ -23,6 +26,8 @@ ovm_code_builder_t ovm_code_builder_new(ovm_program_t *program, i32 param_count, bh_arr_new(bh_heap_allocator(), builder.label_stack, 32); bh_arr_new(bh_heap_allocator(), builder.branch_patches, 32); + builder.highest_value_number = param_count + local_count; + return builder; } @@ -78,12 +83,33 @@ void ovm_code_builder_add_unop(ovm_code_builder_t *builder, u32 instr) { PUSH_VALUE(builder, unop.r); } -void ovm_code_builder_add_branch(ovm_code_builder_t *builder, i32 instr_delta) { +void ovm_code_builder_add_branch(ovm_code_builder_t *builder, i32 label_idx) { + ovm_instr_t branch_instr = {0}; + branch_instr.full_instr = OVMI_BR; + branch_instr.a = -1; + + branch_patch_t patch; + patch.branch_instr = bh_arr_length(builder->program->code); + patch.label_idx = label_idx; + + bh_arr_push(builder->branch_patches, patch); + ovm_program_add_instructions(builder->program, 1, &branch_instr); } void ovm_code_builder_add_cond_branch(ovm_code_builder_t *builder, i32 label_idx) { + ovm_instr_t branch_instr = {0}; + branch_instr.full_instr = OVMI_BR_Z; + branch_instr.a = -1; + branch_instr.b = POP_VALUE(builder); + branch_patch_t patch; + patch.branch_instr = bh_arr_length(builder->program->code); + patch.label_idx = label_idx; + + bh_arr_push(builder->branch_patches, patch); + + ovm_program_add_instructions(builder->program, 1, &branch_instr); } void ovm_code_builder_add_return(ovm_code_builder_t *builder) { @@ -130,21 +156,28 @@ void ovm_code_builder_add_call(ovm_code_builder_t *builder, i32 func_idx, i32 pa } void ovm_code_builder_add_indirect_call(ovm_code_builder_t *builder, i32 param_count, bool has_return_value) { - ovm_instr_t call_instr = {0}; - call_instr.full_instr = OVMI_CALLI; - call_instr.a = POP_VALUE(builder); - call_instr.r = -1; + ovm_instr_t call_instrs[2] = {0}; + + // idxarr %k, table, %j + call_instrs[0].full_instr = OVMI_IDX_ARR; + call_instrs[0].r = NEXT_VALUE(builder); + call_instrs[0].a = builder->func_table_arr_idx; + call_instrs[0].b = POP_VALUE(builder); + + call_instrs[1].full_instr = OVMI_CALLI; + call_instrs[1].a = call_instrs[0].r; + call_instrs[1].r = -1; ovm_code_builder_add_params(builder, param_count); if (has_return_value) { - call_instr.r = NEXT_VALUE(builder); + call_instrs[1].r = NEXT_VALUE(builder); } - ovm_program_add_instructions(builder->program, 1, &call_instr); + ovm_program_add_instructions(builder->program, 2, call_instrs); if (has_return_value) { - PUSH_VALUE(builder, call_instr.r); + PUSH_VALUE(builder, call_instrs[1].r); } } diff --git a/src/wasm.c b/src/wasm.c index 8ad5cf9..5a06b09 100644 --- a/src/wasm.c +++ b/src/wasm.c @@ -1,7 +1,6 @@ #define BH_DEFINE #define BH_NO_TABLE #define STB_DS_IMPLEMENTATION -#define BH_STATIC #include "bh.h" #include "stb_ds.h" diff --git a/src/wasm/module_parsing.c.incl b/src/wasm/module_parsing.c.incl index 1447abb..ead2221 100644 --- a/src/wasm/module_parsing.c.incl +++ b/src/wasm/module_parsing.c.incl @@ -4,6 +4,10 @@ // This file is not to be compile like normal. // It is instead included in wasm/module.c // +// Currently, this file has a lot of code that directly manipulates +// the code builder object. I would like to move this into the API +// for the code builder itself, to make it more portable and easy +// to read. typedef struct build_context build_context; struct build_context { @@ -151,6 +155,16 @@ static void parse_import_section(build_context *ctx) { wasm_importtype_t *import = wasm_importtype_new(&module_name, &import_name, import_type); ctx->module->imports.data[i] = import; + + if (import_type->kind == WASM_EXTERN_FUNC) { + char *external_func_name = bh_aprintf(ctx->program->store->arena_allocator, "%b.%b", + module_name.data, module_name.size, + import_name.data, import_name.size); + + int external_func_idx = ctx->builder.next_external_func_idx++; + + ovm_program_register_external_func(ctx->program, external_func_name, import_type->func.params.size, external_func_idx); + } } } @@ -338,7 +352,7 @@ static void parse_data_count_section(build_context *ctx) { // Instruction building // -static void push_label_target(build_context *ctx, label_kind_t kind) { +static int push_label_target(build_context *ctx, label_kind_t kind) { label_target_t target; target.kind = kind; target.idx = ctx->builder.next_label_idx++; @@ -349,6 +363,8 @@ static void push_label_target(build_context *ctx, label_kind_t kind) { } bh_arr_push(ctx->builder.label_stack, target); + + return target.idx; } static void pop_label_target(build_context *ctx) { @@ -391,13 +407,28 @@ static void parse_instruction(build_context *ctx) { case 0x04: { // Currently, only "void" block types are valid. assert(CONSUME_BYTE(ctx) == 0x40); - push_label_target(ctx, label_kind_block); - // TODO: Ifs + int if_target = push_label_target(ctx, label_kind_block); + + // + // This uses the pattern of "branch if zero" to skip a section of + // code if the condition was not true. + ovm_code_builder_add_cond_branch(&ctx->builder, if_target); break; } case 0x05: { - // TODO: If-elses + // + // HACK HACK HACK + // I'm not entirely happy with the way that this happens, but let me explain. + // When trying to make an "else", you need to have a unconditional branch that + // skips the alternate code path. This branch instruction both needs to know + // the label index of the else statment, and be part of the existing block. + // Therefore, this "peeks" at what the next label index will be, so it can + // know what label to track for where to jump. It feels like there should be + // a better way to do this. + ovm_code_builder_add_branch(&ctx->builder, ctx->builder.next_label_idx); + pop_label_target(ctx); + push_label_target(ctx, label_kind_block); break; } @@ -408,13 +439,17 @@ static void parse_instruction(build_context *ctx) { case 0x0C: { int label_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset); - ovm_code_builder_add_branch(&ctx->builder, label_idx); + + label_target_t target = *((&bh_arr_last(ctx->builder.label_stack)) - label_idx); + ovm_code_builder_add_branch(&ctx->builder, target.idx); break; } case 0x0D: { int label_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset); - ovm_code_builder_add_cond_branch(&ctx->builder, label_idx); + + label_target_t target = *((&bh_arr_last(ctx->builder.label_stack)) - label_idx); + ovm_code_builder_add_cond_branch(&ctx->builder, target.idx); break; } @@ -430,9 +465,9 @@ static void parse_instruction(build_context *ctx) { case 0x10: { int func_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset); - int param_count = ctx->program->funcs[func_idx].param_count; - wasm_functype_t *functype = ctx->module->functypes.data[func_idx]; + wasm_functype_t *functype = wasm_module_index_functype(ctx->module, func_idx); + int param_count = functype->type.func.params.size; ovm_code_builder_add_call(&ctx->builder, func_idx, param_count, functype->type.func.results.size != 0); break; @@ -445,7 +480,6 @@ static void parse_instruction(build_context *ctx) { wasm_functype_t *functype = ctx->module->type_section.data[type_idx]; int param_count = functype->type.func.params.size; - printf("indirect call with %d params.\n", param_count); ovm_code_builder_add_indirect_call(&ctx->builder, param_count, functype->type.func.results.size != 0); break; } @@ -734,14 +768,19 @@ static void parse_code_section(build_context *ctx) { total_locals += local_count; } - // Set up a lot of stuff... - ctx->builder = ovm_code_builder_new(ctx->program, 0, total_locals); - push_label_target(ctx, label_kind_func); - parse_expression(ctx); + i32 param_count = wasm_module_index_functype(ctx->module, i)->type.func.params.size; + ctx->builder = ovm_code_builder_new(ctx->program, param_count, total_locals); + ctx->builder.func_table_arr_idx = 0; // This might not be right + + push_label_target(ctx, label_kind_func); + parse_expression(ctx); ovm_code_builder_add_return(&ctx->builder); + + char *func_name = bh_aprintf(bh_heap_allocator(), "wasm_loaded_%d", bh_arr_length(ctx->program->funcs)); + ovm_program_register_func(ctx->program, func_name, ctx->builder.start_instr, ctx->builder.param_count, ctx->builder.highest_value_number); ovm_code_builder_free(&ctx->builder); } } diff --git a/src/wasm_cli_test.c b/src/wasm_cli_test.c index 91af7a0..1fe6020 100644 --- a/src/wasm_cli_test.c +++ b/src/wasm_cli_test.c @@ -1,5 +1,7 @@ +#define BH_DEFINE #include "bh.h" #include "wasm.h" + #include "onyx_wasm.h" #include "vm.h" @@ -44,6 +46,6 @@ int main(int argc, char *argv[]) { bh_printf("exports: %b %d\n", export_name->data, export_name->size, wasm_externtype_kind(wasm_exporttype_type(exports.data[i]))); } - ovm_program_print_instructions(module->program, 0, 100); + ovm_program_print_instructions(module->program, 0, 128); } diff --git a/tests/wasm/out.wasm b/tests/wasm/out.wasm index 9992ca7..d6d11ba 100644 Binary files a/tests/wasm/out.wasm and b/tests/wasm/out.wasm differ diff --git a/tests/wasm/tiny.onyx b/tests/wasm/tiny.onyx index bd99d3a..c38b6b1 100644 --- a/tests/wasm/tiny.onyx +++ b/tests/wasm/tiny.onyx @@ -14,6 +14,18 @@ g :: (x) => x + 1; z(10); foo(); + + if_test(10); +} + +if_test :: (x: i32) { + if x > 10 { + f(10, 20); + } elseif x > 5 { + foo(); + } else { + y := x * 2; + } } foo :: () -> void #foreign "test" "asdfasdf" ---