if statements are working! close to a testable version!
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 25 Jun 2022 04:35:04 +0000 (23:35 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 25 Jun 2022 04:35:04 +0000 (23:35 -0500)
include/vm_codebuilder.h
src/vm/code_builder.c
src/wasm.c
src/wasm/module_parsing.c.incl
src/wasm_cli_test.c
tests/wasm/out.wasm
tests/wasm/tiny.onyx

index c3e52f90dc661416b7aa05993b3410fcf8ed1af5..5d93b7a399e9f9240270f2a9275b10d7aa0995d2 100644 (file)
@@ -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);
index 467a35e2421b539eaaf77c653fa237a55f8cff73..43ce2b9703ed15e06b4588a1093e86646b524d65 100644 (file)
@@ -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);
     }
 }
 
index 8ad5cf9409944e00b335f7bedbbfae8b8854d19b..5a06b09e23adbd4e81faad9afbbaadd11b539150 100644 (file)
@@ -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"
 
index 1447abbb9c5cc309bed9cf3167e2b8be216c4bab..ead222122d58821f021a0f38e2dcc21b30b6d051 100644 (file)
@@ -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);
     }
 }
index 91af7a0cfd374a3e571c9691cc5236976522d60e..1fe60202c7172eb7a63eb2a31bfac6abb8ec1dbf 100644 (file)
@@ -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);
 }
 
index 9992ca76868a8212443e542d9b2e0a9968c2bd19..d6d11ba4a5ec4bd2435b230cff1269f452f54e16 100644 (file)
Binary files a/tests/wasm/out.wasm and b/tests/wasm/out.wasm differ
index bd99d3a05a03fadb7df55b75eec93d3ae0ad6bd9..c38b6b19eb18da649ce55c239796f90f98aab98b 100644 (file)
@@ -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" ---