big refactor in interpreter for faster non-debugging running
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 17 Dec 2022 19:53:13 +0000 (13:53 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 17 Dec 2022 19:53:13 +0000 (13:53 -0600)
interpreter/include/vm.h
interpreter/src/vm/vm.c
interpreter/src/vm/vm_instrs.h [new file with mode: 0644]
shared/lib/linux_x86_64/lib/libovmwasm.so

index 4048ab16e391f56b0b465c80f3b73a2a593d2bec..c4804e9b83ebc674e081667fc6968f83167e81d8 100644 (file)
@@ -154,6 +154,10 @@ struct ovm_state_t {
     // Temporary value used in computations. Should not be used otherwise.
     ovm_value_t __tmp_value;
 
+    //
+    // TODO Doc
+    ovm_value_t *__frame_values;
+
     debug_thread_state_t *debug;
 };
 
index 46a593795310df0ded9186960940d707bc793ca3..861325f0d2ec55408918d010666a1ad3166b5715 100644 (file)
@@ -565,6 +565,8 @@ static void ovm__func_setup_stack_frame(ovm_state_t *state, ovm_func_t *func, i3
     // Setup value numbers
     bh_arr_insert_end(state->numbered_values, func->value_number_count);
 
+    state->__frame_values = &state->numbered_values[state->value_number_offset];
+
     //
     // Modify debug state so step over works
     if (state->debug) {
@@ -582,6 +584,8 @@ static ovm_stack_frame_t ovm__func_teardown_stack_frame(ovm_state_t *state) {
         state->value_number_offset = bh_arr_last(state->stack_frames).value_number_base;
     }
 
+    state->__frame_values = &state->numbered_values[state->value_number_offset];
+
     if (state->debug) {
         state->debug->extra_frames_since_last_pause--;
         if (state->debug->extra_frames_since_last_pause < 0) {
@@ -729,561 +733,30 @@ static void __ovm_debug_hook(ovm_engine_t *engine, ovm_state_t *state) {
     if (state->debug->run_count > 0) state->debug->run_count--;
 }
 
+#define OVMI_FUNC_NAME(n) ovmi_exec_##n
+#define OVMI_DISPATCH_NAME ovmi_dispatch
+#define OVMI_DEBUG_HOOK ((void)0)
+#include "./vm_instrs.h"
 
-
-//
-// Running code with Continuation-Passing Style
-//
-
-
-
-#define OVMI_INSTR_PROTO(name) \
-    ovm_value_t name(ovm_instr_t *instr, ovm_state_t *state, u8 *memory, ovm_instr_t *code)
-
-#define OVMI_INSTR_EXEC(name) \
-    static OVMI_INSTR_PROTO(name)
-
-#define NEXT_OP \
-    if (instr->full_instr & OVMI_ATOMIC) pthread_mutex_unlock(&state->engine->atomic_mutex); \
-    if (state->debug) __ovm_debug_hook(state->engine, state); \
-    instr = &code[state->pc++]; \
-    if (instr->full_instr & OVMI_ATOMIC) pthread_mutex_lock(&state->engine->atomic_mutex); \
-    return ovmi_dispatch[instr->full_instr & OVM_INSTR_MASK](instr, state, memory, code);
-
-#define VAL(loc) state->numbered_values[(u32) (loc + state->value_number_offset)]
-
-
-typedef OVMI_INSTR_PROTO((* ovmi_instr_exec_t));
-
-static ovmi_instr_exec_t ovmi_dispatch[];
-
-
-//
-// Special Operations
-//
-
-OVMI_INSTR_EXEC(ovmi_exec_nop) {
-    NEXT_OP;
-}
-
-
-//
-// Binary Operations
-//
-
-#define OVM_OP_EXEC(name, op) \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_i32) { OVM_OP(OVM_TYPE_I32, op, i32); NEXT_OP; } \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_i64) { OVM_OP(OVM_TYPE_I64, op, i64); NEXT_OP; } \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_f32) { OVM_OP(OVM_TYPE_F32, op, f32); NEXT_OP; } \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_f64) { OVM_OP(OVM_TYPE_F64, op, f64); NEXT_OP; }
-
-#define OVM_OP_UNSIGNED_EXEC(name, op) \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_i32) { OVM_OP(OVM_TYPE_I32, op, u32); NEXT_OP; } \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_i64) { OVM_OP(OVM_TYPE_I64, op, u64); NEXT_OP; } \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_f32) { OVM_OP(OVM_TYPE_F32, op, f32); NEXT_OP; } \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_f64) { OVM_OP(OVM_TYPE_F64, op, f64); NEXT_OP; }
-
-#define OVM_OP_INTEGER_EXEC(name, op) \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_i32) { OVM_OP(OVM_TYPE_I32, op, i32); NEXT_OP; } \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_i64) { OVM_OP(OVM_TYPE_I64, op, i64); NEXT_OP; }
-
-#define OVM_OP_INTEGER_UNSIGNED_EXEC(name, op) \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_i32) { OVM_OP(OVM_TYPE_I32, op, u32); NEXT_OP; } \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_i64) { OVM_OP(OVM_TYPE_I64, op, u64); NEXT_OP; }
-
-#define OVM_OP_FLOAT_EXEC(name, op) \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_f32) { OVM_OP(OVM_TYPE_F32, op, f32); NEXT_OP; } \
-    OVMI_INSTR_EXEC(ovmi_exec_##name##_f64) { OVM_OP(OVM_TYPE_F64, op, f64); NEXT_OP; }
-
-
-#define OVM_OP(t, op, ctype) \
-    ovm_assert(VAL(instr->a).type == t && VAL(instr->b).type == t); \
-    VAL(instr->r).ctype = VAL(instr->a).ctype op VAL(instr->b).ctype; \
-    VAL(instr->r).type = t;
-
-OVM_OP_EXEC(add, +)
-OVM_OP_EXEC(sub, -)
-OVM_OP_EXEC(mul, *)
-OVM_OP_EXEC(div_s, /)
-OVM_OP_UNSIGNED_EXEC(div, /)
-OVM_OP_INTEGER_UNSIGNED_EXEC(rem, %)
-OVM_OP_INTEGER_EXEC(rem_s, %)
-OVM_OP_INTEGER_UNSIGNED_EXEC(and, &)
-OVM_OP_INTEGER_UNSIGNED_EXEC(or, |)
-OVM_OP_INTEGER_UNSIGNED_EXEC(xor, ^)
-OVM_OP_INTEGER_UNSIGNED_EXEC(shl, <<)
-OVM_OP_INTEGER_UNSIGNED_EXEC(shr, >>)
-OVM_OP_INTEGER_EXEC(sar, >>)
-
-#undef OVM_OP
-
-#define OVM_OP(t, func, ctype) \
-    ovm_assert(VAL(instr->a).type == t && VAL(instr->b).type == t); \
-    VAL(instr->r).ctype = func( VAL(instr->a).ctype, VAL(instr->b).ctype ); \
-    VAL(instr->r).type = t;
-
-OVMI_INSTR_EXEC(ovmi_exec_rotl_i32) { OVM_OP(OVM_TYPE_I32, __rold, u32); NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_rotl_i64) { OVM_OP(OVM_TYPE_I64, __rolq, u64); NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_rotr_i32) { OVM_OP(OVM_TYPE_I32, __rord, u32); NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_rotr_i64) { OVM_OP(OVM_TYPE_I64, __rorq, u64); NEXT_OP; }
-
-OVM_OP_FLOAT_EXEC(min, bh_min)
-OVM_OP_FLOAT_EXEC(max, bh_max)
-OVM_OP_FLOAT_EXEC(copysign, __ovm_copysign)
-
-#undef OVM_OP
-
-
-#define OVM_OP(t, op, ctype) \
-    ovm_assert(VAL(instr->a).type == t); \
-    VAL(instr->r).type = t; \
-    VAL(instr->r).ctype = (ctype) op (VAL(instr->a).ctype);
-
-OVMI_INSTR_EXEC(ovmi_exec_clz_i32) { OVM_OP(OVM_TYPE_I32, __builtin_clz, u32);   NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_clz_i64) { OVM_OP(OVM_TYPE_I64, __builtin_clzll, u64); NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_ctz_i32) { OVM_OP(OVM_TYPE_I32, __builtin_ctz, u32);   NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_ctz_i64) { OVM_OP(OVM_TYPE_I64, __builtin_ctzll, u64); NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_popcount_i32) { OVM_OP(OVM_TYPE_I32, __builtin_popcount, u32);   NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_popcount_i64) { OVM_OP(OVM_TYPE_I64, __builtin_popcountll, u64); NEXT_OP; }
-
-OVM_OP_FLOAT_EXEC(abs,     __ovm_abs)
-OVM_OP_FLOAT_EXEC(neg,     -)
-OVM_OP_FLOAT_EXEC(ceil,    __ovm_ceil)
-OVM_OP_FLOAT_EXEC(floor,   __ovm_floor)
-OVM_OP_FLOAT_EXEC(trunc,   __ovm_trunc)
-OVM_OP_FLOAT_EXEC(nearest, __ovm_nearest)
-OVM_OP_FLOAT_EXEC(sqrt,    sqrt)
-
-#undef OVM_OP
-
-
-#define OVM_OP(t, op, ctype) \
-    ovm_assert(VAL(instr->a).type == t && VAL(instr->b).type == t); \
-    VAL(instr->r).type = OVM_TYPE_I32; \
-    VAL(instr->r).i32 = ((VAL(instr->a).ctype op VAL(instr->b).ctype)) ? 1 : 0;
-
-OVM_OP_EXEC(eq, ==)
-OVM_OP_EXEC(ne, !=)
-OVM_OP_UNSIGNED_EXEC(lt, <)
-OVM_OP_UNSIGNED_EXEC(le, <=)
-OVM_OP_UNSIGNED_EXEC(gt, >)
-OVM_OP_UNSIGNED_EXEC(ge, >=)
-OVM_OP_EXEC(lt_s, <)
-OVM_OP_EXEC(le_s, <=)
-OVM_OP_EXEC(gt_s, >)
-OVM_OP_EXEC(ge_s, >=)
-
-#undef OVM_OP
-
-
-
-//
-// Memory / register operations
-//
-
-#define OVM_IMM(t, dtype, stype) \
-    VAL(instr->r).type = t; \
-    VAL(instr->r).u64 = 0; \
-    VAL(instr->r).dtype = instr->stype;
-
-
-OVMI_INSTR_EXEC(ovmi_exec_imm_i32) { OVM_IMM(OVM_TYPE_I32, u32, i); NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_imm_i64) { OVM_IMM(OVM_TYPE_I64, u64, l); NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_imm_f32) { OVM_IMM(OVM_TYPE_F32, f32, f); NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_imm_f64) { OVM_IMM(OVM_TYPE_F64, f64, d); NEXT_OP; }
-
-#undef OVM_IMM
-
-OVMI_INSTR_EXEC(ovmi_exec_mov) {
-    VAL(instr->r) = VAL(instr->a);
-    NEXT_OP;
-}
-
-#define OVM_LOAD(otype, type_, stype) \
-    OVMI_INSTR_EXEC(ovmi_exec_load_##otype) { \
-        ovm_assert(VAL(instr->a).type == OVM_TYPE_I32); \
-        u32 dest = VAL(instr->a).u32 + (u32) instr->b; \
-        if (dest == 0) __ovm_trigger_exception(state); \
-        VAL(instr->r).stype = * (stype *) &memory[dest]; \
-        VAL(instr->r).type = type_; \
-        NEXT_OP; \
-    }
-
-OVM_LOAD(i8,  OVM_TYPE_I8,  u8)
-OVM_LOAD(i16, OVM_TYPE_I16, u16)
-OVM_LOAD(i32, OVM_TYPE_I32, u32)
-OVM_LOAD(i64, OVM_TYPE_I64, u64)
-OVM_LOAD(f32, OVM_TYPE_F32, f32)
-OVM_LOAD(f64, OVM_TYPE_F64, f64)
-
-#undef OVM_LOAD
-
-#define OVM_STORE(otype, type_, stype) \
-    OVMI_INSTR_EXEC(ovmi_exec_store_##otype) { \
-        ovm_assert(VAL(instr->r).type == OVM_TYPE_I32); \
-        u32 dest = VAL(instr->r).u32 + (u32) instr->b; \
-        if (dest == 0) __ovm_trigger_exception(state); \
-        *(stype *) &memory[dest] = VAL(instr->a).stype; \
-        NEXT_OP; \
-    }
-
-
-OVM_STORE(i8,  OVM_TYPE_I8,  u8)
-OVM_STORE(i16, OVM_TYPE_I16, u16)
-OVM_STORE(i32, OVM_TYPE_I32, u32)
-OVM_STORE(i64, OVM_TYPE_I64, u64)
-OVM_STORE(f32, OVM_TYPE_F32, f32)
-OVM_STORE(f64, OVM_TYPE_F64, f64)
-
-#undef OVM_STORE
-
-OVMI_INSTR_EXEC(ovmi_exec_copy) {
-    u32 dest  = VAL(instr->r).u32;
-    u32 src   = VAL(instr->a).u32;
-    u32 count = VAL(instr->b).u32;
-
-    if (!dest || !src) __ovm_trigger_exception(state);
-
-    memmove(&memory[dest], &memory[src], count);
-
-    NEXT_OP;
-}
-
-OVMI_INSTR_EXEC(ovmi_exec_fill) {
-    i32 dest  = VAL(instr->r).i32;
-    u8  byte  = VAL(instr->a).u8;
-    i32 count = VAL(instr->b).i32;
-
-    if (!dest) __ovm_trigger_exception(state);
-
-    memset(&memory[dest], byte, count);
-
-    NEXT_OP;
-}
-
-OVMI_INSTR_EXEC(ovmi_exec_reg_get) {
-    VAL(instr->r) = state->registers[instr->a];
-
-    NEXT_OP;
-}
-
-OVMI_INSTR_EXEC(ovmi_exec_reg_set) {
-    state->registers[instr->r] = VAL(instr->a);
-
-    NEXT_OP;
-}
-
-OVMI_INSTR_EXEC(ovmi_exec_idx_arr) {
-    ovm_static_integer_array_t data_elem = state->program->static_data[instr->a];
-    ovm_assert(VAL(instr->b).u32 < (u32) data_elem.len);
-
-    VAL(instr->r).i32 = state->program->static_integers[data_elem.start_idx + VAL(instr->b).u32];
-    VAL(instr->r).type = OVM_TYPE_I32;
-
-    NEXT_OP;
-}
-
-
-//
-// Function calling
-//
-
-OVMI_INSTR_EXEC(ovmi_exec_param) {
-    bh_arr_push(state->params, VAL(instr->a));
-
-    NEXT_OP;
-}
-
-OVMI_INSTR_EXEC(ovmi_exec_return) {
-    ovm_value_t val = VAL(instr->a);
-    ovm_stack_frame_t frame = ovm__func_teardown_stack_frame(state);
-    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
-
-    NEXT_OP;
-}
-
-
-#define OVM_CALL_CODE(func_idx) \
-    i32 fidx = func_idx; \
-    ovm_func_t *func = &state->program->funcs[fidx]; \
-    i32 extra_params = bh_arr_length(state->params) - func->param_count; \
-    ovm_assert(extra_params >= 0); \
-    ovm__func_setup_stack_frame(state, func, instr->r); \
-    bh_arr_fastdeleten(state->params, func->param_count); \
-    if (func->kind == OVM_FUNC_INTERNAL) { \
-        memcpy(&VAL(0), &state->params[extra_params], func->param_count * sizeof(ovm_value_t)); \
-        state->pc = func->start_instr; \
-    } else { \
-        ovm_external_func_t external_func = state->external_funcs[func->external_func_idx]; \
-        external_func.native_func(external_func.userdata, &state->params[extra_params], &state->__tmp_value); \
-\
-        ovm__func_teardown_stack_frame(state); \
-\
-        if (instr->r >= 0) { \
-            VAL(instr->r) = state->__tmp_value; \
-        } \
-    }
-
-
-OVMI_INSTR_EXEC(ovmi_exec_call) {
-    OVM_CALL_CODE(instr->a);
-    NEXT_OP;
-}
-
-OVMI_INSTR_EXEC(ovmi_exec_calli) {
-    OVM_CALL_CODE(VAL(instr->a).i32);
-    NEXT_OP;
-}
-
-#undef OVM_CALL_CODE
-
-
-
-//
-// Branching Instructions
-//
-
-OVMI_INSTR_EXEC(ovmi_exec_br)     { state->pc += instr->a; NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_bri)    { state->pc += VAL(instr->a).i32; NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_br_nz)  { if (VAL(instr->b).i32 != 0) state->pc += instr->a; NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_bri_nz) { if (VAL(instr->b).i32 != 0) state->pc += VAL(instr->a).i32; NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_br_z)   { if (VAL(instr->b).i32 == 0) state->pc += instr->a; NEXT_OP; }
-OVMI_INSTR_EXEC(ovmi_exec_bri_z)  { if (VAL(instr->b).i32 == 0) state->pc += VAL(instr->a).i32; NEXT_OP; }
-
-
-//
-// Conversion
-//
-
-#define OVM_CVT(n1, n2, stype, dtype, otype, ctype) \
-    OVMI_INSTR_EXEC(ovmi_exec_cvt_##n1##_##n2) { \
-        state->__tmp_value.dtype = (ctype) VAL(instr->a).stype; \
-        state->__tmp_value.type = otype; \
-        VAL(instr->r) = state->__tmp_value; \
-        NEXT_OP; \
-    }
-
-OVM_CVT(i8, i16,   u8, u16, OVM_TYPE_I16, u16);
-OVM_CVT(i8, i32,   u8, u32, OVM_TYPE_I32, u32);
-OVM_CVT(i8, i64,   u8, u64, OVM_TYPE_I64, u64);
-OVM_CVT(i8_s, i16, i8, i16, OVM_TYPE_I16, i16);
-OVM_CVT(i8_s, i32, i8, i32, OVM_TYPE_I32, i32);
-OVM_CVT(i8_s, i64, i8, i64, OVM_TYPE_I64, i64);
-
-OVM_CVT(i16,   i8,  u16, u8,  OVM_TYPE_I8, u8);
-OVM_CVT(i16,   i32, u16, u32, OVM_TYPE_I32, u32);
-OVM_CVT(i16,   i64, u16, u64, OVM_TYPE_I64, u64);
-OVM_CVT(i16_s, i8,  i16, i8,  OVM_TYPE_I8, i8);
-OVM_CVT(i16_s, i32, i16, i32, OVM_TYPE_I32, i32);
-OVM_CVT(i16_s, i64, i16, i64, OVM_TYPE_I64, i64);
-
-OVM_CVT(i32,   i8,  u32, u8,  OVM_TYPE_I8,  u8);
-OVM_CVT(i32,   i16, u32, u16, OVM_TYPE_I16, u16);
-OVM_CVT(i32,   i64, u32, u64, OVM_TYPE_I64, u64);
-OVM_CVT(i32_s, i8,  i32, i8,  OVM_TYPE_I8,  i8);
-OVM_CVT(i32_s, i16, i32, i16, OVM_TYPE_I16, i16);
-OVM_CVT(i32_s, i64, i32, i64, OVM_TYPE_I64, i64);
-OVM_CVT(i32,   f32, u32, f32, OVM_TYPE_F32, f32);
-OVM_CVT(i32_s, f32, i32, f32, OVM_TYPE_F32, f32);
-OVM_CVT(i32,   f64, u32, f64, OVM_TYPE_F64, f64);
-OVM_CVT(i32_s, f64, i32, f64, OVM_TYPE_F64, f64);
-
-OVM_CVT(i64,   i8,  u64, u8,  OVM_TYPE_I8,  u8);
-OVM_CVT(i64,   i16, u64, u16, OVM_TYPE_I16, u16);
-OVM_CVT(i64,   i32, u64, u32, OVM_TYPE_I32, u32);
-OVM_CVT(i64_s, i8,  i64, i8,  OVM_TYPE_I8,  i8);
-OVM_CVT(i64_s, i16, i64, i16, OVM_TYPE_I16, i16);
-OVM_CVT(i64_s, i32, i64, i32, OVM_TYPE_I32, i32);
-OVM_CVT(i64,   f32, u64, f32, OVM_TYPE_F32, f32);
-OVM_CVT(i64_s, f32, i64, f32, OVM_TYPE_F32, f32);
-OVM_CVT(i64,   f64, u64, f64, OVM_TYPE_F64, f64);
-OVM_CVT(i64_s, f64, i64, f64, OVM_TYPE_F64, f64);
-
-OVM_CVT(f32,   i32, f32, u32, OVM_TYPE_I32, u32);
-OVM_CVT(f32,   i64, f32, u64, OVM_TYPE_I64, u64);
-OVM_CVT(f32,   f64, f32, f64, OVM_TYPE_F64, f64);
-OVM_CVT(f32_s, i32, f32, i32, OVM_TYPE_I32, i32);
-OVM_CVT(f32_s, i64, f32, i64, OVM_TYPE_I64, i64);
-OVM_CVT(f32_s, f64, f32, f64, OVM_TYPE_F64, f64);
-
-OVM_CVT(f64,   i32, f64, u32, OVM_TYPE_I32, u32);
-OVM_CVT(f64,   i64, f64, u64, OVM_TYPE_I64, u64);
-OVM_CVT(f64,   f32, f64, f32, OVM_TYPE_F32, f32);
-OVM_CVT(f64_s, i32, f64, i32, OVM_TYPE_I32, i32);
-OVM_CVT(f64_s, i64, f64, i64, OVM_TYPE_I64, i64);
-OVM_CVT(f64_s, f32, f64, f32, OVM_TYPE_F32, f32);
-
-#undef OVM_CVT
-
-#define OVM_CVT(n1, n2, stype, dtype, otype, ctype) \
-    OVMI_INSTR_EXEC(ovmi_exec_transmute_##n1##_##n2) { \
-        ovm_value_t tmp_val; \
-        tmp_val.dtype = *(ctype *) &VAL(instr->a).stype; \
-        tmp_val.type = otype; \
-        VAL(instr->r) = tmp_val; \
-        NEXT_OP; \
-    }
-
-OVM_CVT(i32, f32, u32, f32, OVM_TYPE_F32, f32);
-OVM_CVT(i64, f64, u64, f64, OVM_TYPE_F64, f64);
-OVM_CVT(f32, i32, f32, u32, OVM_TYPE_I32, u32);
-OVM_CVT(f64, i64, f64, u64, OVM_TYPE_I64, u64);
-
-#undef CVT
-
-
-//
-// Compare exchange
-//
-
-#define CMPXCHG(otype, ctype) \
-    OVMI_INSTR_EXEC(ovmi_exec_cmpxchg_##ctype) { \
-        if (VAL(instr->r).u32 == 0) __ovm_trigger_exception(state); \
-        ctype *addr = (ctype *) &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 ; \
-        } \
-        NEXT_OP; \
-    }
-
-CMPXCHG(OVM_TYPE_I32, i32)
-CMPXCHG(OVM_TYPE_I64, i64)
-
-#undef CMPXCHG
-
-
-OVMI_INSTR_EXEC(ovmi_exec_illegal) {
-    __ovm_trigger_exception(state);
-    return ((ovm_value_t) {0});
-}
-
-//
-// Dispatch table
-//
-
-#define IROW_UNTYPED(name) ovmi_exec_##name, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-#define IROW_TYPED(name)   NULL, ovmi_exec_##name##_i8, ovmi_exec_##name##_i16, ovmi_exec_##name##_i32, ovmi_exec_##name##_i64, ovmi_exec_##name##_f32, ovmi_exec_##name##_f64, NULL,
-#define IROW_PARTIAL(name) NULL, NULL, NULL, ovmi_exec_##name##_i32, ovmi_exec_##name##_i64, ovmi_exec_##name##_f32, ovmi_exec_##name##_f64, NULL,
-#define IROW_INT(name)     NULL, NULL, NULL, ovmi_exec_##name##_i32, ovmi_exec_##name##_i64, NULL, NULL, NULL,
-#define IROW_FLOAT(name)   NULL, NULL, NULL, NULL, NULL, ovmi_exec_##name##_f32, ovmi_exec_##name##_f64, NULL,
-#define IROW_SAME(name)    ovmi_exec_##name,ovmi_exec_##name,ovmi_exec_##name,ovmi_exec_##name,ovmi_exec_##name,ovmi_exec_##name,ovmi_exec_##name,NULL,
-
-static ovmi_instr_exec_t ovmi_dispatch[] = {
-    IROW_UNTYPED(nop) // 0x00
-    IROW_PARTIAL(add)
-    IROW_PARTIAL(sub)
-    IROW_PARTIAL(mul)
-    IROW_PARTIAL(div)
-    IROW_PARTIAL(div_s)
-    IROW_INT(rem)
-    IROW_INT(rem_s)
-    IROW_INT(and)
-    IROW_INT(or)
-    IROW_INT(xor)
-    IROW_INT(shl)
-    IROW_INT(shr)
-    IROW_INT(sar)
-    IROW_SAME(illegal)
-    IROW_SAME(illegal)
-    IROW_PARTIAL(imm) // 0x10
-    IROW_UNTYPED(mov)
-    IROW_TYPED(load)
-    IROW_TYPED(store)
-    IROW_UNTYPED(copy)
-    IROW_UNTYPED(fill)
-    IROW_UNTYPED(reg_get)
-    IROW_UNTYPED(reg_set)
-    IROW_UNTYPED(idx_arr)
-    IROW_PARTIAL(lt)
-    IROW_PARTIAL(lt_s)
-    IROW_PARTIAL(le)
-    IROW_PARTIAL(le_s)
-    IROW_PARTIAL(eq)
-    IROW_PARTIAL(ge)
-    IROW_PARTIAL(ge_s)
-    IROW_PARTIAL(gt)  // 0x20
-    IROW_PARTIAL(gt_s)
-    IROW_PARTIAL(ne)
-    IROW_UNTYPED(param)
-    IROW_UNTYPED(return)
-    IROW_UNTYPED(call)
-    IROW_UNTYPED(calli)
-    IROW_UNTYPED(br)
-    IROW_UNTYPED(br_z)
-    IROW_UNTYPED(br_nz)
-    IROW_UNTYPED(bri)
-    IROW_UNTYPED(bri_z)
-    IROW_UNTYPED(bri_nz)
-    IROW_INT(clz)
-    IROW_INT(ctz)
-    IROW_INT(popcount)
-    IROW_INT(rotl)  // 0x30
-    IROW_INT(rotr)
-    IROW_FLOAT(abs)
-    IROW_FLOAT(neg)
-    IROW_FLOAT(ceil)
-    IROW_FLOAT(floor)
-    IROW_FLOAT(trunc)
-    IROW_FLOAT(nearest)
-    IROW_FLOAT(sqrt)
-    IROW_FLOAT(min)
-    IROW_FLOAT(max)
-    IROW_FLOAT(copysign)
-    NULL, NULL, ovmi_exec_cvt_i8_i16, ovmi_exec_cvt_i8_i32, ovmi_exec_cvt_i8_i64, NULL, NULL, NULL,
-    NULL, NULL, ovmi_exec_cvt_i8_s_i16, ovmi_exec_cvt_i8_s_i32, ovmi_exec_cvt_i8_s_i64, NULL, NULL, NULL,
-    NULL, ovmi_exec_cvt_i16_i8,   NULL, ovmi_exec_cvt_i16_i32, ovmi_exec_cvt_i16_i64, NULL, NULL, NULL,
-    NULL, ovmi_exec_cvt_i16_s_i8, NULL, ovmi_exec_cvt_i16_s_i32, ovmi_exec_cvt_i16_s_i64, NULL, NULL, NULL,
-    NULL, ovmi_exec_cvt_i32_i8,   ovmi_exec_cvt_i32_i16,   NULL, ovmi_exec_cvt_i32_i64, ovmi_exec_cvt_i32_f32, ovmi_exec_cvt_i32_f64, NULL,  // 0x40
-    NULL, ovmi_exec_cvt_i32_s_i8, ovmi_exec_cvt_i32_s_i16, NULL, ovmi_exec_cvt_i32_s_i64, ovmi_exec_cvt_i32_s_f32, ovmi_exec_cvt_i32_s_f64, NULL,
-    NULL, ovmi_exec_cvt_i64_i8,   ovmi_exec_cvt_i64_i16,   ovmi_exec_cvt_i64_i32,   NULL, ovmi_exec_cvt_i64_f32, ovmi_exec_cvt_i64_f64, NULL,
-    NULL, ovmi_exec_cvt_i64_s_i8, ovmi_exec_cvt_i64_s_i16, ovmi_exec_cvt_i64_s_i32, NULL, ovmi_exec_cvt_i64_s_f32, ovmi_exec_cvt_i64_s_f64, NULL,
-    NULL, NULL, NULL, ovmi_exec_cvt_f32_i32, ovmi_exec_cvt_f32_i64, NULL, ovmi_exec_cvt_f32_f64, NULL,
-    NULL, NULL, NULL, ovmi_exec_cvt_f32_s_i32, ovmi_exec_cvt_f32_s_i64, NULL, ovmi_exec_cvt_f32_s_f64, NULL,
-    NULL, NULL, NULL, ovmi_exec_cvt_f64_i32, ovmi_exec_cvt_f64_i64, ovmi_exec_cvt_f64_f32, NULL, NULL,
-    NULL, NULL, NULL, ovmi_exec_cvt_f64_s_i32, ovmi_exec_cvt_f64_s_i64, ovmi_exec_cvt_f64_s_f32, NULL, NULL,
-    NULL, NULL, NULL, ovmi_exec_transmute_i32_f32, NULL, NULL, NULL, NULL,
-    NULL, NULL, NULL, NULL, ovmi_exec_transmute_i64_f64, NULL, NULL, NULL,
-    NULL, NULL, NULL, NULL, NULL, ovmi_exec_transmute_f32_i32, NULL, NULL,
-    NULL, NULL, NULL, NULL, NULL, NULL, ovmi_exec_transmute_f64_i64, NULL,
-    IROW_INT(cmpxchg)
-    IROW_SAME(illegal)
-};
-
+#define OVMI_FUNC_NAME(n) ovmi_exec_debug_##n
+#define OVMI_DISPATCH_NAME ovmi_debug_dispatch
+#define OVMI_DEBUG_HOOK if (state->debug) __ovm_debug_hook(state->engine, state)
+#include "./vm_instrs.h"
 
 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);
 
+    ovmi_instr_exec_t *exec_table = ovmi_dispatch;
+    if (state->debug) exec_table = ovmi_debug_dispatch;
+
     ovm_instr_t *code = program->code;
     u8 *memory = engine->memory;
-
+    ovm_value_t *values = state->__frame_values;
     ovm_instr_t *instr = &code[state->pc];
-    NEXT_OP;
+
+    return exec_table[instr->full_instr & 0x7ff](instr, state, values, memory, code);
 }
 
 
diff --git a/interpreter/src/vm/vm_instrs.h b/interpreter/src/vm/vm_instrs.h
new file mode 100644 (file)
index 0000000..6a5d97f
--- /dev/null
@@ -0,0 +1,582 @@
+
+
+//
+// Running code with Continuation-Passing Style
+//
+// The way OVM code is run now uses "threaded code" or "Continuation-Passing" code.
+// The main idea is that GCC/Clang/MSVC are "smart" and will perform a tail call-elimination
+// on all `ovmi_exec_...` functions listed below. This in effect means that each function
+// ends with a `jmp rax` to the next function, instead of a `call ...`. This prevents the
+// stack from growing until it overflows. Many of the `ovmi_exec_...` functions were
+// designed so no stack based allocation has to happen.
+//
+// There are several things that break this however. Chief among which is the __ovm_debug_hook
+// function call which must be present in ALL instructions for debugging to work properly.
+// When this hook is present, the compiler MUST emit many `push` and `pop` instructions to preserve
+// the needed registers because it cannot know what will happen in the function. This more than
+// doubles the instruction count of all operations and slows down the runner by at least 20 percent.
+//
+
+
+#define OVMI_INSTR_PROTO(name) \
+    ovm_value_t name(ovm_instr_t *instr, ovm_state_t *state, ovm_value_t *values, u8 *memory, ovm_instr_t *code)
+
+#define OVMI_INSTR_EXEC(name) \
+    static OVMI_INSTR_PROTO(OVMI_FUNC_NAME(name))
+
+#define NEXT_OP \
+    OVMI_DEBUG_HOOK; \
+    instr = &code[state->pc++]; \
+    return OVMI_DISPATCH_NAME[instr->full_instr & OVM_INSTR_MASK](instr, state, values, memory, code);
+
+#define VAL(loc) values[loc]
+
+typedef OVMI_INSTR_PROTO((* ovmi_instr_exec_t));
+
+static ovmi_instr_exec_t OVMI_DISPATCH_NAME[];
+
+
+//
+// Special Operations
+//
+
+OVMI_INSTR_EXEC(nop) {
+    NEXT_OP;
+}
+
+
+//
+// Binary Operations
+//
+
+#define OVM_OP_EXEC(name, op) \
+    OVMI_INSTR_EXEC(name##_i32) { OVM_OP(OVM_TYPE_I32, op, i32); NEXT_OP; } \
+    OVMI_INSTR_EXEC(name##_i64) { OVM_OP(OVM_TYPE_I64, op, i64); NEXT_OP; } \
+    OVMI_INSTR_EXEC(name##_f32) { OVM_OP(OVM_TYPE_F32, op, f32); NEXT_OP; } \
+    OVMI_INSTR_EXEC(name##_f64) { OVM_OP(OVM_TYPE_F64, op, f64); NEXT_OP; }
+
+#define OVM_OP_UNSIGNED_EXEC(name, op) \
+    OVMI_INSTR_EXEC(name##_i32) { OVM_OP(OVM_TYPE_I32, op, u32); NEXT_OP; } \
+    OVMI_INSTR_EXEC(name##_i64) { OVM_OP(OVM_TYPE_I64, op, u64); NEXT_OP; } \
+    OVMI_INSTR_EXEC(name##_f32) { OVM_OP(OVM_TYPE_F32, op, f32); NEXT_OP; } \
+    OVMI_INSTR_EXEC(name##_f64) { OVM_OP(OVM_TYPE_F64, op, f64); NEXT_OP; }
+
+#define OVM_OP_INTEGER_EXEC(name, op) \
+    OVMI_INSTR_EXEC(name##_i32) { OVM_OP(OVM_TYPE_I32, op, i32); NEXT_OP; } \
+    OVMI_INSTR_EXEC(name##_i64) { OVM_OP(OVM_TYPE_I64, op, i64); NEXT_OP; }
+
+#define OVM_OP_INTEGER_UNSIGNED_EXEC(name, op) \
+    OVMI_INSTR_EXEC(name##_i32) { OVM_OP(OVM_TYPE_I32, op, u32); NEXT_OP; } \
+    OVMI_INSTR_EXEC(name##_i64) { OVM_OP(OVM_TYPE_I64, op, u64); NEXT_OP; }
+
+#define OVM_OP_FLOAT_EXEC(name, op) \
+    OVMI_INSTR_EXEC(name##_f32) { OVM_OP(OVM_TYPE_F32, op, f32); NEXT_OP; } \
+    OVMI_INSTR_EXEC(name##_f64) { OVM_OP(OVM_TYPE_F64, op, f64); NEXT_OP; }
+
+
+#define OVM_OP(t, op, ctype) \
+    ovm_assert(VAL(instr->a).type == t && VAL(instr->b).type == t); \
+    VAL(instr->r).ctype = VAL(instr->a).ctype op VAL(instr->b).ctype; \
+    VAL(instr->r).type = t;
+
+OVM_OP_EXEC(add, +)
+OVM_OP_EXEC(sub, -)
+OVM_OP_EXEC(mul, *)
+OVM_OP_EXEC(div_s, /)
+OVM_OP_UNSIGNED_EXEC(div, /)
+OVM_OP_INTEGER_UNSIGNED_EXEC(rem, %)
+OVM_OP_INTEGER_EXEC(rem_s, %)
+OVM_OP_INTEGER_UNSIGNED_EXEC(and, &)
+OVM_OP_INTEGER_UNSIGNED_EXEC(or, |)
+OVM_OP_INTEGER_UNSIGNED_EXEC(xor, ^)
+OVM_OP_INTEGER_UNSIGNED_EXEC(shl, <<)
+OVM_OP_INTEGER_UNSIGNED_EXEC(shr, >>)
+OVM_OP_INTEGER_EXEC(sar, >>)
+
+#undef OVM_OP
+
+#define OVM_OP(t, func, ctype) \
+    ovm_assert(VAL(instr->a).type == t && VAL(instr->b).type == t); \
+    VAL(instr->r).ctype = func( VAL(instr->a).ctype, VAL(instr->b).ctype ); \
+    VAL(instr->r).type = t;
+
+OVMI_INSTR_EXEC(rotl_i32) { OVM_OP(OVM_TYPE_I32, __rold, u32); NEXT_OP; }
+OVMI_INSTR_EXEC(rotl_i64) { OVM_OP(OVM_TYPE_I64, __rolq, u64); NEXT_OP; }
+OVMI_INSTR_EXEC(rotr_i32) { OVM_OP(OVM_TYPE_I32, __rord, u32); NEXT_OP; }
+OVMI_INSTR_EXEC(rotr_i64) { OVM_OP(OVM_TYPE_I64, __rorq, u64); NEXT_OP; }
+
+OVM_OP_FLOAT_EXEC(min, bh_min)
+OVM_OP_FLOAT_EXEC(max, bh_max)
+OVM_OP_FLOAT_EXEC(copysign, __ovm_copysign)
+
+#undef OVM_OP
+
+
+#define OVM_OP(t, op, ctype) \
+    ovm_assert(VAL(instr->a).type == t); \
+    VAL(instr->r).type = t; \
+    VAL(instr->r).ctype = (ctype) op (VAL(instr->a).ctype);
+
+OVMI_INSTR_EXEC(clz_i32) { OVM_OP(OVM_TYPE_I32, __builtin_clz, u32);   NEXT_OP; }
+OVMI_INSTR_EXEC(clz_i64) { OVM_OP(OVM_TYPE_I64, __builtin_clzll, u64); NEXT_OP; }
+OVMI_INSTR_EXEC(ctz_i32) { OVM_OP(OVM_TYPE_I32, __builtin_ctz, u32);   NEXT_OP; }
+OVMI_INSTR_EXEC(ctz_i64) { OVM_OP(OVM_TYPE_I64, __builtin_ctzll, u64); NEXT_OP; }
+OVMI_INSTR_EXEC(popcount_i32) { OVM_OP(OVM_TYPE_I32, __builtin_popcount, u32);   NEXT_OP; }
+OVMI_INSTR_EXEC(popcount_i64) { OVM_OP(OVM_TYPE_I64, __builtin_popcountll, u64); NEXT_OP; }
+
+OVM_OP_FLOAT_EXEC(abs,     __ovm_abs)
+OVM_OP_FLOAT_EXEC(neg,     -)
+OVM_OP_FLOAT_EXEC(ceil,    __ovm_ceil)
+OVM_OP_FLOAT_EXEC(floor,   __ovm_floor)
+OVM_OP_FLOAT_EXEC(trunc,   __ovm_trunc)
+OVM_OP_FLOAT_EXEC(nearest, __ovm_nearest)
+OVM_OP_FLOAT_EXEC(sqrt,    sqrt)
+
+#undef OVM_OP
+
+
+#define OVM_OP(t, op, ctype) \
+    ovm_assert(VAL(instr->a).type == t && VAL(instr->b).type == t); \
+    VAL(instr->r).type = OVM_TYPE_I32; \
+    VAL(instr->r).i32 = ((VAL(instr->a).ctype op VAL(instr->b).ctype)) ? 1 : 0;
+
+OVM_OP_EXEC(eq, ==)
+OVM_OP_EXEC(ne, !=)
+OVM_OP_UNSIGNED_EXEC(lt, <)
+OVM_OP_UNSIGNED_EXEC(le, <=)
+OVM_OP_UNSIGNED_EXEC(gt, >)
+OVM_OP_UNSIGNED_EXEC(ge, >=)
+OVM_OP_EXEC(lt_s, <)
+OVM_OP_EXEC(le_s, <=)
+OVM_OP_EXEC(gt_s, >)
+OVM_OP_EXEC(ge_s, >=)
+
+#undef OVM_OP
+
+
+
+//
+// Memory / register operations
+//
+
+#define OVM_IMM(t, dtype, stype) \
+    VAL(instr->r).type = t; \
+    VAL(instr->r).u64 = 0; \
+    VAL(instr->r).dtype = instr->stype;
+
+
+OVMI_INSTR_EXEC(imm_i32) { OVM_IMM(OVM_TYPE_I32, u32, i); NEXT_OP; }
+OVMI_INSTR_EXEC(imm_i64) { OVM_IMM(OVM_TYPE_I64, u64, l); NEXT_OP; }
+OVMI_INSTR_EXEC(imm_f32) { OVM_IMM(OVM_TYPE_F32, f32, f); NEXT_OP; }
+OVMI_INSTR_EXEC(imm_f64) { OVM_IMM(OVM_TYPE_F64, f64, d); NEXT_OP; }
+
+#undef OVM_IMM
+
+OVMI_INSTR_EXEC(mov) {
+    VAL(instr->r) = VAL(instr->a);
+    NEXT_OP;
+}
+
+#define OVM_LOAD(otype, type_, stype) \
+    OVMI_INSTR_EXEC(load_##otype) { \
+        ovm_assert(VAL(instr->a).type == OVM_TYPE_I32); \
+        u32 dest = VAL(instr->a).u32 + (u32) instr->b; \
+        if (dest == 0) __ovm_trigger_exception(state); \
+        VAL(instr->r).stype = * (stype *) &memory[dest]; \
+        VAL(instr->r).type = type_; \
+        NEXT_OP; \
+    }
+
+OVM_LOAD(i8,  OVM_TYPE_I8,  u8)
+OVM_LOAD(i16, OVM_TYPE_I16, u16)
+OVM_LOAD(i32, OVM_TYPE_I32, u32)
+OVM_LOAD(i64, OVM_TYPE_I64, u64)
+OVM_LOAD(f32, OVM_TYPE_F32, f32)
+OVM_LOAD(f64, OVM_TYPE_F64, f64)
+
+#undef OVM_LOAD
+
+#define OVM_STORE(otype, type_, stype) \
+    OVMI_INSTR_EXEC(store_##otype) { \
+        ovm_assert(VAL(instr->r).type == OVM_TYPE_I32); \
+        u32 dest = VAL(instr->r).u32 + (u32) instr->b; \
+        if (dest == 0) __ovm_trigger_exception(state); \
+        *(stype *) &memory[dest] = VAL(instr->a).stype; \
+        NEXT_OP; \
+    }
+
+
+OVM_STORE(i8,  OVM_TYPE_I8,  u8)
+OVM_STORE(i16, OVM_TYPE_I16, u16)
+OVM_STORE(i32, OVM_TYPE_I32, u32)
+OVM_STORE(i64, OVM_TYPE_I64, u64)
+OVM_STORE(f32, OVM_TYPE_F32, f32)
+OVM_STORE(f64, OVM_TYPE_F64, f64)
+
+#undef OVM_STORE
+
+OVMI_INSTR_EXEC(copy) {
+    u32 dest  = VAL(instr->r).u32;
+    u32 src   = VAL(instr->a).u32;
+    u32 count = VAL(instr->b).u32;
+
+    if (!dest || !src) __ovm_trigger_exception(state);
+
+    memmove(&memory[dest], &memory[src], count);
+
+    NEXT_OP;
+}
+
+OVMI_INSTR_EXEC(fill) {
+    i32 dest  = VAL(instr->r).i32;
+    u8  byte  = VAL(instr->a).u8;
+    i32 count = VAL(instr->b).i32;
+
+    if (!dest) __ovm_trigger_exception(state);
+
+    memset(&memory[dest], byte, count);
+
+    NEXT_OP;
+}
+
+OVMI_INSTR_EXEC(reg_get) {
+    VAL(instr->r) = state->registers[instr->a];
+
+    NEXT_OP;
+}
+
+OVMI_INSTR_EXEC(reg_set) {
+    state->registers[instr->r] = VAL(instr->a);
+
+    NEXT_OP;
+}
+
+OVMI_INSTR_EXEC(idx_arr) {
+    ovm_static_integer_array_t data_elem = state->program->static_data[instr->a];
+    ovm_assert(VAL(instr->b).u32 < (u32) data_elem.len);
+
+    VAL(instr->r).i32 = state->program->static_integers[data_elem.start_idx + VAL(instr->b).u32];
+    VAL(instr->r).type = OVM_TYPE_I32;
+
+    NEXT_OP;
+}
+
+
+//
+// Function calling
+//
+
+OVMI_INSTR_EXEC(param) {
+    bh_arr_push(state->params, VAL(instr->a));
+
+    NEXT_OP;
+}
+
+OVMI_INSTR_EXEC(return) {
+    ovm_value_t val = VAL(instr->a);
+    ovm_stack_frame_t frame = ovm__func_teardown_stack_frame(state);
+    state->pc = frame.return_address;
+    values = state->__frame_values;
+
+    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
+
+    NEXT_OP;
+}
+
+
+#define OVM_CALL_CODE(func_idx) \
+    i32 fidx = func_idx; \
+    ovm_func_t *func = &state->program->funcs[fidx]; \
+    i32 extra_params = bh_arr_length(state->params) - func->param_count; \
+    ovm_assert(extra_params >= 0); \
+    ovm__func_setup_stack_frame(state, func, instr->r); \
+    bh_arr_fastdeleten(state->params, func->param_count); \
+    if (func->kind == OVM_FUNC_INTERNAL) { \
+        values = state->__frame_values; \
+        memcpy(&VAL(0), &state->params[extra_params], func->param_count * sizeof(ovm_value_t)); \
+        state->pc = func->start_instr; \
+    } else { \
+        ovm_external_func_t external_func = state->external_funcs[func->external_func_idx]; \
+        external_func.native_func(external_func.userdata, &state->params[extra_params], &state->__tmp_value); \
+\
+        ovm__func_teardown_stack_frame(state); \
+\
+        if (instr->r >= 0) { \
+            VAL(instr->r) = state->__tmp_value; \
+        } \
+    }
+
+
+OVMI_INSTR_EXEC(call) {
+    OVM_CALL_CODE(instr->a);
+    NEXT_OP;
+}
+
+OVMI_INSTR_EXEC(calli) {
+    OVM_CALL_CODE(VAL(instr->a).i32);
+    NEXT_OP;
+}
+
+#undef OVM_CALL_CODE
+
+
+
+//
+// Branching Instructions
+//
+
+OVMI_INSTR_EXEC(br)     { state->pc += instr->a; NEXT_OP; }
+OVMI_INSTR_EXEC(bri)    { state->pc += VAL(instr->a).i32; NEXT_OP; }
+OVMI_INSTR_EXEC(br_nz)  { if (VAL(instr->b).i32 != 0) state->pc += instr->a; NEXT_OP; }
+OVMI_INSTR_EXEC(bri_nz) { if (VAL(instr->b).i32 != 0) state->pc += VAL(instr->a).i32; NEXT_OP; }
+OVMI_INSTR_EXEC(br_z)   { if (VAL(instr->b).i32 == 0) state->pc += instr->a; NEXT_OP; }
+OVMI_INSTR_EXEC(bri_z)  { if (VAL(instr->b).i32 == 0) state->pc += VAL(instr->a).i32; NEXT_OP; }
+
+
+//
+// Conversion
+//
+
+#define OVM_CVT(n1, n2, stype, dtype, otype, ctype) \
+    OVMI_INSTR_EXEC(cvt_##n1##_##n2) { \
+        state->__tmp_value.dtype = (ctype) VAL(instr->a).stype; \
+        state->__tmp_value.type = otype; \
+        VAL(instr->r) = state->__tmp_value; \
+        NEXT_OP; \
+    }
+
+OVM_CVT(i8, i16,   u8, u16, OVM_TYPE_I16, u16);
+OVM_CVT(i8, i32,   u8, u32, OVM_TYPE_I32, u32);
+OVM_CVT(i8, i64,   u8, u64, OVM_TYPE_I64, u64);
+OVM_CVT(i8_s, i16, i8, i16, OVM_TYPE_I16, i16);
+OVM_CVT(i8_s, i32, i8, i32, OVM_TYPE_I32, i32);
+OVM_CVT(i8_s, i64, i8, i64, OVM_TYPE_I64, i64);
+
+OVM_CVT(i16,   i8,  u16, u8,  OVM_TYPE_I8, u8);
+OVM_CVT(i16,   i32, u16, u32, OVM_TYPE_I32, u32);
+OVM_CVT(i16,   i64, u16, u64, OVM_TYPE_I64, u64);
+OVM_CVT(i16_s, i8,  i16, i8,  OVM_TYPE_I8, i8);
+OVM_CVT(i16_s, i32, i16, i32, OVM_TYPE_I32, i32);
+OVM_CVT(i16_s, i64, i16, i64, OVM_TYPE_I64, i64);
+
+OVM_CVT(i32,   i8,  u32, u8,  OVM_TYPE_I8,  u8);
+OVM_CVT(i32,   i16, u32, u16, OVM_TYPE_I16, u16);
+OVM_CVT(i32,   i64, u32, u64, OVM_TYPE_I64, u64);
+OVM_CVT(i32_s, i8,  i32, i8,  OVM_TYPE_I8,  i8);
+OVM_CVT(i32_s, i16, i32, i16, OVM_TYPE_I16, i16);
+OVM_CVT(i32_s, i64, i32, i64, OVM_TYPE_I64, i64);
+OVM_CVT(i32,   f32, u32, f32, OVM_TYPE_F32, f32);
+OVM_CVT(i32_s, f32, i32, f32, OVM_TYPE_F32, f32);
+OVM_CVT(i32,   f64, u32, f64, OVM_TYPE_F64, f64);
+OVM_CVT(i32_s, f64, i32, f64, OVM_TYPE_F64, f64);
+
+OVM_CVT(i64,   i8,  u64, u8,  OVM_TYPE_I8,  u8);
+OVM_CVT(i64,   i16, u64, u16, OVM_TYPE_I16, u16);
+OVM_CVT(i64,   i32, u64, u32, OVM_TYPE_I32, u32);
+OVM_CVT(i64_s, i8,  i64, i8,  OVM_TYPE_I8,  i8);
+OVM_CVT(i64_s, i16, i64, i16, OVM_TYPE_I16, i16);
+OVM_CVT(i64_s, i32, i64, i32, OVM_TYPE_I32, i32);
+OVM_CVT(i64,   f32, u64, f32, OVM_TYPE_F32, f32);
+OVM_CVT(i64_s, f32, i64, f32, OVM_TYPE_F32, f32);
+OVM_CVT(i64,   f64, u64, f64, OVM_TYPE_F64, f64);
+OVM_CVT(i64_s, f64, i64, f64, OVM_TYPE_F64, f64);
+
+OVM_CVT(f32,   i32, f32, u32, OVM_TYPE_I32, u32);
+OVM_CVT(f32,   i64, f32, u64, OVM_TYPE_I64, u64);
+OVM_CVT(f32,   f64, f32, f64, OVM_TYPE_F64, f64);
+OVM_CVT(f32_s, i32, f32, i32, OVM_TYPE_I32, i32);
+OVM_CVT(f32_s, i64, f32, i64, OVM_TYPE_I64, i64);
+OVM_CVT(f32_s, f64, f32, f64, OVM_TYPE_F64, f64);
+
+OVM_CVT(f64,   i32, f64, u32, OVM_TYPE_I32, u32);
+OVM_CVT(f64,   i64, f64, u64, OVM_TYPE_I64, u64);
+OVM_CVT(f64,   f32, f64, f32, OVM_TYPE_F32, f32);
+OVM_CVT(f64_s, i32, f64, i32, OVM_TYPE_I32, i32);
+OVM_CVT(f64_s, i64, f64, i64, OVM_TYPE_I64, i64);
+OVM_CVT(f64_s, f32, f64, f32, OVM_TYPE_F32, f32);
+
+#undef OVM_CVT
+
+#define OVM_CVT(n1, n2, stype, dtype, otype, ctype) \
+    OVMI_INSTR_EXEC(transmute_##n1##_##n2) { \
+        ovm_value_t tmp_val; \
+        tmp_val.dtype = *(ctype *) &VAL(instr->a).stype; \
+        tmp_val.type = otype; \
+        VAL(instr->r) = tmp_val; \
+        NEXT_OP; \
+    }
+
+OVM_CVT(i32, f32, u32, f32, OVM_TYPE_F32, f32);
+OVM_CVT(i64, f64, u64, f64, OVM_TYPE_F64, f64);
+OVM_CVT(f32, i32, f32, u32, OVM_TYPE_I32, u32);
+OVM_CVT(f64, i64, f64, u64, OVM_TYPE_I64, u64);
+
+#undef OVM_CVT
+
+
+//
+// Compare exchange
+//
+
+#define CMPXCHG(otype, ctype) \
+    OVMI_INSTR_EXEC(cmpxchg_##ctype) { \
+        pthread_mutex_lock(&state->engine->atomic_mutex); \
+        if (VAL(instr->r).u32 == 0) __ovm_trigger_exception(state); \
+        ctype *addr = (ctype *) &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 ; \
+        } \
+ \
+        pthread_mutex_unlock(&state->engine->atomic_mutex); \
+        NEXT_OP; \
+    }
+
+CMPXCHG(OVM_TYPE_I32, i32)
+CMPXCHG(OVM_TYPE_I64, i64)
+
+#undef CMPXCHG
+
+
+OVMI_INSTR_EXEC(illegal) {
+    __ovm_trigger_exception(state);
+    return ((ovm_value_t) {0});
+}
+
+//
+// Dispatch table
+//
+
+#define D(n) OVMI_FUNC_NAME(n)
+
+#define IROW_UNTYPED(name) D(name), NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+#define IROW_TYPED(name)   NULL, D(name##_i8), D(name##_i16), D(name##_i32), D(name##_i64), D(name##_f32), D(name##_f64), NULL,
+#define IROW_PARTIAL(name) NULL, NULL, NULL, D(name##_i32), D(name##_i64), D(name##_f32), D(name##_f64), NULL,
+#define IROW_INT(name)     NULL, NULL, NULL, D(name##_i32), D(name##_i64), NULL, NULL, NULL,
+#define IROW_FLOAT(name)   NULL, NULL, NULL, NULL, NULL, D(name##_f32), D(name##_f64), NULL,
+#define IROW_SAME(name)    D(name),D(name),D(name),D(name),D(name),D(name),D(name),NULL,
+
+static ovmi_instr_exec_t OVMI_DISPATCH_NAME[] = {
+    IROW_UNTYPED(nop) // 0x00
+    IROW_PARTIAL(add)
+    IROW_PARTIAL(sub)
+    IROW_PARTIAL(mul)
+    IROW_PARTIAL(div)
+    IROW_PARTIAL(div_s)
+    IROW_INT(rem)
+    IROW_INT(rem_s)
+    IROW_INT(and)
+    IROW_INT(or)
+    IROW_INT(xor)
+    IROW_INT(shl)
+    IROW_INT(shr)
+    IROW_INT(sar)
+    IROW_SAME(illegal)
+    IROW_SAME(illegal)
+    IROW_PARTIAL(imm) // 0x10
+    IROW_UNTYPED(mov)
+    IROW_TYPED(load)
+    IROW_TYPED(store)
+    IROW_UNTYPED(copy)
+    IROW_UNTYPED(fill)
+    IROW_UNTYPED(reg_get)
+    IROW_UNTYPED(reg_set)
+    IROW_UNTYPED(idx_arr)
+    IROW_PARTIAL(lt)
+    IROW_PARTIAL(lt_s)
+    IROW_PARTIAL(le)
+    IROW_PARTIAL(le_s)
+    IROW_PARTIAL(eq)
+    IROW_PARTIAL(ge)
+    IROW_PARTIAL(ge_s)
+    IROW_PARTIAL(gt)  // 0x20
+    IROW_PARTIAL(gt_s)
+    IROW_PARTIAL(ne)
+    IROW_UNTYPED(param)
+    IROW_UNTYPED(return)
+    IROW_UNTYPED(call)
+    IROW_UNTYPED(calli)
+    IROW_UNTYPED(br)
+    IROW_UNTYPED(br_z)
+    IROW_UNTYPED(br_nz)
+    IROW_UNTYPED(bri)
+    IROW_UNTYPED(bri_z)
+    IROW_UNTYPED(bri_nz)
+    IROW_INT(clz)
+    IROW_INT(ctz)
+    IROW_INT(popcount)
+    IROW_INT(rotl)  // 0x30
+    IROW_INT(rotr)
+    IROW_FLOAT(abs)
+    IROW_FLOAT(neg)
+    IROW_FLOAT(ceil)
+    IROW_FLOAT(floor)
+    IROW_FLOAT(trunc)
+    IROW_FLOAT(nearest)
+    IROW_FLOAT(sqrt)
+    IROW_FLOAT(min)
+    IROW_FLOAT(max)
+    IROW_FLOAT(copysign)
+    NULL, NULL, D(cvt_i8_i16), D(cvt_i8_i32), D(cvt_i8_i64), NULL, NULL, NULL,
+    NULL, NULL, D(cvt_i8_s_i16), D(cvt_i8_s_i32), D(cvt_i8_s_i64), NULL, NULL, NULL,
+    NULL, D(cvt_i16_i8),   NULL, D(cvt_i16_i32), D(cvt_i16_i64), NULL, NULL, NULL,
+    NULL, D(cvt_i16_s_i8), NULL, D(cvt_i16_s_i32), D(cvt_i16_s_i64), NULL, NULL, NULL,
+    NULL, D(cvt_i32_i8),   D(cvt_i32_i16),   NULL, D(cvt_i32_i64), D(cvt_i32_f32), D(cvt_i32_f64), NULL,  // 0x40
+    NULL, D(cvt_i32_s_i8), D(cvt_i32_s_i16), NULL, D(cvt_i32_s_i64), D(cvt_i32_s_f32), D(cvt_i32_s_f64), NULL,
+    NULL, D(cvt_i64_i8),   D(cvt_i64_i16),   D(cvt_i64_i32),   NULL, D(cvt_i64_f32), D(cvt_i64_f64), NULL,
+    NULL, D(cvt_i64_s_i8), D(cvt_i64_s_i16), D(cvt_i64_s_i32), NULL, D(cvt_i64_s_f32), D(cvt_i64_s_f64), NULL,
+    NULL, NULL, NULL, D(cvt_f32_i32), D(cvt_f32_i64), NULL, D(cvt_f32_f64), NULL,
+    NULL, NULL, NULL, D(cvt_f32_s_i32), D(cvt_f32_s_i64), NULL, D(cvt_f32_s_f64), NULL,
+    NULL, NULL, NULL, D(cvt_f64_i32), D(cvt_f64_i64), D(cvt_f64_f32), NULL, NULL,
+    NULL, NULL, NULL, D(cvt_f64_s_i32), D(cvt_f64_s_i64), D(cvt_f64_s_f32), NULL, NULL,
+    NULL, NULL, NULL, D(transmute_i32_f32), NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, D(transmute_i64_f64), NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, D(transmute_f32_i32), NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, D(transmute_f64_i64), NULL,
+    IROW_INT(cmpxchg)
+    IROW_SAME(illegal)
+};
+
+#undef D
+#undef IROW_UNTYPED
+#undef IROW_TYPED
+#undef IROW_PARTIAL
+#undef IROW_INT
+#undef IROW_FLOAT
+#undef IROW_SAME
+
+#undef OVM_OP_EXEC
+#undef OVM_OP_UNSIGNED_EXEC
+#undef OVM_OP_INTEGER_EXEC
+#undef OVM_OP_INTEGER_UNSIGNED_EXEC
+#undef OVM_OP_FLOAT_EXEC
+#undef OVMI_INSTR_PROTO
+#undef OVMI_INSTR_EXEC
+#undef NEXT_OP
+#undef VAL
+
+#undef OVMI_FUNC_NAME
+#undef OVMI_DISPATCH_NAME
+#undef OVMI_DEBUG_HOOK
+
index be1da68b6930fcd14b1634d42aa91b805701822e..5dd76b8520aed91b5240f664eca72213cb147f6c 100755 (executable)
Binary files a/shared/lib/linux_x86_64/lib/libovmwasm.so and b/shared/lib/linux_x86_64/lib/libovmwasm.so differ