small refactoring and more thorough testing
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 14 Jun 2022 02:57:08 +0000 (21:57 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 14 Jun 2022 02:57:08 +0000 (21:57 -0500)
include/vm.h
src/cli.c
src/vm/vm.c

index 4005773a8b7d23a9791dba2c6dc6b97291521900..e0b73251f7a0ebc7e94b4a2651fadcc51fc6ecca 100644 (file)
@@ -29,7 +29,7 @@ struct ovm_store_t {
 };
 
 ovm_store_t *ovm_store_new();
-void         ovm_Store_delete(ovm_store_t *store);
+void         ovm_store_delete(ovm_store_t *store);
 
 
 struct ovm_static_data_t {
@@ -45,9 +45,8 @@ struct ovm_static_data_t {
 struct ovm_program_t {
     ovm_store_t *store;
 
-    bh_arr(ovm_func_t *)      funcs;
+    bh_arr(ovm_func_t)        funcs;
     bh_arr(ovm_native_func_t) native_funcs;
-    bh_arr(ovm_static_data_t) initialized_data;
 
     bh_arr(ovm_instr_t)       code;
 };
@@ -55,10 +54,12 @@ struct ovm_program_t {
 ovm_program_t *ovm_program_new(ovm_store_t *store);
 void           ovm_program_delete(ovm_program_t *program);
 void           ovm_program_add_instructions(ovm_program_t *program, i32 instr_count, ovm_instr_t *instrs);
-void           ovm_program_register_func(ovm_program_t *program, i32 instr, i32 param_count, i32 value_number_count);
-void           ovm_program_register_native_func(ovm_program_t *program, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count);
 void           ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count);
 
+void           ovm_program_register_func(ovm_program_t *program, char *name, i32 instr, i32 param_count, i32 value_number_count);
+void           ovm_program_begin_func(ovm_program_t *program, char *name, i32 param_count, i32 value_number_count);
+void           ovm_program_register_native_func(ovm_program_t *program, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count);
+
 //
 // Represents the running configuration and static
 // data needed by the VM. This is for more "global" data.
@@ -73,6 +74,7 @@ struct ovm_engine_t {
 
 ovm_engine_t *ovm_engine_new(ovm_store_t *store);
 void          ovm_engine_delete(ovm_engine_t *engine);
+void          ovm_engine_memory_copy(ovm_engine_t *engine, i64 target, void *data, i64 size);
 
 //
 // Represents ephemeral state / execution context.
@@ -113,6 +115,7 @@ struct ovm_func_t {
     // This ID is used as the index into the `funcs` member on ovm_program_t
     // to reference this function. It is only here for debugging and posterity.
     i32 id;
+    char *name;
 
     i32 start_instr;
 
@@ -195,45 +198,62 @@ struct ovm_instr_t {
     };
 };
 
+// Missing instructions
+//    - mov
+//    - load
+//    - store
+//    - copy
+//    - fill
+
+
 #define OVMI_NOP               0x00
-#define OVMI_ADD               0x01   // r = a + b
-#define OVMI_SUB               0x02   // r = a - b
-#define OVMI_MUL               0x03   // r = a * b
-#define OVMI_DIV               0x04   // r = a / b
-#define OVMI_REM               0x05   // r = a % b
-
-#define OVMI_AND               0x06   // r = a & b
-#define OVMI_OR                0x07   // r = a | b
-#define OVMI_XOR               0x08   // r = a ^ b
-#define OVMI_NOT               0x09   // r = ~a
-#define OVMI_SHL               0x0A   // r = a << b
-#define OVMI_SHR               0x0B   // r = a >> b
-#define OVMI_SAR               0x0C   // r = a >>> b
-
-#define OVMI_IMM               0x0D   // r = imm a
-
-#define OVMI_LT                0x20   // r = a < b
-#define OVMI_LT_S              0x21   // r = a < b
-#define OVMI_LE                0x22   // r = a <= b
-#define OVMI_LE_S              0x23   // r = a <= b
-#define OVMI_EQ                0x24   // r = a == b
-#define OVMI_GE                0x25   // r = a >= b
-#define OVMI_GE_S              0x26   // r = a >= b
-#define OVMI_GT                0x27   // r = a > b
-#define OVMI_GT_S              0x28   // r = a > b
-#define OVMI_NE                0x29   // r = a != b
-
-#define OVMI_PARAM             0x30   // push a
-#define OVMI_RETURN            0x31   // return a
-#define OVMI_CALL              0x32   // r = a(...)
-#define OVMI_NATIVE_CALL       0x33   // r = a(...)
-
-#define OVMI_BR                0x40   // br a
-#define OVMI_BR_NZ             0x41   // br a if b != 0
-
-#define OVMI_CLZ               0x50   // r = clz(a)
-#define OVMI_CTZ               0x51   // r = ctr(a)
-#define OVMI_POPCNT            0x52   // r = popcnt(a)
+#define OVMI_ADD               0x01   // $r = $a + $b
+#define OVMI_SUB               0x02   // $r = $a - $b
+#define OVMI_MUL               0x03   // $r = $a * $b
+#define OVMI_DIV               0x04   // $r = $a / $b
+#define OVMI_REM               0x05   // $r = $a % $b
+
+#define OVMI_AND               0x06   // $r = $a & $b
+#define OVMI_OR                0x07   // $r = $a | $b
+#define OVMI_XOR               0x08   // $r = $a ^ $b
+#define OVMI_NOT               0x09   // $r = ~$a
+#define OVMI_SHL               0x0A   // $r = $a << $b
+#define OVMI_SHR               0x0B   // $r = $a >> $b
+#define OVMI_SAR               0x0C   // $r = $a >>> $b
+
+#define OVMI_IMM               0x10   // $r = i/l/f/d
+#define OVMI_MOV               0x10   // $r = $a
+#define OVMI_LOAD              0x11   // $r = mem[$a]
+#define OVMI_STORE             0x12   // mem[$a] = $b
+#define OVMI_COPY              0x13   // memcpy($r, $a, $b)
+#define OVMI_FILL              0x14   // memset($r, $a, $b)
+
+#define OVMI_LT                0x20   // $r = $a < $b
+#define OVMI_LT_S              0x21   // $r = $a < $b
+#define OVMI_LE                0x22   // $r = $a <= $b
+#define OVMI_LE_S              0x23   // $r = $a <= $b
+#define OVMI_EQ                0x24   // $r = $a == $b
+#define OVMI_GE                0x25   // $r = $a >= $b
+#define OVMI_GE_S              0x26   // $r = $a >= $b
+#define OVMI_GT                0x27   // $r = $a > $b
+#define OVMI_GT_S              0x28   // $r = $a > $b
+#define OVMI_NE                0x29   // $r = $a != $b
+
+#define OVMI_PARAM             0x30   // push $a
+#define OVMI_RETURN            0x31   // return $a
+#define OVMI_CALL              0x32   // $r = a(...)
+#define OVMI_NATIVE_CALL       0x33   // $r = a(...)
+#define OVMI_CALLI             0x34   // $r = $a(...)
+#define OVMI_NATIVE_CALLI      0x35   // $r = $a(...)
+
+#define OVMI_BR                0x40   // br pc + a             // Relative branching
+#define OVMI_BR_NZ             0x41   // br pc + a if $b != 0
+#define OVMI_BRI               0x42   // br pc + $a             // Relative branching
+#define OVMI_BRI_NZ            0x43   // br pc + $a if $b != 0
+
+#define OVMI_CLZ               0x50   // $r = clz($a)
+#define OVMI_CTZ               0x51   // $r = ctr($a)
+#define OVMI_POPCNT            0x52   // $r = popcnt($a)
 
 
 //
index 0fe38824517e2fd27a6f97f64af1f9270b9d37f0..ea9a97eccd099ebd30fca5d30bcfc440e64eb21e 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
@@ -13,30 +13,42 @@ int main(int argc, char *argv[]) {
 
     ovm_program_t *prog = ovm_program_new(store);
 
-    static ovm_instr_t instrs[] = {
+    ovm_instr_t func1_instrs[] = {
         // { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 0, 10 },
         // { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 1, 20 },
         // { .full_instr = OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F32), 2, 0, 1 },
+        { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 6, 10 },
+        { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 7, 1 },
         { .full_instr = OVMI_PARAM, 0, 0 },
         { .full_instr = OVMI_PARAM, 0, 1 },
-        { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 5, 1 },
-        { .full_instr = OVMI_CALL, 2, 5 },
+        { .full_instr = OVMI_CALL, 2, 1 },
         { .full_instr = OVMI_PARAM, 0, 2 },
-        { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 3, 0 },
-        { .full_instr = OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_F32), 4, 3 },
+        { .full_instr = OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_F32), 4, 0 },
+        { .full_instr = OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I32), 6, 6, 7 },
+        { .full_instr = OVMI_BR_NZ, 0, -4, 6 },
         { .full_instr = OVMI_RETURN, 0, 4 },
+    };
 
-
+    ovm_instr_t func2_instrs[] = {
         { .full_instr = OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F32), 2, 0, 1 },
+        { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 3, 0x1000 },
+        { .full_instr = OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_F32), 0, 3, 2 },
+        { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 4, 0x2000 },
+        { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 5, 4 },
+        { .full_instr = OVMI_COPY, 4, 3, 5 },
+
+        { .full_instr = OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_F32), 2, 4 },
         { .full_instr = OVMI_RETURN, 0, 2 },
     };
     
-    ovm_program_add_instructions(prog, sizeof(instrs) / sizeof(*instrs), instrs);
-    ovm_program_register_func(prog, 0, 2, 5);
-    ovm_program_register_func(prog, 8, 2, 3);
+    ovm_program_begin_func(prog, "main", 2, 8);
+    ovm_program_add_instructions(prog, sizeof(func1_instrs) / sizeof(*func1_instrs), func1_instrs);
+    ovm_program_begin_func(prog, "add", 2, 6);
+    ovm_program_add_instructions(prog, sizeof(func2_instrs) / sizeof(*func2_instrs), func2_instrs);
+
     ovm_program_register_native_func(prog, print_result, NULL, 1);
 
-    ovm_program_print_instructions(prog, 0, sizeof(instrs) / sizeof(*instrs));
+    ovm_program_print_instructions(prog, 0, bh_arr_length(prog->code));
 
     ovm_engine_t *engine = ovm_engine_new(store);
     ovm_state_t  *state = ovm_state_new(engine);
@@ -49,5 +61,10 @@ int main(int argc, char *argv[]) {
     ovm_value_t result = ovm_func_call(engine, state, prog, 0, 2, values);
     printf("%d %f\n", result.type, result.f32);
 
+    ovm_state_delete(state);
+    ovm_engine_delete(engine);
+    ovm_program_delete(prog);
+    ovm_store_delete(store);
+
     return 0;
 }
index 9465cb5d83dab48461e87658860eb3e422909657..cf6ec4aa348777ce4d2ff032aabd5335c594b5f0 100644 (file)
@@ -29,7 +29,6 @@ ovm_program_t *ovm_program_new(ovm_store_t *store) {
 
     bh_arr_new(store->heap_allocator, program->funcs, 16);
     bh_arr_new(store->heap_allocator, program->native_funcs, 16);
-    bh_arr_new(store->heap_allocator, program->initialized_data, 16);
 
     bh_arr_new(store->heap_allocator, program->code, 1024);
 
@@ -39,19 +38,18 @@ ovm_program_t *ovm_program_new(ovm_store_t *store) {
 void ovm_program_delete(ovm_program_t *program) {
     bh_arr_free(program->funcs);
     bh_arr_free(program->native_funcs);
-    bh_arr_free(program->initialized_data);
     bh_arr_free(program->code);
 
     bh_free(program->store->heap_allocator, program);
 }
 
-void ovm_program_register_func(ovm_program_t *program, i32 instr, i32 param_count, i32 value_number_count) {
-    ovm_func_t *func = bh_alloc_item(program->store->arena_allocator, ovm_func_t);
-
-    func->start_instr = instr;
-    func->param_count = param_count;
-    func->value_number_count = value_number_count;
-    func->id = bh_arr_length(program->funcs);
+void ovm_program_register_func(ovm_program_t *program, char *name, i32 instr, i32 param_count, i32 value_number_count) {
+    ovm_func_t func;
+    func.id = bh_arr_length(program->funcs);
+    func.name = name;
+    func.start_instr = instr;
+    func.param_count = param_count;
+    func.value_number_count = value_number_count;
 
     bh_arr_push(program->funcs, func);
 }
@@ -65,6 +63,17 @@ void ovm_program_register_native_func(ovm_program_t *program, void (*func)(void
     bh_arr_push(program->native_funcs, native_func);
 }
 
+void ovm_program_begin_func(ovm_program_t *program, char *name, i32 param_count, i32 value_number_count) {
+    ovm_func_t func;
+    func.id = bh_arr_length(program->funcs);
+    func.name = name;
+    func.start_instr = bh_arr_length(program->code);
+    func.param_count = param_count;
+    func.value_number_count = value_number_count;
+
+    bh_arr_push(program->funcs, func);
+}
+
 void ovm_program_add_instructions(ovm_program_t *program, i32 instr_count, ovm_instr_t *instrs) {
     fori (i, 0, instr_count) {
         bh_arr_push(program->code, instrs[i]);
@@ -78,6 +87,8 @@ static char *ovm_instr_name(i32 full_instr) {
     static char buf[64];
 
     switch (full_instr) {
+        C(OVMI_NOP)
+
         C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I8))
         C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I16))
         C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I32))
@@ -153,10 +164,96 @@ static char *ovm_instr_name(i32 full_instr) {
         C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_F32))
         C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_F64))
 
+        C(OVMI_MOV)
+        C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_LOAD, OVM_TYPE_F64))
+        C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_STORE, OVM_TYPE_F64))
+        C(OVMI_COPY)
+        C(OVMI_FILL)
+
+        C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_LT_S, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_F64))
+
         C(OVMI_PARAM)
-        C(OVMI_CALL)
         C(OVMI_RETURN)
-
+        C(OVMI_CALL)
+        C(OVMI_CALLI)
         C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_NONE))
         C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I8))
         C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I16))
@@ -164,6 +261,16 @@ static char *ovm_instr_name(i32 full_instr) {
         C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I64))
         C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_F32))
         C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_F64))
+        C(OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_NONE))
+        C(OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_F64))
+
+        C(OVMI_BR)
+        C(OVMI_BR_NZ)
 
         default:
             snprintf(buf, 64, "unknown (%d)", full_instr);
@@ -173,6 +280,15 @@ static char *ovm_instr_name(i32 full_instr) {
 
 void ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count) {
     fori (i, start_instr, instr_count) {
+        //
+        // Horribly inefficient way of checking to see if this instruction
+        // 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) {
+                printf("\n[%d]%s:\n", func->id, func->name);
+            }
+        }
+
         ovm_instr_t instr = program->code[i];
         printf("%48s | r=%02d a=%02d b=%02d  i=%d f=%f l=%ld d=%lf\n", ovm_instr_name(instr.full_instr), instr.r, instr.a, instr.b, instr.i, instr.f, instr.l, instr.d);
     }
@@ -186,7 +302,7 @@ ovm_engine_t *ovm_engine_new(ovm_store_t *store) {
     ovm_engine_t *engine = bh_alloc_item(store->heap_allocator, ovm_engine_t);
 
     engine->store = store;
-    engine->memory_size = 1 << 16;
+    engine->memory_size = 1ull << 32;
     engine->memory = mmap(NULL, engine->memory_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
 
     return engine;
@@ -199,6 +315,15 @@ void ovm_engine_delete(ovm_engine_t *engine) {
     bh_free(store->heap_allocator, engine);
 }
 
+void ovm_engine_memory_copy(ovm_engine_t *engine, i64 target, void *data, i64 size) {
+    assert(engine);
+    assert(engine->memory);
+    assert(data);
+    assert(size + target < engine->memory_size);
+    memcpy(((u8 *) engine->memory) + target, data, size);
+}
+
+
 
 //
 // State
@@ -232,14 +357,14 @@ void ovm_state_delete(ovm_state_t *state) {
 // Function calling
 
 static void ovm__func_setup_stack_frame(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program, i32 func_idx, i32 result_number) {
-    ovm_func_t *func = program->funcs[func_idx];
+    ovm_func_t *func = &program->funcs[func_idx];
 
     //
     // Push a stack frame
     ovm_stack_frame_t frame;
     frame.func = func;
     frame.value_number_count = func->value_number_count;
-    frame.value_number_base  = state->value_number_offset + func->value_number_count;
+    frame.value_number_base  = bh_arr_length(state->numbered_values);
     frame.return_address = state->pc;
     frame.return_number_value = result_number;
     bh_arr_push(state->stack_frames, frame);
@@ -264,7 +389,7 @@ ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_
         state->numbered_values[i + state->value_number_offset] = params[i];
     }
 
-    state->pc = program->funcs[func_idx]->start_instr;
+    state->pc = program->funcs[func_idx].start_instr;
     ovm_run_code(engine, state, program);
 
     return state->numbered_values[state->value_number_offset--];
@@ -294,7 +419,8 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr
 
 #define OVM_OP(i, t, op, ctype) \
             case OVM_TYPED_INSTR(i, t): \
-                (VAL(instr.r).ctype = VAL(instr.a).ctype op VAL(instr.b).ctype); \
+                 VAL(instr.r).type = t; \
+                 VAL(instr.r).ctype = VAL(instr.a).ctype op VAL(instr.b).ctype; \
                  break;
 
             OVM_OP(OVMI_ADD, OVM_TYPE_I8 , +, i8)
@@ -359,7 +485,8 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr
 
 #define OVM_OP(i, t, op, ctype) \
             case OVM_TYPED_INSTR(i, t): \
-                (VAL(instr.r).ctype = op VAL(instr.a).ctype); \
+                 VAL(instr.r).type = t; \
+                 VAL(instr.r).ctype = op VAL(instr.a).ctype; \
                  break;
             
             OVM_OP(OVMI_NOT, OVM_TYPE_I8 , ~, i8)
@@ -371,7 +498,8 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr
 
 #define OVM_OP(i, t, op, ctype, cast_type) \
             case OVM_TYPED_INSTR(i, t): \
-                (VAL(instr.r).i32 = (i32) ((cast_type) VAL(instr.a).ctype op (cast_type) VAL(instr.b).ctype)); \
+                 VAL(instr.r).type = OVM_TYPE_I32; \
+                 VAL(instr.r).i32 = (i32) ((cast_type) VAL(instr.a).ctype op (cast_type) VAL(instr.b).ctype); \
                  break;
 
             OVM_OP(OVMI_LT, OVM_TYPE_I8 , <, i8,  u8)
@@ -446,9 +574,10 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr
 
 #undef OVM_OP
 
-#define OVM_IMM(type, dtype, stype) \
-            case OVM_TYPED_INSTR(OVMI_IMM, type): \
-                (VAL(instr.r).dtype = instr.stype); \
+#define OVM_IMM(t, dtype, stype) \
+            case OVM_TYPED_INSTR(OVMI_IMM, t): \
+                VAL(instr.r).type = t; \
+                VAL(instr.r).dtype = instr.stype; \
                 break;
             
             OVM_IMM(OVM_TYPE_I8,  i8,  i)
@@ -460,39 +589,113 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr
 
 #undef OVM_IMM
 
-            case OVMI_PARAM:
-                bh_arr_push(state->params, VAL(instr.a));
+            case OVMI_MOV:
+                VAL(instr.r) = VAL(instr.a);
                 break;
 
-            case OVMI_CALL: {
-                i32 func_idx = VAL(instr.a).i32;
-                ovm__func_setup_stack_frame(engine, state, program, func_idx, instr.r);
-
-                //
-                // Apply parameters
-                ovm_func_t *func = program->funcs[func_idx];
-                fori (i, 0, func->param_count) {
-                    VAL(i) = state->params[i];
-                }
-                bh_arr_set_length(state->params, 0);
-
-                state->pc = func->start_instr;
+#define OVM_LOAD(type_, stype) \
+            case OVM_TYPED_INSTR(OVMI_LOAD, type_): \
+                VAL(instr.r).type = type_; \
+                VAL(instr.r).stype = * (stype *) &((u8 *) engine->memory)[(u32) VAL(instr.a).i32]; \
+                break;
+
+            OVM_LOAD(OVM_TYPE_I8,  i8)
+            OVM_LOAD(OVM_TYPE_I16, i16)
+            OVM_LOAD(OVM_TYPE_I32, i32)
+            OVM_LOAD(OVM_TYPE_I64, i64)
+            OVM_LOAD(OVM_TYPE_F32, f32)
+            OVM_LOAD(OVM_TYPE_F64, f64)
+
+#undef OVM_LOAD
+
+#define OVM_STORE(type_, stype) \
+            case OVM_TYPED_INSTR(OVMI_STORE, type_): \
+                * (stype *) &((u8 *) engine->memory)[(u32) VAL(instr.a).i32] = VAL(instr.b).stype; \
+                break;
+
+            OVM_STORE(OVM_TYPE_I8,  i8)
+            OVM_STORE(OVM_TYPE_I16, i16)
+            OVM_STORE(OVM_TYPE_I32, i32)
+            OVM_STORE(OVM_TYPE_I64, i64)
+            OVM_STORE(OVM_TYPE_F32, f32)
+            OVM_STORE(OVM_TYPE_F64, f64)
+
+#undef OVM_STORE
+
+            case OVMI_COPY: {
+                i32 dest  = VAL(instr.r).i32;
+                i32 src   = VAL(instr.a).i32;
+                i32 count = VAL(instr.b).i32;
+
+                u8 *base = engine->memory;
+                memcpy(&base[dest], &base[src], count);
+                break;
+            }
+
+            case OVMI_FILL: {
+                i32 dest  = VAL(instr.r).i32;
+                i8  byte  = VAL(instr.a).i8;
+                i32 count = VAL(instr.b).i32;
+
+                u8 *base = engine->memory;
+                memset(&base[dest], byte, count);
                 break;
             }
 
+            case OVMI_PARAM:
+                bh_arr_push(state->params, VAL(instr.a));
+                break;
+
             case OVMI_RETURN: {
                 ovm_stack_frame_t frame = bh_arr_pop(state->stack_frames); 
 
                 ovm_value_t val = VAL(instr.a);
                 bh_arr_fastdeleten(state->numbered_values, frame.value_number_count);
 
-                state->value_number_offset = frame.value_number_base;
+                state->value_number_offset = bh_arr_last(state->stack_frames).value_number_base;
                 VAL(frame.return_number_value) = val;
 
                 state->pc = frame.return_address;
                 break;
             }
 
+#define OVM_CALL_CODE(func_idx) \
+            i32 fidx = func_idx; \
+            ovm__func_setup_stack_frame(engine, state, program, fidx, instr.r); \
+ \
+            ovm_func_t *func = &program->funcs[fidx]; \
+            assert(func->param_count == bh_arr_length(state->params)); \
+            fori (i, 0, func->param_count) { \
+                VAL(i) = state->params[i]; \
+            } \
+            bh_arr_set_length(state->params, 0); \
+ \
+            state->pc = func->start_instr;
+
+            case OVMI_CALL: {
+                OVM_CALL_CODE(instr.a);
+                break;
+            }
+
+            case OVMI_CALLI: {
+                OVM_CALL_CODE(VAL(instr.a).i32);
+                break;
+            }
+
+#undef OVM_CALL_CODE
+
+#define OVM_NATIVE_CALL_CODE(func_idx) \
+            ovm_native_func_t native_func = program->native_funcs[func_idx]; \
+            assert(native_func.param_count == bh_arr_length(state->params)); \
+ \
+            ovm_value_t result = {0}; \
+            native_func.native_func(native_func.userdata, state->params, &result); \
+            bh_arr_set_length(state->params, 0); \
+ \
+            if (instr.type != OVM_TYPE_NONE) { \
+                VAL(instr.r) = result; \
+            }
+
             case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_NONE):
             case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I8):
             case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I16):
@@ -500,28 +703,27 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr
             case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I64):
             case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_F32):
             case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_F64): {
-                ovm_native_func_t native_func = program->native_funcs[VAL(instr.a).i32];
-
-                ovm_value_t result = {0};
-                native_func.native_func(native_func.userdata, state->params, &result);
-                bh_arr_set_length(state->params, 0);
-
-                if (instr.type != OVM_TYPE_NONE) {
-                    VAL(instr.r) = result;
-                }
-
+                OVM_NATIVE_CALL_CODE(instr.a);
                 break;
             }
 
-            case OVMI_BR:
-                state->pc = VAL(instr.a).i32;
+            case OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_NONE):
+            case OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_I8):
+            case OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_I16):
+            case OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_I32):
+            case OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_I64):
+            case OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_F32):
+            case OVM_TYPED_INSTR(OVMI_NATIVE_CALLI, OVM_TYPE_F64): {
+                OVM_NATIVE_CALL_CODE(VAL(instr.a).i32);
                 break;
+            }
 
-            case OVMI_BR_NZ:
-                if (VAL(instr.a).i32 != 0) {
-                    state->pc = VAL(instr.r).i32;
-                }
-                break;
+#undef OVM_NATIVE_CALL_CODE
+
+            case OVMI_BR:     state->pc += instr.a; break;
+            case OVMI_BRI:    state->pc += VAL(instr.a).i32; break;
+            case OVMI_BR_NZ:  if (VAL(instr.b).i32 != 0) state->pc += instr.a; break;
+            case OVMI_BRI_NZ: if (VAL(instr.b).i32 != 0) state->pc += VAL(instr.a).i32; break;
 
             default:
                 printf("ERROR:\n");