From: Brendan Hansen Date: Sat, 17 Dec 2022 19:53:13 +0000 (-0600) Subject: big refactor in interpreter for faster non-debugging running X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=b9f41dbf65d21b3dc91a1c103cac87be0f67506d;p=onyx.git big refactor in interpreter for faster non-debugging running --- diff --git a/interpreter/include/vm.h b/interpreter/include/vm.h index 4048ab16..c4804e9b 100644 --- a/interpreter/include/vm.h +++ b/interpreter/include/vm.h @@ -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; }; diff --git a/interpreter/src/vm/vm.c b/interpreter/src/vm/vm.c index 46a59379..861325f0 100644 --- a/interpreter/src/vm/vm.c +++ b/interpreter/src/vm/vm.c @@ -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 index 00000000..6a5d97fa --- /dev/null +++ b/interpreter/src/vm/vm_instrs.h @@ -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 + diff --git a/shared/lib/linux_x86_64/lib/libovmwasm.so b/shared/lib/linux_x86_64/lib/libovmwasm.so index be1da68b..5dd76b85 100755 Binary files a/shared/lib/linux_x86_64/lib/libovmwasm.so and b/shared/lib/linux_x86_64/lib/libovmwasm.so differ