#include "vm.h"
+
+#include <sys/mman.h>
+
+//
+// Store
+ovm_store_t *ovm_store_new() {
+ ovm_store_t *store = malloc(sizeof(*store));
+
+ store->heap_allocator = bh_heap_allocator();
+
+ bh_arena_init(&store->arena, store->heap_allocator, 1 << 20);
+ store->arena_allocator = bh_arena_allocator(&store->arena);
+
+ return store;
+}
+
+void ovm_store_delete(ovm_store_t *store) {
+ bh_arena_free(&store->arena);
+ free(store);
+}
+
+
+//
+// Program
+ovm_program_t *ovm_program_new(ovm_store_t *store) {
+ ovm_program_t *program = bh_alloc_item(store->heap_allocator, ovm_program_t);
+ program->store = store;
+
+ bh_arr_new(store->heap_allocator, program->funcs, 16);
+ bh_arr_new(store->heap_allocator, program->native_funcs, 16);
+ bh_arr_new(store->heap_allocator, program->initialized_data, 16);
+
+ bh_arr_new(store->heap_allocator, program->code, 1024);
+
+ return program;
+}
+
+void ovm_program_delete(ovm_program_t *program) {
+ bh_arr_free(program->funcs);
+ bh_arr_free(program->native_funcs);
+ bh_arr_free(program->initialized_data);
+ bh_arr_free(program->code);
+
+ bh_free(program->store->heap_allocator, program);
+}
+
+void ovm_program_register_func(ovm_program_t *program, i32 instr, i32 param_count, i32 value_number_count) {
+ ovm_func_t *func = bh_alloc_item(program->store->arena_allocator, ovm_func_t);
+
+ func->start_instr = instr;
+ func->param_count = param_count;
+ func->value_number_count = value_number_count;
+ func->id = bh_arr_length(program->funcs);
+
+ bh_arr_push(program->funcs, func);
+}
+
+void ovm_program_register_native_func(ovm_program_t *program, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count) {
+ ovm_native_func_t native_func;
+ native_func.param_count = param_count;
+ native_func.native_func = func;
+ native_func.userdata = data;
+
+ bh_arr_push(program->native_funcs, native_func);
+}
+
+void ovm_program_add_instructions(ovm_program_t *program, i32 instr_count, ovm_instr_t *instrs) {
+ fori (i, 0, instr_count) {
+ bh_arr_push(program->code, instrs[i]);
+ }
+}
+
+
+static char *ovm_instr_name(i32 full_instr) {
+#define C(...) case __VA_ARGS__: return #__VA_ARGS__;
+
+ static char buf[64];
+
+ switch (full_instr) {
+ C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I64))
+ C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F32))
+ C(OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F64))
+
+ C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_I64))
+ C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_F32))
+ C(OVM_TYPED_INSTR(OVMI_SUB, OVM_TYPE_F64))
+
+ C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_I64))
+ C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_F32))
+ C(OVM_TYPED_INSTR(OVMI_MUL, OVM_TYPE_F64))
+
+ C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_I64))
+ C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_F32))
+ C(OVM_TYPED_INSTR(OVMI_DIV, OVM_TYPE_F64))
+
+ C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_REM, OVM_TYPE_I64))
+
+ C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_AND, OVM_TYPE_I64))
+
+ C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_OR, OVM_TYPE_I64))
+
+ C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_XOR, OVM_TYPE_I64))
+
+ C(OVM_TYPED_INSTR(OVMI_NOT, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_NOT, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_NOT, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_NOT, OVM_TYPE_I64))
+
+ C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_SHL, OVM_TYPE_I64))
+
+ C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_SHR, OVM_TYPE_I64))
+
+ C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_SAR, OVM_TYPE_I64))
+
+ C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I64))
+ C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_F32))
+ C(OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_F64))
+
+ C(OVMI_PARAM)
+ C(OVMI_CALL)
+ C(OVMI_RETURN)
+
+ C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_NONE))
+ C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I8))
+ C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I16))
+ C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I32))
+ C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I64))
+ C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_F32))
+ C(OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_F64))
+
+ default:
+ snprintf(buf, 64, "unknown (%d)", full_instr);
+ return buf;
+ }
+}
+
+void ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count) {
+ fori (i, start_instr, instr_count) {
+ ovm_instr_t instr = program->code[i];
+ printf("%48s | r=%02d a=%02d b=%02d i=%d f=%f l=%ld d=%lf\n", ovm_instr_name(instr.full_instr), instr.r, instr.a, instr.b, instr.i, instr.f, instr.l, instr.d);
+ }
+}
+
+
+
+//
+// Engine
+ovm_engine_t *ovm_engine_new(ovm_store_t *store) {
+ ovm_engine_t *engine = bh_alloc_item(store->heap_allocator, ovm_engine_t);
+
+ engine->store = store;
+ engine->memory_size = 1 << 16;
+ engine->memory = mmap(NULL, engine->memory_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+
+ return engine;
+}
+
+void ovm_engine_delete(ovm_engine_t *engine) {
+ ovm_store_t *store = engine->store;
+
+ munmap(engine->memory, engine->memory_size);
+ bh_free(store->heap_allocator, engine);
+}
+
+
+//
+// State
+ovm_state_t *ovm_state_new(ovm_engine_t *engine) {
+ ovm_store_t *store = engine->store;
+ ovm_state_t *state = bh_alloc_item(store->heap_allocator, ovm_state_t);
+
+ state->store = store;
+ state->pc = 0;
+ state->value_number_offset = 0;
+
+ bh_arr_new(store->heap_allocator, state->numbered_values, 64);
+ bh_arr_new(store->heap_allocator, state->params, 16);
+ bh_arr_new(store->heap_allocator, state->stack_frames, 32);
+
+ return state;
+}
+
+void ovm_state_delete(ovm_state_t *state) {
+ ovm_store_t *store = state->store;
+
+ bh_arr_free(state->numbered_values);
+ bh_arr_free(state->params);
+ bh_arr_free(state->stack_frames);
+
+ bh_free(store->heap_allocator, state);
+}
+
+
+//
+// Function calling
+
+static void ovm__func_setup_stack_frame(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program, i32 func_idx, i32 result_number) {
+ ovm_func_t *func = program->funcs[func_idx];
+
+ //
+ // Push a stack frame
+ ovm_stack_frame_t frame;
+ frame.func = func;
+ frame.value_number_count = func->value_number_count;
+ frame.value_number_base = state->value_number_offset + func->value_number_count;
+ frame.return_address = state->pc;
+ frame.return_number_value = result_number;
+ bh_arr_push(state->stack_frames, frame);
+
+ //
+ // Move the base pointer to the value numbers.
+ state->value_number_offset = frame.value_number_base;
+
+ //
+ // Setup value numbers
+ bh_arr_insert_end(state->numbered_values, func->value_number_count);
+}
+
+ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program, i32 func_idx, i32 param_count, ovm_value_t *params) {
+ bh_arr_insert_end(state->numbered_values, 1);
+ state->value_number_offset += 1;
+
+ state->pc = 0x7fffffff;
+ ovm__func_setup_stack_frame(engine, state, program, func_idx, 0);
+
+ fori (i, 0, param_count) {
+ state->numbered_values[i + state->value_number_offset] = params[i];
+ }
+
+ state->pc = program->funcs[func_idx]->start_instr;
+ ovm_run_code(engine, state, program);
+
+ return state->numbered_values[state->value_number_offset--];
+}
+
+
+void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program) {
+ assert(engine);
+ assert(state);
+ assert(program);
+
+#define VAL(loc) (state->numbered_values[(u32) (loc + state->value_number_offset)])
+
+ ovm_instr_t *code = program->code;
+
+ while (state->pc < bh_arr_length(program->code)) {
+ //
+ // Incrementing the program counter here.
+ // All instructions that compute something relative
+ // to the program counter have to know that the program
+ // counter will refer to the instruction AFTER the one
+ // being executed. - brendanfh 2022/06/13
+ ovm_instr_t instr = code[state->pc++];
+
+ switch (instr.full_instr) {
+ case OVMI_NOP: break;
+
+#define OVM_OP(i, t, op, ctype) \
+ case OVM_TYPED_INSTR(i, t): \
+ (VAL(instr.r).ctype = VAL(instr.a).ctype op VAL(instr.b).ctype); \
+ break;
+
+ OVM_OP(OVMI_ADD, OVM_TYPE_I8 , +, i8)
+ OVM_OP(OVMI_ADD, OVM_TYPE_I16, +, i16)
+ OVM_OP(OVMI_ADD, OVM_TYPE_I32, +, i32)
+ OVM_OP(OVMI_ADD, OVM_TYPE_I64, +, i64)
+ OVM_OP(OVMI_ADD, OVM_TYPE_F32, +, f32)
+ OVM_OP(OVMI_ADD, OVM_TYPE_F64, +, f64)
+
+ OVM_OP(OVMI_SUB, OVM_TYPE_I8 , -, i8)
+ OVM_OP(OVMI_SUB, OVM_TYPE_I16, -, i16)
+ OVM_OP(OVMI_SUB, OVM_TYPE_I32, -, i32)
+ OVM_OP(OVMI_SUB, OVM_TYPE_I64, -, i64)
+ OVM_OP(OVMI_SUB, OVM_TYPE_F32, -, f32)
+ OVM_OP(OVMI_SUB, OVM_TYPE_F64, -, f64)
+
+ OVM_OP(OVMI_MUL, OVM_TYPE_I8 , *, i8)
+ OVM_OP(OVMI_MUL, OVM_TYPE_I16, *, i16)
+ OVM_OP(OVMI_MUL, OVM_TYPE_I32, *, i32)
+ OVM_OP(OVMI_MUL, OVM_TYPE_I64, *, i64)
+ OVM_OP(OVMI_MUL, OVM_TYPE_F32, *, f32)
+ OVM_OP(OVMI_MUL, OVM_TYPE_F64, *, f64)
+
+ OVM_OP(OVMI_DIV, OVM_TYPE_I8 , /, i8)
+ OVM_OP(OVMI_DIV, OVM_TYPE_I16, /, i16)
+ OVM_OP(OVMI_DIV, OVM_TYPE_I32, /, i32)
+ OVM_OP(OVMI_DIV, OVM_TYPE_I64, /, i64)
+ OVM_OP(OVMI_DIV, OVM_TYPE_F32, /, f32)
+ OVM_OP(OVMI_DIV, OVM_TYPE_F64, /, f64)
+
+ OVM_OP(OVMI_REM, OVM_TYPE_I8 , %, i8)
+ OVM_OP(OVMI_REM, OVM_TYPE_I16, %, i16)
+ OVM_OP(OVMI_REM, OVM_TYPE_I32, %, i32)
+ OVM_OP(OVMI_REM, OVM_TYPE_I64, %, i64)
+
+ OVM_OP(OVMI_AND, OVM_TYPE_I8 , &, i8)
+ OVM_OP(OVMI_AND, OVM_TYPE_I16, &, i16)
+ OVM_OP(OVMI_AND, OVM_TYPE_I32, &, i32)
+ OVM_OP(OVMI_AND, OVM_TYPE_I64, &, i64)
+
+ OVM_OP(OVMI_OR, OVM_TYPE_I8 , |, i8)
+ OVM_OP(OVMI_OR, OVM_TYPE_I16, |, i16)
+ OVM_OP(OVMI_OR, OVM_TYPE_I32, |, i32)
+ OVM_OP(OVMI_OR, OVM_TYPE_I64, |, i64)
+
+ OVM_OP(OVMI_SHL, OVM_TYPE_I8 , <<, i8)
+ OVM_OP(OVMI_SHL, OVM_TYPE_I16, <<, i16)
+ OVM_OP(OVMI_SHL, OVM_TYPE_I32, <<, i32)
+ OVM_OP(OVMI_SHL, OVM_TYPE_I64, <<, i64)
+
+ OVM_OP(OVMI_SHR, OVM_TYPE_I8 , >> (u8), i8)
+ OVM_OP(OVMI_SHR, OVM_TYPE_I16, >> (u16), i16)
+ OVM_OP(OVMI_SHR, OVM_TYPE_I32, >> (u32), i32)
+ OVM_OP(OVMI_SHR, OVM_TYPE_I64, >> (u64), i64)
+
+ OVM_OP(OVMI_SAR, OVM_TYPE_I8 , >> (i8), i8)
+ OVM_OP(OVMI_SAR, OVM_TYPE_I16, >> (i16), i16)
+ OVM_OP(OVMI_SAR, OVM_TYPE_I32, >> (i32), i32)
+ OVM_OP(OVMI_SAR, OVM_TYPE_I64, >> (i64), i64)
+
+#undef OVM_OP
+
+#define OVM_OP(i, t, op, ctype) \
+ case OVM_TYPED_INSTR(i, t): \
+ (VAL(instr.r).ctype = op VAL(instr.a).ctype); \
+ break;
+
+ OVM_OP(OVMI_NOT, OVM_TYPE_I8 , ~, i8)
+ OVM_OP(OVMI_NOT, OVM_TYPE_I16, ~, i16)
+ OVM_OP(OVMI_NOT, OVM_TYPE_I32, ~, i32)
+ OVM_OP(OVMI_NOT, OVM_TYPE_I64, ~, i64)
+
+#undef OVM_OP
+
+#define OVM_OP(i, t, op, ctype, cast_type) \
+ case OVM_TYPED_INSTR(i, t): \
+ (VAL(instr.r).i32 = (i32) ((cast_type) VAL(instr.a).ctype op (cast_type) VAL(instr.b).ctype)); \
+ break;
+
+ OVM_OP(OVMI_LT, OVM_TYPE_I8 , <, i8, u8)
+ OVM_OP(OVMI_LT, OVM_TYPE_I16, <, i16, u16)
+ OVM_OP(OVMI_LT, OVM_TYPE_I32, <, i32, u32)
+ OVM_OP(OVMI_LT, OVM_TYPE_I64, <, i64, u64)
+ OVM_OP(OVMI_LT, OVM_TYPE_F32, <, f32, f32)
+ OVM_OP(OVMI_LT, OVM_TYPE_F64, <, f64, f32)
+
+ OVM_OP(OVMI_LT_S, OVM_TYPE_I8 , <, i8, i8)
+ OVM_OP(OVMI_LT_S, OVM_TYPE_I16, <, i16, i16)
+ OVM_OP(OVMI_LT_S, OVM_TYPE_I32, <, i32, i32)
+ OVM_OP(OVMI_LT_S, OVM_TYPE_I64, <, i64, i64)
+ OVM_OP(OVMI_LT_S, OVM_TYPE_F32, <, f32, f32)
+ OVM_OP(OVMI_LT_S, OVM_TYPE_F64, <, f64, f32)
+
+ OVM_OP(OVMI_LE, OVM_TYPE_I8 , <=, i8, u8)
+ OVM_OP(OVMI_LE, OVM_TYPE_I16, <=, i16, u16)
+ OVM_OP(OVMI_LE, OVM_TYPE_I32, <=, i32, u32)
+ OVM_OP(OVMI_LE, OVM_TYPE_I64, <=, i64, u64)
+ OVM_OP(OVMI_LE, OVM_TYPE_F32, <=, f32, f32)
+ OVM_OP(OVMI_LE, OVM_TYPE_F64, <=, f64, f64)
+
+ OVM_OP(OVMI_LE_S, OVM_TYPE_I8 , <=, i8, i8)
+ OVM_OP(OVMI_LE_S, OVM_TYPE_I16, <=, i16, i16)
+ OVM_OP(OVMI_LE_S, OVM_TYPE_I32, <=, i32, i32)
+ OVM_OP(OVMI_LE_S, OVM_TYPE_I64, <=, i64, i64)
+ OVM_OP(OVMI_LE_S, OVM_TYPE_F32, <=, f32, f32)
+ OVM_OP(OVMI_LE_S, OVM_TYPE_F64, <=, f64, f64)
+
+ OVM_OP(OVMI_EQ, OVM_TYPE_I8 , ==, i8, i8)
+ OVM_OP(OVMI_EQ, OVM_TYPE_I16, ==, i16, i16)
+ OVM_OP(OVMI_EQ, OVM_TYPE_I32, ==, i32, i32)
+ OVM_OP(OVMI_EQ, OVM_TYPE_I64, ==, i64, i64)
+ OVM_OP(OVMI_EQ, OVM_TYPE_F32, ==, f32, f32)
+ OVM_OP(OVMI_EQ, OVM_TYPE_F64, ==, f64, f64)
+
+ OVM_OP(OVMI_GE, OVM_TYPE_I8 , >=, i8, u8)
+ OVM_OP(OVMI_GE, OVM_TYPE_I16, >=, i16, u16)
+ OVM_OP(OVMI_GE, OVM_TYPE_I32, >=, i32, u32)
+ OVM_OP(OVMI_GE, OVM_TYPE_I64, >=, i64, u64)
+ OVM_OP(OVMI_GE, OVM_TYPE_F32, >=, f32, f32)
+ OVM_OP(OVMI_GE, OVM_TYPE_F64, >=, f64, f64)
+
+ OVM_OP(OVMI_GE_S, OVM_TYPE_I8 , >=, i8, i8)
+ OVM_OP(OVMI_GE_S, OVM_TYPE_I16, >=, i16, i16)
+ OVM_OP(OVMI_GE_S, OVM_TYPE_I32, >=, i32, i32)
+ OVM_OP(OVMI_GE_S, OVM_TYPE_I64, >=, i64, i64)
+ OVM_OP(OVMI_GE_S, OVM_TYPE_F32, >=, f32, f32)
+ OVM_OP(OVMI_GE_S, OVM_TYPE_F64, >=, f64, f64)
+
+ OVM_OP(OVMI_GT, OVM_TYPE_I8 , >, i8, u8)
+ OVM_OP(OVMI_GT, OVM_TYPE_I16, >, i16, u16)
+ OVM_OP(OVMI_GT, OVM_TYPE_I32, >, i32, u32)
+ OVM_OP(OVMI_GT, OVM_TYPE_I64, >, i64, u64)
+ OVM_OP(OVMI_GT, OVM_TYPE_F32, >, f32, f32)
+ OVM_OP(OVMI_GT, OVM_TYPE_F64, >, f64, f64)
+
+ OVM_OP(OVMI_GT_S, OVM_TYPE_I8 , >, i8, i8)
+ OVM_OP(OVMI_GT_S, OVM_TYPE_I16, >, i16, i16)
+ OVM_OP(OVMI_GT_S, OVM_TYPE_I32, >, i32, i32)
+ OVM_OP(OVMI_GT_S, OVM_TYPE_I64, >, i64, i64)
+ OVM_OP(OVMI_GT_S, OVM_TYPE_F32, >, f32, f32)
+ OVM_OP(OVMI_GT_S, OVM_TYPE_F64, >, f64, f64)
+
+ OVM_OP(OVMI_NE, OVM_TYPE_I8 , !=, i8, i8)
+ OVM_OP(OVMI_NE, OVM_TYPE_I16, !=, i16, i16)
+ OVM_OP(OVMI_NE, OVM_TYPE_I32, !=, i32, i32)
+ OVM_OP(OVMI_NE, OVM_TYPE_I64, !=, i64, i64)
+ OVM_OP(OVMI_NE, OVM_TYPE_F32, !=, f32, f32)
+ OVM_OP(OVMI_NE, OVM_TYPE_F64, !=, f64, f64)
+
+#undef OVM_OP
+
+#define OVM_IMM(type, dtype, stype) \
+ case OVM_TYPED_INSTR(OVMI_IMM, type): \
+ (VAL(instr.r).dtype = instr.stype); \
+ break;
+
+ OVM_IMM(OVM_TYPE_I8, i8, i)
+ OVM_IMM(OVM_TYPE_I16, i16, i)
+ OVM_IMM(OVM_TYPE_I32, i32, i)
+ OVM_IMM(OVM_TYPE_I64, i64, l)
+ OVM_IMM(OVM_TYPE_F32, f32, f)
+ OVM_IMM(OVM_TYPE_F64, f64, d)
+
+#undef OVM_IMM
+
+ case OVMI_PARAM:
+ bh_arr_push(state->params, VAL(instr.a));
+ break;
+
+ case OVMI_CALL: {
+ i32 func_idx = VAL(instr.a).i32;
+ ovm__func_setup_stack_frame(engine, state, program, func_idx, instr.r);
+
+ //
+ // Apply parameters
+ ovm_func_t *func = program->funcs[func_idx];
+ fori (i, 0, func->param_count) {
+ VAL(i) = state->params[i];
+ }
+ bh_arr_set_length(state->params, 0);
+
+ state->pc = func->start_instr;
+ break;
+ }
+
+ case OVMI_RETURN: {
+ ovm_stack_frame_t frame = bh_arr_pop(state->stack_frames);
+
+ ovm_value_t val = VAL(instr.a);
+ bh_arr_fastdeleten(state->numbered_values, frame.value_number_count);
+
+ state->value_number_offset = frame.value_number_base;
+ VAL(frame.return_number_value) = val;
+
+ state->pc = frame.return_address;
+ break;
+ }
+
+ case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_NONE):
+ case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I8):
+ case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I16):
+ case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I32):
+ case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_I64):
+ case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_F32):
+ case OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_F64): {
+ ovm_native_func_t native_func = program->native_funcs[VAL(instr.a).i32];
+
+ ovm_value_t result = {0};
+ native_func.native_func(native_func.userdata, state->params, &result);
+ bh_arr_set_length(state->params, 0);
+
+ if (instr.type != OVM_TYPE_NONE) {
+ VAL(instr.r) = result;
+ }
+
+ break;
+ }
+
+ case OVMI_BR:
+ state->pc = VAL(instr.a).i32;
+ break;
+
+ case OVMI_BR_NZ:
+ if (VAL(instr.a).i32 != 0) {
+ state->pc = VAL(instr.r).i32;
+ }
+ break;
+
+ default:
+ printf("ERROR:\n");
+ ovm_program_print_instructions(program, state->pc - 1, 1);
+ fflush(stdout);
+ assert(("ILLEGAL INSTRUCTION", 0));
+ break;
+ }
+ }
+}
+