almost all MVP instructions can be parsed and compiled
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 25 Jun 2022 23:10:44 +0000 (18:10 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 25 Jun 2022 23:10:44 +0000 (18:10 -0500)
include/onyx_wasm.h
include/vm.h
include/vm_codebuilder.h
src/vm/code_builder.c
src/vm/vm.c
src/wasm/module_parsing.c.incl
src/wasm_cli_test.c
tests/wasm/out.wasm
tests/wasm/real.onyx [new file with mode: 0644]
tests/wasm/tiny.onyx

index b42b8bae0de51dc4b84f613860075a5372fc33b1..eae8c3d922a93bca230cffb8c2d8b7620f0c4787 100644 (file)
@@ -127,7 +127,7 @@ struct wasm_module_t {
 
 struct wasm_func_inner_t {
     wasm_instance_t *instance;
-    ovm_func_t *func;
+    i32 func_idx;
 
     wasm_functype_t *type;
 };
index f777423ee04f5031c697002df67b4fb730a49b29..2b5157b4a41d0e495c7735a9abd66e77823d3682 100644 (file)
@@ -247,10 +247,9 @@ struct ovm_instr_t {
 #define OVMI_AND               0x08   // %r = %a & %b
 #define OVMI_OR                0x09   // %r = %a | %b
 #define OVMI_XOR               0x0A   // %r = %a ^ %b
-#define OVMI_NOT               0x0B   // %r = ~%a           // This one might not be needed, as WASM doesn't provide it
-#define OVMI_SHL               0x0C   // %r = %a << %b
-#define OVMI_SHR               0x0D   // %r = %a >> %b
-#define OVMI_SAR               0x0E   // %r = %a >>> %b
+#define OVMI_SHL               0x0B   // %r = %a << %b
+#define OVMI_SHR               0x0C   // %r = %a >> %b
+#define OVMI_SAR               0x0D   // %r = %a >>> %b
 
 #define OVMI_IMM               0x10   // %r = i/l/f/d
 #define OVMI_MOV               0x10   // %r = %a
index 5d93b7a399e9f9240270f2a9275b10d7aa0995d2..cd078d0d98fee810ca61cb90e1bca99747188779 100644 (file)
@@ -62,5 +62,7 @@ void               ovm_code_builder_add_register_get(ovm_code_builder_t *builder
 void               ovm_code_builder_add_register_set(ovm_code_builder_t *builder, i32 local_idx);
 void               ovm_code_builder_add_load(ovm_code_builder_t *builder, u32 ovm_type, i32 offset);
 void               ovm_code_builder_add_store(ovm_code_builder_t *builder, u32 ovm_type, i32 offset);
+void               ovm_code_builder_add_memory_copy(ovm_code_builder_t *builder);
+void               ovm_code_builder_add_memory_fill(ovm_code_builder_t *builder);
 
 #endif
index 43ce2b9703ed15e06b4588a1093e86646b524d65..3701078ea2b88c7f3036d0c4c609b6fc841db8d1 100644 (file)
@@ -3,13 +3,17 @@
 
 #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 : \
-        ((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)
 
+static inline int NEXT_VALUE(ovm_code_builder_t *b) {
+    if (bh_arr_length(b->execution_stack) == 0) {
+        return b->param_count + b->local_count;
+    }
+
+    b->highest_value_number = bh_max(b->highest_value_number, bh_arr_last(b->execution_stack));
+    return bh_arr_last(b->execution_stack) + 1;
+}
+
 ovm_code_builder_t ovm_code_builder_new(ovm_program_t *program, i32 param_count, i32 local_count) {
     ovm_code_builder_t builder;
     builder.param_count = param_count;
@@ -300,3 +304,22 @@ void ovm_code_builder_add_store(ovm_code_builder_t *builder, u32 ovm_type, i32 o
     ovm_program_add_instructions(builder->program, 3, instrs);
 }
 
+void ovm_code_builder_add_memory_copy(ovm_code_builder_t *builder) {
+    ovm_instr_t instr = {0};
+    instr.full_instr = OVMI_COPY;
+    instr.b = POP_VALUE(builder);
+    instr.a = POP_VALUE(builder);
+    instr.r = POP_VALUE(builder);
+
+    ovm_program_add_instructions(builder->program, 1, &instr);
+}
+
+void ovm_code_builder_add_memory_fill(ovm_code_builder_t *builder) {
+    ovm_instr_t instr = {0};
+    instr.full_instr = OVMI_FILL;
+    instr.b = POP_VALUE(builder);
+    instr.a = POP_VALUE(builder);
+    instr.r = POP_VALUE(builder);
+
+    ovm_program_add_instructions(builder->program, 1, &instr);
+}
index 00eea16a5e1cc35463b3695121e6316e4d449c59..3ef3af151a9e8a5e0dd6efebc27f6a9c18cd3022 100644 (file)
@@ -154,11 +154,6 @@ static char *ovm_instr_name(i32 full_instr) {
         C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I32))
         C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I64))
 
-        C(OVM_TYPED_INSTR(OVMI_NOT, OVM_TYPE_I8))
-        C(OVM_TYPED_INSTR(OVMI_NOT, OVM_TYPE_I16))
-        C(OVM_TYPED_INSTR(OVMI_NOT, OVM_TYPE_I32))
-        C(OVM_TYPED_INSTR(OVMI_NOT, OVM_TYPE_I64))
-
         C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I8))
         C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I16))
         C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I32))
@@ -368,7 +363,7 @@ void ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32
         // is the start of a function, but for now, it'll do. -brendanfh 06/13/2022
         bh_arr_each(ovm_func_t, func, program->funcs) {
             if (i == func->start_instr && func->kind == OVM_FUNC_INTERNAL) {
-                printf("\n[%d]%s:\n", func->id, func->name);
+                printf("\n[%d] %s  values=%d:\n", func->id, func->name, func->value_number_count);
             }
         }
 
@@ -660,11 +655,6 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr
                  VAL(instr.r).type = t; \
                  VAL(instr.r).ctype = (ctype) op (VAL(instr.a).ctype); \
                  break;
-            
-            OVM_OP(OVMI_NOT, OVM_TYPE_I8 , ~, i8)
-            OVM_OP(OVMI_NOT, OVM_TYPE_I16, ~, i16)
-            OVM_OP(OVMI_NOT, OVM_TYPE_I32, ~, i32)
-            OVM_OP(OVMI_NOT, OVM_TYPE_I64, ~, i64)
 
             OVM_OP(OVMI_CLZ, OVM_TYPE_I8 , __builtin_clz, i8)
             OVM_OP(OVMI_CLZ, OVM_TYPE_I16, __builtin_clz, i16)
index ead222122d58821f021a0f38e2dcc21b30b6d051..2ee8941affbadbbffe3e6c72ec6c82b7a06e6996 100644 (file)
@@ -385,9 +385,53 @@ static void pop_label_target(build_context *ctx) {
 
 static void parse_expression(build_context *ctx);
 
+static void parse_fc_instruction(build_context *ctx) {
+    int instr_num = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    switch (instr_num) {
+        case 0: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F32, OVM_TYPE_I32)); break;
+        case 1: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F32, OVM_TYPE_I32)); break;
+        case 2: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F64, OVM_TYPE_I32)); break;
+        case 3: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F64, OVM_TYPE_I32)); break;
+        case 4: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F32, OVM_TYPE_I64)); break;
+        case 5: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F32, OVM_TYPE_I64)); break;
+        case 6: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F64, OVM_TYPE_I64)); break;
+        case 7: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F64, OVM_TYPE_I64)); break;
+
+        case 8: {
+            int dataidx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            assert(CONSUME_BYTE(ctx) == 0x00);
+
+            ovm_code_builder_drop_value(&ctx->builder);
+            ovm_code_builder_drop_value(&ctx->builder);
+            ovm_code_builder_drop_value(&ctx->builder);
+
+            // TODO: MEMORY INIT INSTRUCTION
+            break;
+        }
+
+        case 10: {
+            assert(CONSUME_BYTE(ctx) == 0x00);
+            assert(CONSUME_BYTE(ctx) == 0x00);
+
+            ovm_code_builder_add_memory_copy(&ctx->builder);
+            break;
+        }
+
+        case 11: {
+            assert(CONSUME_BYTE(ctx) == 0x00);
+
+            ovm_code_builder_add_memory_fill(&ctx->builder);
+            break;
+        }
+
+        default: assert(("UNHANDLED FC INSTRUCTION", 0));
+    }
+}
+
 static void parse_instruction(build_context *ctx) {
-    unsigned char instr_byte;
-    switch (instr_byte = CONSUME_BYTE(ctx)) {
+    unsigned char instr_byte = CONSUME_BYTE(ctx);
+    switch (instr_byte) {
         case 0x00: break;
         case 0x01: break;
         case 0x02: {
@@ -522,61 +566,62 @@ static void parse_instruction(build_context *ctx) {
             break;
         }
 
-        case 0x28: {
-            int alignment = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            int offset    = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            ovm_code_builder_add_load(&ctx->builder, OVM_TYPE_I32, offset);
-            break;
+#define LOAD_CASE(num, type) \
+        case num : { \
+            int alignment = uleb128_to_uint(ctx->binary.data, &ctx->offset); \
+            int offset    = uleb128_to_uint(ctx->binary.data, &ctx->offset); \
+            ovm_code_builder_add_load(&ctx->builder, type, offset); \
+            break; \
         }
 
-        case 0x29: {
-            int alignment = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            int offset    = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            ovm_code_builder_add_load(&ctx->builder, OVM_TYPE_I64, offset);
-            break;
-        }
+        LOAD_CASE(0x28, OVM_TYPE_I32)
+        LOAD_CASE(0x29, OVM_TYPE_I64)
+        LOAD_CASE(0x2A, OVM_TYPE_F32)
+        LOAD_CASE(0x2B, OVM_TYPE_F64)
 
-        case 0x2A: {
-            int alignment = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            int offset    = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            ovm_code_builder_add_load(&ctx->builder, OVM_TYPE_F32, offset);
-            break;
-        }
+        LOAD_CASE(0x2C, OVM_TYPE_I8)
+        LOAD_CASE(0x2D, OVM_TYPE_I8)
+        LOAD_CASE(0x2E, OVM_TYPE_I16)
+        LOAD_CASE(0x2F, OVM_TYPE_I16)
+        LOAD_CASE(0x30, OVM_TYPE_I8)
+        LOAD_CASE(0x31, OVM_TYPE_I8)
+        LOAD_CASE(0x32, OVM_TYPE_I16)
+        LOAD_CASE(0x33, OVM_TYPE_I16)
+        LOAD_CASE(0x34, OVM_TYPE_I32)
+        LOAD_CASE(0x35, OVM_TYPE_I32)
 
-        case 0x2B: {
-            int alignment = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            int offset    = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            ovm_code_builder_add_load(&ctx->builder, OVM_TYPE_F64, offset);
-            break;
+#undef LOAD_CASE
+
+#define STORE_CASE(num, type) \
+        case num : { \
+            int alignment = uleb128_to_uint(ctx->binary.data, &ctx->offset); \
+            int offset    = uleb128_to_uint(ctx->binary.data, &ctx->offset); \
+            ovm_code_builder_add_store(&ctx->builder, type, offset); \
+            break; \
         }
 
-        // ... More loading and storing instruction here ...
+        STORE_CASE(0x36, OVM_TYPE_I32);
+        STORE_CASE(0x37, OVM_TYPE_I64);
+        STORE_CASE(0x38, OVM_TYPE_F32);
+        STORE_CASE(0x39, OVM_TYPE_F64);
 
-        case 0x36: {
-            int alignment = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            int offset    = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            ovm_code_builder_add_store(&ctx->builder, OVM_TYPE_I32, offset);
-            break;
-        }
+        STORE_CASE(0x3A, OVM_TYPE_I8);
+        STORE_CASE(0x3B, OVM_TYPE_I16);
+        STORE_CASE(0x3C, OVM_TYPE_I8);
+        STORE_CASE(0x3D, OVM_TYPE_I16);
+        STORE_CASE(0x3E, OVM_TYPE_I32);
 
-        case 0x37: {
-            int alignment = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            int offset    = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            ovm_code_builder_add_store(&ctx->builder, OVM_TYPE_I64, offset);
-            break;
-        }
+#undef STORE_CASE
 
-        case 0x38: {
-            int alignment = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            int offset    = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            ovm_code_builder_add_store(&ctx->builder, OVM_TYPE_F32, offset);
-            break;
+        case 0x3F: {
+            assert(CONSUME_BYTE(ctx) == 0x00);
+
+            int memory_size = 65536;
+            ovm_code_builder_add_imm(&ctx->builder, OVM_TYPE_I32, &memory_size);
         }
 
-        case 0x39: {
-            int alignment = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            int offset    = uleb128_to_uint(ctx->binary.data, &ctx->offset);
-            ovm_code_builder_add_store(&ctx->builder, OVM_TYPE_F64, offset);
+        case 0x40: {
+            assert(CONSUME_BYTE(ctx) == 0x00);
             break;
         }
 
@@ -606,6 +651,13 @@ static void parse_instruction(build_context *ctx) {
             break;
         }
 
+        case 0x45: {
+            int zero = 0;
+            ovm_code_builder_add_imm(&ctx->builder, OVM_TYPE_I32, &zero);
+            ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I32));
+            break;
+        }
+
         case 0x46: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I32)); break;
         case 0x47: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_I32)); break;
         case 0x48: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_I32)); break;
@@ -617,6 +669,12 @@ static void parse_instruction(build_context *ctx) {
         case 0x4E: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_I32)); break;
         case 0x4F: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_I32)); break;
 
+        case 0x50: {
+            long long zero = 0;
+            ovm_code_builder_add_imm(&ctx->builder, OVM_TYPE_I64, &zero);
+            ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I64));
+            break;
+        }
         case 0x51: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I64)); break;
         case 0x52: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_I64)); break;
         case 0x53: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_I64)); break;
@@ -741,6 +799,8 @@ static void parse_instruction(build_context *ctx) {
         case 0xC3: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I16_S, OVM_TYPE_I64)); break;
         case 0xC4: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I32_S, OVM_TYPE_I64)); break;
 
+        case 0xFC: parse_fc_instruction(ctx); break;
+
         default: assert(("UNHANDLED INSTRUCTION", 0));
     }
 }
@@ -780,7 +840,7 @@ static void parse_code_section(build_context *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_program_register_func(ctx->program, func_name, ctx->builder.start_instr, ctx->builder.param_count, ctx->builder.highest_value_number + 1);
         ovm_code_builder_free(&ctx->builder);
     }
 }
index 1fe60202c7172eb7a63eb2a31bfac6abb8ec1dbf..f0d841419009dfe8b4eafcdecd659bd3ec0c646c 100644 (file)
@@ -46,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, 128);
+    ovm_program_print_instructions(module->program, 0, 150);
 }
 
index d6d11ba4a5ec4bd2435b230cff1269f452f54e16..a88779ef4c8e112ed54e9e97518da309976c8b1d 100644 (file)
Binary files a/tests/wasm/out.wasm and b/tests/wasm/out.wasm differ
diff --git a/tests/wasm/real.onyx b/tests/wasm/real.onyx
new file mode 100644 (file)
index 0000000..cbb4128
--- /dev/null
@@ -0,0 +1,5 @@
+#load "core/std"
+
+main :: () {
+    core.println("Hello, World!");
+}
\ No newline at end of file
index c38b6b19eb18da649ce55c239796f90f98aab98b..b16a1147b6a9298bca970a0f45cf1c455254ffb2 100644 (file)
@@ -16,10 +16,11 @@ g :: (x) => x + 1;
     foo();
 
     if_test(10);
+    while_test(10);
 }
 
 if_test :: (x: i32) {
-    if x > 10 {
+    if !(x > 10) {
         f(10, 20);
     } elseif x > 5 {
         foo();
@@ -28,4 +29,10 @@ if_test :: (x: i32) {
     }
 }
 
+while_test :: (x: i32) {
+    while i := 0; i < x {
+        i += 1;
+    }
+}
+
 foo :: () -> void #foreign "test" "asdfasdf" ---