basic VM with func calls setup!
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 13 Jun 2022 22:48:49 +0000 (17:48 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 13 Jun 2022 22:48:49 +0000 (17:48 -0500)
build.sh
include/bh.h
include/vm.h
src/cli.c
src/vm/vm.c
src/wasm.c

index f3c2fe45394d86bb67dd3c567dccfaeb17241e71..829b2400ca20e8324be8ac8e5758128e179c14cd 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -12,6 +12,6 @@ $CC $FLAGS $INCLUDES -shared -fPIC -o $TARGET $C_FILES $LIBS $WARNINGS
 
 C_FILES="src/cli.c"
 TARGET=onyx-debug
-LIBS="-L$(pwd) -lonyx_embedder"
+LIBS="-L$(pwd) -lonyx_embedder -Wl,-rpath=./"
 
 $CC $FLAGS $INCLUDES -o $TARGET $C_FILES $LIBS $WARNINGS
index b187b96de7456ce6efc74eb506a2e789ee3331fc..b83fd81211ea36a6c519635342fe3de9d0c27220 100644 (file)
@@ -585,6 +585,7 @@ typedef struct bh__arr {
 
 #define bh_arr_deleten(arr, i, n)     (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n))
 #define bh_arr_fastdelete(arr, i)     (arr[i] = arr[--bh__arrhead(arr)->length])
+#define bh_arr_fastdeleten(arr, n)    (bh__arrhead(arr)->length -= n)
 
 #define bh_arr_each(T, var, arr)      for (T* var = (arr); !bh_arr_end((arr), var); var++)
 #define bh_arr_rev_each(T, var, arr)  for (T* var = &bh_arr_last((arr)); !bh_arr_start((arr), var); var--)
@@ -2318,7 +2319,7 @@ void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) {
 
     memmove(
         (char *)(*arr) + elemsize * index,                    // Target
-        (char *)(*arr) + elemsize * (index + numelems),        // Source
+        (char *)(*arr) + elemsize * (index + numelems),       // Source
         elemsize * (arrptr->length - (index + numelems)));    // Length
     arrptr->length -= numelems;
 }
index 08087eaead4d99a271bcf145d2867e91c10391dd..4005773a8b7d23a9791dba2c6dc6b97291521900 100644 (file)
@@ -8,13 +8,30 @@ typedef u8  ovm_valtype_t;
 typedef i32 ovm_valnum_t;
 typedef u32 ovm_instr_kind_t;
 
+typedef struct ovm_store_t ovm_store_t;
 typedef struct ovm_engine_t ovm_engine_t;
 typedef struct ovm_program_t ovm_program_t;
 typedef struct ovm_state_t ovm_state_t;
+typedef struct ovm_stack_frame_t ovm_stack_frame_t;
 typedef struct ovm_func_t ovm_func_t;
+typedef struct ovm_native_func_t ovm_native_func_t;
+typedef struct ovm_value_t ovm_value_t;
 typedef struct ovm_instr_t ovm_instr_t;
 typedef struct ovm_static_data_t ovm_static_data_t;
 
+
+//
+// Contains storage.
+struct ovm_store_t {
+    bh_allocator heap_allocator;
+    bh_arena     arena;
+    bh_allocator arena_allocator;
+};
+
+ovm_store_t *ovm_store_new();
+void         ovm_Store_delete(ovm_store_t *store);
+
+
 struct ovm_static_data_t {
     i64   dest_addr;
     void *data;
@@ -26,12 +43,21 @@ struct ovm_static_data_t {
 // VM. It can be constructed incrementally as needed.
 //
 struct ovm_program_t {
+    ovm_store_t *store;
+
     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;
 };
 
-ovm_program_t *ovm_program_new();
+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);
 
 //
 // Represents the running configuration and static
@@ -39,11 +65,13 @@ void           ovm_program_delete(ovm_program_t *program);
 // If multiple threads are used, only one engine is needed.
 // 
 struct ovm_engine_t {
+    ovm_store_t *store;
+
     i64   memory_size; // This is probably going to always be 4GiB.
     void *memory;
 };
 
-ovm_engine_t *ovm_engine_new();
+ovm_engine_t *ovm_engine_new(ovm_store_t *store);
 void          ovm_engine_delete(ovm_engine_t *engine);
 
 //
@@ -51,12 +79,32 @@ void          ovm_engine_delete(ovm_engine_t *engine);
 // If multiple threads are used, multiple states are needed.
 // 
 struct ovm_state_t {
+    ovm_store_t *store;
+
     i32 pc;
+    i32 value_number_offset;
+    
+    bh_arr(ovm_value_t) numbered_values;
+    bh_arr(ovm_value_t) params;
+    bh_arr(ovm_stack_frame_t) stack_frames;
 };
 
-ovm_state_t *ovm_state_new();
+ovm_state_t *ovm_state_new(ovm_engine_t *engine);
 void         ovm_state_delete(ovm_state_t *state);
 
+
+//
+//
+struct ovm_stack_frame_t {
+    ovm_func_t *func;
+    i32 value_number_count;
+    i32 value_number_base;
+
+    i32 return_address;
+    i32 return_number_value;
+};
+
+
 //
 // Represents a function that can be executed on the VM.
 //
@@ -66,17 +114,28 @@ struct ovm_func_t {
     // to reference this function. It is only here for debugging and posterity.
     i32 id;
 
-    //
-    // parameters
-    // return type
+    i32 start_instr;
+
+    i32 param_count;
+    i32 value_number_count;
 
-    bh_arr(ovm_instr_t) instructions;
+    // return type
 };
  
 ovm_func_t  *ovm_func_new();
 ovm_instr_t *ovm_func_add_instruction(ovm_func_t *func, ovm_instr_kind_t instr, ovm_valtype_t type);
 void         ovm_func_delete(ovm_func_t *func);
 
+struct ovm_native_func_t {
+    i32 param_count;
+
+    void (*native_func)(void *userdata, ovm_value_t* params, ovm_value_t* result);
+    void *userdata;
+};
+
+ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program, i32 func_idx, i32 param_count, ovm_value_t *params);
+void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program);
+
 //
 // Instruction encoding
 //
@@ -93,9 +152,31 @@ void         ovm_func_delete(ovm_func_t *func);
 #define OVM_TYPE_F64    0x06
 #define OVM_TYPE_V128   0x07
 
+struct ovm_value_t {
+    ovm_valtype_t type;
+    union {
+        i8  i8;
+        i16 i16;
+        i32 i32;
+        i64 i64;
+        u8  u8;
+        u16 u16;
+        u32 u32;
+        u64 u64;
+        f32 f32;
+        f64 f64;
+    };
+};
+
 struct ovm_instr_t {
-    ovm_instr_kind_t instr : 24;
-    ovm_valtype_t    type;
+    union {
+        struct {
+            ovm_valtype_t    type  : 8;
+            ovm_instr_kind_t instr : 24;
+        };
+
+        u32 full_instr;
+    };
 
     // Destination value number.
     ovm_valnum_t r;
@@ -115,10 +196,45 @@ struct ovm_instr_t {
 };
 
 #define OVMI_NOP               0x00
-#define OVMI_ADD               0x01
-#define OVMI_SUB               0x02
-#define OVMI_MUL               0x03
-#define OVMI_DIV               0x04
+#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)
+
 
 //
 // OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I32) == instruction for adding i32s
index 3324024e707599c458642336373851e6d4cf8626..0fe38824517e2fd27a6f97f64af1f9270b9d37f0 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
@@ -1,7 +1,53 @@
+#include "vm.h"
+
+void print_result(void *data, ovm_value_t *params, ovm_value_t *result) {
+    printf("Result: %f\n", params[0].f32);
+
+    result->type = OVM_TYPE_F32;
+    result->f32 = 123;
+}
 
 int main(int argc, char *argv[]) {
-    int embed_test();
-    embed_test();
+
+    ovm_store_t *store = ovm_store_new();
+
+    ovm_program_t *prog = ovm_program_new(store);
+
+    static ovm_instr_t 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 = 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_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 = OVMI_RETURN, 0, 4 },
+
+
+        { .full_instr = OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F32), 2, 0, 1 },
+        { .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_register_native_func(prog, print_result, NULL, 1);
+
+    ovm_program_print_instructions(prog, 0, sizeof(instrs) / sizeof(*instrs));
+
+    ovm_engine_t *engine = ovm_engine_new(store);
+    ovm_state_t  *state = ovm_state_new(engine);
+
+    state->pc = 0;
+    ovm_value_t values[] = {
+        { .type = OVM_TYPE_F32, .f32 = 20 },
+        { .type = OVM_TYPE_F32, .f32 = 40 },
+    };
+    ovm_value_t result = ovm_func_call(engine, state, prog, 0, 2, values);
+    printf("%d %f\n", result.type, result.f32);
 
     return 0;
 }
index a1999888f094a8c714b175bea1dd5ed09dd638c7..9465cb5d83dab48461e87658860eb3e422909657 100644 (file)
@@ -1 +1,535 @@
 #include "vm.h"
+
+#include <sys/mman.h>
+
+//
+// Store
+ovm_store_t *ovm_store_new() {
+    ovm_store_t *store = malloc(sizeof(*store));
+
+    store->heap_allocator = bh_heap_allocator();
+
+    bh_arena_init(&store->arena, store->heap_allocator, 1 << 20);
+    store->arena_allocator = bh_arena_allocator(&store->arena);
+
+    return store;
+}
+
+void ovm_store_delete(ovm_store_t *store) {
+    bh_arena_free(&store->arena);
+    free(store);
+}
+
+
+//
+// Program
+ovm_program_t *ovm_program_new(ovm_store_t *store) {
+    ovm_program_t *program = bh_alloc_item(store->heap_allocator, ovm_program_t);
+    program->store = 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);
+
+    return program;
+}
+
+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);
+
+    bh_arr_push(program->funcs, func);
+}
+
+void ovm_program_register_native_func(ovm_program_t *program, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count) {
+    ovm_native_func_t native_func;
+    native_func.param_count = param_count;
+    native_func.native_func = func;
+    native_func.userdata = data;
+
+    bh_arr_push(program->native_funcs, native_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]);
+    }
+}
+
+
+static char *ovm_instr_name(i32 full_instr) {
+#define C(...) case __VA_ARGS__: return #__VA_ARGS__;
+
+    static char buf[64];
+
+    switch (full_instr) {
+        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))
+        C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I64))
+
+        C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I64))
+
+        C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I64))
+
+        C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I16))
+        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))
+        C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I64))
+
+        C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I64))
+
+        C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I64))
+
+        C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_F64))
+
+        C(OVMI_PARAM)
+        C(OVMI_CALL)
+        C(OVMI_RETURN)
+
+        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))
+        C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I32))
+        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))
+
+        default:
+            snprintf(buf, 64, "unknown (%d)", full_instr);
+            return buf;
+    }
+}
+
+void ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count) {
+    fori (i, start_instr, instr_count) {
+        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);
+    }
+}
+
+
+
+//
+// Engine
+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 = mmap(NULL, engine->memory_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+
+    return engine;
+}
+
+void ovm_engine_delete(ovm_engine_t *engine) {
+    ovm_store_t *store = engine->store;
+
+    munmap(engine->memory, engine->memory_size);
+    bh_free(store->heap_allocator, engine);
+}
+
+
+//
+// State
+ovm_state_t *ovm_state_new(ovm_engine_t *engine) {
+    ovm_store_t *store = engine->store;
+    ovm_state_t *state = bh_alloc_item(store->heap_allocator, ovm_state_t);
+
+    state->store = store;
+    state->pc = 0;
+    state->value_number_offset = 0;
+
+    bh_arr_new(store->heap_allocator, state->numbered_values, 64);
+    bh_arr_new(store->heap_allocator, state->params, 16);
+    bh_arr_new(store->heap_allocator, state->stack_frames, 32);
+
+    return state;
+}
+
+void ovm_state_delete(ovm_state_t *state) {
+    ovm_store_t *store = state->store;
+
+    bh_arr_free(state->numbered_values);
+    bh_arr_free(state->params);
+    bh_arr_free(state->stack_frames);
+
+    bh_free(store->heap_allocator, 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];
+
+    //
+    // 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.return_address = state->pc;
+    frame.return_number_value = result_number;
+    bh_arr_push(state->stack_frames, frame);
+
+    //
+    // Move the base pointer to the value numbers.
+    state->value_number_offset = frame.value_number_base;
+
+    //
+    // Setup value numbers
+    bh_arr_insert_end(state->numbered_values, func->value_number_count);
+}
+
+ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program, i32 func_idx, i32 param_count, ovm_value_t *params) {
+    bh_arr_insert_end(state->numbered_values, 1);
+    state->value_number_offset += 1;
+
+    state->pc = 0x7fffffff;
+    ovm__func_setup_stack_frame(engine, state, program, func_idx, 0);
+
+    fori (i, 0, param_count) {
+        state->numbered_values[i + state->value_number_offset] = params[i];
+    }
+
+    state->pc = program->funcs[func_idx]->start_instr;
+    ovm_run_code(engine, state, program);
+
+    return state->numbered_values[state->value_number_offset--];
+}
+
+
+void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program) {
+    assert(engine);
+    assert(state);
+    assert(program);
+
+#define VAL(loc) (state->numbered_values[(u32) (loc + state->value_number_offset)])
+
+    ovm_instr_t *code = program->code;
+   
+    while (state->pc < bh_arr_length(program->code)) {
+        //
+        // Incrementing the program counter here.
+        // All instructions that compute something relative
+        // to the program counter have to know that the program
+        // counter will refer to the instruction AFTER the one
+        // being executed.                  - brendanfh 2022/06/13
+        ovm_instr_t instr = code[state->pc++];
+
+        switch (instr.full_instr) {
+            case OVMI_NOP: break;
+
+#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); \
+                 break;
+
+            OVM_OP(OVMI_ADD, OVM_TYPE_I8 , +, i8)
+            OVM_OP(OVMI_ADD, OVM_TYPE_I16, +, i16)
+            OVM_OP(OVMI_ADD, OVM_TYPE_I32, +, i32)
+            OVM_OP(OVMI_ADD, OVM_TYPE_I64, +, i64)
+            OVM_OP(OVMI_ADD, OVM_TYPE_F32, +, f32)
+            OVM_OP(OVMI_ADD, OVM_TYPE_F64, +, f64)
+
+            OVM_OP(OVMI_SUB, OVM_TYPE_I8 , -, i8)
+            OVM_OP(OVMI_SUB, OVM_TYPE_I16, -, i16)
+            OVM_OP(OVMI_SUB, OVM_TYPE_I32, -, i32)
+            OVM_OP(OVMI_SUB, OVM_TYPE_I64, -, i64)
+            OVM_OP(OVMI_SUB, OVM_TYPE_F32, -, f32)
+            OVM_OP(OVMI_SUB, OVM_TYPE_F64, -, f64)
+
+            OVM_OP(OVMI_MUL, OVM_TYPE_I8 , *, i8)
+            OVM_OP(OVMI_MUL, OVM_TYPE_I16, *, i16)
+            OVM_OP(OVMI_MUL, OVM_TYPE_I32, *, i32)
+            OVM_OP(OVMI_MUL, OVM_TYPE_I64, *, i64)
+            OVM_OP(OVMI_MUL, OVM_TYPE_F32, *, f32)
+            OVM_OP(OVMI_MUL, OVM_TYPE_F64, *, f64)
+
+            OVM_OP(OVMI_DIV, OVM_TYPE_I8 , /, i8)
+            OVM_OP(OVMI_DIV, OVM_TYPE_I16, /, i16)
+            OVM_OP(OVMI_DIV, OVM_TYPE_I32, /, i32)
+            OVM_OP(OVMI_DIV, OVM_TYPE_I64, /, i64)
+            OVM_OP(OVMI_DIV, OVM_TYPE_F32, /, f32)
+            OVM_OP(OVMI_DIV, OVM_TYPE_F64, /, f64)
+
+            OVM_OP(OVMI_REM, OVM_TYPE_I8 , %, i8)
+            OVM_OP(OVMI_REM, OVM_TYPE_I16, %, i16)
+            OVM_OP(OVMI_REM, OVM_TYPE_I32, %, i32)
+            OVM_OP(OVMI_REM, OVM_TYPE_I64, %, i64)
+
+            OVM_OP(OVMI_AND, OVM_TYPE_I8 , &, i8)
+            OVM_OP(OVMI_AND, OVM_TYPE_I16, &, i16)
+            OVM_OP(OVMI_AND, OVM_TYPE_I32, &, i32)
+            OVM_OP(OVMI_AND, OVM_TYPE_I64, &, i64)
+
+            OVM_OP(OVMI_OR, OVM_TYPE_I8 , |, i8)
+            OVM_OP(OVMI_OR, OVM_TYPE_I16, |, i16)
+            OVM_OP(OVMI_OR, OVM_TYPE_I32, |, i32)
+            OVM_OP(OVMI_OR, OVM_TYPE_I64, |, i64)
+
+            OVM_OP(OVMI_SHL, OVM_TYPE_I8 , <<, i8)
+            OVM_OP(OVMI_SHL, OVM_TYPE_I16, <<, i16)
+            OVM_OP(OVMI_SHL, OVM_TYPE_I32, <<, i32)
+            OVM_OP(OVMI_SHL, OVM_TYPE_I64, <<, i64)
+
+            OVM_OP(OVMI_SHR, OVM_TYPE_I8 , >> (u8), i8)
+            OVM_OP(OVMI_SHR, OVM_TYPE_I16, >> (u16), i16)
+            OVM_OP(OVMI_SHR, OVM_TYPE_I32, >> (u32), i32)
+            OVM_OP(OVMI_SHR, OVM_TYPE_I64, >> (u64), i64)
+
+            OVM_OP(OVMI_SAR, OVM_TYPE_I8 , >> (i8), i8)
+            OVM_OP(OVMI_SAR, OVM_TYPE_I16, >> (i16), i16)
+            OVM_OP(OVMI_SAR, OVM_TYPE_I32, >> (i32), i32)
+            OVM_OP(OVMI_SAR, OVM_TYPE_I64, >> (i64), i64)
+
+#undef OVM_OP
+
+#define OVM_OP(i, t, op, ctype) \
+            case OVM_TYPED_INSTR(i, t): \
+                (VAL(instr.r).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)
+
+#undef OVM_OP
+
+#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)); \
+                 break;
+
+            OVM_OP(OVMI_LT, OVM_TYPE_I8 , <, i8,  u8)
+            OVM_OP(OVMI_LT, OVM_TYPE_I16, <, i16, u16)
+            OVM_OP(OVMI_LT, OVM_TYPE_I32, <, i32, u32)
+            OVM_OP(OVMI_LT, OVM_TYPE_I64, <, i64, u64)
+            OVM_OP(OVMI_LT, OVM_TYPE_F32, <, f32, f32)
+            OVM_OP(OVMI_LT, OVM_TYPE_F64, <, f64, f32)
+
+            OVM_OP(OVMI_LT_S, OVM_TYPE_I8 , <, i8,  i8)
+            OVM_OP(OVMI_LT_S, OVM_TYPE_I16, <, i16, i16)
+            OVM_OP(OVMI_LT_S, OVM_TYPE_I32, <, i32, i32)
+            OVM_OP(OVMI_LT_S, OVM_TYPE_I64, <, i64, i64)
+            OVM_OP(OVMI_LT_S, OVM_TYPE_F32, <, f32, f32)
+            OVM_OP(OVMI_LT_S, OVM_TYPE_F64, <, f64, f32)
+
+            OVM_OP(OVMI_LE, OVM_TYPE_I8 , <=, i8,  u8)
+            OVM_OP(OVMI_LE, OVM_TYPE_I16, <=, i16, u16)
+            OVM_OP(OVMI_LE, OVM_TYPE_I32, <=, i32, u32)
+            OVM_OP(OVMI_LE, OVM_TYPE_I64, <=, i64, u64)
+            OVM_OP(OVMI_LE, OVM_TYPE_F32, <=, f32, f32)
+            OVM_OP(OVMI_LE, OVM_TYPE_F64, <=, f64, f64)
+
+            OVM_OP(OVMI_LE_S, OVM_TYPE_I8 , <=, i8,  i8)
+            OVM_OP(OVMI_LE_S, OVM_TYPE_I16, <=, i16, i16)
+            OVM_OP(OVMI_LE_S, OVM_TYPE_I32, <=, i32, i32)
+            OVM_OP(OVMI_LE_S, OVM_TYPE_I64, <=, i64, i64)
+            OVM_OP(OVMI_LE_S, OVM_TYPE_F32, <=, f32, f32)
+            OVM_OP(OVMI_LE_S, OVM_TYPE_F64, <=, f64, f64)
+
+            OVM_OP(OVMI_EQ, OVM_TYPE_I8 , ==, i8,  i8)
+            OVM_OP(OVMI_EQ, OVM_TYPE_I16, ==, i16, i16)
+            OVM_OP(OVMI_EQ, OVM_TYPE_I32, ==, i32, i32)
+            OVM_OP(OVMI_EQ, OVM_TYPE_I64, ==, i64, i64)
+            OVM_OP(OVMI_EQ, OVM_TYPE_F32, ==, f32, f32)
+            OVM_OP(OVMI_EQ, OVM_TYPE_F64, ==, f64, f64)
+
+            OVM_OP(OVMI_GE, OVM_TYPE_I8 , >=, i8,  u8)
+            OVM_OP(OVMI_GE, OVM_TYPE_I16, >=, i16, u16)
+            OVM_OP(OVMI_GE, OVM_TYPE_I32, >=, i32, u32)
+            OVM_OP(OVMI_GE, OVM_TYPE_I64, >=, i64, u64)
+            OVM_OP(OVMI_GE, OVM_TYPE_F32, >=, f32, f32)
+            OVM_OP(OVMI_GE, OVM_TYPE_F64, >=, f64, f64)
+
+            OVM_OP(OVMI_GE_S, OVM_TYPE_I8 , >=, i8,  i8)
+            OVM_OP(OVMI_GE_S, OVM_TYPE_I16, >=, i16, i16)
+            OVM_OP(OVMI_GE_S, OVM_TYPE_I32, >=, i32, i32)
+            OVM_OP(OVMI_GE_S, OVM_TYPE_I64, >=, i64, i64)
+            OVM_OP(OVMI_GE_S, OVM_TYPE_F32, >=, f32, f32)
+            OVM_OP(OVMI_GE_S, OVM_TYPE_F64, >=, f64, f64)
+
+            OVM_OP(OVMI_GT, OVM_TYPE_I8 , >, i8,  u8)
+            OVM_OP(OVMI_GT, OVM_TYPE_I16, >, i16, u16)
+            OVM_OP(OVMI_GT, OVM_TYPE_I32, >, i32, u32)
+            OVM_OP(OVMI_GT, OVM_TYPE_I64, >, i64, u64)
+            OVM_OP(OVMI_GT, OVM_TYPE_F32, >, f32, f32)
+            OVM_OP(OVMI_GT, OVM_TYPE_F64, >, f64, f64)
+
+            OVM_OP(OVMI_GT_S, OVM_TYPE_I8 , >, i8,  i8)
+            OVM_OP(OVMI_GT_S, OVM_TYPE_I16, >, i16, i16)
+            OVM_OP(OVMI_GT_S, OVM_TYPE_I32, >, i32, i32)
+            OVM_OP(OVMI_GT_S, OVM_TYPE_I64, >, i64, i64)
+            OVM_OP(OVMI_GT_S, OVM_TYPE_F32, >, f32, f32)
+            OVM_OP(OVMI_GT_S, OVM_TYPE_F64, >, f64, f64)
+
+            OVM_OP(OVMI_NE, OVM_TYPE_I8 , !=, i8,  i8)
+            OVM_OP(OVMI_NE, OVM_TYPE_I16, !=, i16, i16)
+            OVM_OP(OVMI_NE, OVM_TYPE_I32, !=, i32, i32)
+            OVM_OP(OVMI_NE, OVM_TYPE_I64, !=, i64, i64)
+            OVM_OP(OVMI_NE, OVM_TYPE_F32, !=, f32, f32)
+            OVM_OP(OVMI_NE, OVM_TYPE_F64, !=, f64, f64)
+
+#undef OVM_OP
+
+#define OVM_IMM(type, dtype, stype) \
+            case OVM_TYPED_INSTR(OVMI_IMM, type): \
+                (VAL(instr.r).dtype = instr.stype); \
+                break;
+            
+            OVM_IMM(OVM_TYPE_I8,  i8,  i)
+            OVM_IMM(OVM_TYPE_I16, i16, i)
+            OVM_IMM(OVM_TYPE_I32, i32, i)
+            OVM_IMM(OVM_TYPE_I64, i64, l)
+            OVM_IMM(OVM_TYPE_F32, f32, f)
+            OVM_IMM(OVM_TYPE_F64, f64, d)
+
+#undef OVM_IMM
+
+            case OVMI_PARAM:
+                bh_arr_push(state->params, 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;
+                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;
+                VAL(frame.return_number_value) = val;
+
+                state->pc = frame.return_address;
+                break;
+            }
+
+            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):
+            case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I32):
+            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;
+                }
+
+                break;
+            }
+
+            case OVMI_BR:
+                state->pc = VAL(instr.a).i32;
+                break;
+
+            case OVMI_BR_NZ:
+                if (VAL(instr.a).i32 != 0) {
+                    state->pc = VAL(instr.r).i32;
+                }
+                break;
+
+            default:
+                printf("ERROR:\n");
+                ovm_program_print_instructions(program, state->pc - 1, 1);
+                fflush(stdout);
+                assert(("ILLEGAL INSTRUCTION", 0));
+                break;
+        }
+    }
+}
+
index 5478453ce69bd3121f649c0c92bef83d3cfac49c..8ad5cf9409944e00b335f7bedbbfae8b8854d19b 100644 (file)
@@ -6,8 +6,3 @@
 #include "stb_ds.h"
 
 #include "onyx_wasm.h"
-
-i32 embed_test() {
-    printf("Build works.\n");
-    return 0;
-}