added interpreter to this repo
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 5 Sep 2022 16:45:41 +0000 (11:45 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 5 Sep 2022 16:45:41 +0000 (11:45 -0500)
37 files changed:
build.sh
interpreter/build.sh [new file with mode: 0755]
interpreter/include/assembler.h [new file with mode: 0644]
interpreter/include/ovm_debug.h [new file with mode: 0644]
interpreter/include/ovm_wasm.h [new file with mode: 0644]
interpreter/include/vm.h [new file with mode: 0644]
interpreter/include/vm_codebuilder.h [new file with mode: 0644]
interpreter/src/debug/debug_host.c [new file with mode: 0644]
interpreter/src/debug/debug_info.c [new file with mode: 0644]
interpreter/src/debug/debug_info_builder.c [new file with mode: 0644]
interpreter/src/debug/debug_runtime_values.c [new file with mode: 0644]
interpreter/src/debug/debug_thread.c [new file with mode: 0644]
interpreter/src/ovm_cli_test.c [new file with mode: 0644]
interpreter/src/vm/code_builder.c [new file with mode: 0644]
interpreter/src/vm/program_loader.c [new file with mode: 0644]
interpreter/src/vm/vm.c [new file with mode: 0644]
interpreter/src/wasm.c [new file with mode: 0644]
interpreter/src/wasm/config.c [new file with mode: 0644]
interpreter/src/wasm/engine.c [new file with mode: 0644]
interpreter/src/wasm/extern.c [new file with mode: 0644]
interpreter/src/wasm/frame.c [new file with mode: 0644]
interpreter/src/wasm/func.c [new file with mode: 0644]
interpreter/src/wasm/global.c [new file with mode: 0644]
interpreter/src/wasm/instance.c [new file with mode: 0644]
interpreter/src/wasm/memory.c [new file with mode: 0644]
interpreter/src/wasm/module.c [new file with mode: 0644]
interpreter/src/wasm/module_parsing.h [new file with mode: 0644]
interpreter/src/wasm/ref.c [new file with mode: 0644]
interpreter/src/wasm/store.c [new file with mode: 0644]
interpreter/src/wasm/table.c [new file with mode: 0644]
interpreter/src/wasm/trap.c [new file with mode: 0644]
interpreter/src/wasm/type.c [new file with mode: 0644]
interpreter/src/wasm/value.c [new file with mode: 0644]
interpreter/src/wasm_cli_test.c [new file with mode: 0644]
settings.sh
shared/include/bh.h
shared/lib/linux_x86_64/lib/libovmwasm.so

index 87c89484f8cbc623d3d72bb0508e78b7251bc851..9a8a169f08e0ec668c187b4710fb52b56fdabff6 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -6,18 +6,6 @@ echo "Installing core libs"
 sudo mkdir -p "$CORE_DIR"
 sudo cp -r ./core/ "$CORE_DIR"
 
-if [ ! -f "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so" ] || true; then
-    echo "Copying lib$RUNTIME_LIBRARY to $CORE_DIR/lib (first install)"
-
-    sudo mkdir -p "$CORE_DIR/lib"
-    sudo mkdir -p "$CORE_DIR/include"
-
-    sudo cp "$WASMER_LIBRARY_DIR/lib$RUNTIME_LIBRARY.so" "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so"
-
-    sudo cp "shared/include/onyx_library.h" "$CORE_DIR/include/onyx_library.h"
-    sudo cp "$WASMER_INCLUDE_DIR/wasm.h" "$CORE_DIR/include/wasm.h"
-fi
-
 # This is a development feature to allow for quickly reinstalling core libraries
 # without have to recompile the entire compiler
 [ "$1" = "core" ] && exit 0
@@ -34,5 +22,25 @@ cd runtime
 ./build.sh $1
 cd ..
 
+if [ "$RUNTIME_LIBRARY" = "ovmwasm" ]; then
+    cd interpreter
+    ./build.sh $1
+    cd ..
+fi
+
+if [ ! -f "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so" ] || true; then
+    echo "Copying lib$RUNTIME_LIBRARY to $CORE_DIR/lib (first install)"
+
+    sudo mkdir -p "$CORE_DIR/lib"
+    sudo mkdir -p "$CORE_DIR/include"
+
+    sudo cp "$WASMER_LIBRARY_DIR/lib$RUNTIME_LIBRARY.so" "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so"
+
+    sudo cp "shared/include/onyx_library.h" "$CORE_DIR/include/onyx_library.h"
+    sudo cp "$WASMER_INCLUDE_DIR/wasm.h" "$CORE_DIR/include/wasm.h"
+fi
+
+
+
 # Otherwise the prompt ends on the same line
 printf "\n"
diff --git a/interpreter/build.sh b/interpreter/build.sh
new file mode 100755 (executable)
index 0000000..adbb53c
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+. ../settings.sh
+
+# FLAGS="-g3"
+# FLAGS="-g3 -DOVM_VERBOSE=1"
+FLAGS="-Ofast"
+LIBS="-pthread"
+TARGET="../shared/lib/linux_$(uname -m)/lib/libovmwasm.so"
+C_FILES="src/wasm.c src/vm/*.c src/wasm/*.c src/debug/*.c"
+INCLUDES="-I../shared/include -Iinclude"
+
+echo "Compiling libovmwasm.so"
+$CC $FLAGS $INCLUDES -shared -fPIC -o $TARGET $C_FILES $LIBS $WARNINGS
+
diff --git a/interpreter/include/assembler.h b/interpreter/include/assembler.h
new file mode 100644 (file)
index 0000000..7ea656e
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _ASSEMBLER_H
+#define _ASSEMBLER_H
+
+typedef enum token_type_t token_type_t;
+typedef struct token_t token_t;
+
+enum token_type_t {
+    token_none,
+    token_newline,
+
+    token_integer,
+    token_integer_long,
+    token_float,
+    token_float_double,
+
+    token_command,
+    token_symbol,
+    token_register,
+    token_label,
+    token_comma,
+};
+
+struct token_t {
+    token_type_t type;
+    char        *text;
+    int          line;
+};
+
+token_t asm_lexer_next_token();
+token_t asm_lexer_peek_token();
+void asm_lexer_input(char *data, int size);
+
+extern char *token_names[];
+
+#endif
+
diff --git a/interpreter/include/ovm_debug.h b/interpreter/include/ovm_debug.h
new file mode 100644 (file)
index 0000000..ac112c2
--- /dev/null
@@ -0,0 +1,304 @@
+#ifndef _OVM_DEBUG_H
+#define _OVM_DEBUG_H
+
+#include "bh.h"
+#include <semaphore.h>
+#include <stdbool.h>
+
+typedef struct debug_loc_info_t {
+    u32 file_id;
+    u32 line;
+    u32 symbol_scope;
+} debug_loc_info_t;
+
+typedef struct debug_func_info_t {
+    u32 func_id;
+    u32 file_id;
+    u32 line;
+    char *name;
+    b32 internal;
+    u32 stack_ptr_idx;
+
+    u32 debug_op_offset;
+} debug_func_info_t;
+
+typedef struct debug_file_info_t {
+    char *name;
+    u32 file_id;
+    u32 line_count;
+    i32 line_buffer_offset;
+} debug_file_info_t;
+
+typedef enum debug_sym_loc_kind_t {
+    debug_sym_loc_register = 1,
+    debug_sym_loc_stack    = 2,
+    debug_sym_loc_global   = 3
+} debug_sym_loc_kind_t;
+
+typedef struct debug_sym_info_t {
+    char *name;
+    u32 sym_id;
+    debug_sym_loc_kind_t loc_kind;
+    u32 loc;
+    u32 type;
+} debug_sym_info_t;
+
+typedef struct debug_sym_scope_t {
+    bh_arr(u32) symbols;
+    i32 parent; // -1 for root
+} debug_sym_scope_t;
+
+typedef enum debug_type_kind_t {
+    debug_type_kind_primitive = 1,
+    debug_type_kind_modifier  = 2,
+    debug_type_kind_structure = 3,
+    debug_type_kind_array     = 4,
+    debug_type_kind_alias     = 5,
+    debug_type_kind_function  = 6,
+} debug_type_kind_t;
+
+typedef enum debug_type_primitive_kind_t {
+    debug_type_primitive_kind_void = 0,
+    debug_type_primitive_kind_signed_integer = 1,
+    debug_type_primitive_kind_unsigned_integer = 2,
+    debug_type_primitive_kind_float = 3,
+    debug_type_primitive_kind_boolean = 4,
+    debug_type_primitive_kind_character = 5,
+    debug_type_primitive_kind_vector = 6,
+} debug_primitive_kind_t;
+
+typedef struct debug_type_primitive_t {
+    debug_primitive_kind_t primitive_kind;
+} debug_type_primitive_t;
+
+typedef enum debug_type_modifier_kind_t {
+    debug_type_modifier_kind_pointer  = 1,
+    debug_type_modifier_kind_const    = 2,
+    debug_type_modifier_kind_restrict = 3,
+} debug_type_modifier_kind_t;
+
+typedef struct debug_type_modifier_t {
+    debug_type_modifier_kind_t modifier_kind;
+    u32 modified_type;
+} debug_type_modifier_t;
+
+typedef struct debug_type_structure_member_t {
+    u32 offset;
+    u32 type;
+    char *name;
+} debug_type_structure_member_t;
+
+typedef struct debug_type_structure_t {
+    u32 member_count;
+    debug_type_structure_member_t *members;
+} debug_type_structure_t;
+
+typedef struct debug_type_array_t {
+    u32 count;
+    u32 type;
+} debug_type_array_t;
+
+typedef enum debug_type_alias_kind_t {
+    debug_type_alias_kind_transparent = 1,
+    debug_type_alias_kind_distinct = 2
+} debug_type_alias_kind_t;
+
+typedef struct debug_type_alias_t {
+    debug_type_alias_kind_t alias_kind;
+    u32                     aliased_type;
+} debug_type_alias_t;
+
+typedef struct debug_type_function_t {
+    u32  param_count;
+    u32 *param_types;
+    u32  return_type;
+} debug_type_function_t;
+
+typedef struct debug_type_info_t {
+    u32 id;
+    char *name;
+    u32 size;
+    debug_type_kind_t kind;
+
+    union {
+        debug_type_primitive_t primitive;
+        debug_type_modifier_t  modifier;
+        debug_type_structure_t structure;
+        debug_type_array_t     array;
+        debug_type_alias_t     alias;
+        debug_type_function_t  function;
+    };
+} debug_type_info_t;
+
+typedef struct debug_info_t {
+    bh_allocator alloc;
+
+    bool has_debug_info;
+
+    // func index -> func info
+    bh_arr(debug_func_info_t) funcs;
+
+    // reducer output -> line info
+    bh_arr(debug_loc_info_t) line_info;
+
+    // instruction index -> reducer output
+    bh_arr(u32) instruction_reducer;
+
+    // line index -> instruction index
+    bh_arr(u32) line_to_instruction;
+
+    // file_id -> file info
+    bh_arr(debug_file_info_t) files;
+
+    // sym_id -> sym info
+    bh_arr(debug_sym_info_t) symbols;
+
+    // scope id  -> symbol scope
+    bh_arr(debug_sym_scope_t) symbol_scopes;
+
+    // type id -> type info
+    bh_arr(debug_type_info_t) types;
+} debug_info_t;
+
+void debug_info_init(debug_info_t *);
+void debug_info_free(debug_info_t *);
+void debug_info_import_file_info(debug_info_t *, u8 *data, u32 len);
+void debug_info_import_func_info(debug_info_t *, u8 *data, u32 len);
+void debug_info_import_sym_info(debug_info_t *, u8 *data, u32 len);
+void debug_info_import_type_info(debug_info_t *, u8 *data, u32 len);
+
+bool debug_info_lookup_location(debug_info_t *info, u32 instruction, debug_loc_info_t *out);
+bool debug_info_lookup_file(debug_info_t *info, u32 file_id, debug_file_info_t *out);
+bool debug_info_lookup_file_by_name(debug_info_t *info, char *name, debug_file_info_t *out);
+bool debug_info_lookup_func(debug_info_t *info, u32 func_id, debug_func_info_t *out);
+
+//
+// This builder is used in conjunction with code builder to output
+// debug information for each instruction that is generated in OVM.
+//
+typedef struct debug_info_builder_t {
+    debug_info_t *info;
+
+    u8 *data;
+    u32 reader_offset;
+
+    u32 current_file_id;
+    u32 current_line;
+    u32 next_file_line_offset;
+
+    i32 current_scope;
+
+    u32 remaining_reps;
+
+    b32 locked : 1;
+} debug_info_builder_t;
+
+void debug_info_builder_init(debug_info_builder_t *, debug_info_t *);
+void debug_info_builder_prepare(debug_info_builder_t *, u8 *);
+void debug_info_builder_emit_location(debug_info_builder_t *);
+void debug_info_builder_step(debug_info_builder_t *);
+void debug_info_builder_begin_func(debug_info_builder_t *, i32 func_idx);
+void debug_info_builder_end_func(debug_info_builder_t *);
+
+
+typedef enum debug_exec_state_t {
+    debug_state_starting,
+    debug_state_ready,
+    debug_state_running,
+    debug_state_paused,
+
+    debug_state_pausing,
+    debug_state_hit_breakpoint,
+} debug_exec_state_t;
+
+typedef enum debug_pause_reason_t {
+    debug_pause_entry = 1,
+    debug_pause_step = 2,
+} debug_pause_reason_t;
+
+typedef struct debug_breakpoint_t {
+    u32 id;
+    u32 instr;
+    u32 file_id;
+    u32 line;
+} debug_breakpoint_t;
+
+typedef struct debug_thread_state_t {
+    u32 id;
+
+    debug_exec_state_t state;
+    struct ovm_state_t *ovm_state;
+
+    i32 run_count;
+    sem_t wait_semaphore;
+
+    bool pause_at_next_line;
+    i32 pause_within;
+    i32 extra_frames_since_last_pause;
+    debug_pause_reason_t pause_reason;
+
+    bh_arr(debug_breakpoint_t) breakpoints;
+    u32 last_breakpoint_hit;
+
+    u32 state_change_write_fd;
+} debug_thread_state_t;
+
+//
+// This represents known state of the debugger. There should only
+// be one state per running program, as it is tied to the ovm_engine_t.
+//
+typedef struct debug_state_t {
+    bh_allocator alloc;
+
+    bh_arena tmp_arena;
+    bh_allocator tmp_alloc;
+
+    debug_info_t *info;
+    struct ovm_engine_t *ovm_engine;
+
+    bh_arr(debug_thread_state_t *) threads;
+    u32 next_thread_id;
+
+    u32 next_breakpoint_id;
+
+    pthread_t debug_thread;
+    bool debug_thread_running;
+
+    u32 listen_socket_fd;
+    u32 client_fd;
+
+    bh_buffer send_buffer;
+    
+    u32 state_change_pipes[2];
+} debug_state_t;
+
+void debug_host_init(debug_state_t *debug, struct ovm_engine_t *ovm_engine);
+void debug_host_start(debug_state_t *debug);
+void debug_host_stop(debug_state_t *debug);
+u32  debug_host_register_thread(debug_state_t *debug, struct ovm_state_t *ovm_state);
+debug_thread_state_t *debug_host_lookup_thread(debug_state_t *debug, u32 id);
+
+
+
+typedef struct debug_runtime_value_builder_t {
+    debug_state_t *state;
+    debug_info_t *info;
+
+    bh_buffer output;
+    struct ovm_state_t *ovm_state;
+    struct ovm_stack_frame_t *ovm_frame;
+
+    debug_sym_scope_t sym_scope;
+    debug_func_info_t func_info;
+    debug_file_info_t file_info;
+    debug_loc_info_t loc_info;
+    debug_sym_info_t sym_info;
+} debug_runtime_value_builder_t;
+
+void debug_runtime_value_build_init(debug_runtime_value_builder_t *builder, bh_allocator alloc);
+void debug_runtime_value_build_free(debug_runtime_value_builder_t *builder);
+void debug_runtime_value_build_string(debug_runtime_value_builder_t *builder);
+
+void *__debug_thread_entry(void *);
+
+#endif
diff --git a/interpreter/include/ovm_wasm.h b/interpreter/include/ovm_wasm.h
new file mode 100644 (file)
index 0000000..71bb19c
--- /dev/null
@@ -0,0 +1,260 @@
+#ifndef _OVM_WASM_H
+#define _OVM_WASM_H
+
+#include "wasm.h"
+#include "vm.h"
+#include "ovm_debug.h"
+
+// Core Utils
+
+struct wasm_config_t {
+    bool debug_enabled;
+};
+
+void wasm_config_enable_debug(wasm_config_t *config, bool enabled);
+
+struct wasm_engine_t {
+    wasm_config_t *config;
+
+    ovm_store_t  *store;
+    ovm_engine_t *engine;
+};
+
+struct wasm_store_t {
+    wasm_engine_t   *engine;
+    wasm_instance_t *instance;
+};
+
+
+// Types
+
+struct wasm_valtype_t {
+    wasm_valkind_t kind;
+};
+
+struct wasm_functype_inner_t {
+    wasm_valtype_vec_t params;
+    wasm_valtype_vec_t results;
+};
+
+struct wasm_globaltype_inner_t {
+    wasm_valtype_t *content;
+    wasm_mutability_t mutability; 
+
+    wasm_val_t initial_value;
+};
+
+struct wasm_tabletype_inner_t {
+    wasm_valtype_t *element;
+    wasm_limits_t   limits;
+
+    i32 static_arr;
+};
+
+struct wasm_memorytype_inner_t {
+    wasm_limits_t   limits;
+};
+
+struct wasm_externtype_t {
+    wasm_externkind_t kind;
+    union {
+        struct wasm_functype_inner_t   func;
+        struct wasm_globaltype_inner_t global;
+        struct wasm_tabletype_inner_t  table;
+        struct wasm_memorytype_inner_t memory;
+    };
+};
+
+struct wasm_functype_t { wasm_externtype_t type; };
+struct wasm_globaltype_t { wasm_externtype_t type; };
+struct wasm_tabletype_t { wasm_externtype_t type; };
+struct wasm_memorytype_t { wasm_externtype_t type; };
+
+struct wasm_importtype_t {
+    wasm_name_t module_name;
+    wasm_name_t import_name;
+    wasm_externtype_t *type;
+
+    //
+    // This is only used by imported functions
+    // to specify which slot the function binding
+    // should be placed. When making a functype by
+    // hand, this proably will never be used.
+    int external_func_idx;
+};
+
+struct wasm_exporttype_t {
+    wasm_name_t name;
+    wasm_externtype_t *type;
+    int index;
+};
+
+
+// Runtime Objects
+
+struct wasm_ref_t {
+};
+
+struct wasm_frame_t {
+    wasm_instance_t *instance;
+    int func_idx;
+    size_t func_offset;
+    size_t module_offset;
+};
+
+struct wasm_trap_t {
+    wasm_message_t msg;
+    wasm_frame_vec_t frames;
+
+    wasm_store_t *store;
+};
+
+struct wasm_foreign_t {
+};
+
+struct wasm_data_t {
+    void *data;
+    unsigned int length;
+    unsigned int offset;
+    bool passive;
+};
+
+struct wasm_custom_section_t {
+    unsigned int size;
+    char        *data;
+};
+
+struct wasm_module_t {
+    wasm_store_t *store;
+
+    wasm_functype_vec_t type_section;
+
+    wasm_functype_vec_t functypes;
+    wasm_globaltype_vec_t globaltypes;
+    wasm_tabletype_vec_t tabletypes;
+    wasm_memorytype_vec_t memorytypes;
+    wasm_importtype_vec_t imports;
+    wasm_exporttype_vec_t exports;
+
+    int start_func_idx;
+
+    unsigned int elem_count;
+    unsigned int *elem_entries; // Array of function indicies
+
+    bool data_count_present;
+    unsigned int data_count;
+    struct wasm_data_t *data_entries;
+
+    ovm_program_t *program;
+    bool valid;
+    
+    int memory_init_idx;
+    int memory_init_external_idx;
+
+    Table(struct wasm_custom_section_t) custom_sections;
+
+    debug_info_t debug_info;
+};
+
+struct wasm_func_inner_t {
+    bool env_present;
+    void *env;
+    void (*func_ptr)();
+    void (*finalizer)(void *);
+
+    const wasm_functype_t *type;
+};
+
+struct wasm_global_inner_t {
+    int register_index;
+    ovm_state_t  *state;
+    ovm_engine_t *engine;
+
+    wasm_val_t initial_value;
+
+    const wasm_globaltype_t *type;
+};
+
+struct wasm_table_inner_t {
+    ovm_program_t *program;
+    ovm_engine_t  *engine;
+
+    int static_arr;
+
+    const wasm_tabletype_t *type;
+};
+
+struct wasm_memory_inner_t {
+    ovm_engine_t* engine;
+
+    const wasm_memorytype_t *type;
+};
+
+struct wasm_extern_t {
+    const wasm_externtype_t *type;
+    union {
+        struct wasm_func_inner_t   func;
+        struct wasm_global_inner_t global;
+        struct wasm_table_inner_t  table;
+        struct wasm_memory_inner_t memory;
+    };
+};
+
+struct wasm_func_t { wasm_extern_t inner; };
+struct wasm_global_t { wasm_extern_t inner; };
+struct wasm_table_t { wasm_extern_t inner; };
+struct wasm_memory_t { wasm_extern_t inner; };
+
+struct wasm_instance_t {
+    const wasm_module_t *module;
+    wasm_store_t  *store;
+
+    bh_arr(wasm_func_t *)   funcs;
+    bh_arr(wasm_memory_t *) memories;
+    bh_arr(wasm_table_t *)  tables;
+    bh_arr(wasm_global_t *) globals;
+
+    wasm_extern_vec_t exports;
+
+    ovm_state_t *state;
+};
+
+
+bool wasm_functype_equals(wasm_functype_t *a, wasm_functype_t *b);
+
+wasm_functype_t   *wasm_module_index_functype(wasm_module_t *module, int index);
+wasm_tabletype_t  *wasm_module_index_tabletype(wasm_module_t *module, int index);
+wasm_globaltype_t *wasm_module_index_globaltype(wasm_module_t *module, int index);
+wasm_memorytype_t *wasm_module_index_memorytype(wasm_module_t *module, int index);
+
+
+
+#define WASM_DECLARE_VEC_IMPL(type, ptr_or_none) \
+    void wasm_##type##_vec_new_empty(wasm_##type##_vec_t *out) { \
+        out->size = 0; \
+        out->data = NULL; \
+    } \
+     \
+    void wasm_##type##_vec_new_uninitialized(wasm_##type##_vec_t *out, size_t size) { \
+        out->data = malloc(sizeof(wasm_##type##_t ptr_or_none) * size); \
+        out->size = size; \
+    } \
+     \
+    void wasm_##type##_vec_new(wasm_##type##_vec_t *out, size_t size, wasm_##type##_t ptr_or_none const data[]) { \
+        out->data = malloc(sizeof(wasm_##type##_t ptr_or_none) * size); \
+        out->size = size; \
+     \
+        fori (i, 0, (i32) size) { \
+            out->data[i] = data[i]; \
+        } \
+    } \
+     \
+    void wasm_##type##_vec_copy(wasm_##type##_vec_t *out, const wasm_##type##_vec_t *in) { \
+        wasm_##type##_vec_new(out, in->size, in->data); \
+    } \
+     \
+    void wasm_##type##_vec_delete(wasm_##type##_vec_t *vec) { \
+        if (vec->data) free(vec->data); \
+    }
+
+#endif
diff --git a/interpreter/include/vm.h b/interpreter/include/vm.h
new file mode 100644 (file)
index 0000000..44868f8
--- /dev/null
@@ -0,0 +1,345 @@
+#ifndef _ONYX_VM_H
+#define _ONYX_VM_H
+
+#include "bh.h"
+#include "ovm_debug.h"
+#include <stdbool.h>
+#include <pthread.h>
+
+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 enum   ovm_func_kind_t ovm_func_kind_t;
+typedef struct ovm_func_t ovm_func_t;
+typedef struct ovm_external_func_t ovm_external_func_t;
+typedef struct ovm_linkable_func_t ovm_linkable_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;
+typedef struct ovm_static_integer_array_t ovm_static_integer_array_t;
+
+
+//
+// Contains storage.
+struct ovm_store_t {
+    bh_allocator    heap_allocator;
+    bh_atomic_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;
+    i64   length;
+};
+
+struct ovm_static_integer_array_t {
+    i32 start_idx;
+    i32 len;
+};
+
+//
+// Represents a program that is runnable by the
+// VM. It can be constructed incrementally as needed.
+//
+struct ovm_program_t {
+    bh_arr(ovm_instr_t)       code;
+    bh_arr(ovm_func_t)        funcs;
+
+    //
+    // TODO: Document these, and rename them.
+    bh_arr(i32) static_integers;
+    bh_arr(ovm_static_integer_array_t) static_data;
+
+    i32 register_count;
+    ovm_store_t *store;
+};
+
+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_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count);
+void ovm_raw_print_instructions(i32 instr_count, ovm_instr_t *instrs);
+
+int  ovm_program_register_static_ints(ovm_program_t *program, int len, int *data);
+int  ovm_program_register_func(ovm_program_t *program, char *name, i32 instr, i32 param_count, i32 value_number_count);
+int  ovm_program_register_external_func(ovm_program_t *program, char *name, i32 param_count, i32 external_func_idx);
+void ovm_program_begin_func(ovm_program_t *program, char *name, i32 param_count, i32 value_number_count);
+void ovm_program_modify_static_int(ovm_program_t *program, int arr, int idx, int new_value);
+
+//
+// Represents the running configuration and static
+// data needed by the VM. This is for more "global" data.
+// If multiple threads are used, only one engine is needed.
+// 
+struct ovm_engine_t {
+    ovm_store_t *store;
+
+    pthread_mutex_t atomic_mutex;
+
+    i64   memory_size; // This is probably going to always be 4GiB.
+    void *memory;
+
+    debug_state_t *debug;
+};
+
+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);
+
+bool ovm_program_load_from_file(ovm_program_t *program, ovm_engine_t *engine, char *filename);
+
+//
+// Represents ephemeral state / execution context.
+// 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;
+    bh_arr(ovm_value_t) registers;
+
+    //
+    // Originally, these were stored on the ovm_program that
+    // this state corresponds with. However, that does not line
+    // up with the specifications needed by WASM. In theory, different
+    // running instances of the program *could* have different
+    // native functions linked.
+    bh_arr(ovm_external_func_t) external_funcs;
+
+    debug_thread_state_t *debug;
+};
+
+ovm_state_t *ovm_state_new(ovm_engine_t *engine, ovm_program_t *program);
+void         ovm_state_delete(ovm_state_t *state);
+void ovm_state_link_external_funcs(ovm_program_t *program, ovm_state_t *state, ovm_linkable_func_t *funcs);
+void ovm_state_register_external_func(ovm_state_t *state, i32 idx, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data);
+ovm_value_t ovm_state_register_get(ovm_state_t *state, i32 idx);
+void ovm_state_register_set(ovm_state_t *state, i32 idx, ovm_value_t val);
+
+//
+//
+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.
+//
+enum ovm_func_kind_t {
+    OVM_FUNC_INTERNAL,
+    OVM_FUNC_EXTERNAL
+};
+
+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;
+    ovm_func_kind_t kind;
+    char *name;
+    i32 param_count;
+    i32 value_number_count;
+
+    union {
+        i32 start_instr;
+        i32 external_func_idx;
+    };
+};
+
+struct ovm_external_func_t {
+    void (*native_func)(void *userdata, ovm_value_t* params, ovm_value_t* result);
+    void *userdata;
+};
+
+struct ovm_linkable_func_t {
+    char *name;
+    i32 param_count;
+    ovm_external_func_t func;
+};
+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);
+
+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);
+ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program);
+
+//
+// Instruction encoding
+//
+// This engine uses a simple Three Address Code (3AC) instruction representation
+// with an "infinite" value number (register) count.
+//
+
+#define OVM_TYPE_NONE   0x00
+#define OVM_TYPE_I8     0x01
+#define OVM_TYPE_I16    0x02
+#define OVM_TYPE_I32    0x03
+#define OVM_TYPE_I64    0x04
+#define OVM_TYPE_F32    0x05
+#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 {
+    u32 full_instr;
+
+    // Destination value number.
+    ovm_valnum_t r;
+
+    union {
+        // Input value numbers.
+        struct {
+            ovm_valnum_t a, b;
+        };
+
+        // Immediates in different types.
+        i32 i;
+        f32 f;
+        i64 l;
+        f64 d;
+    };
+};
+
+#define OVM_INSTR_TYPE(instr)  ((instr).full_instr >> 24)
+#define OVM_INSTR_INSTR(instr) ((instr).full_instr & 0xffffff)
+
+#define OVMI_ATOMIC            0x00800000 // Flag an instruction as atomic
+
+#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_DIV_S             0x05   // %r = %a / %b
+#define OVMI_REM               0x06   // %r = %a % %b
+#define OVMI_REM_S             0x07   // %r = %a % %b
+
+#define OVMI_AND               0x08   // %r = %a & %b
+#define OVMI_OR                0x09   // %r = %a | %b
+#define OVMI_XOR               0x0A   // %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               0x11   // %r = %a
+#define OVMI_LOAD              0x12   // %r = mem[%a + %b]
+#define OVMI_STORE             0x13   // mem[%r + %b] = %a
+#define OVMI_COPY              0x14   // memcpy(%r, %a, %b)
+#define OVMI_FILL              0x15   // memset(%r, %a, %b)
+#define OVMI_REG_GET           0x16   // %r = #a
+#define OVMI_REG_SET           0x17   // #r = %a
+#define OVMI_IDX_ARR           0x18   // %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_CALLI             0x33   // %r = %a(...)
+
+#define OVMI_BR                0x40   // br pc + a              // Relative branching
+#define OVMI_BR_Z              0x41   // br pc + a if %b == 0
+#define OVMI_BR_NZ             0x42   // br pc + a if %b != 0
+#define OVMI_BRI               0x43   // br pc + %a             // Relative branching
+#define OVMI_BRI_Z             0x44   // br pc + %a if %b == 0
+#define OVMI_BRI_NZ            0x45   // 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)
+#define OVMI_ROTL              0x53   // %r = rotl(%a, %b)
+#define OVMI_ROTR              0x54   // %r = rotr(%a, %b)
+
+// These instructions are only implemented for floats.
+#define OVMI_ABS               0x55   // %r = |%a|
+#define OVMI_NEG               0x56   // %r = -%a
+#define OVMI_CEIL              0x57   // %r = ceil(%a)
+#define OVMI_FLOOR             0x58   // %r = floor(%a)
+#define OVMI_TRUNC             0x59   // %r = trunc(%a)
+#define OVMI_NEAREST           0x5A   // %r = nearest(%a)
+#define OVMI_SQRT              0x5B   // %r = sqrt(%a)
+#define OVMI_MIN               0x5C   // %r = min(%a, %b)
+#define OVMI_MAX               0x5D   // %r = max(%a, %b)
+#define OVMI_COPYSIGN          0x5E   // %r = copysign(%a, %b)
+
+// For conversion operations, the "type" of the instruction is
+// destination type, the type in the name is the source type.
+//
+// There are a couple of cast operations that are not available,
+// such as unsigned conversion from 32-bit integers to floats.
+#define OVMI_CVT_I8            0x60   // %r = (t) %a
+#define OVMI_CVT_I8_S          0x61   // %r = (t) %a (sign aware)
+#define OVMI_CVT_I16           0x62   // %r = (t) %a
+#define OVMI_CVT_I16_S         0x63   // %r = (t) %a (sign aware)
+#define OVMI_CVT_I32           0x64   // %r = (t) %a
+#define OVMI_CVT_I32_S         0x65   // %r = (t) %a (sign aware)
+#define OVMI_CVT_I64           0x66   // %r = (t) %a
+#define OVMI_CVT_I64_S         0x67   // %r = (t) %a (sign aware)
+#define OVMI_CVT_F32           0x68   // %r = (t) %a
+#define OVMI_CVT_F32_S         0x69   // %r = (t) %a (sign aware)
+#define OVMI_CVT_F64           0x6A   // %r = (t) %a
+#define OVMI_CVT_F64_S         0x6B   // %r = (t) %a (sign aware)
+#define OVMI_TRANSMUTE_I32     0x6C   // %r = *(t *) &%a (reinterpret bytes)
+#define OVMI_TRANSMUTE_I64     0x6D   // %r = *(t *) &%a (reinterpret bytes)
+#define OVMI_TRANSMUTE_F32     0x6E   // %r = *(t *) &%a (reinterpret bytes)
+#define OVMI_TRANSMUTE_F64     0x6F   // %r = *(t *) &%a (reinterpret bytes)
+
+#define OVMI_CMPXCHG           0x70   // %r = %r == %a ? %b : %r 
+
+//
+// OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I32) == instruction for adding i32s
+//
+#define OVM_TYPED_INSTR(instr, type)  (((type) << 24) | (instr))
+
+
+#endif
+
diff --git a/interpreter/include/vm_codebuilder.h b/interpreter/include/vm_codebuilder.h
new file mode 100644 (file)
index 0000000..80a062b
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef _OVM_CODE_BUILDER_H
+#define _OVM_CODE_BUILDER_H
+
+#include "vm.h"
+#include "ovm_debug.h"
+
+typedef struct ovm_code_builder_t ovm_code_builder_t;
+typedef struct label_target_t label_target_t;
+typedef struct branch_patch_t branch_patch_t;
+typedef enum label_kind_t label_kind_t;
+
+//
+// A new code builder will be "made" for each function
+// being compiled.
+struct ovm_code_builder_t {
+    bh_arr(i32) execution_stack;
+
+    i32 next_label_idx;
+    bh_arr(label_target_t) label_stack;
+    bh_arr(branch_patch_t) branch_patches;
+
+    i32 param_count, local_count;
+
+    ovm_program_t *program;
+    i32 start_instr;
+
+    i32 func_table_arr_idx;
+    i32 highest_value_number;
+
+    debug_info_builder_t *debug_builder;
+};
+
+enum label_kind_t {
+    label_kind_func,
+    label_kind_block,
+    label_kind_loop,
+    label_kind_if,
+};
+
+struct label_target_t {
+    i32 idx;
+    label_kind_t kind;
+    i32 instr;
+};
+
+enum branch_patch_kind_t {
+    branch_patch_instr_a,    // For patching the '.a' register of a branch instruction.
+    branch_patch_static_idx, // For patching an integer in the static integers section.
+};
+
+struct branch_patch_t {
+    enum branch_patch_kind_t kind;
+    i32 branch_instr;
+    i32 label_idx;
+    i32 static_arr;
+    i32 static_idx;
+    bool targets_else;
+};
+
+ovm_code_builder_t ovm_code_builder_new(ovm_program_t *program, debug_info_builder_t *debug, i32 param_count, i32 local_count);
+label_target_t     ovm_code_builder_wasm_target_idx(ovm_code_builder_t *builder, i32 idx);
+i32                ovm_code_builder_push_label_target(ovm_code_builder_t *builder, label_kind_t kind);
+void               ovm_code_builder_pop_label_target(ovm_code_builder_t *builder);
+void               ovm_code_builder_patch_else(ovm_code_builder_t *builder, label_target_t if_target);
+void               ovm_code_builder_free(ovm_code_builder_t *builder);
+void               ovm_code_builder_add_nop(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 label_idx);
+void               ovm_code_builder_add_cond_branch(ovm_code_builder_t *builder, i32 label_idx, bool branch_if_true, bool targets_else);
+void               ovm_code_builder_add_branch_table(ovm_code_builder_t *builder, i32 count, i32 *label_indicies, i32 default_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);
+void               ovm_code_builder_add_indirect_call(ovm_code_builder_t *builder, i32 param_count, bool has_return_value);
+void               ovm_code_builder_drop_value(ovm_code_builder_t *builder);
+void               ovm_code_builder_add_local_get(ovm_code_builder_t *builder, i32 local_idx);
+void               ovm_code_builder_add_local_set(ovm_code_builder_t *builder, i32 local_idx);
+void               ovm_code_builder_add_local_tee(ovm_code_builder_t *builder, i32 local_idx);
+void               ovm_code_builder_add_register_get(ovm_code_builder_t *builder, i32 local_idx);
+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_atomic_load(ovm_code_builder_t *builder, u32 ovm_type, i32 offset);
+void               ovm_code_builder_add_atomic_store(ovm_code_builder_t *builder, u32 ovm_type, i32 offset);
+void               ovm_code_builder_add_cmpxchg(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
diff --git a/interpreter/src/debug/debug_host.c b/interpreter/src/debug/debug_host.c
new file mode 100644 (file)
index 0000000..8ae0ce1
--- /dev/null
@@ -0,0 +1,65 @@
+
+#include "ovm_debug.h"
+#include "vm.h"
+
+void debug_host_init(debug_state_t *debug, struct ovm_engine_t *ovm_engine) {
+    memset(debug, 0, sizeof(*debug));
+    debug->alloc = bh_heap_allocator();
+    debug->ovm_engine = ovm_engine;
+
+    bh_arena_init(&debug->tmp_arena, bh_heap_allocator(), 16 * 1024);
+    debug->tmp_alloc = bh_arena_allocator(&debug->tmp_arena);
+    
+    debug->info = NULL;
+
+    debug->threads = NULL;
+    debug->next_thread_id = 1;
+    bh_arr_new(debug->alloc, debug->threads, 4);
+
+    debug->listen_socket_fd = 0;
+    debug->client_fd = 0;
+
+    if (pipe(debug->state_change_pipes) != 0) {
+        printf("[ERROR] Failed to create thread notification pipes.\n");
+    }
+}
+
+void debug_host_start(debug_state_t *debug) {
+    if (debug->debug_thread_running) return;
+
+    pthread_create(&debug->debug_thread, NULL, __debug_thread_entry, debug);
+}
+
+void debug_host_stop(debug_state_t *debug) {
+    debug->debug_thread_running = false;
+    pthread_join(debug->debug_thread, NULL);
+}
+
+u32 debug_host_register_thread(debug_state_t *debug, ovm_state_t *ovm_state) {
+    debug_thread_state_t *new_thread = bh_alloc(debug->alloc, sizeof(*new_thread));
+    memset(new_thread, 0, sizeof(*new_thread));
+
+    new_thread->state = debug_state_starting;
+    new_thread->ovm_state = ovm_state;
+    new_thread->run_count = 0;                    // Start threads in stopped state.
+    sem_init(&new_thread->wait_semaphore, 0, 0);
+
+    new_thread->breakpoints = NULL;
+    bh_arr_new(debug->alloc, new_thread->breakpoints, 8);
+
+    u32 id = debug->next_thread_id++;
+    new_thread->id = id;
+
+    new_thread->state_change_write_fd = debug->state_change_pipes[1];
+
+    bh_arr_push(debug->threads, new_thread);
+    return id;
+}
+
+debug_thread_state_t *debug_host_lookup_thread(debug_state_t *debug, u32 id) {
+    bh_arr_each(debug_thread_state_t *, pthread, debug->threads) {
+        if ((*pthread)->id == id) return *pthread;
+    }
+    return NULL;
+}
+
diff --git a/interpreter/src/debug/debug_info.c b/interpreter/src/debug/debug_info.c
new file mode 100644 (file)
index 0000000..6f10003
--- /dev/null
@@ -0,0 +1,236 @@
+
+#include "ovm_debug.h"
+
+void debug_info_init(debug_info_t *info) {
+    memset(info, 0, sizeof(*info));
+
+    info->alloc = bh_heap_allocator();
+    info->has_debug_info = false;
+    bh_arr_new(info->alloc, info->funcs, 16);
+    bh_arr_new(info->alloc, info->line_info, 1024);
+    bh_arr_new(info->alloc, info->instruction_reducer, 4096);
+    bh_arr_new(info->alloc, info->files, 16);
+    bh_arr_new(info->alloc, info->line_to_instruction, 1024);
+    bh_arr_new(info->alloc, info->symbols, 128);
+    bh_arr_new(info->alloc, info->symbol_scopes, 128);
+}
+
+void debug_info_free(debug_info_t *info) {
+    bh_arr_free(info->funcs);
+    bh_arr_free(info->line_info);
+    bh_arr_free(info->instruction_reducer);
+
+    bh_arr_each(debug_file_info_t, file, info->files) {
+        bh_free(info->alloc, file->name);
+    }
+    bh_arr_free(info->files);
+}
+
+void debug_info_import_file_info(debug_info_t *info, u8 *data, u32 len) {
+    u32 offset = 0;
+    info->has_debug_info = true;
+
+    i32 count = uleb128_to_uint(data, &offset);
+    fori (i, 0, (i32) count) {
+        debug_file_info_t file_info;
+        file_info.line_buffer_offset = -1;
+
+        u32 file_id = uleb128_to_uint(data, &offset);
+        file_info.file_id = file_id;
+        file_info.line_count = uleb128_to_uint(data, &offset);
+
+        u32 name_length = uleb128_to_uint(data, &offset);
+        file_info.name = bh_alloc_array(info->alloc, char, name_length + 1);
+        memcpy(file_info.name, data + offset, name_length);
+        file_info.name[name_length] = 0;
+        offset += name_length;
+
+        bh_arr_set_at(info->files, file_id, file_info);
+    }
+
+    assert(offset == len);
+}
+
+void debug_info_import_func_info(debug_info_t *info, u8 *data, u32 len) {
+    u32 offset = 0;
+    info->has_debug_info = true;
+
+    i32 count = uleb128_to_uint(data, &offset);
+    fori (i, 0, (i32) count) {
+        debug_func_info_t func_info;
+        func_info.func_id = uleb128_to_uint(data, &offset);
+        func_info.file_id = uleb128_to_uint(data, &offset);
+        func_info.line    = uleb128_to_uint(data, &offset);
+
+        u32 name_length = uleb128_to_uint(data, &offset);
+        if (name_length == 0) {
+            func_info.name = NULL;
+        } else {
+            func_info.name = bh_alloc_array(info->alloc, char, name_length + 1);
+            memcpy(func_info.name, data + offset, name_length);
+            func_info.name[name_length] = 0;
+            offset += name_length;
+        }
+
+        func_info.internal = data[offset++] != 0;
+        func_info.debug_op_offset = uleb128_to_uint(data, &offset);
+        func_info.stack_ptr_idx = uleb128_to_uint(data, &offset);
+
+        uleb128_to_uint(data, &offset);
+
+        bh_arr_set_at(info->funcs, func_info.func_id, func_info);
+    }
+
+    assert(offset == len);
+}
+
+void debug_info_import_sym_info(debug_info_t *info, u8 *data, u32 len) {
+    u32 offset = 0;
+    info->has_debug_info = true;
+
+    i32 count = uleb128_to_uint(data, &offset);
+    fori (i, 0, count) {
+        debug_sym_info_t sym_info;
+        sym_info.sym_id = uleb128_to_uint(data, &offset);
+
+        u32 name_length = uleb128_to_uint(data, &offset);
+        if (name_length == 0) {
+            sym_info.name = NULL;
+        } else {
+            sym_info.name = bh_alloc_array(info->alloc, char, name_length + 1);
+            memcpy(sym_info.name, data + offset, name_length);
+            sym_info.name[name_length] = 0;
+            offset += name_length;
+        }
+        
+        sym_info.loc_kind = uleb128_to_uint(data, &offset);
+        sym_info.loc = uleb128_to_uint(data, &offset);
+        sym_info.type = uleb128_to_uint(data, &offset);
+
+        bh_arr_set_at(info->symbols, sym_info.sym_id, sym_info);
+    }
+
+    assert(offset == len);
+}
+
+void debug_info_import_type_info(debug_info_t *info, u8 *data, u32 len) {
+    u32 offset = 0;
+    info->has_debug_info = true;
+
+    i32 count = uleb128_to_uint(data, &offset);
+    fori (i, 0, count) {
+        debug_type_info_t type;
+        type.id = uleb128_to_uint(data, &offset);
+
+        u32 name_length = uleb128_to_uint(data, &offset);
+        if (name_length == 0) {
+            type.name = NULL;
+        } else {
+            type.name = bh_alloc_array(info->alloc, char, name_length + 1);
+            memcpy(type.name, data + offset, name_length);
+            type.name[name_length] = 0;
+            offset += name_length;
+        }
+
+        type.size = uleb128_to_uint(data, &offset);
+        type.kind = uleb128_to_uint(data, &offset);
+
+        switch (type.kind) {
+            case debug_type_kind_primitive:
+                type.primitive.primitive_kind = uleb128_to_uint(data, &offset);
+                break;
+
+            case debug_type_kind_modifier:
+                type.modifier.modifier_kind = uleb128_to_uint(data, &offset);
+                type.modifier.modified_type = uleb128_to_uint(data, &offset);
+                break;
+
+            case debug_type_kind_structure:
+                type.structure.member_count = uleb128_to_uint(data, &offset);
+                type.structure.members = bh_alloc_array(info->alloc, debug_type_structure_member_t, type.structure.member_count);
+
+                fori (i, 0, type.structure.member_count) {
+                    type.structure.members[i].offset = uleb128_to_uint(data, &offset);
+                    type.structure.members[i].type   = uleb128_to_uint(data, &offset);
+
+                    u32 name_length = uleb128_to_uint(data, &offset);
+                    type.structure.members[i].name = bh_alloc_array(info->alloc, char, name_length + 1);
+                    memcpy(type.structure.members[i].name, data + offset, name_length);
+                    type.structure.members[i].name[name_length] = 0;
+                    offset += name_length;
+                }
+                break;
+
+            case debug_type_kind_array:
+                type.array.count = uleb128_to_uint(data, &offset);
+                type.array.type  = uleb128_to_uint(data, &offset);
+                break;
+
+            case debug_type_kind_alias:
+                type.alias.alias_kind   = uleb128_to_uint(data, &offset);
+                type.alias.aliased_type = uleb128_to_uint(data, &offset);
+                break;
+
+            case debug_type_kind_function:
+                type.function.param_count = uleb128_to_uint(data, &offset);
+                type.function.param_types = bh_alloc_array(info->alloc, u32, type.function.param_count);
+
+                fori (i, 0, type.function.param_count) {
+                    type.function.param_types[i] = uleb128_to_uint(data, &offset);
+                }
+
+                type.function.return_type = uleb128_to_uint(data, &offset);
+                break;
+
+            // Error handling
+            default: assert(("Unrecognized type kind", 0));
+        }
+
+        bh_arr_set_at(info->types, type.id, type);
+    }
+
+    assert(offset == len);
+}
+
+bool debug_info_lookup_location(debug_info_t *info, u32 instruction, debug_loc_info_t *out) {
+    if (!info || !info->has_debug_info) return false;
+
+    if (instruction > (u32) bh_arr_length(info->instruction_reducer)) return false;
+    i32 loc = info->instruction_reducer[instruction];
+    if (loc < 0) return false;
+
+    *out = info->line_info[loc];
+    return true;
+}
+
+bool debug_info_lookup_file(debug_info_t *info, u32 file_id, debug_file_info_t *out) {
+    if (!info || !info->has_debug_info) return false;
+
+    if (file_id > (u32) bh_arr_length(info->files)) return false;
+    *out = info->files[file_id];
+    return true;
+}
+
+//
+// For now, this is going to compare the strings exactly. In the future, it might be a good
+// to do a levenschtein distance or something, so the full path isn't needed.
+bool debug_info_lookup_file_by_name(debug_info_t *info, char *name, debug_file_info_t *out) {
+    if (!info || !info->has_debug_info) return false;
+
+    bh_arr_each(debug_file_info_t, file, info->files) {
+        if (!strcmp(file->name, name)) {
+            *out = *file;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool debug_info_lookup_func(debug_info_t *info, u32 func_id, debug_func_info_t *out) {
+    if (!info || !info->has_debug_info) return false;
+
+    if (func_id > (u32) bh_arr_length(info->funcs)) return false;
+    *out = info->funcs[func_id];
+    return true;
+}
\ No newline at end of file
diff --git a/interpreter/src/debug/debug_info_builder.c b/interpreter/src/debug/debug_info_builder.c
new file mode 100644 (file)
index 0000000..13c33df
--- /dev/null
@@ -0,0 +1,158 @@
+
+
+#include "ovm_debug.h"
+
+
+void debug_info_builder_init(debug_info_builder_t *builder, debug_info_t *info) {
+    memset(builder, 0, sizeof(*builder));
+
+    builder->info = info;
+}
+
+void debug_info_builder_prepare(debug_info_builder_t *builder, u8 *data) {
+    builder->data = data;
+    builder->reader_offset = 0;
+    builder->current_file_id = 0;
+    builder->current_line = 0;
+    builder->current_scope = -1;
+    builder->next_file_line_offset = 0;
+    builder->remaining_reps = 0;
+}
+
+static i32 debug_info_builder_push_scope(debug_info_builder_t *builder) {
+    debug_sym_scope_t scope;
+    scope.symbols = NULL;
+    bh_arr_new(builder->info->alloc, scope.symbols, 4);    
+    scope.parent = builder->current_scope;
+
+    i32 scope_id = bh_arr_length(builder->info->symbol_scopes);
+    builder->current_scope = scope_id;
+
+    bh_arr_push(builder->info->symbol_scopes, scope);
+    return scope_id;
+}
+
+static i32 debug_info_builder_pop_scope(debug_info_builder_t *builder) {
+    if (builder->current_scope == -1) return -1;
+
+    debug_sym_scope_t *scope = &builder->info->symbol_scopes[builder->current_scope];
+    builder->current_scope = scope->parent;
+    return builder->current_scope;
+}
+
+static void debug_info_builder_add_symbol(debug_info_builder_t *builder, u32 sym_id) {
+    if (builder->current_scope == -1) return;
+
+    debug_sym_scope_t *scope = &builder->info->symbol_scopes[builder->current_scope];
+    bh_arr_push(scope->symbols, sym_id);
+}
+
+static void debug_info_builder_parse(debug_info_builder_t *builder) {
+    u32 count = 0;
+
+    while (!builder->locked) {
+        u8 instr = builder->data[builder->reader_offset++];
+        switch (instr & 0b11000000) {
+            case 0b00000000:
+                instr &= 0b00111111;
+                switch (instr) {
+                    case 0: builder->locked = 1; break; // Early return!
+                    case 1:
+                        builder->current_file_id = uleb128_to_uint(builder->data, &builder->reader_offset);
+                        builder->current_line    = uleb128_to_uint(builder->data, &builder->reader_offset);
+                        break;
+
+                    case 2:
+                        debug_info_builder_push_scope(builder);
+                        break;
+                        
+                    case 3:
+                        debug_info_builder_pop_scope(builder);
+                        break;
+
+                    case 4:
+                        debug_info_builder_add_symbol(
+                            builder,
+                            uleb128_to_uint(builder->data, &builder->reader_offset));
+                        break;
+                }
+                break;
+
+            case 0b01000000:
+                count = instr & 0x3f;
+                builder->current_line += count + 1;
+                builder->remaining_reps = 1;
+                return;
+
+            case 0b10000000:
+                count = instr & 0x3f;
+                builder->current_line -= count + 1;
+                builder->remaining_reps = 1;
+                return;
+
+            case 0b11000000:
+                count = instr & 0x3f;
+                builder->remaining_reps = count + 1;
+                return;
+        }
+    }
+}
+
+void debug_info_builder_step(debug_info_builder_t *builder) {
+    if (builder->data == NULL) return;
+
+    while (builder->remaining_reps == 0 && !builder->locked) {
+        debug_info_builder_parse(builder);
+
+        debug_loc_info_t info; 
+        info.file_id      = builder->current_file_id;
+        info.line         = builder->current_line;
+        info.symbol_scope = builder->current_scope;
+        bh_arr_push(builder->info->line_info, info);
+
+        debug_file_info_t *file_info = &builder->info->files[info.file_id];
+        if (file_info->line_buffer_offset == -1) {
+            file_info->line_buffer_offset = builder->next_file_line_offset;
+            builder->next_file_line_offset += file_info->line_count;
+        }
+
+        i32 line_index = file_info->line_buffer_offset + builder->current_line;
+        u32 target     = bh_arr_length(builder->info->instruction_reducer);
+
+        if (line_index >= bh_arr_capacity(builder->info->line_to_instruction) ||
+            builder->info->line_to_instruction[line_index] == 0) {
+            bh_arr_set_at(builder->info->line_to_instruction, line_index, target);
+        }
+    }
+
+    if (builder->locked) return;
+
+    assert(builder->remaining_reps);
+    builder->remaining_reps -= 1;
+    return;
+}
+
+void debug_info_builder_emit_location(debug_info_builder_t *builder) {
+    bh_arr_push(builder->info->instruction_reducer, bh_arr_length(builder->info->line_info) - 1);
+}
+
+void debug_info_builder_begin_func(debug_info_builder_t *builder, i32 func_idx) {
+    if (!builder->data) return;
+    if (func_idx >= bh_arr_length(builder->info->funcs)) return;
+
+    debug_func_info_t *func_info = &builder->info->funcs[func_idx];
+
+    builder->reader_offset = func_info->debug_op_offset;
+    assert(builder->data[builder->reader_offset] == 2);
+    assert(builder->data[builder->reader_offset+1] == 1);
+    builder->remaining_reps = 0;
+    builder->locked = 0;
+}
+
+void debug_info_builder_end_func(debug_info_builder_t *builder) {
+    if (!builder->data) return;
+    
+    assert(!builder->locked);
+    debug_info_builder_step(builder);
+    assert(builder->locked);
+}
diff --git a/interpreter/src/debug/debug_runtime_values.c b/interpreter/src/debug/debug_runtime_values.c
new file mode 100644 (file)
index 0000000..2edde56
--- /dev/null
@@ -0,0 +1,279 @@
+
+#include "ovm_debug.h"
+#include "vm.h"
+
+static char write_buf[4096];
+
+#define WRITE(str) do {    \
+        bh_buffer_write_string(&builder->output, str); \
+    } while (0);
+
+#define WRITE_FORMAT(format, ...) do {    \
+        u32 len = snprintf(write_buf, 4096, format, __VA_ARGS__); \
+        bh_buffer_append(&builder->output, write_buf, len); \
+    } while (0);
+
+static bool lookup_register_in_frame(ovm_state_t *state, ovm_stack_frame_t *frame, u32 reg, ovm_value_t *out) {
+
+    u32 val_num_base;
+    if (frame == &bh_arr_last(state->stack_frames)) {
+        val_num_base = state->value_number_offset;
+    } else {
+        val_num_base = (frame + 1)->value_number_base;
+    }
+
+    *out = state->numbered_values[val_num_base + reg];
+    return true;
+}
+
+static void append_value_from_memory_with_type(debug_runtime_value_builder_t *builder, void *base, u32 type_id) {
+    debug_type_info_t *type = &builder->info->types[type_id];
+
+    switch (type->kind) {
+        case debug_type_kind_primitive:
+            switch (type->primitive.primitive_kind) {
+                case debug_type_primitive_kind_void: WRITE("void"); break;
+                case debug_type_primitive_kind_signed_integer:
+                    switch (type->size) {
+                        case 1: WRITE_FORMAT("%hhd", *(i8 *)  base); break;
+                        case 2: WRITE_FORMAT("%hd",  *(i16 *) base); break;
+                        case 4: WRITE_FORMAT("%d",   *(i32 *) base); break;
+                        case 8: WRITE_FORMAT("%ld",  *(i64 *) base); break;
+                        default: WRITE("(err)"); break;
+                    }
+                    break;
+
+                case debug_type_primitive_kind_unsigned_integer:
+                    switch (type->size) {
+                        case 1: WRITE_FORMAT("%hhu", *(u8 *) base); break;
+                        case 2: WRITE_FORMAT("%hu",  *(u16 *) base); break;
+                        case 4: WRITE_FORMAT("%u",   *(u32 *) base); break;
+                        case 8: WRITE_FORMAT("%lu",  *(u64 *) base); break;
+                        default: WRITE("(err)"); break;
+                    }
+                    break;
+
+                case debug_type_primitive_kind_float:
+                    switch (type->size) {
+                        case 4: WRITE_FORMAT("%f",   *(f32 *) base); break;
+                        case 8: WRITE_FORMAT("%f",   *(f64 *) base); break;
+                        default: WRITE("(err)"); break;
+                    }
+                    break;
+
+                case debug_type_primitive_kind_boolean:
+                    if ((*(u8 *) base) != 0) { WRITE("true"); }
+                    else                     { WRITE("false"); }
+                    break;
+
+                default:
+                    WRITE("(err)");
+            }
+            break;
+
+        case debug_type_kind_modifier:
+            switch (type->modifier.modifier_kind) {
+                case debug_type_modifier_kind_pointer:
+                    switch (type->size) {
+                        case 4: WRITE_FORMAT("0x%x",   *(u32 *) base); break;
+                        case 8: WRITE_FORMAT("0x%lx",  *(u64 *) base); break;
+                        default: WRITE("(err)"); break;
+                    }
+                    break;
+
+                default:
+                    append_value_from_memory_with_type(builder, base, type->modifier.modified_type);
+                    break;
+            }
+            break;
+
+        case debug_type_kind_alias:
+            append_value_from_memory_with_type(builder, base, type->alias.aliased_type);
+            break;
+
+        case debug_type_kind_function:
+            WRITE_FORMAT("func[%d]", *(u32 *) base);
+            break;
+
+        case debug_type_kind_structure: {
+            WRITE("{ ");
+
+            fori (i, 0, (i32) type->structure.member_count) {
+                if (i != 0) WRITE(", ");
+
+                WRITE_FORMAT("%s=", type->structure.members[i].name);
+
+                u32 offset  = type->structure.members[i].offset;
+                u32 type_id = type->structure.members[i].type;
+                append_value_from_memory_with_type(builder, bh_pointer_add(base, offset), type_id);
+            }
+
+            WRITE(" }");
+            break;
+        }
+
+        case debug_type_kind_array: {
+            WRITE("[");
+
+            debug_type_info_t *elem_type = &builder->info->types[type->array.type];
+            fori (i, 0, (i32) type->array.count) {
+                if (i != 0) WRITE(", ");
+
+                append_value_from_memory_with_type(builder, bh_pointer_add(base, i * elem_type->size), elem_type->id);
+            }
+
+            WRITE("]");
+            break;
+        }
+
+        default: WRITE("(unknown)"); break;
+    }
+}
+
+static void append_ovm_value_with_type(debug_runtime_value_builder_t *builder, ovm_value_t value, u32 type_id) {
+    debug_type_info_t *type = &builder->info->types[type_id];
+
+    switch (type->kind) {
+        case debug_type_kind_primitive:
+            switch (type->primitive.primitive_kind) {
+                case debug_type_primitive_kind_void: WRITE("void"); break;
+                case debug_type_primitive_kind_signed_integer:
+                    switch (type->size) {
+                        case 1: WRITE_FORMAT("%hhd", value.i8); break;
+                        case 2: WRITE_FORMAT("%hd",  value.i16); break;
+                        case 4: WRITE_FORMAT("%d",   value.i32); break;
+                        case 8: WRITE_FORMAT("%ld",  value.i64); break;
+                        default: WRITE("(err)"); break;
+                    }
+                    break;
+
+                case debug_type_primitive_kind_unsigned_integer:
+                    switch (type->size) {
+                        case 1: WRITE_FORMAT("%hhu", value.u8); break;
+                        case 2: WRITE_FORMAT("%hu",  value.u16); break;
+                        case 4: WRITE_FORMAT("%u",   value.u32); break;
+                        case 8: WRITE_FORMAT("%lu",  value.u64); break;
+                        default: WRITE("(err)"); break;
+                    }
+                    break;
+
+                case debug_type_primitive_kind_float:
+                    switch (type->size) {
+                        case 4: WRITE_FORMAT("%f",   value.f32); break;
+                        case 8: WRITE_FORMAT("%f",   value.f64); break;
+                        default: WRITE("(err)"); break;
+                    }
+                    break;
+
+                case debug_type_primitive_kind_boolean:
+                    if (value.u64 != 0) { WRITE("true"); }
+                    else                { WRITE("false"); }
+                    break;
+
+                default:
+                    WRITE("(err)");
+            }
+            break;
+
+        case debug_type_kind_modifier:
+            switch (type->modifier.modifier_kind) {
+                case debug_type_modifier_kind_pointer:
+                    switch (type->size) {
+                        case 4: WRITE_FORMAT("0x%x",   value.u32); break;
+                        case 8: WRITE_FORMAT("0x%lx",  value.u64); break;
+                        default: WRITE("(err)"); break;
+                    }
+                    break;
+
+                default:
+                    append_ovm_value_with_type(builder, value, type->modifier.modified_type);
+                    break;
+            }
+            break;
+
+        case debug_type_kind_alias:
+            append_ovm_value_with_type(builder, value, type->alias.aliased_type);
+            break;
+
+        case debug_type_kind_function:
+            WRITE_FORMAT("func[%d]", value.u32);
+            break;
+
+        case debug_type_kind_array: {
+            void *base = bh_pointer_add(builder->state->ovm_engine->memory, value.u32);
+            append_value_from_memory_with_type(builder, base, type_id);
+            break;
+        }
+
+        default: WRITE("(unknown)"); break;
+    }
+}
+
+static void append_value_from_stack(debug_runtime_value_builder_t *builder, u32 offset, u32 type_id) {
+    ovm_value_t stack_ptr;
+    if (!lookup_register_in_frame(builder->ovm_state, builder->ovm_frame, builder->func_info.stack_ptr_idx, &stack_ptr)) {
+        WRITE("(no stack ptr)");
+        return;
+    }
+
+    void *base = bh_pointer_add(builder->state->ovm_engine->memory, stack_ptr.u32 + offset);
+
+    append_value_from_memory_with_type(builder, base, type_id);
+}
+
+static void append_value_from_register(debug_runtime_value_builder_t *builder, u32 reg, u32 type_id) {
+    ovm_value_t value;
+
+    debug_type_info_t *type = &builder->info->types[type_id];
+    if (type->kind == debug_type_kind_structure) {
+        WRITE("{ ");
+
+        fori (i, 0, (i32) type->structure.member_count) {
+            if (i != 0) WRITE(", ");
+
+            WRITE_FORMAT("%s=", type->structure.members[i].name);
+
+            if (!lookup_register_in_frame(builder->ovm_state, builder->ovm_frame, reg + i, &value)) {
+                WRITE("(err)")
+                continue;
+            }
+
+            append_ovm_value_with_type(builder, value, type->structure.members[i].type);
+        }
+
+        WRITE(" }");
+        return;
+    }
+
+    if (!lookup_register_in_frame(builder->ovm_state, builder->ovm_frame, reg, &value)) {
+        WRITE("(err)")
+        return;
+    }
+
+    append_ovm_value_with_type(builder, value, type_id);
+}
+
+
+
+
+void debug_runtime_value_build_init(debug_runtime_value_builder_t *builder, bh_allocator alloc) {
+    bh_buffer_init(&builder->output, alloc, 1024);
+}
+
+void debug_runtime_value_build_free(debug_runtime_value_builder_t *builder) {
+    bh_buffer_free(&builder->output);
+}
+
+void debug_runtime_value_build_string(debug_runtime_value_builder_t *builder) {
+    if (builder->sym_info.loc_kind == debug_sym_loc_register) {
+        append_value_from_register(builder, builder->sym_info.loc, builder->sym_info.type);
+        return;
+    }
+
+    if (builder->sym_info.loc_kind == debug_sym_loc_stack) {
+        append_value_from_stack(builder, builder->sym_info.loc, builder->sym_info.type);
+        return;
+    }
+
+    WRITE("(location unknown)");
+}
diff --git a/interpreter/src/debug/debug_thread.c b/interpreter/src/debug/debug_thread.c
new file mode 100644 (file)
index 0000000..ba0fa28
--- /dev/null
@@ -0,0 +1,544 @@
+
+#include "ovm_debug.h"
+#include "vm.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <poll.h>
+
+
+#define CMD_NOP 0
+#define CMD_RES 1
+#define CMD_PAUSE 2
+#define CMD_BRK 3
+#define CMD_CLR_BRK 4
+#define CMD_STEP 5
+#define CMD_TRACE 6
+#define CMD_THREADS 7
+#define CMD_VARS 8
+
+#define EVT_NOP 0
+#define EVT_BRK_HIT 1
+#define EVT_PAUSE 2
+#define EVT_NEW_THREAD 3 // Not Implemented
+#define EVT_RESPONSE 0xffffffff
+
+struct msg_parse_ctx_t {
+    char *data;
+    unsigned int offset;
+    unsigned int bytes_read;
+};
+
+static void send_response_header(debug_state_t *debug, unsigned int message_number) {
+    unsigned int RESPONSE_HEADER = EVT_RESPONSE;
+    bh_buffer_write_u32(&debug->send_buffer, EVT_RESPONSE);
+    bh_buffer_write_u32(&debug->send_buffer, message_number);
+}
+
+static void send_string(debug_state_t *debug, const char *str) {
+    if (!str) {
+        bh_buffer_write_u32(&debug->send_buffer, 0);
+    } else {
+        unsigned int len = strlen(str);
+        bh_buffer_write_u32(&debug->send_buffer, len);
+        bh_buffer_append(&debug->send_buffer, str, len);
+    }
+}
+
+static void send_bytes(debug_state_t *debug, const char *bytes, unsigned int len) {
+    bh_buffer_write_u32(&debug->send_buffer, len);
+    bh_buffer_append(&debug->send_buffer, bytes, len);
+}
+
+static void send_int(debug_state_t *debug, unsigned int x) {
+    bh_buffer_write_u32(&debug->send_buffer, x);
+}
+
+static void send_bool(debug_state_t *debug, bool x) {
+    bool v = x ? 1 : 0;
+    bh_buffer_write_byte(&debug->send_buffer, v);
+}
+
+static int parse_int(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
+    int i = *(unsigned int *) &ctx->data[ctx->offset];
+    ctx->offset += sizeof(unsigned int);
+    return i;
+}
+
+static char *parse_bytes(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
+    int len = parse_int(debug, ctx);
+
+    char *buf = bh_alloc_array(debug->tmp_alloc, char, len);
+    memcpy(buf, &ctx->data[ctx->offset], len);
+    ctx->offset += len;
+
+    return buf;
+}
+
+static char *parse_string(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
+    int len = parse_int(debug, ctx);
+
+    char *buf = bh_alloc_array(debug->tmp_alloc, char, len + 1);
+    memcpy(buf, &ctx->data[ctx->offset], len);
+    ctx->offset += len;
+
+    buf[len] = 0;
+    return buf;
+}
+
+static void resume_thread(debug_thread_state_t *thread) {
+    thread->run_count = -1;
+    sem_post(&thread->wait_semaphore);
+}
+
+static void get_stack_frame_location(debug_state_t *debug,
+    debug_func_info_t *func_info, debug_file_info_t *file_info, debug_loc_info_t *loc_info,
+    debug_thread_state_t *thread, ovm_stack_frame_t *frame) {
+
+    ovm_func_t *func = frame->func;
+
+    assert(debug_info_lookup_func(debug->info, func->id, func_info));
+
+    u32 instr;
+    if (frame == &bh_arr_last(thread->ovm_state->stack_frames)) {
+        instr = thread->ovm_state->pc;
+    } else {
+        instr = (frame + 1)->return_address;
+    }
+
+    while (!debug_info_lookup_location(debug->info, instr, loc_info))
+        instr++;
+
+    assert(debug_info_lookup_file(debug->info, loc_info->file_id, file_info));
+}
+
+static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
+#define ON_THREAD(tid) \
+    bh_arr_each(debug_thread_state_t *, thread, debug->threads) \
+        if ((*thread)->id == tid)
+
+    u32 msg_id     = parse_int(debug, ctx);
+    u32 command_id = parse_int(debug, ctx);
+
+    // printf("[INFO ] Recv command: %d\n", command_id);
+
+    switch (command_id) {
+        case CMD_NOP: {
+            send_response_header(debug, msg_id);
+            break;
+        }
+
+        case CMD_RES: {
+            u32 thread_id = parse_int(debug, ctx);
+
+            bool resumed_a_thread = false;
+
+            // Release the thread(s)
+            bh_arr_each(debug_thread_state_t *, thread, debug->threads) {
+                if (thread_id == 0xffffffff || (*thread)->id == thread_id) {
+                    resume_thread(*thread);
+                    resumed_a_thread = true;
+                }
+            }
+
+            send_response_header(debug, msg_id);
+            send_bool(debug, resumed_a_thread);
+            break;
+        }
+
+        case CMD_PAUSE: {
+            u32 thread_id = parse_int(debug, ctx);
+
+            ON_THREAD(thread_id) {
+                (*thread)->run_count = 0;
+            }
+
+            send_response_header(debug, msg_id);
+            break;
+        }
+
+        case CMD_BRK: {
+            char    *filename = parse_string(debug, ctx);
+            unsigned int line = parse_int(debug, ctx);
+
+            //
+            // TODO: This translation logic will have to be abstracted
+            debug_file_info_t file_info;
+            bool file_found = debug_info_lookup_file_by_name(debug->info, filename, &file_info);
+            if (!file_found) {
+                goto brk_send_error;
+            }
+
+            if (line > file_info.line_count) {
+                goto brk_send_error;
+            }
+
+            u32 instr;
+            while ((instr = debug->info->line_to_instruction[file_info.line_buffer_offset + line]) == 0) {
+                line += 1;
+
+                if (line > file_info.line_count) {
+                    goto brk_send_error;
+                }
+            }
+
+            printf("[INFO ] Setting breakpoint at %s:%d (%xd)\n", filename, line, instr);
+            
+            debug_breakpoint_t bp;
+            bp.id = debug->next_breakpoint_id++;
+            bp.instr = instr;
+            bp.file_id = file_info.file_id;
+            bp.line = line;
+            bh_arr_each(debug_thread_state_t *, thread, debug->threads) {
+                bh_arr_push((*thread)->breakpoints, bp);
+            }
+
+            send_response_header(debug, msg_id);
+            send_bool(debug, true);
+            send_int(debug, bp.id);    // TODO: This should be a unique breakpoint ID
+            send_int(debug, line);
+            break;
+
+          brk_send_error:
+            send_response_header(debug, msg_id);
+            send_bool(debug, false);
+            send_int(debug, -1);
+            send_int(debug, 0);
+            break;
+        }
+
+        case CMD_CLR_BRK: {
+            char *filename = parse_string(debug, ctx);
+
+            debug_file_info_t file_info;
+            bool file_found = debug_info_lookup_file_by_name(debug->info, filename, &file_info);
+            if (!file_found) {
+                goto clr_brk_send_error;
+            }
+
+            bh_arr_each(debug_thread_state_t *, thread, debug->threads) {
+                bh_arr_each(debug_breakpoint_t, bp, (*thread)->breakpoints) {
+                    if (bp->file_id == file_info.file_id) {
+                        // This is kind of hacky but it does successfully delete
+                        // a single element from the array and move the iterator.
+                        bh_arr_fastdelete((*thread)->breakpoints, bp - (*thread)->breakpoints);
+                        bp--;
+                    }
+                }
+            }
+
+            send_response_header(debug, msg_id);
+            send_bool(debug, true);
+            break;
+
+          clr_brk_send_error:
+            send_response_header(debug, msg_id);
+            send_bool(debug, false);
+            break;
+        }
+
+        case CMD_STEP: {
+            u32 granularity = parse_int(debug, ctx);
+            u32 thread_id = parse_int(debug, ctx);
+            
+            if (granularity == 1) {
+                ON_THREAD(thread_id) {
+                    (*thread)->pause_at_next_line = true;
+                    (*thread)->pause_within = -1;
+                    resume_thread(*thread);
+                }
+            }
+
+            if (granularity == 2) {
+                ON_THREAD(thread_id) {
+                    (*thread)->run_count = 1;
+                    resume_thread(*thread);
+                }
+            }
+
+            if (granularity == 3) {
+                ON_THREAD(thread_id) {
+                    ovm_stack_frame_t *last_frame = &bh_arr_last((*thread)->ovm_state->stack_frames);
+                    (*thread)->pause_at_next_line = true;
+                    (*thread)->pause_within = last_frame->func->id;
+                    (*thread)->extra_frames_since_last_pause = 0;
+                    resume_thread(*thread);
+                }
+            }
+
+            if (granularity == 4) {
+                ON_THREAD(thread_id) {
+                    if (bh_arr_length((*thread)->ovm_state->stack_frames) == 1) {
+                        (*thread)->pause_within = -1;
+                    } else {
+                        ovm_stack_frame_t *last_frame = &bh_arr_last((*thread)->ovm_state->stack_frames);
+                        (*thread)->pause_within = (last_frame - 1)->func->id;
+                    }
+
+                    (*thread)->pause_at_next_line = true;
+                    (*thread)->extra_frames_since_last_pause = 0;
+                    resume_thread(*thread);
+                }
+            }
+
+            send_response_header(debug, msg_id);
+            break;
+        }
+
+        case CMD_TRACE: {
+            unsigned int thread_id = parse_int(debug, ctx);
+
+            debug_thread_state_t *thread = NULL;
+            bh_arr_each(debug_thread_state_t *, pthread, debug->threads) {
+                if ((*pthread)->id == thread_id) {
+                    thread = *pthread;
+                    break;
+                }
+            }
+
+            if (thread == NULL) {
+                send_response_header(debug, msg_id);
+                send_int(debug, 0);
+                break;
+            }
+
+            bh_arr(ovm_stack_frame_t) frames = thread->ovm_state->stack_frames;
+
+            send_response_header(debug, msg_id);
+            send_int(debug, bh_arr_length(frames));
+
+            bh_arr_rev_each(ovm_stack_frame_t, frame, frames) {
+                debug_func_info_t func_info;
+                debug_file_info_t file_info;
+                debug_loc_info_t  loc_info;
+
+                get_stack_frame_location(debug, &func_info, &file_info, &loc_info, thread, frame);
+
+                send_string(debug, func_info.name);
+                send_string(debug, file_info.name);
+                send_int(debug, loc_info.line);
+            }
+
+            break;
+        }
+
+        case CMD_THREADS: {
+            bh_arr(debug_thread_state_t *) threads = debug->threads;
+
+            send_response_header(debug, msg_id);
+            send_int(debug, bh_arr_length(threads));
+
+            char buf[128];
+            bh_arr_each(debug_thread_state_t *, thread, threads) {
+                send_int(debug, (*thread)->id);
+
+                snprintf(buf, 128, "thread #%d", (*thread)->id);
+                send_string(debug, buf);
+            }
+
+            break;
+        }
+
+        case CMD_VARS: {
+            i32 stack_frame = parse_int(debug, ctx);
+            u32 thread_id   = parse_int(debug, ctx);
+
+            ON_THREAD(thread_id) {
+                bh_arr(ovm_stack_frame_t) frames = (*thread)->ovm_state->stack_frames;
+                if (stack_frame >= bh_arr_length(frames)) {
+                    goto vars_error;
+                }
+
+                ovm_stack_frame_t *frame = &frames[bh_arr_length(frames) - 1 - stack_frame];
+
+                debug_func_info_t func_info;
+                debug_file_info_t file_info;
+                debug_loc_info_t  loc_info;
+
+                get_stack_frame_location(debug, &func_info, &file_info, &loc_info, *thread, frame);
+
+                send_response_header(debug, msg_id);
+
+                i32 symbol_scope = loc_info.symbol_scope;
+                while (symbol_scope != -1) {
+                    debug_sym_scope_t sym_scope = debug->info->symbol_scopes[symbol_scope];
+
+                    bh_arr_each(u32, sym_id, sym_scope.symbols) {
+                        debug_sym_info_t *sym = &debug->info->symbols[*sym_id];
+
+                        send_int(debug, 0);
+                        send_string(debug, sym->name);
+
+                        debug_runtime_value_builder_t builder;
+                        builder.state = debug;
+                        builder.info = debug->info;
+                        builder.ovm_state = (*thread)->ovm_state;
+                        builder.ovm_frame = frame;
+                        builder.sym_scope = sym_scope;
+                        builder.func_info = func_info;
+                        builder.file_info = file_info;
+                        builder.loc_info = loc_info;
+                        builder.sym_info = *sym;
+
+                        debug_runtime_value_build_init(&builder, bh_heap_allocator());
+                        debug_runtime_value_build_string(&builder);
+                        send_bytes(debug, builder.output.data, builder.output.length);
+                        debug_runtime_value_build_free(&builder);
+
+                        debug_type_info_t *type = &debug->info->types[sym->type];
+                        send_string(debug, type->name);
+                    }
+
+                    symbol_scope = sym_scope.parent;
+                }
+                
+                send_int(debug, 1);
+                return;
+            }
+
+          vars_error:
+            send_response_header(debug, msg_id);
+            send_int(debug, 1);
+            break;
+        }
+    }
+}
+
+static void process_message(debug_state_t *debug, char *msg, unsigned int bytes_read) {
+    struct msg_parse_ctx_t ctx;
+    ctx.data = msg;
+    ctx.offset = 0;
+    ctx.bytes_read = bytes_read;
+
+    while (ctx.offset < ctx.bytes_read) {
+        u32 msg_id = *(u32 *) &ctx.data[ctx.offset];
+        assert(msg_id != 0xffffffff);
+
+        process_command(debug, &ctx);
+    }
+}
+
+void *__debug_thread_entry(void * data) {
+    debug_state_t *debug = data;
+    debug->debug_thread_running = true;
+
+    // Set up socket listener
+    // Wait for initial connection/handshake before entering loop.
+    
+    debug->listen_socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+    struct sockaddr_un local_addr, remote_addr;
+    local_addr.sun_family = AF_UNIX;
+    strcpy(local_addr.sun_path, "/tmp/ovm-debug.0000"); // TODO: Make this dynamic so mulitple servers can exist at a time.
+    unlink(local_addr.sun_path);                        // TODO: Remove this line for the same reason.
+    int len = strlen(local_addr.sun_path) + sizeof(local_addr.sun_family);
+    bind(debug->listen_socket_fd, (struct sockaddr *)&local_addr, len);
+    
+    //
+    // Currently, there can only be 1 connected debugger instance at a time.
+    listen(debug->listen_socket_fd, 1);
+
+    len = sizeof(struct sockaddr_un);
+    debug->client_fd = accept(debug->listen_socket_fd, (void * restrict)&remote_addr, &len);
+
+    close(debug->listen_socket_fd);
+
+    // Disable blocking reads and write in the client socket
+    // Alternatively, a MSG_DONTWAIT could be used below
+    fcntl(debug->client_fd, F_SETFL, O_NONBLOCK);
+    fcntl(debug->state_change_pipes[0], F_SETFL, O_NONBLOCK);
+
+    printf("[INFO ] Client connected\n");
+
+    bh_buffer_init(&debug->send_buffer, bh_heap_allocator(), 1024);
+
+    struct pollfd poll_fds[2];
+    poll_fds[0].fd = debug->state_change_pipes[0];
+    poll_fds[0].events = POLLIN;
+    poll_fds[1].fd = debug->client_fd;
+    poll_fds[1].events = POLLIN;
+
+    char command[4096];
+    while (debug->debug_thread_running) {
+        poll(poll_fds, 2, 1000);
+
+        //
+        // Try to read commands from the client.
+        // If an error was returned, bail out of this thread.
+        i32 bytes_read = recv(debug->client_fd, command, 4096, 0);
+        if (bytes_read == -1) {
+            switch (errno) {
+                case EAGAIN: break;
+
+                case ECONNRESET:
+                    printf("[ERROR] OVM Debugger connection closed by peer.\n");
+                    debug->debug_thread_running = false;
+                    break;
+
+                default:
+                    printf("[ERROR] OVM Debugger crashed when reading from UNIX socket.\n");
+                    debug->debug_thread_running = false;
+                    break;
+            }
+        }
+
+        //
+        // If data was returned, process the commands that are inside of it.
+        if (bytes_read > 0) {
+            process_message(debug, command, bytes_read);
+        }
+
+        //
+        // Check the state of the running program and report any changes.
+
+        char buf;
+        int dummy = read(debug->state_change_pipes[0], &buf, 1);
+
+        bh_arr_each(debug_thread_state_t *, thread, debug->threads) {
+            if ((*thread)->state == debug_state_hit_breakpoint) {
+                (*thread)->state = debug_state_paused;
+
+                i32 instr = -1, bp_id = (*thread)->last_breakpoint_hit;
+                bh_arr_each(debug_breakpoint_t, bp, (*thread)->breakpoints) {
+                    if (bp->id == bp_id) {
+                        instr = bp->instr;
+                        break;
+                    }
+                }
+
+                if (instr == -1) continue;
+
+                debug_loc_info_t loc_info;
+                debug_info_lookup_location(debug->info, instr, &loc_info);
+
+                debug_file_info_t file_info;
+                debug_info_lookup_file(debug->info, loc_info.file_id, &file_info);
+
+                send_int(debug, EVT_BRK_HIT);
+                send_int(debug, bp_id);
+                send_int(debug, (*thread)->id);
+            }
+
+            if ((*thread)->state == debug_state_pausing) {
+                (*thread)->state = debug_state_paused;
+
+                send_int(debug, EVT_PAUSE);
+                send_int(debug, (*thread)->id);
+                send_int(debug, (*thread)->pause_reason);
+            }
+        }
+
+        send(debug->client_fd, debug->send_buffer.data, debug->send_buffer.length, 0);
+        bh_buffer_clear(&debug->send_buffer);
+
+        bh_arena_clear(&debug->tmp_arena);
+    }
+
+    close(debug->client_fd);
+    printf("[INFO ] Session closed\n");
+
+    unlink(local_addr.sun_path);
+    return NULL;
+}
diff --git a/interpreter/src/ovm_cli_test.c b/interpreter/src/ovm_cli_test.c
new file mode 100644 (file)
index 0000000..98caf1e
--- /dev/null
@@ -0,0 +1,53 @@
+#include "vm.h"
+
+#include <math.h>
+
+void print_result(void *data, ovm_value_t *params, ovm_value_t *result) {
+    switch (params[0].type) {
+        case OVM_TYPE_I32: printf("Result: %d\n", params[0].i32); break;
+        case OVM_TYPE_F32: printf("Result: %f\n", params[0].f32); break;
+    }
+}
+
+void c_call_1f64(void *data, ovm_value_t *params, ovm_value_t *result) {
+     result->type = OVM_TYPE_F32;
+     result->f32  = (f32) ((f64 (*)(f64)) data)(params[0].f32);
+}
+
+int main(int argc, char *argv[]) {
+
+    static ovm_linkable_func_t native_funcs[] = {
+        { "dummy", 0, { print_result, NULL } },
+        { "print", 1, { print_result, NULL } },
+        { "sin",   1, { c_call_1f64, sin } },
+        { NULL },
+    };
+
+    ovm_store_t *store = ovm_store_new();
+    ovm_program_t *prog = ovm_program_new(store);
+    ovm_engine_t *engine = ovm_engine_new(store);
+    ovm_state_t  *state = ovm_state_new(engine, prog);
+
+    ovm_program_load_from_file(prog, engine, argv[1]);
+    ovm_program_print_instructions(prog, 0, bh_arr_length(prog->code));
+
+    static int func_table[] = { 0, 1, 6 };
+    ovm_program_register_static_ints(prog, 3, func_table);
+
+    ovm_state_link_external_funcs(prog, state, native_funcs);
+
+    state->pc = 0;
+    ovm_value_t values[] = {
+        { .type = OVM_TYPE_I32, .i32 = 1 },
+        { .type = OVM_TYPE_I32, .i32 = 2 },
+    };
+    ovm_value_t result = ovm_func_call(engine, state, prog, 5, 0, values);
+    // printf("%d %d\n", result.type, result.i32);
+
+    ovm_state_delete(state);
+    ovm_engine_delete(engine);
+    ovm_program_delete(prog);
+    ovm_store_delete(store);
+
+    return 0;
+}
diff --git a/interpreter/src/vm/code_builder.c b/interpreter/src/vm/code_builder.c
new file mode 100644 (file)
index 0000000..ec84b47
--- /dev/null
@@ -0,0 +1,555 @@
+
+#include "vm_codebuilder.h"
+#include "ovm_debug.h"
+
+// #define BUILDER_DEBUG
+
+#if defined(BUILDER_DEBUG)
+    #define POP_VALUE(b)     (bh_arr_length((b)->execution_stack) == 0 ? (assert(("invalid value pop", 0)), 0) : bh_arr_pop((b)->execution_stack))
+#else
+    #define POP_VALUE(b) bh_arr_pop((b)->execution_stack)
+#endif
+
+#define PUSH_VALUE(b, r) (bh_arr_push((b)->execution_stack, r))
+
+static inline int NEXT_VALUE(ovm_code_builder_t *b) {
+#if defined(BUILDER_DEBUG)
+    b->highest_value_number += 1;
+    return b->highest_value_number - 1;
+
+#else
+    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) + 1);
+    return bh_arr_last(b->execution_stack) + 1;
+#endif
+}
+
+ovm_code_builder_t ovm_code_builder_new(ovm_program_t *program, debug_info_builder_t *debug, i32 param_count, i32 local_count) {
+    ovm_code_builder_t builder;
+    builder.param_count = param_count;
+    builder.local_count = local_count;
+    builder.start_instr = bh_arr_length(program->code);
+    builder.program = program;
+
+    builder.execution_stack = NULL;
+    bh_arr_new(bh_heap_allocator(), builder.execution_stack, 32);
+
+    builder.next_label_idx = 0;
+    builder.label_stack = NULL;
+    builder.branch_patches = NULL;
+    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;
+
+    builder.debug_builder = debug;
+
+    return builder;
+}
+
+void ovm_code_builder_free(ovm_code_builder_t *builder) {
+    bh_arr_free(builder->execution_stack);
+    bh_arr_free(builder->label_stack);
+    bh_arr_free(builder->branch_patches);
+}
+
+label_target_t ovm_code_builder_wasm_target_idx(ovm_code_builder_t *builder, i32 idx) {
+    i32 walker = bh_arr_length(builder->label_stack) - 1 - idx;
+    assert(walker >= 0);
+    return builder->label_stack[walker];
+}
+
+i32 ovm_code_builder_push_label_target(ovm_code_builder_t *builder, label_kind_t kind) {
+    label_target_t target;
+    target.kind = kind;
+    target.idx = builder->next_label_idx++;
+    target.instr = -1;
+
+    if (kind == label_kind_loop) {
+        target.instr = bh_arr_length(builder->program->code);
+    }
+
+    bh_arr_push(builder->label_stack, target);
+
+    return target.idx;
+}
+
+void ovm_code_builder_pop_label_target(ovm_code_builder_t *builder) {
+    label_target_t target = bh_arr_pop(builder->label_stack);
+    if (target.instr == -1) {
+        target.instr = bh_arr_length(builder->program->code);
+    }
+
+    fori (i, 0, bh_arr_length(builder->branch_patches)) {
+        branch_patch_t patch = builder->branch_patches[i];
+        if (patch.label_idx != target.idx) continue;
+
+        int br_delta = target.instr - patch.branch_instr - 1;
+
+        switch (patch.kind) {
+            case branch_patch_instr_a:
+                builder->program->code[patch.branch_instr].a = br_delta;
+                break;
+
+            case branch_patch_static_idx:
+                ovm_program_modify_static_int(builder->program, patch.static_arr, patch.static_idx, br_delta);
+                break;
+        }
+        
+        bh_arr_fastdelete(builder->branch_patches, i);
+        i--;
+    }
+}
+
+void ovm_code_builder_patch_else(ovm_code_builder_t *builder, label_target_t if_target) {
+    assert(if_target.kind == label_kind_if);
+
+    fori (i, 0, bh_arr_length(builder->branch_patches)) {
+        branch_patch_t patch = builder->branch_patches[i];
+        if (patch.label_idx != if_target.idx) continue;
+        if (!patch.targets_else) continue;
+
+        int br_delta = bh_arr_length(builder->program->code) - patch.branch_instr - 1;
+        assert(patch.kind == branch_patch_instr_a);
+
+        builder->program->code[patch.branch_instr].a = br_delta;
+
+        bh_arr_fastdelete(builder->branch_patches, i);
+        return;
+    }
+}
+
+void ovm_code_builder_add_nop(ovm_code_builder_t *builder) {
+    ovm_instr_t nop = {0};
+    nop.full_instr = OVMI_NOP;
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &nop);
+}
+
+void ovm_code_builder_add_binop(ovm_code_builder_t *builder, u32 instr) {
+    i32 right  = POP_VALUE(builder);
+    i32 left   = POP_VALUE(builder);
+    i32 result = NEXT_VALUE(builder);
+
+    ovm_instr_t binop;
+    binop.full_instr = instr;
+    binop.r = result;
+    binop.a = left;
+    binop.b = right;
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &binop);
+    PUSH_VALUE(builder, result);
+}
+
+void ovm_code_builder_add_imm(ovm_code_builder_t *builder, u32 ovm_type, void *imm) {
+    ovm_instr_t imm_instr = {0};
+    imm_instr.full_instr = OVM_TYPED_INSTR(OVMI_IMM, ovm_type);
+    imm_instr.r = NEXT_VALUE(builder);
+
+    switch (ovm_type) {
+        case OVM_TYPE_I8:  imm_instr.i = (u32) *(u8 *) imm; break;
+        case OVM_TYPE_I16: imm_instr.i = (u32) *(u16 *) imm; break;
+        case OVM_TYPE_I32: imm_instr.i =       *(u32 *) imm; break;
+        case OVM_TYPE_I64: imm_instr.l =       *(u64 *) imm; break;
+        case OVM_TYPE_F32: imm_instr.f =       *(f32 *) imm; break;
+        case OVM_TYPE_F64: imm_instr.d =       *(f64 *) imm; break;
+        default: assert(("bad ovm type for add_imm", 0));
+    }
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &imm_instr);
+    PUSH_VALUE(builder, imm_instr.r);
+}
+
+void ovm_code_builder_add_unop(ovm_code_builder_t *builder, u32 instr) {
+    i32 operand = POP_VALUE(builder);
+
+    ovm_instr_t unop = {0};
+    unop.full_instr = instr;
+    unop.r = NEXT_VALUE(builder);
+    unop.a = operand;
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &unop);
+    PUSH_VALUE(builder, unop.r);
+}
+
+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.kind = branch_patch_instr_a;
+    patch.branch_instr = bh_arr_length(builder->program->code);
+    patch.label_idx = label_idx;
+    patch.targets_else = false;
+
+    bh_arr_push(builder->branch_patches, patch);
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &branch_instr);
+}
+
+void ovm_code_builder_add_cond_branch(ovm_code_builder_t *builder, i32 label_idx, bool branch_if_true, bool targets_else) {
+    ovm_instr_t branch_instr = {0};
+    if (branch_if_true) {
+        branch_instr.full_instr = OVMI_BR_NZ;
+    } else {
+        branch_instr.full_instr = OVMI_BR_Z;
+    }
+
+    branch_instr.a = -1;
+    branch_instr.b = POP_VALUE(builder);
+
+    branch_patch_t patch;
+    patch.kind = branch_patch_instr_a;
+    patch.branch_instr = bh_arr_length(builder->program->code);
+    patch.label_idx = label_idx;
+    patch.targets_else = targets_else;
+
+    bh_arr_push(builder->branch_patches, patch);
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &branch_instr);
+}
+
+void ovm_code_builder_add_branch_table(ovm_code_builder_t *builder, i32 count, i32 *label_indicies, i32 default_label_idx) {
+    //
+    // Passing label indicies here is a little disingenuous, because that is not
+    // what the data will have to be. But since it is already the correct length
+    // I am using it as a substitute.
+    int table_idx = ovm_program_register_static_ints(builder->program, count, label_indicies);
+
+    ovm_instr_t instrs[5] = {0};
+    int tmp_register = NEXT_VALUE(builder);
+    int index_register = POP_VALUE(builder);
+    PUSH_VALUE(builder, tmp_register);
+
+    instrs[0].full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32);
+    instrs[0].r = tmp_register;
+    instrs[0].i = count;
+
+    instrs[1].full_instr = OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_I32);
+    instrs[1].r = tmp_register;
+    instrs[1].a = index_register;
+    instrs[1].b = tmp_register;
+
+    instrs[2].full_instr = OVMI_BR_Z;
+    instrs[2].a = -1;
+    instrs[2].b = tmp_register;
+
+    instrs[3].full_instr = OVMI_IDX_ARR;
+    instrs[3].r = tmp_register;
+    instrs[3].a = table_idx;
+    instrs[3].b = index_register;
+    
+    instrs[4].full_instr = OVMI_BRI;
+    instrs[4].a = tmp_register;
+    
+    POP_VALUE(builder);
+    
+    fori (i, 0, count) {
+        branch_patch_t patch;
+        patch.kind = branch_patch_static_idx;
+        patch.branch_instr = bh_arr_length(builder->program->code) + 4;
+        patch.label_idx = label_indicies[i];
+        patch.static_arr = table_idx;
+        patch.static_idx = i;
+        patch.targets_else = false;
+        bh_arr_push(builder->branch_patches, patch);
+    }
+
+    branch_patch_t default_patch;
+    default_patch.kind = branch_patch_instr_a;
+    default_patch.branch_instr = bh_arr_length(builder->program->code) + 2;
+    default_patch.label_idx = default_label_idx;
+    default_patch.targets_else = false;
+    bh_arr_push(builder->branch_patches, default_patch);
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    debug_info_builder_emit_location(builder->debug_builder);
+    debug_info_builder_emit_location(builder->debug_builder);
+    debug_info_builder_emit_location(builder->debug_builder);
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 5, instrs);
+}
+
+void ovm_code_builder_add_return(ovm_code_builder_t *builder) {
+    ovm_instr_t instr = {0};
+    instr.full_instr = OVMI_RETURN;
+
+    i32 values_on_stack = bh_arr_length(builder->execution_stack);
+    assert(values_on_stack == 0 || values_on_stack == 1);
+
+    if (values_on_stack == 1) {
+        instr.a = POP_VALUE(builder);
+    }
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &instr);
+}
+
+static void ovm_code_builder_add_params(ovm_code_builder_t *builder, i32 param_count) {
+    i32 *flipped_params = alloca(param_count * sizeof(i32));
+
+    fori (i, 0, param_count) {
+        flipped_params[i] = POP_VALUE(builder);
+    }
+
+    fori (i, 0, param_count) {
+        ovm_instr_t param_instr = {0};
+        param_instr.full_instr = OVMI_PARAM;
+        param_instr.a = flipped_params[param_count - 1 - i];
+
+        debug_info_builder_emit_location(builder->debug_builder);
+        ovm_program_add_instructions(builder->program, 1, &param_instr);
+    }
+}
+
+void ovm_code_builder_add_call(ovm_code_builder_t *builder, i32 func_idx, i32 param_count, bool has_return_value) {
+    ovm_code_builder_add_params(builder, param_count);
+
+    ovm_instr_t call_instr = {0};
+    call_instr.full_instr = OVMI_CALL;
+    call_instr.a = func_idx;
+    call_instr.r = -1;
+
+    if (has_return_value) {
+        call_instr.r = NEXT_VALUE(builder);
+    }
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &call_instr);
+
+    if (has_return_value) {
+        PUSH_VALUE(builder, call_instr.r);
+    }
+}
+
+void ovm_code_builder_add_indirect_call(ovm_code_builder_t *builder, i32 param_count, bool has_return_value) {
+    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_instrs[1].r = NEXT_VALUE(builder);
+    }
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 2, call_instrs);
+
+    if (has_return_value) {
+        PUSH_VALUE(builder, call_instrs[1].r);
+    }
+}
+
+void ovm_code_builder_drop_value(ovm_code_builder_t *builder) {
+    POP_VALUE(builder);
+}
+
+void ovm_code_builder_add_local_get(ovm_code_builder_t *builder, i32 local_idx) {
+    ovm_instr_t instr = {0};
+    instr.full_instr = OVMI_MOV;
+    instr.r = NEXT_VALUE(builder);
+    instr.a = local_idx; // This makes the assumption that the params will be in
+                         // the lower "address space" of the value numbers. This
+                         // will be true for web assembly, because that's how it
+                         // it was spec'd; but in the future for other things,
+                         // this will be incorrect.
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &instr);
+
+    PUSH_VALUE(builder, instr.r);
+}
+
+void ovm_code_builder_add_local_set(ovm_code_builder_t *builder, i32 local_idx) {
+    ovm_instr_t instr = {0};
+    instr.full_instr = OVMI_MOV;
+    instr.r = local_idx; // This makes the assumption that the params will be in
+                         // the lower "address space" of the value numbers. This
+                         // will be true for web assembly, because that's how it
+                         // it was spec'd; but in the future for other things,
+                         // this will be incorrect.
+    instr.a = POP_VALUE(builder);
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &instr);
+}
+
+void ovm_code_builder_add_local_tee(ovm_code_builder_t *builder, i32 local_idx) {
+    ovm_instr_t instr = {0};
+    instr.full_instr = OVMI_MOV;
+    instr.r = local_idx; // This makes the assumption that the params will be in
+                         // the lower "address space" of the value numbers. This
+                         // will be true for web assembly, because that's how it
+                         // it was spec'd; but in the future for other things,
+                         // this will be incorrect.
+    instr.a = POP_VALUE(builder);
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &instr);
+
+    PUSH_VALUE(builder, instr.a);
+}
+
+void ovm_code_builder_add_register_get(ovm_code_builder_t *builder, i32 reg_idx) {
+    ovm_instr_t instr = {0};
+    instr.full_instr = OVMI_REG_GET;
+    instr.r = NEXT_VALUE(builder);
+    instr.a = reg_idx;
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &instr);
+
+    PUSH_VALUE(builder, instr.r);
+}
+
+void ovm_code_builder_add_register_set(ovm_code_builder_t *builder, i32 reg_idx) {
+    ovm_instr_t instr = {0};
+    instr.full_instr = OVMI_REG_SET;
+    instr.r = reg_idx;
+    instr.a = POP_VALUE(builder);
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &instr);
+}
+
+void ovm_code_builder_add_load(ovm_code_builder_t *builder, u32 ovm_type, i32 offset) {
+    ovm_instr_t load_instr = {0};
+    load_instr.full_instr = OVM_TYPED_INSTR(OVMI_LOAD, ovm_type);
+    load_instr.b = offset;
+    load_instr.a = POP_VALUE(builder);
+    load_instr.r = NEXT_VALUE(builder);
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &load_instr);
+
+    PUSH_VALUE(builder, load_instr.r);
+}
+
+void ovm_code_builder_add_store(ovm_code_builder_t *builder, u32 ovm_type, i32 offset) {
+    ovm_instr_t store_instr = {0};
+    store_instr.full_instr = OVM_TYPED_INSTR(OVMI_STORE, ovm_type);
+    store_instr.b = offset;
+    store_instr.a = POP_VALUE(builder);
+    store_instr.r = POP_VALUE(builder);
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &store_instr);
+    return;
+}
+
+void ovm_code_builder_add_cmpxchg(ovm_code_builder_t *builder, u32 ovm_type, i32 offset) {
+    if (offset == 0) {
+        ovm_instr_t cmpxchg_instr = {0};
+        cmpxchg_instr.full_instr = OVM_TYPED_INSTR(OVMI_ATOMIC | OVMI_CMPXCHG, ovm_type);
+        cmpxchg_instr.b = POP_VALUE(builder);
+        cmpxchg_instr.a = POP_VALUE(builder);
+        cmpxchg_instr.r = POP_VALUE(builder);
+
+        debug_info_builder_emit_location(builder->debug_builder);
+        ovm_program_add_instructions(builder->program, 1, &cmpxchg_instr);
+
+        PUSH_VALUE(builder, cmpxchg_instr.r);
+        return;
+    }
+    
+    // TODO: explain the ordering here.
+
+    ovm_instr_t instrs[3] = {0};
+    // imm.i32 %n, offset
+    instrs[0].full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32);
+    instrs[0].i = offset;
+    instrs[0].r = NEXT_VALUE(builder);
+
+    int value_reg = POP_VALUE(builder);
+    int expected_reg = POP_VALUE(builder);
+    int addr_reg = POP_VALUE(builder);
+
+    // add.i32 %n, %n, %i
+    instrs[1].full_instr = OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I32);
+    instrs[1].r = instrs[0].r;
+    instrs[1].a = addr_reg;
+    instrs[1].b = instrs[0].r;
+
+    // cmpxchg.x %m, %n
+    instrs[2].full_instr = OVM_TYPED_INSTR(OVMI_ATOMIC | OVMI_CMPXCHG, ovm_type);
+    instrs[2].r = instrs[1].r;
+    instrs[2].a = expected_reg;
+    instrs[2].b = value_reg;
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    debug_info_builder_emit_location(builder->debug_builder);
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 3, instrs);
+
+    PUSH_VALUE(builder, instrs[2].r);
+}
+
+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);
+
+    debug_info_builder_emit_location(builder->debug_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);
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &instr);
+}
+
+//
+// CopyNPaste from _add_load
+void ovm_code_builder_add_atomic_load(ovm_code_builder_t *builder, u32 ovm_type, i32 offset) {
+    ovm_instr_t load_instr = {0};
+    load_instr.full_instr = OVM_TYPED_INSTR(OVMI_ATOMIC | OVMI_LOAD, ovm_type);
+    load_instr.b = offset;
+    load_instr.a = POP_VALUE(builder);
+    load_instr.r = NEXT_VALUE(builder);
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &load_instr);
+
+    PUSH_VALUE(builder, load_instr.r);
+}
+
+//
+// CopyNPaste from _add_store
+void ovm_code_builder_add_atomic_store(ovm_code_builder_t *builder, u32 ovm_type, i32 offset) {
+    ovm_instr_t store_instr = {0};
+    store_instr.full_instr = OVM_TYPED_INSTR(OVMI_ATOMIC | OVMI_STORE, ovm_type);
+    store_instr.b = offset;
+    store_instr.a = POP_VALUE(builder);
+    store_instr.r = POP_VALUE(builder);
+
+    debug_info_builder_emit_location(builder->debug_builder);
+    ovm_program_add_instructions(builder->program, 1, &store_instr);
+}
diff --git a/interpreter/src/vm/program_loader.c b/interpreter/src/vm/program_loader.c
new file mode 100644 (file)
index 0000000..f3162d3
--- /dev/null
@@ -0,0 +1,126 @@
+#include "vm.h"
+
+//
+// I'm very lazy and silly, so this code make the drastic assumption that the
+// endianness of the machine that the file was built on and the machine the
+// VM is running on are the SAME. If this is not the case, the integers very
+// well could be f-ed up. Might be worth switching all integers to use ntohl,
+// so at least that part is consistent...               -brendanfh 06/13/2022
+//
+
+//
+// I wish this didn't take the engine as a parameter... but currently the memory
+// is stored on the engine. So unless the data section elements can be aggregated
+// into an array to be applied later, this is best I got...
+//
+// ALSO, I wish this didn't have to take a state... but because native_funcs are stored
+// on the state, this is also the best I got...
+bool ovm_program_load_from_file(ovm_program_t *program, ovm_engine_t *engine, char *filename) {
+    bh_file file;
+    bh_file_error error = bh_file_open(&file, filename);
+    if (error != BH_FILE_ERROR_NONE) {
+        fprintf(stderr, "Failed to open '%s' for reading.\n", filename);
+        return false;
+    }
+
+    char magic[4], version[4];
+    bh_file_read(&file, magic, 4);
+    bh_file_read(&file, version, 4);
+
+    if (strncmp(magic, "OVMI", 4) || strncmp(version, "\x00\x00\x00\x01", 4)) {
+        fprintf(stderr, "Mismatched version/magic number in '%s'.\n", filename);
+        return false;
+    }
+
+    //
+    // Code section
+    // Just copy in the bytes directly.
+    // What's validation anyway?
+    //
+    i32 entry_count;
+    bh_file_read(&file, &entry_count, sizeof(i32));
+    bh_arr_insert_end(program->code, entry_count);
+    bh_file_read(&file, program->code, entry_count * sizeof(ovm_instr_t));
+
+    //
+    // Data section
+    //
+    bh_file_read(&file, &entry_count, sizeof(i32));
+    fori (i, 0, entry_count) {
+        i32 offset, size;
+        bh_file_read(&file, &offset, sizeof(i32));
+        bh_file_read(&file, &size, sizeof(i32));
+
+        assert(engine);
+        assert(engine->memory);
+        bh_file_read(&file, ((u8 *) engine->memory) + offset, size);
+    }
+
+    //
+    // Native link section
+    //
+    i32 next_external_func_idx = 0;
+    bh_file_read(&file, &entry_count, sizeof(i32));
+    fori (i, 0, entry_count) {
+        i32 param_count, name_len;
+        bh_file_read(&file, &param_count, sizeof(i32));
+        bh_file_read(&file, &name_len, sizeof(i32));
+
+        char *name_buf = bh_alloc_array(program->store->arena_allocator, char, name_len);
+        bh_file_read(&file, name_buf, name_len);
+
+        ovm_program_register_external_func(program, name_buf, param_count, next_external_func_idx++);
+    }
+
+    //
+    // Func section
+    //
+    bh_file_read(&file, &entry_count, sizeof(i32));
+    fori (i, 0, entry_count) {
+        i32 start_instr, param_count, value_number_count;
+        bh_file_read(&file, &start_instr, sizeof(i32));
+        bh_file_read(&file, &param_count, sizeof(i32));
+        bh_file_read(&file, &value_number_count, sizeof(i32));
+
+        ovm_program_register_func(program, "LOADED", start_instr, param_count, value_number_count);
+    }
+
+    //
+    // Register section
+    //
+    bh_file_read(&file, &entry_count, sizeof(i32));
+    fori (i, 0, entry_count) {
+        i32 param_count, name_len;
+        bh_file_read(&file, &param_count, sizeof(i32));
+        bh_file_read(&file, &name_len, sizeof(i32));
+
+        char *name_buf = bh_alloc_array(program->store->arena_allocator, char, name_len);
+        bh_file_read(&file, name_buf, name_len);
+
+        // For now, these are just ignored...
+    }
+    program->register_count = entry_count;
+
+    return true;
+}
+
+void ovm_state_link_external_funcs(ovm_program_t *program, ovm_state_t *state, ovm_linkable_func_t *funcs) {
+    bh_arr_each(ovm_func_t, f, program->funcs) {
+        if (f->kind == OVM_FUNC_INTERNAL) continue;
+        
+        ovm_linkable_func_t *func = funcs;
+        while (func->name) {
+            if (!strcmp(f->name, func->name) && f->param_count == func->param_count) {
+                ovm_state_register_external_func(state, f->external_func_idx, func->func.native_func, func->func.userdata);
+                break;
+            }
+
+            func++;
+        }
+
+        if (!state->external_funcs[f->external_func_idx].native_func) {
+            fprintf(stderr, "Failed to link to native function '%s'.\n", f->name);
+            exit(1);
+        }
+    }
+}
diff --git a/interpreter/src/vm/vm.c b/interpreter/src/vm/vm.c
new file mode 100644 (file)
index 0000000..28a4262
--- /dev/null
@@ -0,0 +1,1276 @@
+#include "vm.h"
+
+#include <sys/mman.h>
+#include <x86intrin.h>
+#include <math.h> // REMOVE THIS!!!  only needed for sqrt
+#include <pthread.h>
+
+#ifdef OVM_DEBUG
+#define ovm_assert(c) assert((c))
+#else
+#define ovm_assert(c)
+#endif
+
+
+static inline void ovm_print_val(ovm_value_t val) {
+    switch (val.type) {
+        case OVM_TYPE_I32: printf("i32[%d]", val.i32); break;
+        case OVM_TYPE_I64: printf("i64[%ld]", val.i64); break;
+        case OVM_TYPE_F32: printf("f32[%f]", val.f32); break;
+        case OVM_TYPE_F64: printf("f64[%lf]", val.f64); break;
+    }
+}
+
+
+//
+// Store
+ovm_store_t *ovm_store_new() {
+    ovm_store_t *store = malloc(sizeof(*store));
+
+    store->heap_allocator = bh_heap_allocator();
+
+    bh_atomic_arena_init(&store->arena, store->heap_allocator, 1 << 20);
+    store->arena_allocator = bh_atomic_arena_allocator(&store->arena);
+
+    return store;
+}
+
+void ovm_store_delete(ovm_store_t *store) {
+    bh_atomic_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;
+    program->register_count = 0;
+
+    program->funcs = NULL;
+    program->code  = NULL;
+    program->static_integers = NULL;
+    program->static_data  = NULL;
+    bh_arr_new(store->heap_allocator, program->funcs, 16);
+    bh_arr_new(store->heap_allocator, program->code, 1024);
+    bh_arr_new(store->heap_allocator, program->static_integers, 128);
+    bh_arr_new(store->heap_allocator, program->static_data, 128);
+
+    return program;
+}
+
+void ovm_program_delete(ovm_program_t *program) {
+    bh_arr_free(program->funcs);
+    bh_arr_free(program->code);
+    bh_arr_free(program->static_integers);
+    bh_arr_free(program->static_data);
+
+    bh_free(program->store->heap_allocator, program);
+}
+
+int ovm_program_register_static_ints(ovm_program_t *program, int len, int *data) {
+    ovm_static_integer_array_t new_entry;
+    new_entry.start_idx = bh_arr_length(program->static_integers);
+    new_entry.len = len;
+
+    fori (i, 0, (int) len) {
+        bh_arr_push(program->static_integers, data[i]);
+    }
+
+    bh_arr_push(program->static_data, new_entry);
+    return bh_arr_length(program->static_data) - 1;
+}
+
+void ovm_program_modify_static_int(ovm_program_t *program, int arr, int idx, int new_value) {
+    if (arr >= bh_arr_length(program->static_data)) return;
+
+    ovm_static_integer_array_t array = program->static_data[arr];
+    if (idx >= array.len) return;
+
+    program->static_integers[array.start_idx + idx] = new_value;
+}
+
+int ovm_program_register_func(ovm_program_t *program, char *name, i32 instr, i32 param_count, i32 value_number_count) {
+    ovm_func_t func;
+    func.kind = OVM_FUNC_INTERNAL;
+    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);
+    return func.id;
+}
+
+int ovm_program_register_external_func(ovm_program_t *program, char *name, i32 param_count, i32 external_func_idx) {
+    ovm_func_t func;
+    func.kind = OVM_FUNC_EXTERNAL;
+    func.id = bh_arr_length(program->funcs);
+    func.name = name;
+    func.param_count = param_count;
+    func.external_func_idx = external_func_idx;
+    func.value_number_count = param_count;
+
+    bh_arr_push(program->funcs, func);
+    return func.id;
+}
+
+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]);
+    }
+}
+
+
+static char *ovm_instr_name(i32 full_instr) {
+#define C(...) \
+    case __VA_ARGS__: return #__VA_ARGS__; \
+    case __VA_ARGS__ | OVMI_ATOMIC: return "ATOMIC_" #__VA_ARGS__;
+
+    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))
+        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_DIV_S, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_DIV_S, 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_REM_S, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_REM_S, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_REM_S, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_REM_S, 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_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_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(OVMI_REG_GET)
+        C(OVMI_REG_SET)
+
+        C(OVMI_IDX_ARR)
+
+        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_RETURN)
+        C(OVMI_CALL)
+        C(OVMI_CALLI)
+
+        C(OVMI_BR)
+        C(OVMI_BR_Z)
+        C(OVMI_BR_NZ)
+        C(OVMI_BRI)
+        C(OVMI_BRI_Z)
+        C(OVMI_BRI_NZ)
+
+        C(OVM_TYPED_INSTR(OVMI_CLZ, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_CLZ, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_CLZ, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_CLZ, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_CTZ, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_CTZ, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_CTZ, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_CTZ, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_POPCNT, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_POPCNT, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_POPCNT, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_POPCNT, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_ROTL, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_ROTL, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_ROTL, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_ROTL, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_ROTR, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_ROTR, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_ROTR, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_ROTR, OVM_TYPE_I64))
+
+        C(OVM_TYPED_INSTR(OVMI_ABS, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_NEG, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_CEIL, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_FLOOR, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_TRUNC, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_NEAREST, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_SQRT, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_MIN, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_MAX, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_COPYSIGN, OVM_TYPE_F32))
+
+        C(OVM_TYPED_INSTR(OVMI_ABS, OVM_TYPE_F64))
+        C(OVM_TYPED_INSTR(OVMI_NEG, OVM_TYPE_F64))
+        C(OVM_TYPED_INSTR(OVMI_CEIL, OVM_TYPE_F64))
+        C(OVM_TYPED_INSTR(OVMI_FLOOR, OVM_TYPE_F64))
+        C(OVM_TYPED_INSTR(OVMI_TRUNC, OVM_TYPE_F64))
+        C(OVM_TYPED_INSTR(OVMI_NEAREST, OVM_TYPE_F64))
+        C(OVM_TYPED_INSTR(OVMI_SQRT, OVM_TYPE_F64))
+        C(OVM_TYPED_INSTR(OVMI_MIN, OVM_TYPE_F64))
+        C(OVM_TYPED_INSTR(OVMI_MAX, OVM_TYPE_F64))
+        C(OVM_TYPED_INSTR(OVMI_COPYSIGN, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_CVT_I8, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I8, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I8, OVM_TYPE_I64))
+
+        C(OVM_TYPED_INSTR(OVMI_CVT_I16, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I16, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I16, OVM_TYPE_I64))
+
+        C(OVM_TYPED_INSTR(OVMI_CVT_I32, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I32, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I32, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I32, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I32, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_F32))
+        C(OVM_TYPED_INSTR(OVMI_CVT_I64, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_CVT_F32, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_CVT_F32, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_CVT_F32, OVM_TYPE_F64))
+
+        C(OVM_TYPED_INSTR(OVMI_CVT_F64, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_CVT_F64, OVM_TYPE_I64))
+        C(OVM_TYPED_INSTR(OVMI_CVT_F64, OVM_TYPE_F32))
+
+        C(OVM_TYPED_INSTR(OVMI_CMPXCHG, OVM_TYPE_I8))
+        C(OVM_TYPED_INSTR(OVMI_CMPXCHG, OVM_TYPE_I16))
+        C(OVM_TYPED_INSTR(OVMI_CMPXCHG, OVM_TYPE_I32))
+        C(OVM_TYPED_INSTR(OVMI_CMPXCHG, OVM_TYPE_I64))
+
+        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, 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 && func->kind == OVM_FUNC_INTERNAL) {
+                printf("\n[%d] %s  values=%d:\n", func->id, func->name, func->value_number_count);
+            }
+        }
+
+        ovm_instr_t instr = program->code[i];
+        printf("%6lx  %50s | r=%02d a=%02d b=%02d  i=%d f=%f l=%ld d=%lf\n", i, ovm_instr_name(instr.full_instr), instr.r, instr.a, instr.b, instr.i, instr.f, instr.l, instr.d);
+    }
+}
+
+void ovm_raw_print_instructions(i32 instr_count, ovm_instr_t *instrs) {
+    fori (i, 0, instr_count) {
+        ovm_instr_t instr = instrs[i];
+        printf("%6lx  %50s | r=%02d a=%02d b=%02d  i=%d f=%f l=%ld d=%lf\n", i, 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 = 1ull << 32;
+    engine->memory = mmap(NULL, engine->memory_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+    pthread_mutex_init(&engine->atomic_mutex, NULL);
+
+    engine->debug = NULL;
+
+    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);
+}
+
+void ovm_engine_memory_copy(ovm_engine_t *engine, i64 target, void *data, i64 size) {
+    ovm_assert(engine);
+    ovm_assert(engine->memory);
+    ovm_assert(data);
+    ovm_assert(size + target < engine->memory_size);
+    memcpy(((u8 *) engine->memory) + target, data, size);
+}
+
+
+
+//
+// State
+//
+// This takes in a program because it needs to know how many registers to allocate.
+// Should there be another mechanism for this? or is this the most concise way?
+ovm_state_t *ovm_state_new(ovm_engine_t *engine, ovm_program_t *program) {
+    ovm_store_t *store = engine->store;
+    ovm_state_t *state = bh_alloc_item(store->arena_allocator, ovm_state_t);
+
+    state->store = store;
+    state->pc = 0;
+    state->value_number_offset = 0;
+
+    state->numbered_values = NULL;
+    state->params = NULL;
+    state->stack_frames = NULL;
+    state->registers = NULL;
+    bh_arr_new(store->heap_allocator, state->numbered_values, 128);
+    bh_arr_new(store->heap_allocator, state->params, 16);
+    bh_arr_new(store->heap_allocator, state->stack_frames, 32);
+    bh_arr_new(store->heap_allocator, state->registers, program->register_count);
+    bh_arr_insert_end(state->registers, program->register_count);
+
+    state->external_funcs = NULL;
+    bh_arr_new(store->heap_allocator, state->external_funcs, 8);
+
+    if (engine->debug) {
+        u32 thread_id = debug_host_register_thread(engine->debug, state);
+        state->debug = debug_host_lookup_thread(engine->debug, thread_id);
+    }
+
+#ifdef OVM_VERBOSE
+    ovm_program_print_instructions(program, 0, bh_arr_length(program->code));
+#endif
+
+    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_arr_free(state->registers);
+    bh_arr_free(state->external_funcs);
+}
+
+void ovm_state_register_external_func(ovm_state_t *state, i32 idx, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data) {
+    ovm_external_func_t external_func;
+    external_func.native_func = func;
+    external_func.userdata = data;
+
+    bh_arr_set_at(state->external_funcs, idx, external_func);
+}
+
+ovm_value_t ovm_state_register_get(ovm_state_t *state, i32 idx) {
+    ovm_assert(idx < bh_arr_length(state->registers));
+
+    return state->registers[idx];
+}
+
+void ovm_state_register_set(ovm_state_t *state, i32 idx, ovm_value_t val) {
+    if (idx >= bh_arr_length(state->registers)) return;
+
+    state->registers[idx] = val;
+}
+
+//
+// Function calling
+
+static inline 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  = bh_arr_length(state->numbered_values);
+    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);
+
+    //
+    // Modify debug state so step over works
+    if (state->debug) {
+        state->debug->extra_frames_since_last_pause++;
+    }
+}
+
+static inline ovm_stack_frame_t ovm__func_teardown_stack_frame(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program) {
+    ovm_stack_frame_t frame = bh_arr_pop(state->stack_frames);
+    bh_arr_fastdeleten(state->numbered_values, frame.value_number_count);
+
+    if (bh_arr_length(state->stack_frames) == 0) {
+        state->value_number_offset = 0;
+    } else {
+        state->value_number_offset = bh_arr_last(state->stack_frames).value_number_base;
+    }
+
+    if (state->debug) {
+        state->debug->extra_frames_since_last_pause--;
+        if (state->debug->extra_frames_since_last_pause < 0) {
+            state->debug->pause_within = -1;
+        }
+    }
+
+    return frame;
+}
+
+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) {
+    ovm_func_t func = program->funcs[func_idx];
+    ovm_assert(func.value_number_count >= func.param_count);
+
+    switch (func.kind) {
+        case OVM_FUNC_INTERNAL: {
+            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 = func.start_instr;
+            ovm_value_t result = ovm_run_code(engine, state, program);
+
+            return result;
+        }
+
+        case OVM_FUNC_EXTERNAL: {
+            ovm__func_setup_stack_frame(engine, state, program, func_idx, 0);
+
+            ovm_value_t result = {0};
+            ovm_external_func_t external_func = state->external_funcs[func.external_func_idx];
+            external_func.native_func(external_func.userdata, params, &result);
+
+            ovm__func_teardown_stack_frame(engine, state, program);
+            return result;
+        }
+
+        default: return (ovm_value_t) {};
+    }
+}
+
+static inline double __ovm_abs(double f) {
+    return f >= 0 ? f : -f;
+}
+
+static inline double __ovm_floor(double f) {
+    if (f < 0) {
+        return (double) (((long long) f) - 1);
+    } else {
+        return (double) (long long) f;
+    }
+}
+
+static inline double __ovm_ceil(double f) {
+    if (f - (long long) f == 0) {
+        return (double) (long long) f;
+    } else {
+        return __ovm_floor(f) + 1;
+    }
+}
+
+static inline double __ovm_trunc(double f) {
+    return (double) (long long) f;
+}
+
+static inline double __ovm_nearest(double f) {
+    if (f > 0 && f <= 0.5) {
+        return +0;
+    }
+
+    if (f >= -0.5 && f < 0) {
+        return -0;
+    }
+
+    if (f - __ovm_floor(f) < 0.5) return __ovm_floor(f);
+    else                          return __ovm_ceil(f);
+}
+
+static inline double __ovm_copysign(a, b) double a, b; {
+    if ((a > 0 && b > 0) || (a < 0 && b < 0)) return a;
+    return -a;
+}
+
+
+ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program) {
+    ovm_assert(engine);
+    ovm_assert(state);
+    ovm_assert(program);
+
+#define VAL(loc) state->numbered_values[(u32) (loc + state->value_number_offset)]
+
+    ovm_instr_t *code = program->code;
+    bool release_mutex_at_end = false;
+
+    ovm_value_t tmp_val;
+
+    while (state->pc < bh_arr_length(program->code)) {
+        // This will become the line change detection
+        // debug_loc_info_t loc_info;
+        // debug_info_lookup_location(engine->debug->info, state->pc, &loc_info);
+        // if (loc_info.file_id != last_file || loc_info.line != last_line) {
+        //     last_file = loc_info.file_id;
+        //     last_line = loc_info.line;
+
+        //     debug_file_info_t file_info;
+        //     debug_info_lookup_file(engine->debug->info, last_file, &file_info);
+        //     
+        //     printf("(%d, %d) %s:%d\n", last_file, last_line, file_info.name, last_line);
+        // }
+        
+#ifdef OVM_VERBOSE
+        ovm_program_print_instructions(program, state->pc, 1);
+#endif
+
+        tmp_val.type = OVM_TYPE_NONE;
+        tmp_val.u64  = 0;
+
+        // Check if breakpoints are hit
+        if (state->debug) {
+            if (state->debug->run_count == 0) {
+                state->debug->state = debug_state_pausing;
+                state->debug->pause_reason = debug_pause_entry; // This is not always due to entry...
+                goto should_wait;
+            }
+
+            if (state->debug->pause_at_next_line) {
+                if (state->debug->pause_within == -1 || state->debug->pause_within == bh_arr_last(state->stack_frames).func->id) {
+
+                    debug_loc_info_t l1, l2;
+                    debug_info_lookup_location(engine->debug->info, state->pc - 1, &l1);
+                    debug_info_lookup_location(engine->debug->info, state->pc,     &l2);
+
+                    if (l1.file_id != l2.file_id || l1.line != l2.line) {
+                        state->debug->pause_at_next_line = false;
+                        state->debug->pause_reason = debug_pause_step;
+                        state->debug->state = debug_state_pausing;
+                        goto should_wait;
+                    }
+                }
+            }
+
+            bh_arr_each(debug_breakpoint_t, bp, state->debug->breakpoints) {
+                if (bp->instr == (u32) state->pc) {
+                    state->debug->state = debug_state_hit_breakpoint;
+                    state->debug->last_breakpoint_hit = bp->id;
+                    goto should_wait;
+                }
+            }
+
+            goto shouldnt_wait;
+
+          should_wait:
+            assert(write(state->debug->state_change_write_fd, "1", 1));
+            sem_wait(&state->debug->wait_semaphore);
+            state->debug->state = debug_state_running;
+
+          shouldnt_wait:
+            if (state->debug->run_count > 0) state->debug->run_count--;
+        }
+
+        //
+        // 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++];
+
+        if (instr.full_instr & OVMI_ATOMIC) {
+            pthread_mutex_lock(&engine->atomic_mutex);
+            release_mutex_at_end = true;
+
+            instr.full_instr &= ~OVMI_ATOMIC;
+        }
+
+        switch (instr.full_instr) {
+            case OVMI_NOP: break;
+
+#define OVM_OP(i, t, op, ctype) \
+            case OVM_TYPED_INSTR(i, t): \
+                ovm_assert(VAL(instr.a).type == t && VAL(instr.b).type == t); \
+                tmp_val.type = t; \
+                tmp_val.ctype = VAL(instr.a).ctype op VAL(instr.b).ctype; \
+                VAL(instr.r) = tmp_val; \
+                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 , /, u8)
+            OVM_OP(OVMI_DIV, OVM_TYPE_I16, /, u16)
+            OVM_OP(OVMI_DIV, OVM_TYPE_I32, /, u32)
+            OVM_OP(OVMI_DIV, OVM_TYPE_I64, /, u64)
+            OVM_OP(OVMI_DIV, OVM_TYPE_F32, /, f32)
+            OVM_OP(OVMI_DIV, OVM_TYPE_F64, /, f64)
+
+            OVM_OP(OVMI_DIV_S, OVM_TYPE_I8 , /, i8)
+            OVM_OP(OVMI_DIV_S, OVM_TYPE_I16, /, i16)
+            OVM_OP(OVMI_DIV_S, OVM_TYPE_I32, /, i32)
+            OVM_OP(OVMI_DIV_S, OVM_TYPE_I64, /, i64)
+            OVM_OP(OVMI_DIV_S, OVM_TYPE_F32, /, f32)
+            OVM_OP(OVMI_DIV_S, OVM_TYPE_F64, /, f64)
+
+            OVM_OP(OVMI_REM, OVM_TYPE_I8 , %, u8)
+            OVM_OP(OVMI_REM, OVM_TYPE_I16, %, u16)
+            OVM_OP(OVMI_REM, OVM_TYPE_I32, %, u32)
+            OVM_OP(OVMI_REM, OVM_TYPE_I64, %, u64)
+
+            OVM_OP(OVMI_REM_S, OVM_TYPE_I8 , %, i8)
+            OVM_OP(OVMI_REM_S, OVM_TYPE_I16, %, i16)
+            OVM_OP(OVMI_REM_S, OVM_TYPE_I32, %, i32)
+            OVM_OP(OVMI_REM_S, OVM_TYPE_I64, %, i64)
+
+            OVM_OP(OVMI_AND, OVM_TYPE_I8 , &, u8)
+            OVM_OP(OVMI_AND, OVM_TYPE_I16, &, u16)
+            OVM_OP(OVMI_AND, OVM_TYPE_I32, &, u32)
+            OVM_OP(OVMI_AND, OVM_TYPE_I64, &, u64)
+
+            OVM_OP(OVMI_OR, OVM_TYPE_I8 , |, u8)
+            OVM_OP(OVMI_OR, OVM_TYPE_I16, |, u16)
+            OVM_OP(OVMI_OR, OVM_TYPE_I32, |, u32)
+            OVM_OP(OVMI_OR, OVM_TYPE_I64, |, u64)
+
+            OVM_OP(OVMI_XOR, OVM_TYPE_I8 , ^, u8)
+            OVM_OP(OVMI_XOR, OVM_TYPE_I16, ^, u16)
+            OVM_OP(OVMI_XOR, OVM_TYPE_I32, ^, u32)
+            OVM_OP(OVMI_XOR, OVM_TYPE_I64, ^, u64)
+
+            OVM_OP(OVMI_SHL, OVM_TYPE_I8 , <<, u8)
+            OVM_OP(OVMI_SHL, OVM_TYPE_I16, <<, u16)
+            OVM_OP(OVMI_SHL, OVM_TYPE_I32, <<, u32)
+            OVM_OP(OVMI_SHL, OVM_TYPE_I64, <<, u64)
+
+            OVM_OP(OVMI_SHR, OVM_TYPE_I8 , >>, u8)
+            OVM_OP(OVMI_SHR, OVM_TYPE_I16, >>, u16)
+            OVM_OP(OVMI_SHR, OVM_TYPE_I32, >>, u32)
+            OVM_OP(OVMI_SHR, OVM_TYPE_I64, >>, u64)
+
+            OVM_OP(OVMI_SAR, OVM_TYPE_I8 , >>, i8)
+            OVM_OP(OVMI_SAR, OVM_TYPE_I16, >>, i16)
+            OVM_OP(OVMI_SAR, OVM_TYPE_I32, >>, i32)
+            OVM_OP(OVMI_SAR, OVM_TYPE_I64, >>, i64)
+
+#undef OVM_OP
+
+#define OVM_OP(i, t, func, ctype) \
+            case OVM_TYPED_INSTR(i, t): \
+                ovm_assert(VAL(instr.a).type == t && VAL(instr.b).type == t); \
+                tmp_val.type = t; \
+                tmp_val.ctype = func( VAL(instr.a).ctype, VAL(instr.b).ctype ); \
+                VAL(instr.r) = tmp_val; \
+                break;
+
+            OVM_OP(OVMI_ROTL, OVM_TYPE_I8 , __rolb, u8)
+            OVM_OP(OVMI_ROTL, OVM_TYPE_I16, __rolw, u16)
+            OVM_OP(OVMI_ROTL, OVM_TYPE_I32, __rold, u32)
+            OVM_OP(OVMI_ROTL, OVM_TYPE_I64, __rolq, u64)
+
+            OVM_OP(OVMI_ROTR, OVM_TYPE_I8 , __rorb, u8)
+            OVM_OP(OVMI_ROTR, OVM_TYPE_I16, __rorw, u16)
+            OVM_OP(OVMI_ROTR, OVM_TYPE_I32, __rord, u32)
+            OVM_OP(OVMI_ROTR, OVM_TYPE_I64, __rorq, u64)
+
+            OVM_OP(OVMI_MIN, OVM_TYPE_F32, bh_min, f32)
+            OVM_OP(OVMI_MAX, OVM_TYPE_F32, bh_max, f32)
+
+            OVM_OP(OVMI_MIN, OVM_TYPE_F64, bh_min, f64)
+            OVM_OP(OVMI_MAX, OVM_TYPE_F64, bh_max, f64)
+
+#undef OVM_OP
+
+#define OVM_OP(i, t, op, ctype) \
+            case OVM_TYPED_INSTR(i, t): \
+                ovm_assert(VAL(instr.a).type == t); \
+                tmp_val.type = t; \
+                tmp_val.ctype = (ctype) op (VAL(instr.a).ctype); \
+                VAL(instr.r) = tmp_val; \
+                break;
+
+            OVM_OP(OVMI_CLZ, OVM_TYPE_I8 , __builtin_clz, u8)
+            OVM_OP(OVMI_CLZ, OVM_TYPE_I16, __builtin_clz, u16)
+            OVM_OP(OVMI_CLZ, OVM_TYPE_I32, __builtin_clz, u32)
+            OVM_OP(OVMI_CLZ, OVM_TYPE_I64, __builtin_clz, u64)
+
+            OVM_OP(OVMI_CTZ, OVM_TYPE_I8 , __builtin_ctz, u8)
+            OVM_OP(OVMI_CTZ, OVM_TYPE_I16, __builtin_ctz, u16)
+            OVM_OP(OVMI_CTZ, OVM_TYPE_I32, __builtin_ctz, u32)
+            OVM_OP(OVMI_CTZ, OVM_TYPE_I64, __builtin_ctz, u64)
+
+            OVM_OP(OVMI_POPCNT, OVM_TYPE_I8 , __builtin_popcount, u8)
+            OVM_OP(OVMI_POPCNT, OVM_TYPE_I16, __builtin_popcount, u16)
+            OVM_OP(OVMI_POPCNT, OVM_TYPE_I32, __builtin_popcount, u32)
+            OVM_OP(OVMI_POPCNT, OVM_TYPE_I64, __builtin_popcount, u64)
+
+            OVM_OP(OVMI_ABS,     OVM_TYPE_F32, __ovm_abs, f32);
+            OVM_OP(OVMI_NEG,     OVM_TYPE_F32, -, f32);
+            OVM_OP(OVMI_CEIL,    OVM_TYPE_F32, __ovm_ceil, f32);
+            OVM_OP(OVMI_FLOOR,   OVM_TYPE_F32, __ovm_floor, f32);
+            OVM_OP(OVMI_TRUNC,   OVM_TYPE_F32, __ovm_trunc, f32);
+            OVM_OP(OVMI_NEAREST, OVM_TYPE_F32, __ovm_nearest, f32);
+            OVM_OP(OVMI_SQRT,    OVM_TYPE_F32, sqrt, f32);  // TODO: REMOVE THE NEED FOR libm!!!
+
+            OVM_OP(OVMI_ABS,     OVM_TYPE_F64, __ovm_abs, f64);
+            OVM_OP(OVMI_NEG,     OVM_TYPE_F64, -, f64);
+            OVM_OP(OVMI_CEIL,    OVM_TYPE_F64, __ovm_ceil, f64);
+            OVM_OP(OVMI_FLOOR,   OVM_TYPE_F64, __ovm_floor, f64);
+            OVM_OP(OVMI_TRUNC,   OVM_TYPE_F64, __ovm_trunc, f64);
+            OVM_OP(OVMI_NEAREST, OVM_TYPE_F64, __ovm_nearest, f64);
+            OVM_OP(OVMI_SQRT,    OVM_TYPE_F64, sqrt, f64);  // TODO: REMOVE THE NEED FOR libm!!!
+
+#undef OVM_OP
+
+#define OVM_OP(i, t, op, ctype, cast_type) \
+            case OVM_TYPED_INSTR(i, t): \
+                ovm_assert(VAL(instr.a).type == t && VAL(instr.b).type == t); \
+                tmp_val.type = OVM_TYPE_I32; \
+                tmp_val.i32 = ((VAL(instr.a).ctype op VAL(instr.b).ctype)) ? 1 : 0; \
+                VAL(instr.r) = tmp_val; \
+                break;
+
+            OVM_OP(OVMI_LT, OVM_TYPE_I8 , <, u8,  u8)
+            OVM_OP(OVMI_LT, OVM_TYPE_I16, <, u16, u16)
+            OVM_OP(OVMI_LT, OVM_TYPE_I32, <, u32, u32)
+            OVM_OP(OVMI_LT, OVM_TYPE_I64, <, u64, 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 , <=, u8,  u8)
+            OVM_OP(OVMI_LE, OVM_TYPE_I16, <=, u16, u16)
+            OVM_OP(OVMI_LE, OVM_TYPE_I32, <=, u32, u32)
+            OVM_OP(OVMI_LE, OVM_TYPE_I64, <=, u64, 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 , >=, u8,  u8)
+            OVM_OP(OVMI_GE, OVM_TYPE_I16, >=, u16, u16)
+            OVM_OP(OVMI_GE, OVM_TYPE_I32, >=, u32, u32)
+            OVM_OP(OVMI_GE, OVM_TYPE_I64, >=, u64, 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 , >, u8,  u8)
+            OVM_OP(OVMI_GT, OVM_TYPE_I16, >, u16, u16)
+            OVM_OP(OVMI_GT, OVM_TYPE_I32, >, u32, u32)
+            OVM_OP(OVMI_GT, OVM_TYPE_I64, >, u64, 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(t, dtype, stype) \
+            case OVM_TYPED_INSTR(OVMI_IMM, t): \
+                VAL(instr.r).type = t; \
+                VAL(instr.r).u64 = 0; \
+                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_MOV:
+                VAL(instr.r) = VAL(instr.a);
+
+#ifdef OVM_VERBOSE
+                printf("$%d = %lx\n", instr.r, VAL(instr.r).u64);
+#endif
+
+                break;
+
+#define OVM_LOAD(type_, stype) \
+            case OVM_TYPED_INSTR(OVMI_LOAD, type_): {\
+                ovm_assert(VAL(instr.a).type == OVM_TYPE_I32); \
+                tmp_val.type = type_; \
+                tmp_val.stype = * (stype *) &((u8 *) engine->memory)[VAL(instr.a).u32 + (u32) instr.b]; \
+                VAL(instr.r) = tmp_val; \
+                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_): \
+                ovm_assert(VAL(instr.r).type == OVM_TYPE_I32); \
+                *(stype *) &((u8 *) engine->memory)[VAL(instr.r).u32 + (u32) instr.b] = VAL(instr.a).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: {
+                u32 dest  = VAL(instr.r).u32;
+                u32 src   = VAL(instr.a).u32;
+                u32 count = VAL(instr.b).u32;
+
+                u8 *base = engine->memory;
+                memmove(&base[dest], &base[src], count);
+                break;
+            }
+
+            case OVMI_FILL: {
+                i32 dest  = VAL(instr.r).i32;
+                u8  byte  = VAL(instr.a).u8;
+                i32 count = VAL(instr.b).i32;
+
+                u8 *base = engine->memory;
+                memset(&base[dest], byte, count);
+                break;
+            }
+
+            case OVMI_REG_GET: {
+                VAL(instr.r) = state->registers[instr.a];
+                break;
+            }
+
+            case OVMI_REG_SET: {
+                state->registers[instr.r] = VAL(instr.a);
+                break;
+            }
+
+            case OVMI_IDX_ARR: {
+                ovm_static_integer_array_t data_elem = program->static_data[instr.a];
+                ovm_assert(VAL(instr.b).u32 < (u32) data_elem.len);
+
+                tmp_val.type = OVM_TYPE_I32;
+                tmp_val.i32 = program->static_integers[data_elem.start_idx + VAL(instr.b).u32];
+                VAL(instr.r) = tmp_val;
+                break;
+            }
+
+            case OVMI_PARAM:
+                bh_arr_push(state->params, VAL(instr.a));
+                break;
+
+            case OVMI_RETURN: {
+                ovm_value_t val = VAL(instr.a);
+                ovm_stack_frame_t frame = ovm__func_teardown_stack_frame(engine, state, program);
+                state->pc = frame.return_address;
+
+                if (bh_arr_length(state->stack_frames) == 0) {
+                    return val;
+                }
+
+                ovm_func_t *new_func = bh_arr_last(state->stack_frames).func;
+                if (new_func->kind == OVM_FUNC_EXTERNAL) {
+                    return val;
+                }
+
+                if (frame.return_number_value >= 0) {
+                    VAL(frame.return_number_value) = val;
+                }
+
+#ifdef OVM_VERBOSE
+                printf("Returning from %s to %s: ", frame.func->name, bh_arr_last(state->stack_frames).func->name);
+                ovm_print_val(val);
+                printf("\n\n");
+#endif
+
+                break;
+            }
+
+#define OVM_CALL_CODE(func_idx) \
+            i32 fidx = func_idx; \
+            ovm_func_t *func = &program->funcs[fidx]; \
+            i32 extra_params = bh_arr_length(state->params) - func->param_count; \
+            ovm_assert(extra_params >= 0); \
+            ovm__func_setup_stack_frame(engine, state, program, fidx, instr.r); \
+            if (func->kind == OVM_FUNC_INTERNAL) { \
+                fori (i, 0, func->param_count) { \
+                    VAL(i) = state->params[i + extra_params]; \
+                } \
+                bh_arr_fastdeleten(state->params, func->param_count); \
+ \
+                state->pc = func->start_instr; \
+            } else { \
+                ovm_value_t result = {0}; \
+                ovm_external_func_t external_func = state->external_funcs[func->external_func_idx]; \
+                external_func.native_func(external_func.userdata, &state->params[extra_params], &result); \
+                bh_arr_fastdeleten(state->params, func->param_count); \
+ \
+                ovm__func_teardown_stack_frame(engine, state, program); \
+ \
+                if (instr.r >= 0) { \
+                    VAL(instr.r) = result; \
+                } \
+            }
+
+            case OVMI_CALL: {
+                OVM_CALL_CODE(instr.a);
+                break;
+            }
+
+            case OVMI_CALLI: {
+                OVM_CALL_CODE(VAL(instr.a).i32);
+                break;
+            }
+
+#undef OVM_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;
+            case OVMI_BR_Z:   if (VAL(instr.b).i32 == 0) state->pc += instr.a; break;
+            case OVMI_BRI_Z:  if (VAL(instr.b).i32 == 0) state->pc += VAL(instr.a).i32; break;
+
+
+#define CVT(stype, dtype, otype, ctype) \
+    tmp_val.type = otype; \
+    tmp_val.dtype = (ctype) VAL(instr.a).stype; \
+    VAL(instr.r) = tmp_val; \
+    break
+
+            case OVM_TYPED_INSTR(OVMI_CVT_I8,   OVM_TYPE_I16): CVT(u8, u16, OVM_TYPE_I16, u16);
+            case OVM_TYPED_INSTR(OVMI_CVT_I8,   OVM_TYPE_I32): CVT(u8, u32, OVM_TYPE_I32, u32);
+            case OVM_TYPED_INSTR(OVMI_CVT_I8,   OVM_TYPE_I64): CVT(u8, u64, OVM_TYPE_I64, u64);
+            case OVM_TYPED_INSTR(OVMI_CVT_I8_S, OVM_TYPE_I16): CVT(i8, i16, OVM_TYPE_I16, i16);
+            case OVM_TYPED_INSTR(OVMI_CVT_I8_S, OVM_TYPE_I32): CVT(i8, i32, OVM_TYPE_I32, i32);
+            case OVM_TYPED_INSTR(OVMI_CVT_I8_S, OVM_TYPE_I64): CVT(i8, i64, OVM_TYPE_I64, i64);
+
+            case OVM_TYPED_INSTR(OVMI_CVT_I16,   OVM_TYPE_I8):  CVT(u16, u8,  OVM_TYPE_I8, u8);
+            case OVM_TYPED_INSTR(OVMI_CVT_I16,   OVM_TYPE_I32): CVT(u16, u32, OVM_TYPE_I32, u32);
+            case OVM_TYPED_INSTR(OVMI_CVT_I16,   OVM_TYPE_I64): CVT(u16, u64, OVM_TYPE_I64, u64);
+            case OVM_TYPED_INSTR(OVMI_CVT_I16_S, OVM_TYPE_I8):  CVT(i16, i8,  OVM_TYPE_I8, i8);
+            case OVM_TYPED_INSTR(OVMI_CVT_I16_S, OVM_TYPE_I32): CVT(i16, i32, OVM_TYPE_I32, i32);
+            case OVM_TYPED_INSTR(OVMI_CVT_I16_S, OVM_TYPE_I64): CVT(i16, i64, OVM_TYPE_I64, i64);
+
+            case OVM_TYPED_INSTR(OVMI_CVT_I32,   OVM_TYPE_I8):  CVT(u32, u8,  OVM_TYPE_I8,  u8);
+            case OVM_TYPED_INSTR(OVMI_CVT_I32,   OVM_TYPE_I16): CVT(u32, u16, OVM_TYPE_I16, u16);
+            case OVM_TYPED_INSTR(OVMI_CVT_I32,   OVM_TYPE_I64): CVT(u32, u64, OVM_TYPE_I64, u64);
+            case OVM_TYPED_INSTR(OVMI_CVT_I32_S, OVM_TYPE_I8):  CVT(i32, i8,  OVM_TYPE_I8,  i8);
+            case OVM_TYPED_INSTR(OVMI_CVT_I32_S, OVM_TYPE_I16): CVT(i32, i16, OVM_TYPE_I16, i16);
+            case OVM_TYPED_INSTR(OVMI_CVT_I32_S, OVM_TYPE_I64): CVT(i32, i64, OVM_TYPE_I64, i64);
+
+            case OVM_TYPED_INSTR(OVMI_CVT_I32,   OVM_TYPE_F32): CVT(u32, f32, OVM_TYPE_F32, f32);
+            case OVM_TYPED_INSTR(OVMI_CVT_I32_S, OVM_TYPE_F32): CVT(i32, f32, OVM_TYPE_F32, f32);
+            case OVM_TYPED_INSTR(OVMI_CVT_I32,   OVM_TYPE_F64): CVT(u32, f64, OVM_TYPE_F64, f64);
+            case OVM_TYPED_INSTR(OVMI_CVT_I32_S, OVM_TYPE_F64): CVT(i32, f64, OVM_TYPE_F64, f64);
+
+            case OVM_TYPED_INSTR(OVMI_CVT_I64,   OVM_TYPE_I8):  CVT(u64, u8,  OVM_TYPE_I8,  u8);
+            case OVM_TYPED_INSTR(OVMI_CVT_I64,   OVM_TYPE_I16): CVT(u64, u16, OVM_TYPE_I16, u16);
+            case OVM_TYPED_INSTR(OVMI_CVT_I64,   OVM_TYPE_I32): CVT(u64, u32, OVM_TYPE_I32, u32);
+            case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_I8):  CVT(i64, i8,  OVM_TYPE_I8,  i8);
+            case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_I16): CVT(i64, i16, OVM_TYPE_I16, i16);
+            case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_I32): CVT(i64, i32, OVM_TYPE_I32, i32);
+
+            case OVM_TYPED_INSTR(OVMI_CVT_I64,   OVM_TYPE_F32): CVT(u64, f32, OVM_TYPE_F32, f32);
+            case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_F32): CVT(i64, f32, OVM_TYPE_F32, f32);
+            case OVM_TYPED_INSTR(OVMI_CVT_I64,   OVM_TYPE_F64): CVT(u64, f64, OVM_TYPE_F64, f64);
+            case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_F64): CVT(i64, f64, OVM_TYPE_F64, f64);
+
+            case OVM_TYPED_INSTR(OVMI_CVT_F32,   OVM_TYPE_I32): CVT(f32, u32, OVM_TYPE_I32, u32);
+            case OVM_TYPED_INSTR(OVMI_CVT_F32,   OVM_TYPE_I64): CVT(f32, u64, OVM_TYPE_I64, u64);
+            case OVM_TYPED_INSTR(OVMI_CVT_F32,   OVM_TYPE_F64): CVT(f32, f64, OVM_TYPE_F64, f64);
+            case OVM_TYPED_INSTR(OVMI_CVT_F32_S, OVM_TYPE_I32): CVT(f32, i32, OVM_TYPE_I32, i32);
+            case OVM_TYPED_INSTR(OVMI_CVT_F32_S, OVM_TYPE_I64): CVT(f32, i64, OVM_TYPE_I64, i64);
+            case OVM_TYPED_INSTR(OVMI_CVT_F32_S, OVM_TYPE_F64): CVT(f32, f64, OVM_TYPE_F64, f64);
+
+            case OVM_TYPED_INSTR(OVMI_CVT_F64,   OVM_TYPE_I32): CVT(f64, u32, OVM_TYPE_I32, u32);
+            case OVM_TYPED_INSTR(OVMI_CVT_F64,   OVM_TYPE_I64): CVT(f64, u64, OVM_TYPE_I64, u64);
+            case OVM_TYPED_INSTR(OVMI_CVT_F64,   OVM_TYPE_F32): CVT(f64, f32, OVM_TYPE_F32, f32);
+            case OVM_TYPED_INSTR(OVMI_CVT_F64_S, OVM_TYPE_I32): CVT(f64, i32, OVM_TYPE_I32, i32);
+            case OVM_TYPED_INSTR(OVMI_CVT_F64_S, OVM_TYPE_I64): CVT(f64, i64, OVM_TYPE_I64, i64);
+            case OVM_TYPED_INSTR(OVMI_CVT_F64_S, OVM_TYPE_F32): CVT(f64, f32, OVM_TYPE_F32, f32);
+
+#undef CVT
+
+#define CVT(stype, dtype, otype, ctype) \
+    tmp_val.type = otype; \
+    tmp_val.dtype = *(ctype *) &VAL(instr.a).stype; \
+    VAL(instr.r) = tmp_val; \
+    break
+
+            case OVM_TYPED_INSTR(OVMI_TRANSMUTE_I32, OVM_TYPE_F32): CVT(u32, f32, OVM_TYPE_F32, f32);
+            case OVM_TYPED_INSTR(OVMI_TRANSMUTE_I64, OVM_TYPE_F64): CVT(u64, f64, OVM_TYPE_F64, f64);
+            case OVM_TYPED_INSTR(OVMI_TRANSMUTE_F32, OVM_TYPE_I32): CVT(f32, u32, OVM_TYPE_I32, u32);
+            case OVM_TYPED_INSTR(OVMI_TRANSMUTE_F64, OVM_TYPE_I64): CVT(f64, u64, OVM_TYPE_I64, u64);
+
+#undef CVT
+
+
+#define CMPXCHG(otype, ctype) \
+    case OVM_TYPED_INSTR(OVMI_CMPXCHG, otype): {\
+        ctype *addr = (ctype *) &((u8 *) engine->memory)[VAL(instr.r).u32]; \
+ \
+        VAL(instr.r).u64 = 0; \
+        VAL(instr.r).type = otype; \
+        VAL(instr.r).ctype = *addr; \
+ \
+        if (*addr == VAL(instr.a).ctype) { \
+            *addr = VAL(instr.b).ctype ; \
+        } \
+        break; \
+    }
+
+            CMPXCHG(OVM_TYPE_I8,  i8)
+            CMPXCHG(OVM_TYPE_I16, i16)
+            CMPXCHG(OVM_TYPE_I32, i32)
+            CMPXCHG(OVM_TYPE_I64, i64)
+
+#undef CMPXCHG
+
+            default:
+                printf("ERROR:\n");
+                ovm_program_print_instructions(program, state->pc - 1, 1);
+                fflush(stdout);
+                ovm_assert(("ILLEGAL INSTRUCTION", 0));
+                break;
+        }
+
+        if (release_mutex_at_end) {
+            pthread_mutex_unlock(&engine->atomic_mutex);
+            release_mutex_at_end = false;
+        }
+    }
+
+    return ((ovm_value_t) {0});
+}
diff --git a/interpreter/src/wasm.c b/interpreter/src/wasm.c
new file mode 100644 (file)
index 0000000..6b0da56
--- /dev/null
@@ -0,0 +1,8 @@
+#define BH_DEFINE
+#define BH_NO_TABLE
+#define BH_INTERNAL
+#define STB_DS_IMPLEMENTATION
+#include "bh.h"
+#include "stb_ds.h"
+
+#include "ovm_wasm.h"
diff --git a/interpreter/src/wasm/config.c b/interpreter/src/wasm/config.c
new file mode 100644 (file)
index 0000000..07ea474
--- /dev/null
@@ -0,0 +1,18 @@
+
+#include "ovm_wasm.h"
+
+wasm_config_t *wasm_config_new() {
+    wasm_config_t *config = malloc(sizeof(*config));
+    config->debug_enabled = false;
+    return config;
+}
+
+void wasm_config_delete(wasm_config_t *config) {
+    free(config);
+}
+
+void wasm_config_enable_debug(wasm_config_t *config, bool enabled) {
+    config->debug_enabled = enabled;
+}
+
+
diff --git a/interpreter/src/wasm/engine.c b/interpreter/src/wasm/engine.c
new file mode 100644 (file)
index 0000000..db4fbea
--- /dev/null
@@ -0,0 +1,42 @@
+
+#include "ovm_wasm.h"
+#include "vm.h"
+
+wasm_engine_t *wasm_engine_new() {
+    wasm_engine_t *engine = wasm_engine_new_with_config(NULL);
+    return engine;
+}
+
+wasm_engine_t *wasm_engine_new_with_config(wasm_config_t *config) {
+    ovm_store_t *store = ovm_store_new();
+
+    wasm_engine_t *engine = bh_alloc_item(store->heap_allocator, wasm_engine_t);
+    engine->config = config;
+    engine->store = store;
+    
+    ovm_engine_t *ovm_engine = ovm_engine_new(store);
+    engine->engine = ovm_engine;
+
+    if (config && config->debug_enabled) {
+        // This should maybe be moved elsewhere?
+        debug_state_t *debug  = bh_alloc_item(store->heap_allocator, debug_state_t);
+        engine->engine->debug = debug;
+
+        debug_host_init(engine->engine->debug, engine->engine);
+        debug_host_start(engine->engine->debug);
+    }
+
+    return engine;
+}
+
+void wasm_engine_delete(wasm_engine_t *engine) {
+    if (engine->engine->debug) {
+        debug_host_stop(engine->engine->debug);
+    }
+
+    ovm_store_t *store = engine->store;
+    ovm_engine_delete(engine->engine);
+    bh_free(store->heap_allocator, engine);
+    ovm_store_delete(store);
+}
+
diff --git a/interpreter/src/wasm/extern.c b/interpreter/src/wasm/extern.c
new file mode 100644 (file)
index 0000000..9cda46c
--- /dev/null
@@ -0,0 +1,35 @@
+
+
+#include "ovm_wasm.h"
+
+
+WASM_DECLARE_VEC_IMPL(extern, *)
+
+wasm_externkind_t wasm_extern_kind(const wasm_extern_t* ext) {
+    return ext->type->kind;
+}
+
+wasm_externtype_t* wasm_extern_type(const wasm_extern_t* ext) {
+    return (wasm_externtype_t *) ext->type;
+}
+
+wasm_extern_t* wasm_func_as_extern(wasm_func_t* ext)     { return (wasm_extern_t *) ext; }
+wasm_extern_t* wasm_global_as_extern(wasm_global_t* ext) { return (wasm_extern_t *) ext; }
+wasm_extern_t* wasm_table_as_extern(wasm_table_t* ext)   { return (wasm_extern_t *) ext; }
+wasm_extern_t* wasm_memory_as_extern(wasm_memory_t* ext) { return (wasm_extern_t *) ext; }
+
+wasm_func_t* wasm_extern_as_func(wasm_extern_t* ext)     { return ext->type->kind == WASM_EXTERN_FUNC ? (wasm_func_t *) ext : NULL; }
+wasm_global_t* wasm_extern_as_global(wasm_extern_t* ext) { return ext->type->kind == WASM_EXTERN_GLOBAL ? (wasm_global_t *) ext : NULL; }
+wasm_table_t* wasm_extern_as_table(wasm_extern_t* ext)   { return ext->type->kind == WASM_EXTERN_TABLE ? (wasm_table_t *) ext : NULL; }
+wasm_memory_t* wasm_extern_as_memory(wasm_extern_t* ext) { return ext->type->kind == WASM_EXTERN_MEMORY ? (wasm_memory_t *) ext : NULL; }
+
+const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t* ext)     { return (const wasm_extern_t *) ext; }
+const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t* ext) { return (const wasm_extern_t *) ext; }
+const wasm_extern_t* wasm_table_as_extern_const(const wasm_table_t* ext)   { return (const wasm_extern_t *) ext; }
+const wasm_extern_t* wasm_memory_as_extern_const(const wasm_memory_t* ext) { return (const wasm_extern_t *) ext; }
+
+const wasm_func_t* wasm_extern_as_func_const(const wasm_extern_t* ext)     { return ext->type->kind == WASM_EXTERN_FUNC ? (const wasm_func_t *) ext : NULL; }
+const wasm_global_t* wasm_extern_as_global_const(const wasm_extern_t* ext) { return ext->type->kind == WASM_EXTERN_GLOBAL ? (const wasm_global_t *) ext : NULL; }
+const wasm_table_t* wasm_extern_as_table_const(const wasm_extern_t* ext)   { return ext->type->kind == WASM_EXTERN_TABLE ? (const wasm_table_t *) ext : NULL; }
+const wasm_memory_t* wasm_extern_as_memory_const(const wasm_extern_t* ext) { return ext->type->kind == WASM_EXTERN_MEMORY ? (const wasm_memory_t *) ext : NULL; }
+
diff --git a/interpreter/src/wasm/frame.c b/interpreter/src/wasm/frame.c
new file mode 100644 (file)
index 0000000..26bfb04
--- /dev/null
@@ -0,0 +1,37 @@
+
+#include "ovm_wasm.h"
+
+
+void wasm_frame_delete(wasm_frame_t *frame) {
+    // Don't have to free the frame because it was allocated on the
+    // arena allocator.
+}
+
+WASM_DECLARE_VEC_IMPL(frame, *)
+
+wasm_frame_t *wasm_frame_copy(const wasm_frame_t* frame) {
+    if (!frame) return NULL;
+    assert(frame->instance);
+
+    wasm_frame_t *new_frame = bh_alloc_item(frame->instance->store->engine->store->arena_allocator, wasm_frame_t);
+    memcpy(new_frame, frame, sizeof(*frame));
+
+    return new_frame;
+}
+
+wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame) {
+    return frame->instance;
+}
+
+u32 wasm_frame_func_index(const wasm_frame_t *frame) {
+    return frame->func_idx;
+}
+
+size_t wasm_frame_func_offset(const wasm_frame_t *frame) {
+    return frame->func_offset;
+}
+
+size_t wasm_frame_module_offset(const wasm_frame_t *frame) {
+    return frame->module_offset;
+}
+
diff --git a/interpreter/src/wasm/func.c b/interpreter/src/wasm/func.c
new file mode 100644 (file)
index 0000000..bd3fad8
--- /dev/null
@@ -0,0 +1,61 @@
+
+#include "ovm_wasm.h"
+#include "vm.h"
+
+wasm_func_t *wasm_func_new(wasm_store_t *store, const wasm_functype_t *type, wasm_func_callback_t callback) {
+    wasm_func_t *func = bh_alloc(store->engine->store->arena_allocator, sizeof(*func));
+    func->inner.type = wasm_functype_as_externtype_const(type);
+    func->inner.func.type = type;
+    func->inner.func.env_present = false;
+    func->inner.func.env = NULL;
+    func->inner.func.func_ptr = (void (*)()) callback;
+    func->inner.func.finalizer = NULL;
+
+    return func;
+}
+
+wasm_func_t *wasm_func_new_with_env(wasm_store_t *store, const wasm_functype_t *type,
+    wasm_func_callback_with_env_t callback, void *env, void (*finalizer)(void *)) {
+    
+    wasm_func_t *func = bh_alloc(store->engine->store->arena_allocator, sizeof(*func));
+    func->inner.type = wasm_functype_as_externtype_const(type);
+    func->inner.func.type = type;
+    func->inner.func.env_present = true;
+    func->inner.func.env = env;
+    func->inner.func.func_ptr = (void (*)()) callback;
+    func->inner.func.finalizer = finalizer;
+
+    return func;
+}
+
+wasm_functype_t *wasm_func_type(const wasm_func_t *func) {
+    return (wasm_functype_t *) func->inner.func.type;
+}
+
+size_t wasm_func_param_arity(const wasm_func_t *func) {
+    // Wow this is gross...
+    return func->inner.func.type->type.func.params.size;
+}
+
+size_t wasm_func_result_arity(const wasm_func_t *func) {
+    // Wow this is gross...
+    return func->inner.func.type->type.func.results.size;
+}
+
+wasm_trap_t *wasm_func_call(const wasm_func_t *func, const wasm_val_vec_t *args, wasm_val_vec_t *results) {
+    if (func->inner.func.env_present) {
+        wasm_func_callback_with_env_t cb = (wasm_func_callback_with_env_t) func->inner.func.func_ptr;
+        wasm_trap_t *trap = cb(func->inner.func.env, args, results);
+
+        if (func->inner.func.finalizer) {
+            func->inner.func.finalizer(func->inner.func.env);
+        }
+
+        return trap;
+
+    } else {
+        wasm_func_callback_t cb = (wasm_func_callback_t) func->inner.func.func_ptr;
+        wasm_trap_t *trap = cb(args, results);
+        return trap;
+    }
+}
diff --git a/interpreter/src/wasm/global.c b/interpreter/src/wasm/global.c
new file mode 100644 (file)
index 0000000..81370eb
--- /dev/null
@@ -0,0 +1,29 @@
+
+#include "ovm_wasm.h"
+#include "vm.h"
+
+wasm_global_t *wasm_global_new(wasm_store_t *store, const wasm_globaltype_t *type, const wasm_val_t *initial) {
+    wasm_global_t *global = bh_alloc(store->engine->store->arena_allocator, sizeof(*global));
+    global->inner.type = wasm_globaltype_as_externtype_const(type);
+    global->inner.global.register_index = -1;
+    global->inner.global.engine = NULL;
+
+    if (initial) {
+        global->inner.global.initial_value = *initial;
+    }
+
+    return global;
+}
+
+wasm_globaltype_t *wasm_global_type(const wasm_global_t *global) {
+    return (wasm_globaltype_t *) global->inner.global.type;
+}
+
+void wasm_global_get(const wasm_global_t *global, wasm_val_t *value) {
+    assert(("unimplemented", 0));
+}
+
+void wasm_global_set(wasm_global_t *global, const wasm_val_t *value) {
+    assert(("unimplemented", 0));
+}
+
diff --git a/interpreter/src/wasm/instance.c b/interpreter/src/wasm/instance.c
new file mode 100644 (file)
index 0000000..4a998f6
--- /dev/null
@@ -0,0 +1,349 @@
+
+
+#include "ovm_wasm.h"
+#include "vm.h"
+#include <alloca.h>
+
+static_assert(sizeof(ovm_value_t) == sizeof(wasm_val_t));
+
+typedef struct wasm_ovm_binding wasm_ovm_binding;
+struct wasm_ovm_binding {
+    int func_idx;
+    ovm_engine_t  *engine;
+    ovm_state_t   *state;
+    ovm_program_t *program;
+};
+
+typedef struct ovm_wasm_binding ovm_wasm_binding;
+struct ovm_wasm_binding {
+    int param_count;
+    int result_count;
+    wasm_func_t *func;
+    wasm_val_vec_t param_buffer;
+};
+
+#define WASM_TO_OVM(w, o) { \
+    (o).u64 = 0;\
+    switch ((w).kind) { \
+        case WASM_I32: \
+            (o).type = OVM_TYPE_I32; \
+            (o).i32  = (w).of.i32; \
+            break; \
+ \
+        case WASM_I64: \
+            (o).type = OVM_TYPE_I64; \
+            (o).i64  = (w).of.i64; \
+            break; \
+ \
+        case WASM_F32: \
+            (o).type = OVM_TYPE_F32; \
+            (o).f32  = (w).of.f32; \
+            break; \
+ \
+        case WASM_F64: \
+            (o).type = OVM_TYPE_F64; \
+            (o).f64  = (w).of.f64; \
+            break; \
+ \
+        default: assert(("invalid wasm value type for conversion", 0)); \
+    } }
+
+#define OVM_TO_WASM(o, w) { \
+    (w).of.i64 = 0;\
+    switch ((o).type) { \
+        case OVM_TYPE_I8: \
+            (w).kind = WASM_I32; \
+            (w).of.i32 = (i32) (o).i8; \
+            break; \
+ \
+        case OVM_TYPE_I16: \
+            (w).kind = WASM_I32; \
+            (w).of.i32 = (i32) (o).i16; \
+            break; \
+ \
+        case OVM_TYPE_I32: \
+            (w).kind = WASM_I32; \
+            (w).of.i32 = (i32) (o).i32; \
+            break; \
+ \
+        case OVM_TYPE_I64: \
+            (w).kind = WASM_I64; \
+            (w).of.i64 = (o).i64; \
+            break; \
+ \
+        case OVM_TYPE_F32: \
+            (w).kind = WASM_F32; \
+            (w).of.f32 = (o).f32; \
+            break; \
+ \
+        case OVM_TYPE_F64: \
+            (w).kind = WASM_F64; \
+            (w).of.f64 = (o).f64; \
+            break; \
+ \
+        default: \
+            printf("INVALID: %d\n", (o).type); \
+            assert(("invalid ovm value type for conversion", 0)); \
+    } }
+
+static wasm_trap_t *wasm_to_ovm_func_call_binding(void *vbinding, const wasm_val_vec_t *args, wasm_val_vec_t *res) {
+    wasm_ovm_binding *binding = (wasm_ovm_binding *) vbinding;
+
+    ovm_value_t *vals = alloca(sizeof(*vals) * args->size);
+    fori (i, 0, (int) args->size) {
+        WASM_TO_OVM(args->data[i], vals[i]);
+    }
+
+    ovm_value_t ovm_res = ovm_func_call(binding->engine, binding->state, binding->program, binding->func_idx, args->size, vals);
+    if (!res || res->size == 0) return NULL;
+
+    OVM_TO_WASM(ovm_res, res->data[0]);
+
+    return NULL;
+}
+
+static void ovm_to_wasm_func_call_binding(void *env, ovm_value_t* params, ovm_value_t *res) {
+    ovm_wasm_binding *binding = (ovm_wasm_binding *) env;
+
+    fori (i, 0, binding->param_count) {
+        OVM_TO_WASM(params[i], binding->param_buffer.data[i]);
+    }
+
+    wasm_val_t return_value;
+    wasm_val_vec_t wasm_results;
+    wasm_results.data = &return_value;
+    wasm_results.size = binding->result_count;
+
+    wasm_trap_t *trap = wasm_func_call(binding->func, &binding->param_buffer, &wasm_results);
+    assert(!trap);
+
+    if (binding->result_count > 0) {
+        assert(wasm_results.data[0].kind == binding->func->inner.type->func.results.data[0]->kind);
+        WASM_TO_OVM(return_value, *res);
+    }
+}
+
+static void wasm_memory_init(void *env, ovm_value_t* params, ovm_value_t *res) {
+    wasm_instance_t *instr = (wasm_instance_t *) env;
+
+    assert(params[0].type == OVM_TYPE_I32);
+    assert(params[1].type == OVM_TYPE_I32);
+    assert(params[2].type == OVM_TYPE_I32);
+    assert(params[3].type == OVM_TYPE_I32);
+
+    ovm_engine_memory_copy(instr->store->engine->engine, params[0].i32, instr->module->data_entries[params[3].i32].data, params[2].i32);
+}
+
+static void prepare_instance(wasm_instance_t *instance, const wasm_extern_vec_t *imports) {
+    ovm_store_t   *ovm_store   = instance->store->engine->store;
+    ovm_engine_t  *ovm_engine  = instance->store->engine->engine;
+    ovm_state_t   *ovm_state   = instance->state;
+    ovm_program_t *ovm_program = instance->module->program;
+
+    //
+    // Place imports in their corresponding "bucket"
+    fori (i, 0, (int) imports->size) {
+        assert(instance->module->imports.data[i]->type->kind == imports->data[i]->type->kind);
+
+        switch (wasm_extern_kind(imports->data[i])) {
+            case WASM_EXTERN_FUNC: {
+                wasm_importtype_t *importtype = instance->module->imports.data[i];
+                struct wasm_functype_inner_t *functype = &importtype->type->func;
+
+                if (!wasm_functype_equals(
+                        wasm_externtype_as_functype(importtype->type),
+                        wasm_externtype_as_functype((wasm_externtype_t *) imports->data[i]->type))) {
+                    assert(("MISMATCHED FUNCTION TYPE", 0));
+                }
+
+                wasm_func_t *func = wasm_extern_as_func(imports->data[i]);
+                bh_arr_push(instance->funcs, func);
+
+                ovm_wasm_binding *binding = bh_alloc(ovm_store->arena_allocator, sizeof(*binding));
+                binding->param_count  = functype->params.size;
+                binding->result_count = functype->results.size;
+                binding->func         = func;
+                binding->param_buffer.data = bh_alloc(ovm_store->arena_allocator, sizeof(wasm_val_t) * binding->param_count);
+                binding->param_buffer.size = binding->param_count; 
+
+                ovm_state_register_external_func(ovm_state, importtype->external_func_idx, ovm_to_wasm_func_call_binding, binding);
+                break;
+            }
+
+            case WASM_EXTERN_MEMORY: {
+                wasm_memory_t *memory = wasm_extern_as_memory(imports->data[i]);
+                bh_arr_push(instance->memories, memory);
+
+                memory->inner.memory.engine = ovm_engine;
+                break;
+            }
+
+            case WASM_EXTERN_GLOBAL: {
+                wasm_global_t *global = wasm_extern_as_global(imports->data[i]);
+
+                global->inner.global.engine = ovm_engine;
+                global->inner.global.state  = ovm_state;
+                global->inner.global.register_index = bh_arr_length(instance->globals);
+
+                ovm_value_t val = {0};
+                WASM_TO_OVM(global->inner.global.initial_value, val);
+                ovm_state_register_set(ovm_state, global->inner.global.register_index, val);
+
+                bh_arr_push(instance->globals, global);
+                break;
+            }
+
+            case WASM_EXTERN_TABLE: {
+                wasm_table_t *table = wasm_extern_as_table(imports->data[i]);
+                table->inner.table.engine = ovm_engine;
+                table->inner.table.program = ovm_program;
+                table->inner.table.static_arr = instance->module->imports.data[i]->type->table.static_arr;
+
+                bh_arr_push(instance->tables, table);
+                break;
+            }
+        }
+    }
+
+    ovm_state_register_external_func(ovm_state, instance->module->memory_init_external_idx, wasm_memory_init, instance);
+
+    //
+    // Create function objects
+    fori (i, 0, (int) instance->module->functypes.size) {
+        wasm_ovm_binding *binding = bh_alloc(instance->store->engine->store->arena_allocator, sizeof(*binding));
+        binding->engine   = ovm_engine;
+        binding->func_idx = bh_arr_length(instance->funcs);
+        binding->program  = ovm_program;
+        binding->state    = ovm_state;
+        
+        wasm_func_t *func = wasm_func_new_with_env(instance->store, instance->module->functypes.data[i], 
+            wasm_to_ovm_func_call_binding, binding, NULL);
+
+        bh_arr_push(instance->funcs, func);
+    }
+
+    //
+    // Create memory objects
+    fori (i, 0, (int) instance->module->memorytypes.size) {
+        wasm_memory_t *memory = wasm_memory_new(instance->store, instance->module->memorytypes.data[i]);
+        memory->inner.memory.engine = ovm_engine;
+
+        bh_arr_push(instance->memories, memory);
+    }
+
+    //
+    // Create table objects
+    fori (i, 0, (int) instance->module->tabletypes.size) {
+        wasm_table_t *table = wasm_table_new(instance->store, instance->module->tabletypes.data[i], NULL);
+        table->inner.table.engine     = ovm_engine;
+        table->inner.table.program    = ovm_program;
+        table->inner.table.static_arr = instance->module->tabletypes.data[i]->type.table.static_arr;
+
+        bh_arr_push(instance->tables, table);
+    }
+
+    //
+    // Create global objects
+    fori (i, 0, (int) instance->module->globaltypes.size) {
+        wasm_global_t *global = wasm_global_new(instance->store, instance->module->globaltypes.data[i],
+            &instance->module->globaltypes.data[i]->type.global.initial_value);
+
+        global->inner.global.engine         = ovm_engine;
+        global->inner.global.state          = ovm_state;
+        global->inner.global.register_index = bh_arr_length(instance->globals);
+
+        ovm_value_t val = {0};
+        WASM_TO_OVM(global->inner.global.initial_value, val);
+        ovm_state_register_set(ovm_state, global->inner.global.register_index, val);
+
+        bh_arr_push(instance->globals, global);
+    }
+
+
+    //
+    // Initialize all non-passive data segments
+    fori (i, 0, (int) instance->module->data_count) {
+        struct wasm_data_t *datum = &instance->module->data_entries[i];
+        if (datum->passive) continue;
+
+        ovm_engine_memory_copy(ovm_engine, datum->offset, datum->data, datum->length);
+    }
+
+    wasm_extern_vec_new_uninitialized(&instance->exports, instance->module->exports.size);
+    fori (i, 0, (int) instance->module->exports.size) {
+        wasm_exporttype_t *externtype = instance->module->exports.data[i];
+
+        switch (externtype->type->kind) {
+            case WASM_EXTERN_FUNC: {
+                wasm_func_t *func = instance->funcs[externtype->index];
+                instance->exports.data[i] = wasm_func_as_extern(func);
+                break;
+            }
+
+            case WASM_EXTERN_MEMORY: {
+                wasm_memory_t *memory = instance->memories[externtype->index];
+                instance->exports.data[i] = wasm_memory_as_extern(memory);
+                break;
+            }
+
+            case WASM_EXTERN_GLOBAL: {
+                wasm_global_t *global = instance->globals[externtype->index];
+                instance->exports.data[i] = wasm_global_as_extern(global);
+                break;
+            }
+
+            case WASM_EXTERN_TABLE: {
+                wasm_table_t *table = instance->tables[externtype->index];
+                instance->exports.data[i] = wasm_table_as_extern(table);
+                break;
+            }
+        }
+    }
+}
+
+wasm_instance_t *wasm_instance_new(wasm_store_t *store, const wasm_module_t *module,
+    const wasm_extern_vec_t *imports, wasm_trap_t **trap) {
+
+    wasm_instance_t *instance = bh_alloc(store->engine->store->heap_allocator, sizeof(*instance));
+    instance->store = store;
+    instance->module = module;
+
+    if (store->instance) {
+        bh_printf("A WASM store should only be used for a single instance!\n");
+        return NULL;
+    }
+
+    store->instance = instance;
+
+    instance->funcs = NULL;
+    instance->memories = NULL;
+    instance->tables = NULL;
+    instance->globals = NULL;
+    bh_arr_new(store->engine->store->heap_allocator, instance->funcs, module->functypes.size);
+    bh_arr_new(store->engine->store->heap_allocator, instance->memories, 1);
+    bh_arr_new(store->engine->store->heap_allocator, instance->tables, 1);
+    bh_arr_new(store->engine->store->heap_allocator, instance->globals, module->globaltypes.size);
+
+    instance->state = ovm_state_new(store->engine->engine, module->program);
+
+    prepare_instance(instance, imports);
+
+    if (trap) *trap = NULL;
+
+    return instance;
+}
+
+void wasm_instance_delete(wasm_instance_t *instance) {
+    bh_arr_free(instance->funcs);
+    bh_arr_free(instance->memories);
+    bh_arr_free(instance->globals);
+    bh_arr_free(instance->tables);
+
+    wasm_extern_vec_delete(&instance->exports);
+    ovm_state_delete(instance->state);
+    bh_free(instance->store->engine->store->heap_allocator, instance);
+}
+
+void wasm_instance_exports(const wasm_instance_t *instance, wasm_extern_vec_t *out) {
+    *out = instance->exports;
+}
diff --git a/interpreter/src/wasm/memory.c b/interpreter/src/wasm/memory.c
new file mode 100644 (file)
index 0000000..3844579
--- /dev/null
@@ -0,0 +1,42 @@
+
+#include "ovm_wasm.h"
+#include "vm.h"
+
+wasm_memory_t *wasm_memory_new(wasm_store_t *store, const wasm_memorytype_t *type) {
+    wasm_memory_t *memory = bh_alloc(store->engine->store->arena_allocator, sizeof(*store));
+    memory->inner.type = wasm_memorytype_as_externtype_const(type);
+    memory->inner.memory.type = type;
+    memory->inner.memory.engine = NULL;
+
+    return memory;
+}
+
+wasm_memorytype_t *wasm_memory_type(const wasm_memory_t *memory) {
+    return (wasm_memorytype_t *) memory->inner.memory.type;
+}
+
+byte_t *wasm_memory_data(wasm_memory_t *memory) {
+    assert(memory && memory->inner.memory.engine);
+    return memory->inner.memory.engine->memory;
+}
+
+size_t wasm_memory_data_size(const wasm_memory_t *memory) {
+    assert(memory && memory->inner.memory.engine);
+    return memory->inner.memory.engine->memory_size;
+}
+
+wasm_memory_pages_t wasm_memory_size(const wasm_memory_t *memory) {
+    assert(memory && memory->inner.memory.engine);
+    return memory->inner.memory.engine->memory_size / MEMORY_PAGE_SIZE;
+}
+
+bool wasm_memory_grow(wasm_memory_t *memory, wasm_memory_pages_t pages) {
+    //
+    // This will always fail, as initially the VM is created with
+    // a 4GiB mmap, so growing it will not be an option. If that
+    // changes and a dynamically allocated solution is used, then
+    // this can change. I don't see that changing however, as I will
+    // never need to use this on 32-bit systems, and that would be the
+    // only case that I would not like to try to mmap 4 gigs.
+    return false;
+}
diff --git a/interpreter/src/wasm/module.c b/interpreter/src/wasm/module.c
new file mode 100644 (file)
index 0000000..8e469a6
--- /dev/null
@@ -0,0 +1,101 @@
+
+
+#include "ovm_wasm.h"
+#include "vm_codebuilder.h"
+#include "stb_ds.h"
+
+#include "./module_parsing.h"
+
+static bool module_build(wasm_module_t *module, const wasm_byte_vec_t *binary) {
+    wasm_engine_t *engine = module->store->engine;
+    module->program = ovm_program_new(engine->store);
+
+    build_context ctx;
+    ctx.binary  = *binary;
+    ctx.offset  = 8;  // Skip the magic bytes and version
+    ctx.module  = module;
+    ctx.program = module->program;
+    ctx.store   = engine->store;
+    ctx.next_external_func_idx = 0;
+
+    debug_info_builder_init(&ctx.debug_builder, &module->debug_info);
+    sh_new_arena(module->custom_sections);
+
+    while (ctx.offset < binary->size) {
+        parse_section(&ctx);
+    }
+
+    // TODO: This is not correct when the module imports a global.
+    // But Onyx does not do this, so I don't care at the moment.
+    module->program->register_count = module->globaltypes.size;
+
+    return true;
+}
+
+
+#define WASM_MODULE_INDEX(k1, k2) \
+    wasm_##k1##type_t *wasm_module_index_##k1##type(wasm_module_t *module, int index) { \
+        fori (i, 0, (int) module->imports.size) { \
+            if (module->imports.data[i]->type->kind == k2) { \
+                if (index == 0) { \
+                    return wasm_externtype_as_##k1##type(module->imports.data[i]->type); \
+                } \
+     \
+                index -= 1; \
+            } \
+        } \
+     \
+        if (index < (int) module->k1##types.size) { \
+            return module->k1##types.data[index]; \
+        } \
+     \
+        return NULL; \
+    }
+
+WASM_MODULE_INDEX(func, WASM_EXTERN_FUNC)
+WASM_MODULE_INDEX(memory, WASM_EXTERN_MEMORY)
+WASM_MODULE_INDEX(table, WASM_EXTERN_TABLE)
+WASM_MODULE_INDEX(global, WASM_EXTERN_GLOBAL)
+
+#undef WASM_MODULE_INDEX
+
+
+// Ommitting the "sharable ref" crap that I don't think will
+// ever be needed for a module.
+
+
+wasm_module_t *wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary) {
+    wasm_module_t *module = bh_alloc(store->engine->store->arena_allocator, sizeof(*module));
+    memset(module, 0, sizeof(*module));
+    module->store = store;
+
+    debug_info_init(&module->debug_info);
+
+    if (store->engine->engine->debug) {
+        assert(store->engine->engine->debug->info == NULL);
+        store->engine->engine->debug->info = &module->debug_info;
+    }
+
+    bool success = module_build(module, binary); 
+    return module;
+}
+
+void wasm_module_delete(wasm_module_t *module) {
+    ovm_program_delete(module->program);
+}
+
+bool wasm_module_validate(wasm_store_t *store, const wasm_byte_vec_t *binary) {
+    // Hmmm...
+    return false;
+}
+
+void wasm_module_imports(const wasm_module_t *module, wasm_importtype_vec_t *out_imports) {
+    *out_imports = module->imports;
+}
+
+void wasm_module_exports(const wasm_module_t *module, wasm_exporttype_vec_t *out_exports) {
+    *out_exports = module->exports;
+}
+
+
+
diff --git a/interpreter/src/wasm/module_parsing.h b/interpreter/src/wasm/module_parsing.h
new file mode 100644 (file)
index 0000000..8eade30
--- /dev/null
@@ -0,0 +1,984 @@
+// vim: ft=c:
+
+//
+// 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 {
+    wasm_byte_vec_t binary;
+    unsigned int    offset;
+
+    wasm_module_t *module;
+    ovm_program_t *program;
+    ovm_store_t   *store;
+
+    int func_table_arr_idx;
+    int next_external_func_idx;
+    
+    debug_info_builder_t debug_builder;
+
+    // This will be set/reset for every code (function) entry.
+    ovm_code_builder_t builder;
+};
+
+#define PEEK_BYTE(ctx)    ((ctx)->binary.data[(ctx)->offset])
+#define CONSUME_BYTE(ctx) ((ctx)->binary.data[(ctx)->offset++])
+
+enum wasm_section_numbers_t {
+    WASM_CUSTOM_SECTION = 0,
+    WASM_TYPE_SECTION   = 1,
+    WASM_IMPORT_SECTION = 2,
+    WASM_FUNC_SECTION   = 3,
+    WASM_TABLE_SECTION  = 4,
+    WASM_MEMORY_SECTION = 5,
+    WASM_GLOBAL_SECTION = 6,
+    WASM_EXPORT_SECTION = 7,
+    WASM_START_SECTION  = 8,
+    WASM_ELEM_SECTION   = 9,
+    WASM_CODE_SECTION   = 10,
+    WASM_DATA_SECTION   = 11,
+    WASM_DATAC_SECTION  = 12,
+};
+
+static inline wasm_valkind_t parse_valtype(build_context *ctx) {
+    switch (CONSUME_BYTE(ctx)) {
+        case 0x7f: return WASM_I32;
+        case 0x7e: return WASM_I64;
+        case 0x7d: return WASM_F32;
+        case 0x7c: return WASM_F64;
+        case 0x7b: assert(("SIMD values are not currently supported", 0));
+        case 0x70: return WASM_FUNCREF;
+        case 0x6F: return WASM_ANYREF;
+        default:   assert(("Invalid valtype.", 0));
+    }
+}
+
+static void parse_custom_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int end_of_section = ctx->offset + section_size;
+
+    struct wasm_custom_section_t cs;
+
+    char name[256];
+    unsigned int name_len = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    if (name_len < sizeof(name) - 1) {
+        strncpy(name, &((char *) ctx->binary.data)[ctx->offset], name_len);
+        name[name_len] = '\0';
+
+        ctx->offset += name_len;
+        cs.size = end_of_section - ctx->offset;
+
+        unsigned int data_size = end_of_section - ctx->offset;
+        cs.data = bh_alloc_array(ctx->store->heap_allocator, char, data_size);
+        memcpy(cs.data, &((char *) ctx->binary.data)[ctx->offset], data_size);
+
+        shput(ctx->module->custom_sections, name, cs);
+
+        if (!strcmp(name, "ovm_debug_files")) {
+            debug_info_import_file_info(ctx->debug_builder.info, cs.data, cs.size);
+        }
+
+        if (!strcmp(name, "ovm_debug_funcs")) {
+            debug_info_import_func_info(ctx->debug_builder.info, cs.data, cs.size);
+        }
+
+        if (!strcmp(name, "ovm_debug_ops")) {
+            debug_info_builder_prepare(&ctx->debug_builder, cs.data);
+        }
+
+        if (!strcmp(name, "ovm_debug_syms")) {
+            debug_info_import_sym_info(ctx->debug_builder.info, cs.data, cs.size);
+        }
+
+        if (!strcmp(name, "ovm_debug_types")) {
+            debug_info_import_type_info(ctx->debug_builder.info, cs.data, cs.size);
+        }
+    }
+
+    ctx->offset = end_of_section;
+}
+
+static void parse_type_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int type_count   = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    wasm_functype_vec_new_uninitialized(&ctx->module->type_section, type_count);
+
+    fori (i, 0, (int) type_count) {
+        assert(CONSUME_BYTE(ctx) == 0x60); // @ReportError
+
+        unsigned int param_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+        wasm_valtype_vec_t param_types;
+        wasm_valtype_vec_new_uninitialized(&param_types, param_count);
+        fori (p, 0, (int) param_count) {
+            param_types.data[p] = wasm_valtype_new(parse_valtype(ctx));
+        }
+
+        unsigned int result_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+        wasm_valtype_vec_t result_types;
+        wasm_valtype_vec_new_uninitialized(&result_types, result_count);
+        fori (p, 0, (int) result_count) {
+            result_types.data[p] = wasm_valtype_new(parse_valtype(ctx));
+        }
+
+        wasm_functype_t *functype = wasm_functype_new(&param_types, &result_types);
+        ctx->module->type_section.data[i] = functype;
+    }
+}
+
+static wasm_limits_t parse_limits(build_context *ctx) {
+    bool maximum_present = CONSUME_BYTE(ctx) == 0x01;
+
+    wasm_limits_t limits;
+    limits.min = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    if (maximum_present) {
+        limits.max = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    } else {
+        limits.max = wasm_limits_max_default;
+    }
+
+    return limits;
+}
+
+static wasm_tabletype_t *parse_tabletype(build_context *ctx) {
+    assert(CONSUME_BYTE(ctx) == 0x70); // @ReportError
+
+    wasm_limits_t limits = parse_limits(ctx);
+    wasm_tabletype_t *tt = wasm_tabletype_new(wasm_valtype_new(WASM_FUNCREF), &limits);
+    return tt;
+}
+
+static wasm_memorytype_t *parse_memorytype(build_context *ctx) {
+    wasm_limits_t limits = parse_limits(ctx);
+    wasm_memorytype_t *mt = wasm_memorytype_new(&limits);
+    return mt;
+}
+
+static wasm_globaltype_t *parse_globaltype(build_context *ctx) {
+    wasm_valtype_t *valtype = wasm_valtype_new(parse_valtype(ctx));
+    bool mutable = CONSUME_BYTE(ctx) == 0x01;
+
+    wasm_globaltype_t *gt = wasm_globaltype_new(valtype, mutable);
+    return gt;
+}
+
+static void parse_import_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int import_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    wasm_importtype_vec_new_uninitialized(&ctx->module->imports, import_count);
+
+    fori (i, 0, (int) import_count) {
+        unsigned int mod_name_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+        wasm_byte_vec_t module_name;
+        wasm_byte_vec_new_uninitialized(&module_name, mod_name_size);
+        fori (n, 0, mod_name_size) module_name.data[n] = CONSUME_BYTE(ctx);
+
+        unsigned int import_name_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+        wasm_byte_vec_t import_name;
+        wasm_byte_vec_new_uninitialized(&import_name, import_name_size);
+        fori (n, 0, import_name_size) import_name.data[n] = CONSUME_BYTE(ctx);
+
+        wasm_externtype_t *import_type = NULL;
+        switch (CONSUME_BYTE(ctx)) {
+            case 0x00: {
+                unsigned int type_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+                import_type = wasm_functype_as_externtype(ctx->module->type_section.data[type_idx]);
+                break;
+            }
+
+            case 0x01: import_type = wasm_tabletype_as_externtype(parse_tabletype(ctx)); break;
+            case 0x02: import_type = wasm_memorytype_as_externtype(parse_memorytype(ctx)); break;
+            case 0x03: import_type = wasm_globaltype_as_externtype(parse_globaltype(ctx)); break;
+        }
+
+        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->next_external_func_idx++;
+            import->external_func_idx = external_func_idx;
+
+            ovm_program_register_external_func(ctx->program, external_func_name, import_type->func.params.size, external_func_idx);
+        }
+    }
+}
+
+static void parse_func_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int func_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    wasm_functype_vec_new_uninitialized(&ctx->module->functypes, func_count);
+
+    fori (i, 0, (int) func_count) {
+        unsigned int index = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+        ctx->module->functypes.data[i] = ctx->module->type_section.data[index];
+    }
+}
+
+static void parse_table_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int table_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    wasm_tabletype_vec_new_uninitialized(&ctx->module->tabletypes, table_count);
+
+    fori (i, 0, (int) table_count) {
+        ctx->module->tabletypes.data[i] = parse_tabletype(ctx);
+    }
+}
+
+static void parse_memory_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int memory_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    wasm_memorytype_vec_new_uninitialized(&ctx->module->memorytypes, memory_count);
+
+    fori (i, 0, (int) memory_count) {
+        ctx->module->memorytypes.data[i] = parse_memorytype(ctx);
+    }
+}
+
+static void parse_global_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int global_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    wasm_globaltype_vec_new_uninitialized(&ctx->module->globaltypes, global_count);
+
+    fori (i, 0, (int) global_count) {
+        wasm_globaltype_t *gt = parse_globaltype(ctx);
+
+        switch (CONSUME_BYTE(ctx)) {
+            case 0x41: {
+                gt->type.global.initial_value.kind = WASM_I32;
+                gt->type.global.initial_value.of.i32 = (i32) uleb128_to_uint(ctx->binary.data, &ctx->offset);
+                break;
+            }
+
+            case 0x42: {
+                gt->type.global.initial_value.kind = WASM_I64;
+                gt->type.global.initial_value.of.i64 = (i64) uleb128_to_uint(ctx->binary.data, &ctx->offset);
+                break;
+            }
+
+            case 0x43: {
+                gt->type.global.initial_value.kind = WASM_F32;
+                gt->type.global.initial_value.of.f32 = *(f32 *) &ctx->binary.data[ctx->offset]; // HACK: This assumes IEEE-754 floats
+                ctx->offset += 4;
+                break;
+            }
+
+            case 0x44: {
+                gt->type.global.initial_value.kind = WASM_F64;
+                gt->type.global.initial_value.of.f64 = *(f64 *) &ctx->binary.data[ctx->offset]; // HACK: This assumes IEEE-754 floats
+                ctx->offset += 8;
+                break;
+            }
+        }
+
+        assert(CONSUME_BYTE(ctx) == 0x0b);
+
+        ctx->module->globaltypes.data[i] = gt;
+    }
+}
+
+static void parse_export_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int export_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    wasm_exporttype_vec_new_uninitialized(&ctx->module->exports, export_count);
+
+    fori (i, 0, (int) export_count) {
+        unsigned int export_name_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+        wasm_byte_vec_t export_name;
+        wasm_byte_vec_new_uninitialized(&export_name, export_name_size);
+        fori (n, 0, export_name_size) export_name.data[n] = CONSUME_BYTE(ctx);
+
+        unsigned int type = CONSUME_BYTE(ctx);
+        unsigned int idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+        wasm_externtype_t *export_type = NULL;
+
+        switch (type) {
+            case 0x00: export_type = wasm_functype_as_externtype(wasm_module_index_functype(ctx->module, idx)); break;
+            case 0x01: export_type = wasm_tabletype_as_externtype(wasm_module_index_tabletype(ctx->module, idx)); break;
+            case 0x02: export_type = wasm_memorytype_as_externtype(wasm_module_index_memorytype(ctx->module, idx)); break;
+            case 0x03: export_type = wasm_globaltype_as_externtype(wasm_module_index_globaltype(ctx->module, idx)); break;
+            default: assert(0);
+        }
+
+        assert(export_type);
+
+        wasm_exporttype_t *export = wasm_exporttype_new(&export_name, export_type);
+        export->index = idx;
+        ctx->module->exports.data[i] = export;
+    }
+}
+
+static void parse_start_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    ctx->module->start_func_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+}
+
+static void parse_elem_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int elem_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    // This is going to be a mess...
+    // I am only going to handle the case of a single, active, offset-0,
+    // element entry. This is all that Onyx uses and will probably ever
+    // use.
+    assert(elem_count == 1);
+    assert(CONSUME_BYTE(ctx) == 0x00);
+    assert(CONSUME_BYTE(ctx) == 0x41);
+    assert(CONSUME_BYTE(ctx) == 0x00);
+    assert(CONSUME_BYTE(ctx) == 0x0B);
+
+    unsigned int entry_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    ctx->module->elem_count = entry_count;
+    ctx->module->elem_entries = malloc(sizeof(unsigned int) * entry_count);
+
+    fori (i, 0, (int) entry_count) {
+        ctx->module->elem_entries[i] = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    }
+
+    ctx->func_table_arr_idx = ovm_program_register_static_ints(ctx->program, entry_count, ctx->module->elem_entries);
+
+    assert(ctx->module->tabletypes.size == 1);
+    ctx->module->tabletypes.data[0]->type.table.static_arr = ctx->func_table_arr_idx;
+}
+
+static void parse_data_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int data_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    if (ctx->module->data_count_present) {
+        assert(ctx->module->data_count == data_count);
+    } else {
+        ctx->module->data_count = data_count;
+    }
+
+    ctx->module->data_entries = malloc(sizeof(struct wasm_data_t) * data_count);
+
+    fori (i, 0, (int) data_count) {
+        struct wasm_data_t data_entry;
+        data_entry.data = NULL;
+        data_entry.offset = 0;
+        data_entry.length = 0;
+        data_entry.passive = true;
+
+        char data_type = CONSUME_BYTE(ctx);
+        if (data_type == 0x00) {
+            assert(CONSUME_BYTE(ctx) == 0x41);
+            data_entry.offset = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            data_entry.passive = false;
+            assert(CONSUME_BYTE(ctx) == 0x0B);
+        }
+
+        data_entry.length = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+        data_entry.data = bh_pointer_add(ctx->binary.data, ctx->offset);
+        ctx->offset += data_entry.length;
+
+        ctx->module->data_entries[i] = data_entry;
+    }
+}
+
+static void parse_data_count_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int data_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    ctx->module->data_count_present = true;
+    ctx->module->data_count = data_count;
+}
+
+
+
+//
+// Instruction building
+//
+
+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_add_imm(&ctx->builder, OVM_TYPE_I32, &dataidx);
+            ovm_code_builder_add_call(&ctx->builder, ctx->module->memory_init_idx, 4, false);
+            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_fe_instruction(build_context *ctx) {
+    int instr_num = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+    switch (instr_num) {
+
+#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_atomic_load(&ctx->builder, type, offset); \
+            break; \
+        }
+
+        LOAD_CASE(0x10, OVM_TYPE_I32)
+        LOAD_CASE(0x11, OVM_TYPE_I64)
+        LOAD_CASE(0x12, OVM_TYPE_I8)
+        LOAD_CASE(0x13, OVM_TYPE_I16)
+        LOAD_CASE(0x14, OVM_TYPE_I8)
+        LOAD_CASE(0x15, OVM_TYPE_I16)
+        LOAD_CASE(0x16, OVM_TYPE_I32)
+
+#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_atomic_store(&ctx->builder, type, offset); \
+            break; \
+        }
+
+        STORE_CASE(0x17, OVM_TYPE_I32)
+        STORE_CASE(0x18, OVM_TYPE_I64)
+        STORE_CASE(0x19, OVM_TYPE_I8)
+        STORE_CASE(0x1A, OVM_TYPE_I16)
+        STORE_CASE(0x1B, OVM_TYPE_I8)
+        STORE_CASE(0x1C, OVM_TYPE_I16)
+        STORE_CASE(0x1D, OVM_TYPE_I32)
+
+#undef STORE_CASE
+
+#define CMPXCHG_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_cmpxchg(&ctx->builder, type, offset); \
+            break; \
+        }
+
+        CMPXCHG_CASE(0x48, OVM_TYPE_I32)
+        CMPXCHG_CASE(0x49, OVM_TYPE_I64)
+        CMPXCHG_CASE(0x4A, OVM_TYPE_I8)
+        CMPXCHG_CASE(0x4B, OVM_TYPE_I16)
+        CMPXCHG_CASE(0x4C, OVM_TYPE_I8)
+        CMPXCHG_CASE(0x4D, OVM_TYPE_I16)
+        CMPXCHG_CASE(0x4E, OVM_TYPE_I32)
+
+#undef CMPXCHG_CASE
+
+        default: assert(("UNHANDLED ATOMIC INSTRUCTION... SORRY :/", 0));
+    }
+}
+
+static void parse_instruction(build_context *ctx) {
+    debug_info_builder_step(&ctx->debug_builder);
+
+    unsigned char instr_byte = CONSUME_BYTE(ctx);
+    switch (instr_byte) {
+        case 0x00:
+            break;
+
+        case 0x01:
+            ovm_code_builder_add_nop(&ctx->builder);
+            break;
+
+        case 0x02: {
+            // Currently, only "void" block types are valid.
+            assert(CONSUME_BYTE(ctx) == 0x40);
+            ovm_code_builder_push_label_target(&ctx->builder, label_kind_block);
+            break;
+        }
+
+        case 0x03: {
+            // Currently, only "void" block types are valid.
+            assert(CONSUME_BYTE(ctx) == 0x40);
+            ovm_code_builder_push_label_target(&ctx->builder, label_kind_loop);
+            break;
+        }
+
+        case 0x04: {
+            // Currently, only "void" block types are valid.
+            assert(CONSUME_BYTE(ctx) == 0x40);
+            int if_target = ovm_code_builder_push_label_target(&ctx->builder, label_kind_if);
+
+            //
+            // 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, false, true);
+            break;
+        }
+
+        case 0x05: {
+            label_target_t if_target = ovm_code_builder_wasm_target_idx(&ctx->builder, 0);
+            ovm_code_builder_add_branch(&ctx->builder, if_target.idx);
+            ovm_code_builder_patch_else(&ctx->builder, if_target);
+            break;
+        }
+
+        case 0x0B: {
+            ovm_code_builder_pop_label_target(&ctx->builder);
+            break;
+        }
+
+        case 0x0C: {
+            int label_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+            label_target_t target = ovm_code_builder_wasm_target_idx(&ctx->builder, 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);
+
+            label_target_t target = ovm_code_builder_wasm_target_idx(&ctx->builder, label_idx);
+            ovm_code_builder_add_cond_branch(&ctx->builder, target.idx, true, false);
+            break;
+        }
+
+        case 0x0E: {
+            // Branch tables are the most complicated thing ever :/
+
+            int entry_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            int *entries = bh_alloc_array(bh_heap_allocator(), int, entry_count);
+
+            fori (i, 0, entry_count) {
+                int label_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+                label_target_t target = ovm_code_builder_wasm_target_idx(&ctx->builder, label_idx);
+                entries[i] = target.idx;
+            }
+
+            int default_entry_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            label_target_t target = ovm_code_builder_wasm_target_idx(&ctx->builder, default_entry_idx);
+            int default_entry = target.idx;
+
+            ovm_code_builder_add_branch_table(&ctx->builder, entry_count, entries, default_entry);
+            break;
+        }
+
+        case 0x0F: {
+            ovm_code_builder_add_return(&ctx->builder);
+            break;
+        }
+
+        case 0x10: {
+            int func_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+            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;
+        }
+
+        case 0x11: {
+            int type_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            int table_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            assert(table_idx == 0);
+
+            wasm_functype_t *functype = ctx->module->type_section.data[type_idx];
+            int param_count = functype->type.func.params.size;
+            ovm_code_builder_add_indirect_call(&ctx->builder, param_count, functype->type.func.results.size != 0);
+            break;
+        }
+
+        case 0x1A: {
+            ovm_code_builder_drop_value(&ctx->builder);
+            break;
+        }
+
+        case 0x1B: assert(0);
+
+        case 0x20: {
+            int local_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            ovm_code_builder_add_local_get(&ctx->builder, local_idx);
+            break;
+        }
+
+        case 0x21: {
+            int local_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            ovm_code_builder_add_local_set(&ctx->builder, local_idx);
+            break;
+        }
+
+        case 0x22: {
+            int local_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            ovm_code_builder_add_local_tee(&ctx->builder, local_idx);
+            break;
+        }
+
+        case 0x23: {
+            int global_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            ovm_code_builder_add_register_get(&ctx->builder, global_idx);
+            break;
+        }
+
+        case 0x24: {
+            int global_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            ovm_code_builder_add_register_set(&ctx->builder, global_idx);
+            break;
+        }
+
+#define LOAD_CASE(num, type, convert, convert_op, convert_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); \
+            if (convert) ovm_code_builder_add_unop(&ctx->builder, OVM_TYPED_INSTR(convert_op, convert_type)); \
+            break; \
+        }
+
+        LOAD_CASE(0x28, OVM_TYPE_I32, false, 0, 0)
+        LOAD_CASE(0x29, OVM_TYPE_I64, false, 0, 0)
+        LOAD_CASE(0x2A, OVM_TYPE_F32, false, 0, 0)
+        LOAD_CASE(0x2B, OVM_TYPE_F64, false, 0, 0)
+
+        LOAD_CASE(0x2C, OVM_TYPE_I8,  true, OVMI_CVT_I8_S,  OVM_TYPE_I32)
+        LOAD_CASE(0x2D, OVM_TYPE_I8,  true, OVMI_CVT_I8,    OVM_TYPE_I32)
+        LOAD_CASE(0x2E, OVM_TYPE_I16, true, OVMI_CVT_I16_S, OVM_TYPE_I32)
+        LOAD_CASE(0x2F, OVM_TYPE_I16, true, OVMI_CVT_I16,   OVM_TYPE_I32)
+        LOAD_CASE(0x30, OVM_TYPE_I8,  true, OVMI_CVT_I8_S,  OVM_TYPE_I64)
+        LOAD_CASE(0x31, OVM_TYPE_I8,  true, OVMI_CVT_I8,    OVM_TYPE_I64)
+        LOAD_CASE(0x32, OVM_TYPE_I16, true, OVMI_CVT_I16_S, OVM_TYPE_I64)
+        LOAD_CASE(0x33, OVM_TYPE_I16, true, OVMI_CVT_I16,   OVM_TYPE_I64)
+        LOAD_CASE(0x34, OVM_TYPE_I32, true, OVMI_CVT_I32_S, OVM_TYPE_I64)
+        LOAD_CASE(0x35, OVM_TYPE_I32, true, OVMI_CVT_I32,   OVM_TYPE_I64)
+
+#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; \
+        }
+
+        STORE_CASE(0x36, OVM_TYPE_I32);
+        STORE_CASE(0x37, OVM_TYPE_I64);
+        STORE_CASE(0x38, OVM_TYPE_F32);
+        STORE_CASE(0x39, OVM_TYPE_F64);
+
+        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);
+
+#undef STORE_CASE
+
+        case 0x3F: {
+            assert(CONSUME_BYTE(ctx) == 0x00);
+
+            int memory_size = 65535;
+            ovm_code_builder_add_imm(&ctx->builder, OVM_TYPE_I32, &memory_size);
+            break;
+        }
+
+        case 0x40: {
+            assert(CONSUME_BYTE(ctx) == 0x00);
+            ovm_code_builder_drop_value(&ctx->builder);
+
+            int value = -1;
+            ovm_code_builder_add_imm(&ctx->builder, OVM_TYPE_I32, &value);
+            break;
+        }
+
+        case 0x41: {
+            long long value = leb128_to_int(ctx->binary.data, &ctx->offset);
+
+            // NOTE: This assumes a little-endian CPU as the address is assumes
+            // to be the least significant byte.
+            ovm_code_builder_add_imm(&ctx->builder, OVM_TYPE_I32, &value);
+            break;
+        }
+
+        case 0x42: {
+            long long value = leb128_to_int(ctx->binary.data, &ctx->offset);
+            ovm_code_builder_add_imm(&ctx->builder, OVM_TYPE_I64, &value);
+            break;
+        }
+
+        case 0x43: {
+            float value = * (f32 *) &ctx->binary.data[ctx->offset];
+            ctx->offset += 4;
+            ovm_code_builder_add_imm(&ctx->builder, OVM_TYPE_F32, &value);
+            break;
+        }
+
+        case 0x44: {
+            double value = * (f64 *) &ctx->binary.data[ctx->offset];
+            ctx->offset += 8;
+            ovm_code_builder_add_imm(&ctx->builder, OVM_TYPE_F64, &value);
+            break;
+        }
+
+        case 0x45: {
+            unsigned 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;
+        case 0x49: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_I32)); break;
+        case 0x4A: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_I32)); break;
+        case 0x4B: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_I32)); break;
+        case 0x4C: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_I32)); break;
+        case 0x4D: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_I32)); break;
+        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: {
+            unsigned 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;
+        case 0x54: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_I64)); break;
+        case 0x55: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GT_S, OVM_TYPE_I64)); break;
+        case 0x56: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_I64)); break;
+        case 0x57: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LE_S, OVM_TYPE_I64)); break;
+        case 0x58: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_I64)); break;
+        case 0x59: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GE_S, OVM_TYPE_I64)); break;
+        case 0x5A: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_I64)); break;
+
+        case 0x5B: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_F32)); break;
+        case 0x5C: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_F32)); break;
+        case 0x5D: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_F32)); break;
+        case 0x5E: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_F32)); break;
+        case 0x5F: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_F32)); break;
+        case 0x60: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_F32)); break;
+
+        case 0x61: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_EQ, OVM_TYPE_F64)); break;
+        case 0x62: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_NE, OVM_TYPE_F64)); break;
+        case 0x63: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LT, OVM_TYPE_F64)); break;
+        case 0x64: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GT, OVM_TYPE_F64)); break;
+        case 0x65: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_LE, OVM_TYPE_F64)); break;
+        case 0x66: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_GE, OVM_TYPE_F64)); break;
+
+        case 0x67: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_CLZ, OVM_TYPE_I32)); break;
+        case 0x68: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_CTZ, OVM_TYPE_I32)); break;
+        case 0x69: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_POPCNT, OVM_TYPE_I32)); break;
+        case 0x6A: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I32)); break;
+        case 0x6B: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I32)); break;
+        case 0x6C: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I32)); break;
+        case 0x6D: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_I32)); break;
+        case 0x6E: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I32)); break;
+        case 0x6F: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_REM_S, OVM_TYPE_I32)); break;
+        case 0x70: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I32)); break;
+        case 0x71: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I32)); break;
+        case 0x72: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I32)); break;
+        case 0x73: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I32)); break;
+        case 0x74: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I32)); break;
+        case 0x75: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I32)); break;
+        case 0x76: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I32)); break;
+        case 0x77: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_ROTL, OVM_TYPE_I32)); break;
+        case 0x78: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_ROTR, OVM_TYPE_I32)); break;
+
+        case 0x79: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CLZ, OVM_TYPE_I64)); break;
+        case 0x7A: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CTZ, OVM_TYPE_I64)); break;
+        case 0x7B: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_POPCNT, OVM_TYPE_I64)); break;
+        case 0x7C: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I64)); break;
+        case 0x7D: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I64)); break;
+        case 0x7E: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I64)); break;
+        case 0x7F: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_DIV_S, OVM_TYPE_I64)); break;
+        case 0x80: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I64)); break;
+        case 0x81: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_REM_S, OVM_TYPE_I64)); break;
+        case 0x82: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I64)); break;
+        case 0x83: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I64)); break;
+        case 0x84: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I64)); break;
+        case 0x85: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I64)); break;
+        case 0x86: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I64)); break;
+        case 0x87: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I64)); break;
+        case 0x88: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I64)); break;
+        case 0x89: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_ROTL, OVM_TYPE_I64)); break;
+        case 0x8A: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_ROTR, OVM_TYPE_I64)); break;
+
+        case 0x8B: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_ABS, OVM_TYPE_F32)); break;
+        case 0x8C: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_NEG, OVM_TYPE_F32)); break;
+        case 0x8D: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CEIL, OVM_TYPE_F32)); break;
+        case 0x8E: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_FLOOR, OVM_TYPE_F32)); break;
+        case 0x8F: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_TRUNC, OVM_TYPE_F32)); break;
+        case 0x90: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_NEAREST, OVM_TYPE_F32)); break;
+        case 0x91: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_SQRT, OVM_TYPE_F32)); break;
+        case 0x92: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F32)); break;
+        case 0x93: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_F32)); break;
+        case 0x94: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_F32)); break;
+        case 0x95: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_F32)); break;
+        case 0x96: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_MIN, OVM_TYPE_F32)); break;
+        case 0x97: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_MAX, OVM_TYPE_F32)); break;
+        case 0x98: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_COPYSIGN, OVM_TYPE_F32)); break;
+
+        case 0x99: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_ABS, OVM_TYPE_F64)); break;
+        case 0x9A: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_NEG, OVM_TYPE_F64)); break;
+        case 0x9B: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CEIL, OVM_TYPE_F64)); break;
+        case 0x9C: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_FLOOR, OVM_TYPE_F64)); break;
+        case 0x9D: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_TRUNC, OVM_TYPE_F64)); break;
+        case 0x9E: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_NEAREST, OVM_TYPE_F64)); break;
+        case 0x9F: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_SQRT, OVM_TYPE_F64)); break;
+        case 0xA0: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F64)); break;
+        case 0xA1: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_F64)); break;
+        case 0xA2: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_F64)); break;
+        case 0xA3: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_F64)); break;
+        case 0xA4: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_MIN, OVM_TYPE_F64)); break;
+        case 0xA5: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_MAX, OVM_TYPE_F64)); break;
+        case 0xA6: ovm_code_builder_add_binop(&ctx->builder, OVM_TYPED_INSTR(OVMI_COPYSIGN, OVM_TYPE_F64)); break;
+
+        case 0xA7: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I64,   OVM_TYPE_I32)); break;
+        case 0xA8: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F32_S, OVM_TYPE_I32)); break;
+        case 0xA9: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F32,   OVM_TYPE_I32)); break;
+        case 0xAA: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F64_S, OVM_TYPE_I32)); break;
+        case 0xAB: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F64,   OVM_TYPE_I32)); break;
+        case 0xAC: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I32_S, OVM_TYPE_I64)); break;
+        case 0xAD: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I32,   OVM_TYPE_I64)); break;
+        case 0xAE: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F32_S, OVM_TYPE_I64)); break;
+        case 0xAF: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F32,   OVM_TYPE_I64)); break;
+        case 0xB0: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F64_S, OVM_TYPE_I64)); break;
+        case 0xB1: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F64,   OVM_TYPE_I64)); break;
+        case 0xB2: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I32_S, OVM_TYPE_F32)); break;
+        case 0xB3: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I32,   OVM_TYPE_F32)); break;
+        case 0xB4: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_F32)); break;
+        case 0xB5: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I64,   OVM_TYPE_F32)); break;
+        case 0xB6: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F64,   OVM_TYPE_F32)); break;
+        case 0xB7: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I32_S, OVM_TYPE_F64)); break;
+        case 0xB8: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I32,   OVM_TYPE_F64)); break;
+        case 0xB9: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_F64)); break;
+        case 0xBA: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I64,   OVM_TYPE_F64)); break;
+        case 0xBB: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_F32,   OVM_TYPE_F64)); break;
+        case 0xBC: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_TRANSMUTE_F32, OVM_TYPE_I32)); break;
+        case 0xBD: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_TRANSMUTE_F64, OVM_TYPE_I64)); break;
+        case 0xBE: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_TRANSMUTE_I32, OVM_TYPE_F32)); break;
+        case 0xBF: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_TRANSMUTE_I64, OVM_TYPE_F64)); break;
+        case 0xC0: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I8_S, OVM_TYPE_I32)); break;
+        case 0xC1: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I16_S, OVM_TYPE_I32)); break;
+        case 0xC2: ovm_code_builder_add_unop (&ctx->builder, OVM_TYPED_INSTR(OVMI_CVT_I8_S, OVM_TYPE_I64)); break;
+        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;
+        case 0xFE: parse_fe_instruction(ctx); break;
+
+        default: assert(("UNHANDLED INSTRUCTION", 0));
+    }
+}
+
+static void parse_expression(build_context *ctx) {
+    while (bh_arr_length(ctx->builder.label_stack) > 0) {
+        parse_instruction(ctx);
+    }
+}
+
+static void parse_code_section(build_context *ctx) {
+    unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    unsigned int code_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+    assert(ctx->module->functypes.size == code_count);
+
+    ctx->module->memory_init_external_idx = ctx->next_external_func_idx++;
+    
+    // HACK HACK HACK THIS IS SUCH A BAD WAY OF DOING THIS
+    ctx->module->memory_init_idx = bh_arr_length(ctx->program->funcs) + code_count;
+
+    fori (i, 0, (int) code_count) {
+        unsigned int code_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+        unsigned int local_sections_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+
+        unsigned int total_locals = 0;
+        fori (j, 0, (int) local_sections_count) {
+            unsigned int local_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+            wasm_valkind_t valtype = parse_valtype(ctx);
+
+            total_locals += local_count;
+        }
+
+        // Set up a lot of stuff...
+
+        i32 func_idx = bh_arr_length(ctx->program->funcs);
+        i32 param_count = ctx->module->functypes.data[i]->type.func.params.size;
+
+        debug_info_builder_begin_func(&ctx->debug_builder, func_idx);
+
+        ctx->builder = ovm_code_builder_new(ctx->program, &ctx->debug_builder, param_count, total_locals);
+        ctx->builder.func_table_arr_idx = ctx->func_table_arr_idx;
+
+        ovm_code_builder_push_label_target(&ctx->builder, label_kind_func);
+        parse_expression(ctx);
+        ovm_code_builder_add_return(&ctx->builder);
+
+        char *func_name = bh_aprintf(bh_heap_allocator(), "wasm_loaded_%d", func_idx);
+        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);
+        debug_info_builder_end_func(&ctx->debug_builder);
+    }
+
+    ovm_program_register_external_func(ctx->program, "__internal_wasm_memory_init", 4, ctx->module->memory_init_external_idx);
+}
+
+
+static void parse_section(build_context *ctx) {
+    char section_number = CONSUME_BYTE(ctx);
+
+    switch (section_number) {
+        case WASM_CUSTOM_SECTION: parse_custom_section(ctx);     break;
+        case WASM_TYPE_SECTION:   parse_type_section(ctx);       break;
+        case WASM_IMPORT_SECTION: parse_import_section(ctx);     break;
+        case WASM_FUNC_SECTION:   parse_func_section(ctx);       break;
+        case WASM_TABLE_SECTION:  parse_table_section(ctx);      break;
+        case WASM_MEMORY_SECTION: parse_memory_section(ctx);     break;
+        case WASM_GLOBAL_SECTION: parse_global_section(ctx);     break;
+        case WASM_EXPORT_SECTION: parse_export_section(ctx);     break;
+        case WASM_START_SECTION:  parse_start_section(ctx);      break;
+        case WASM_ELEM_SECTION:   parse_elem_section(ctx);       break;
+        case WASM_CODE_SECTION:   parse_code_section(ctx);       break;
+        case WASM_DATA_SECTION:   parse_data_section(ctx);       break;
+        case WASM_DATAC_SECTION:  parse_data_count_section(ctx); break;
+        default: assert(("bad section number", 0)); break;
+    }
+}
diff --git a/interpreter/src/wasm/ref.c b/interpreter/src/wasm/ref.c
new file mode 100644 (file)
index 0000000..0ade447
--- /dev/null
@@ -0,0 +1,2 @@
+
+// TODO
diff --git a/interpreter/src/wasm/store.c b/interpreter/src/wasm/store.c
new file mode 100644 (file)
index 0000000..5c06791
--- /dev/null
@@ -0,0 +1,14 @@
+
+#include "ovm_wasm.h"
+#include "vm.h"
+
+wasm_store_t *wasm_store_new(wasm_engine_t *engine) {
+    wasm_store_t *store = bh_alloc(engine->store->arena_allocator, sizeof(wasm_store_t));
+    store->engine = engine;
+    store->instance = NULL;
+    return store;
+}
+
+void wasm_store_delete(wasm_store_t *store) {
+}
+
diff --git a/interpreter/src/wasm/table.c b/interpreter/src/wasm/table.c
new file mode 100644 (file)
index 0000000..b878eba
--- /dev/null
@@ -0,0 +1,16 @@
+
+#include "ovm_wasm.h"
+#include "vm.h"
+
+wasm_table_t *wasm_table_new(wasm_store_t *store, const wasm_tabletype_t *type, wasm_ref_t *init) {
+    wasm_table_t *table = bh_alloc(store->engine->store->arena_allocator, sizeof(*table));
+    table->inner.type = wasm_tabletype_as_externtype_const(type);
+    table->inner.table.type = type;
+    
+    return table;
+}
+
+wasm_tabletype_t *wasm_table_type(const wasm_table_t *table) {
+    return (wasm_tabletype_t *) table->inner.table.type;
+}
+
diff --git a/interpreter/src/wasm/trap.c b/interpreter/src/wasm/trap.c
new file mode 100644 (file)
index 0000000..cb4ba74
--- /dev/null
@@ -0,0 +1,42 @@
+
+#include "ovm_wasm.h"
+#include "vm.h"
+
+wasm_trap_t *wasm_trap_new(wasm_store_t *store, const wasm_message_t *msg) {
+    wasm_trap_t *trap = bh_alloc(store->engine->store->arena_allocator, sizeof(*trap));
+    trap->store = store;
+    trap->msg = *msg;
+
+    //
+    // Generate frames
+    bh_arr(ovm_stack_frame_t) ovm_frames = store->instance->state->stack_frames;
+    int frame_count = bh_arr_length(ovm_frames);
+
+    wasm_frame_vec_new_uninitialized(&trap->frames, frame_count);
+
+    fori (i, 0, frame_count) {
+        ovm_stack_frame_t *ovm_frame = &ovm_frames[frame_count - 1 - i];
+        wasm_frame_t *frame = bh_alloc(store->engine->store->arena_allocator, sizeof(*frame));
+        frame->instance = store->instance;
+        frame->func_idx = ovm_frame->func->id;
+        frame->func_offset = 0;
+        frame->module_offset = 0;
+
+        trap->frames.data[i] = frame;
+    }
+
+    return trap;
+}
+
+void wasm_trap_message(const wasm_trap_t *trap, wasm_message_t *out) {
+    *out = trap->msg;
+}
+
+wasm_frame_t *wasm_trap_origin(const wasm_trap_t *trap) {
+    return trap->frames.data[0];
+}
+
+void wasm_trap_trace(const wasm_trap_t *trap, wasm_frame_vec_t *frames) {
+    frames->size = 0;
+    frames->data = NULL;
+}
diff --git a/interpreter/src/wasm/type.c b/interpreter/src/wasm/type.c
new file mode 100644 (file)
index 0000000..8bc6298
--- /dev/null
@@ -0,0 +1,239 @@
+
+#include "ovm_wasm.h"
+#include "vm.h"
+
+//
+// wasm_byte_t
+//
+
+WASM_DECLARE_VEC_IMPL(byte, )
+
+//
+// wasm_valtype_t
+//
+
+static wasm_valtype_t
+    valtype_i32     = { WASM_I32 },
+    valtype_i64     = { WASM_I64 },
+    valtype_f32     = { WASM_F32 },
+    valtype_f64     = { WASM_F64 },
+    valtype_anyref  = { WASM_ANYREF },
+    valtype_funcref = { WASM_FUNCREF };
+
+
+wasm_valtype_t *wasm_valtype_new(wasm_valkind_t kind) {
+    switch (kind) {
+        case WASM_I32:     return &valtype_i32;
+        case WASM_I64:     return &valtype_i64;
+        case WASM_F32:     return &valtype_f32;
+        case WASM_F64:     return &valtype_f64;
+        case WASM_ANYREF:  return &valtype_anyref;
+        case WASM_FUNCREF: return &valtype_funcref;
+        default: assert(0);
+    }
+}
+
+void wasm_valtype_delete(wasm_valtype_t *type) {}
+
+wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t *type) {
+    assert(type);
+    return type->kind;
+}
+
+WASM_DECLARE_VEC_IMPL(valtype, *)
+
+
+// wasm_functype_t
+
+wasm_functype_t *wasm_functype_new(wasm_valtype_vec_t *params, wasm_valtype_vec_t *results) {
+    wasm_functype_t *functype = malloc(sizeof(*functype));
+    functype->type.kind = WASM_EXTERN_FUNC;
+    functype->type.func.params = *params;
+    functype->type.func.results = *results;
+
+    return functype;
+}
+
+void wasm_functype_delete(wasm_functype_t *functype) {
+    if (functype) free(functype);
+}
+
+const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t *functype) {
+    return &functype->type.func.params;
+}
+
+const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t *functype) {
+    return &functype->type.func.results;
+}
+
+bool wasm_functype_equals(wasm_functype_t *a, wasm_functype_t *b) {
+    if (a->type.func.params.size != b->type.func.params.size) return false;
+    if (a->type.func.results.size != b->type.func.results.size) return false;
+
+    fori (i, 0, (int) a->type.func.params.size) {
+        if (a->type.func.params.data[i]->kind != b->type.func.params.data[i]->kind) return false;
+    }
+
+    fori (i, 0, (int) a->type.func.results.size) {
+        if (a->type.func.results.data[i]->kind != b->type.func.results.data[i]->kind) return false;
+    }
+
+    return true;
+}
+
+WASM_DECLARE_VEC_IMPL(functype, *)
+
+
+// wasm_globaltype_t
+
+wasm_globaltype_t *wasm_globaltype_new(wasm_valtype_t *valtype, wasm_mutability_t mut) {
+    wasm_globaltype_t *globaltype = malloc(sizeof(*globaltype));
+    globaltype->type.kind = WASM_EXTERN_GLOBAL;
+    globaltype->type.global.content = valtype;
+    globaltype->type.global.mutability = mut;
+
+    return globaltype;
+}
+
+void wasm_globaltype_delete(wasm_globaltype_t *globaltype) {
+    if (globaltype) free(globaltype);
+}
+
+const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t *globaltype) {
+    return globaltype->type.global.content;
+}
+
+wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t *globaltype) {
+    return globaltype->type.global.mutability;
+}
+
+WASM_DECLARE_VEC_IMPL(globaltype, *)
+
+
+// wasm_tabletype_t
+
+wasm_tabletype_t *wasm_tabletype_new(wasm_valtype_t *valtype, const wasm_limits_t *limits) {
+    wasm_tabletype_t *tabletype = malloc(sizeof(*tabletype));
+    tabletype->type.kind = WASM_EXTERN_TABLE;
+    tabletype->type.table.element = valtype;
+    tabletype->type.table.limits = *limits;
+
+    return tabletype;
+}
+
+void wasm_tabletype_delete(wasm_tabletype_t *tabletype) {
+    if (tabletype) free(tabletype);
+}
+
+const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t *tabletype) {
+    return tabletype->type.table.element;
+}
+
+const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t *tabletype) {
+    return &tabletype->type.table.limits;
+}
+
+WASM_DECLARE_VEC_IMPL(tabletype, *)
+
+// wasm_memorytype_t
+
+wasm_memorytype_t *wasm_memorytype_new(const wasm_limits_t *limits) {
+    wasm_memorytype_t *memorytype = malloc(sizeof(*memorytype));
+    memorytype->type.kind = WASM_EXTERN_MEMORY;
+    memorytype->type.memory.limits = *limits;
+
+    return memorytype;
+}
+
+void wasm_memorytype_delete(wasm_memorytype_t *memorytype) {
+    if (memorytype) free(memorytype);
+}
+
+const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t *memorytype) {
+    return &memorytype->type.memory.limits;
+}
+
+WASM_DECLARE_VEC_IMPL(memorytype, *)
+
+// wasm_externtype_t
+
+const wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t *externtype) {
+    return externtype->kind;
+}
+
+WASM_DECLARE_VEC_IMPL(externtype, *)
+
+wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t* ext)     { return (wasm_externtype_t *) ext; }
+wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t* ext) { return (wasm_externtype_t *) ext; }
+wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t* ext)   { return (wasm_externtype_t *) ext; }
+wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t* ext) { return (wasm_externtype_t *) ext; }
+
+wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t* ext)     { return ext->kind == WASM_EXTERN_FUNC ? (wasm_functype_t *) ext : NULL; }
+wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t* ext) { return ext->kind == WASM_EXTERN_GLOBAL ? (wasm_globaltype_t *) ext : NULL; }
+wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t* ext)   { return ext->kind == WASM_EXTERN_TABLE ? (wasm_tabletype_t *) ext : NULL; }
+wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t* ext) { return ext->kind == WASM_EXTERN_MEMORY ? (wasm_memorytype_t *) ext : NULL; }
+
+const wasm_externtype_t* wasm_functype_as_externtype_const(const wasm_functype_t* ext)     { return (const wasm_externtype_t *) ext; }
+const wasm_externtype_t* wasm_globaltype_as_externtype_const(const wasm_globaltype_t* ext) { return (const wasm_externtype_t *) ext; }
+const wasm_externtype_t* wasm_tabletype_as_externtype_const(const wasm_tabletype_t* ext)   { return (const wasm_externtype_t *) ext; }
+const wasm_externtype_t* wasm_memorytype_as_externtype_const(const wasm_memorytype_t* ext) { return (const wasm_externtype_t *) ext; }
+
+const wasm_functype_t* wasm_externtype_as_functype_const(const wasm_externtype_t* ext)     { return ext->kind == WASM_EXTERN_FUNC ? (const wasm_functype_t *) ext : NULL; }
+const wasm_globaltype_t* wasm_externtype_as_globaltype_const(const wasm_externtype_t* ext) { return ext->kind == WASM_EXTERN_GLOBAL ? (const wasm_globaltype_t *) ext : NULL; }
+const wasm_tabletype_t* wasm_externtype_as_tabletype_const(const wasm_externtype_t* ext)   { return ext->kind == WASM_EXTERN_TABLE ? (const wasm_tabletype_t *) ext : NULL; }
+const wasm_memorytype_t* wasm_externtype_as_memorytype_const(const wasm_externtype_t* ext) { return ext->kind == WASM_EXTERN_MEMORY ? (const wasm_memorytype_t *) ext : NULL; }
+
+
+
+// wasm_importtype_t
+
+wasm_importtype_t *wasm_importtype_new(wasm_name_t *module, wasm_name_t* name, wasm_externtype_t *ext) {
+    wasm_importtype_t *importtype = malloc(sizeof(*importtype));
+    importtype->module_name = *module;
+    importtype->import_name = *name;
+    importtype->type = ext;
+
+    return importtype;
+}
+
+void wasm_importtype_delete(wasm_importtype_t *importtype) {
+    if (importtype) free(importtype);
+}
+
+const wasm_name_t* wasm_importtype_module(const wasm_importtype_t* importtype) {
+    return &importtype->module_name;
+}
+
+const wasm_name_t* wasm_importtype_name(const wasm_importtype_t* importtype) {
+    return &importtype->import_name;
+}
+
+const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t* importtype) {
+    return importtype->type;
+}
+
+WASM_DECLARE_VEC_IMPL(importtype, *)
+
+// wasm_exporttype_t
+
+wasm_exporttype_t *wasm_exporttype_new(wasm_name_t* name, wasm_externtype_t *ext) {
+    wasm_exporttype_t *exporttype = malloc(sizeof(*exporttype));
+    exporttype->name = *name;
+    exporttype->type = ext;
+
+    return exporttype;
+}
+
+void wasm_exporttype_delete(wasm_exporttype_t *exporttype) {
+    if (exporttype) free(exporttype);
+}
+
+const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t* exporttype) {
+    return &exporttype->name;
+}
+
+const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t* exporttype) {
+    return exporttype->type;
+}
+
+WASM_DECLARE_VEC_IMPL(exporttype, *)
diff --git a/interpreter/src/wasm/value.c b/interpreter/src/wasm/value.c
new file mode 100644 (file)
index 0000000..a533c94
--- /dev/null
@@ -0,0 +1,24 @@
+
+#include "ovm_wasm.h"
+
+void wasm_val_delete(wasm_val_t* v) {
+    // Apparently this is suppose to do nothing...
+}
+
+void wasm_val_copy(wasm_val_t* out, const wasm_val_t* in) {
+    out->kind = in->kind;
+    switch (out->kind) {
+        case WASM_I32: out->of.i32 = in->of.i32; break;
+        case WASM_I64: out->of.i64 = in->of.i64; break;
+        case WASM_F32: out->of.f32 = in->of.i32; break;
+        case WASM_F64: out->of.f64 = in->of.f64; break;
+        case WASM_ANYREF:
+        case WASM_FUNCREF:
+            out->of.ref = in->of.ref;
+            break;
+    }
+}
+
+WASM_DECLARE_VEC_IMPL(val, )
+
+
diff --git a/interpreter/src/wasm_cli_test.c b/interpreter/src/wasm_cli_test.c
new file mode 100644 (file)
index 0000000..8903f27
--- /dev/null
@@ -0,0 +1,51 @@
+#define BH_DEFINE
+#include "bh.h"
+#include "wasm.h"
+
+#include "ovm_wasm.h"
+#include "vm.h"
+
+
+int main(int argc, char *argv[]) {
+    wasm_config_t *config = wasm_config_new();
+
+    // TODO: Add this to public header
+    void wasm_config_enable_debug(wasm_config_t *, bool);
+    wasm_config_enable_debug(config, true);
+
+    wasm_engine_t *engine = wasm_engine_new_with_config(config);
+
+    wasm_store_t *store = wasm_store_new(engine);
+
+    wasm_byte_vec_t wasm_bytes;
+    {
+        bh_file_contents contents = bh_file_read_contents(bh_heap_allocator(), argv[1]);
+
+        wasm_bytes.size = contents.length;
+        wasm_bytes.data = contents.data;
+    }
+
+    wasm_module_t *module = wasm_module_new(store, &wasm_bytes);
+    assert(module);
+
+    wasm_importtype_vec_t imports;
+    wasm_module_imports(module, &imports);
+
+    fori (i, 0, (int) imports.size) {
+        const wasm_name_t *module_name = wasm_importtype_module(imports.data[i]);
+        const wasm_name_t *import_name = wasm_importtype_name(imports.data[i]);
+        bh_printf("imports: %b.%b\n", module_name->data, module_name->size, import_name->data, import_name->size);
+    }
+
+
+    wasm_exporttype_vec_t exports;
+    wasm_module_exports(module, &exports);
+
+    fori (i, 0, (int) exports.size) {
+        const wasm_name_t *export_name = wasm_exporttype_name(exports.data[i]);
+        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, bh_arr_length(module->program->code));
+}
+
index 44d45f8403886cf20550c5908dd525701a8ae3ac..1b1ad45fec5b35837bf50f33f7814a0c8954d69e 100644 (file)
@@ -15,8 +15,8 @@ CC='gcc'
 # The architecture of your system. If your not sure, leave this alone.
 ARCH="$(uname -m)"
 
-RUNTIME_LIBRARY="ovmwasm"
-RUNTIME_LIBRARY="wasmer"
+RUNTIME_LIBRARY="ovmwasm"
+RUNTIME_LIBRARY="wasmer"
 
 # Where the Wasmer library files can be found.
 # They are bundled with the project, but if a different version is available, these can be changed.
index 0049b26985823db074b4bd9c95ce64425143165d..96d6e63964de502436292e6db1d3dc56a3e2e002 100644 (file)
@@ -1,6 +1,17 @@
 #ifndef BH_H
 #define BH_H
 
+#ifdef BH_STATIC
+    #define BH_DEF static
+#else
+    #ifdef BH_INTERNAL
+        #define BH_DEF __attribute__((visibility("hidden")))
+    #else
+        #define BH_DEF
+    #endif
+#endif
+
+
 // NOTE: For lseek64
 #define _LARGEFILE64_SOURCE
 
@@ -21,6 +32,7 @@
     #include <fcntl.h>
     #include <unistd.h>
     #include <dirent.h>
+    #include <pthread.h>
 #endif
 
 #include <stdlib.h>
@@ -168,6 +180,7 @@ u8* float_to_ieee754(f32 f, b32 reverse);
 u8* double_to_ieee754(f64 f, b32 reverse);
 
 u64 uleb128_to_uint(u8* bytes, i32 *byte_walker);
+BH_DEF i64 leb128_to_int(u8* bytes, i32 *byte_count);
 
 
 
@@ -261,11 +274,31 @@ typedef struct bh__arena_internal {
     void* data; // Not actually a pointer, just used for the offset
 } bh__arena_internal;
 
-void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size);
-void bh_arena_free(bh_arena* alloc);
-bh_allocator bh_arena_allocator(bh_arena* alloc);
-BH_ALLOCATOR_PROC(bh_arena_allocator_proc);
+BH_DEF void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size);
+BH_DEF void bh_arena_clear(bh_arena* alloc);
+BH_DEF void bh_arena_free(bh_arena* alloc);
+BH_DEF bh_allocator bh_arena_allocator(bh_arena* alloc);
+BH_DEF BH_ALLOCATOR_PROC(bh_arena_allocator_proc);
+
+
+// ATOMIC ARENA ALLOCATOR
+typedef struct bh_atomic_arena {
+    bh_allocator backing;
+    ptr first_arena, current_arena;
+    isize size, arena_size; // in bytes
+
+    pthread_mutex_t mutex;
+} bh_atomic_arena;
+
+typedef struct bh__atomic_arena_internal {
+    ptr next_arena;
+    void* data; // Not actually a pointer, just used for the offset
+} bh__atomic_arena_internal;
 
+BH_DEF void bh_atomic_arena_init(bh_atomic_arena* alloc, bh_allocator backing, isize arena_size);
+BH_DEF void bh_atomic_arena_free(bh_atomic_arena* alloc);
+BH_DEF bh_allocator bh_atomic_arena_allocator(bh_atomic_arena* alloc);
+BH_DEF BH_ALLOCATOR_PROC(bh_atomic_arena_allocator_proc);
 
 
 
@@ -518,16 +551,17 @@ typedef struct bh_buffer {
 #define BH_BUFFER_GROW_FORMULA(x)            ((x) > 0 ? ((x) << 1) : 16)
 #endif
 
-void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 length);
-void bh_buffer_free(bh_buffer* buffer);
-void bh_buffer_clear(bh_buffer* buffer);
-void bh_buffer_grow(bh_buffer* buffer, i32 length);
-void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length);
-void bh_buffer_concat(bh_buffer* buffer, bh_buffer other);
-void bh_buffer_write_byte(bh_buffer* buffer, u8 byte);
-void bh_buffer_write_u32(bh_buffer* buffer, u32 i);
-void bh_buffer_write_u64(bh_buffer* buffer, u64 i);
-void bh_buffer_align(bh_buffer* buffer, u32 alignment);
+BH_DEF void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 length);
+BH_DEF void bh_buffer_free(bh_buffer* buffer);
+BH_DEF void bh_buffer_clear(bh_buffer* buffer);
+BH_DEF void bh_buffer_grow(bh_buffer* buffer, i32 length);
+BH_DEF void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length);
+BH_DEF void bh_buffer_concat(bh_buffer* buffer, bh_buffer other);
+BH_DEF void bh_buffer_write_byte(bh_buffer* buffer, u8 byte);
+BH_DEF void bh_buffer_write_u32(bh_buffer* buffer, u32 i);
+BH_DEF void bh_buffer_write_u64(bh_buffer* buffer, u64 i);
+BH_DEF void bh_buffer_write_string(bh_buffer* buffer, char *str);
+BH_DEF void bh_buffer_align(bh_buffer* buffer, u32 alignment);
 
 
 
@@ -598,6 +632,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--)
@@ -1038,6 +1073,22 @@ void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size) {
     ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL;
 }
 
+BH_DEF void bh_arena_clear(bh_arena* alloc) {
+    bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena;
+    walker = walker->next_arena;
+    if (walker != NULL) {
+        bh__arena_internal *trailer = walker;
+        while (walker != NULL) {
+            walker = walker->next_arena;
+            bh_free(alloc->backing, trailer);
+            trailer = walker;
+        }
+    }
+    
+    alloc->current_arena = alloc->first_arena;
+    alloc->size = sizeof(ptr);
+}
+
 void bh_arena_free(bh_arena* alloc) {
     bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena;
     bh__arena_internal *trailer = walker;
@@ -1106,6 +1157,93 @@ BH_ALLOCATOR_PROC(bh_arena_allocator_proc) {
 }
 
 
+// ATOMIC ARENA ALLOCATOR IMPLEMENTATION
+BH_DEF void bh_atomic_arena_init(bh_atomic_arena* alloc, bh_allocator backing, isize arena_size) {
+    arena_size = bh_max(arena_size, size_of(ptr));
+    ptr data = bh_alloc(backing, arena_size);
+
+    alloc->backing = backing;
+    alloc->arena_size = arena_size;
+    alloc->size = sizeof(ptr);
+    alloc->first_arena = data;
+    alloc->current_arena = data;
+    pthread_mutex_init(&alloc->mutex, NULL);
+
+    ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL;
+}
+
+BH_DEF void bh_atomic_arena_free(bh_atomic_arena* alloc) {
+    bh__atomic_arena_internal *walker = (bh__atomic_arena_internal *) alloc->first_arena;
+    bh__atomic_arena_internal *trailer = walker;
+    while (walker != NULL) {
+        walker = walker->next_arena;
+        bh_free(alloc->backing, trailer);
+        trailer = walker;
+    }
+
+    alloc->first_arena = NULL;
+    alloc->current_arena = NULL;
+    alloc->arena_size = 0;
+    alloc->size = 0;
+    pthread_mutex_destroy(&alloc->mutex);
+}
+
+BH_DEF bh_allocator bh_atomic_arena_allocator(bh_atomic_arena* alloc) {
+    return (bh_allocator) {
+        .proc = bh_atomic_arena_allocator_proc,
+        .data = alloc,
+    };
+}
+
+BH_DEF BH_ALLOCATOR_PROC(bh_atomic_arena_allocator_proc) {
+    bh_atomic_arena* alloc_arena = (bh_atomic_arena*) data;
+    pthread_mutex_lock(&alloc_arena->mutex);
+
+    ptr retval = NULL;
+
+    switch (action) {
+    case bh_allocator_action_alloc: {
+        bh_align(size, alignment);
+        bh_align(alloc_arena->size, alignment);
+
+        if (size > alloc_arena->arena_size - size_of(ptr)) {
+            // Size too large for the arena
+            break;
+        }
+
+        if (alloc_arena->size + size >= alloc_arena->arena_size) {
+            bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size);
+
+            if (new_arena == NULL) {
+                bh_printf_err("Arena Allocator: couldn't allocate new arena");
+                break;
+            }
+
+            new_arena->next_arena = NULL;
+            ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena;
+            alloc_arena->current_arena = new_arena;
+            alloc_arena->size = sizeof(ptr);
+        }
+
+        retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size);
+        alloc_arena->size += size;
+    } break;
+
+    case bh_allocator_action_resize: {
+        // Do nothing since this is a fixed allocator
+    } break;
+
+    case bh_allocator_action_free: {
+        // Do nothing since this allocator isn't made for freeing memory
+    } break;
+    }
+
+    pthread_mutex_unlock(&alloc_arena->mutex);
+    return retval;
+}
+
+
+
 
 
 // SCRATCH ALLOCATOR IMPLEMENTATION
@@ -1285,6 +1423,27 @@ u64 uleb128_to_uint(u8* bytes, i32 *byte_count) {
     return res;
 }
 
+BH_DEF i64 leb128_to_int(u8* bytes, i32 *byte_count) {
+    i64 res = 0;
+    u64 shift = 0;
+    u64 size = 64;
+
+    u8 byte;
+    do {
+        byte = bytes[(*byte_count)++];
+        res |= (byte & 0x7f) << shift;
+        shift += 7;
+    } while ((byte & 0x80) != 0);
+
+    if ((shift < size) && (byte & 0x40) != 0) {
+        i64 zero_shifted = ~ 0x0;
+        zero_shifted = zero_shifted << shift;
+        return res | zero_shifted;
+    }
+    
+    return res;
+}
+
 
 
 //-------------------------------------------------------------------------------------
@@ -2237,6 +2396,11 @@ void bh_buffer_write_u64(bh_buffer* buffer, u64 i) {
     buffer->length += 8;
 }
 
+BH_DEF void bh_buffer_write_string(bh_buffer* buffer, char *str) {
+    u32 len = strlen(str);
+    bh_buffer_append(buffer, (const char *) str, len);
+}
+
 void bh_buffer_align(bh_buffer* buffer, u32 alignment) {
     if (buffer->length % alignment != 0) {
         u32 difference = alignment - (buffer->length % alignment);
index d1d39b4aa7427d33ff1442fc1b21544cb389c190..27198c9133125fe7b68ef47d6c514696ea179de3 100755 (executable)
Binary files a/shared/lib/linux_x86_64/lib/libovmwasm.so and b/shared/lib/linux_x86_64/lib/libovmwasm.so differ