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
./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"
--- /dev/null
+#!/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
+
--- /dev/null
+#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
+
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+
--- /dev/null
+#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
--- /dev/null
+
+#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;
+}
+
--- /dev/null
+
+#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
--- /dev/null
+
+
+#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);
+}
--- /dev/null
+
+#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)");
+}
--- /dev/null
+
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+
+#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, ¶m_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);
+}
--- /dev/null
+#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, ¶m_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, ¶m_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, ¶m_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);
+ }
+ }
+}
--- /dev/null
+#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});
+}
--- /dev/null
+#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"
--- /dev/null
+
+#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;
+}
+
+
--- /dev/null
+
+#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);
+}
+
--- /dev/null
+
+
+#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; }
+
--- /dev/null
+
+#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;
+}
+
--- /dev/null
+
+#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;
+ }
+}
--- /dev/null
+
+#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));
+}
+
--- /dev/null
+
+
+#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;
+}
--- /dev/null
+
+#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;
+}
--- /dev/null
+
+
+#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;
+}
+
+
+
--- /dev/null
+// 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(¶m_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(¶m_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;
+ }
+}
--- /dev/null
+
+#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) {
+}
+
--- /dev/null
+
+#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;
+}
+
--- /dev/null
+
+#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;
+}
--- /dev/null
+
+#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, *)
--- /dev/null
+
+#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, )
+
+
--- /dev/null
+#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));
+}
+
# 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.
#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
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
+ #include <pthread.h>
#endif
#include <stdlib.h>
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);
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);
#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);
#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--)
((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;
}
+// 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
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;
+}
+
//-------------------------------------------------------------------------------------
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);