From: Brendan Hansen Date: Mon, 13 Jun 2022 22:48:49 +0000 (-0500) Subject: basic VM with func calls setup! X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=dcdaf1e147f4cdbfefd3653e10ee7cbc61a28f36;p=onyx-embedder.git basic VM with func calls setup! --- diff --git a/build.sh b/build.sh index f3c2fe4..829b240 100755 --- a/build.sh +++ b/build.sh @@ -12,6 +12,6 @@ $CC $FLAGS $INCLUDES -shared -fPIC -o $TARGET $C_FILES $LIBS $WARNINGS C_FILES="src/cli.c" TARGET=onyx-debug -LIBS="-L$(pwd) -lonyx_embedder" +LIBS="-L$(pwd) -lonyx_embedder -Wl,-rpath=./" $CC $FLAGS $INCLUDES -o $TARGET $C_FILES $LIBS $WARNINGS diff --git a/include/bh.h b/include/bh.h index b187b96..b83fd81 100644 --- a/include/bh.h +++ b/include/bh.h @@ -585,6 +585,7 @@ typedef struct bh__arr { #define bh_arr_deleten(arr, i, n) (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n)) #define bh_arr_fastdelete(arr, i) (arr[i] = arr[--bh__arrhead(arr)->length]) +#define bh_arr_fastdeleten(arr, n) (bh__arrhead(arr)->length -= n) #define bh_arr_each(T, var, arr) for (T* var = (arr); !bh_arr_end((arr), var); var++) #define bh_arr_rev_each(T, var, arr) for (T* var = &bh_arr_last((arr)); !bh_arr_start((arr), var); var--) @@ -2318,7 +2319,7 @@ void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) { memmove( (char *)(*arr) + elemsize * index, // Target - (char *)(*arr) + elemsize * (index + numelems), // Source + (char *)(*arr) + elemsize * (index + numelems), // Source elemsize * (arrptr->length - (index + numelems))); // Length arrptr->length -= numelems; } diff --git a/include/vm.h b/include/vm.h index 08087ea..4005773 100644 --- a/include/vm.h +++ b/include/vm.h @@ -8,13 +8,30 @@ typedef u8 ovm_valtype_t; typedef i32 ovm_valnum_t; typedef u32 ovm_instr_kind_t; +typedef struct ovm_store_t ovm_store_t; typedef struct ovm_engine_t ovm_engine_t; typedef struct ovm_program_t ovm_program_t; typedef struct ovm_state_t ovm_state_t; +typedef struct ovm_stack_frame_t ovm_stack_frame_t; typedef struct ovm_func_t ovm_func_t; +typedef struct ovm_native_func_t ovm_native_func_t; +typedef struct ovm_value_t ovm_value_t; typedef struct ovm_instr_t ovm_instr_t; typedef struct ovm_static_data_t ovm_static_data_t; + +// +// Contains storage. +struct ovm_store_t { + bh_allocator heap_allocator; + bh_arena arena; + bh_allocator arena_allocator; +}; + +ovm_store_t *ovm_store_new(); +void ovm_Store_delete(ovm_store_t *store); + + struct ovm_static_data_t { i64 dest_addr; void *data; @@ -26,12 +43,21 @@ struct ovm_static_data_t { // VM. It can be constructed incrementally as needed. // struct ovm_program_t { + ovm_store_t *store; + bh_arr(ovm_func_t *) funcs; + bh_arr(ovm_native_func_t) native_funcs; bh_arr(ovm_static_data_t) initialized_data; + + bh_arr(ovm_instr_t) code; }; -ovm_program_t *ovm_program_new(); +ovm_program_t *ovm_program_new(ovm_store_t *store); void ovm_program_delete(ovm_program_t *program); +void ovm_program_add_instructions(ovm_program_t *program, i32 instr_count, ovm_instr_t *instrs); +void ovm_program_register_func(ovm_program_t *program, i32 instr, i32 param_count, i32 value_number_count); +void ovm_program_register_native_func(ovm_program_t *program, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count); +void ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count); // // Represents the running configuration and static @@ -39,11 +65,13 @@ void ovm_program_delete(ovm_program_t *program); // If multiple threads are used, only one engine is needed. // struct ovm_engine_t { + ovm_store_t *store; + i64 memory_size; // This is probably going to always be 4GiB. void *memory; }; -ovm_engine_t *ovm_engine_new(); +ovm_engine_t *ovm_engine_new(ovm_store_t *store); void ovm_engine_delete(ovm_engine_t *engine); // @@ -51,12 +79,32 @@ void ovm_engine_delete(ovm_engine_t *engine); // If multiple threads are used, multiple states are needed. // struct ovm_state_t { + ovm_store_t *store; + i32 pc; + i32 value_number_offset; + + bh_arr(ovm_value_t) numbered_values; + bh_arr(ovm_value_t) params; + bh_arr(ovm_stack_frame_t) stack_frames; }; -ovm_state_t *ovm_state_new(); +ovm_state_t *ovm_state_new(ovm_engine_t *engine); void ovm_state_delete(ovm_state_t *state); + +// +// +struct ovm_stack_frame_t { + ovm_func_t *func; + i32 value_number_count; + i32 value_number_base; + + i32 return_address; + i32 return_number_value; +}; + + // // Represents a function that can be executed on the VM. // @@ -66,17 +114,28 @@ struct ovm_func_t { // to reference this function. It is only here for debugging and posterity. i32 id; - // - // parameters - // return type + i32 start_instr; + + i32 param_count; + i32 value_number_count; - bh_arr(ovm_instr_t) instructions; + // return type }; ovm_func_t *ovm_func_new(); ovm_instr_t *ovm_func_add_instruction(ovm_func_t *func, ovm_instr_kind_t instr, ovm_valtype_t type); void ovm_func_delete(ovm_func_t *func); +struct ovm_native_func_t { + i32 param_count; + + void (*native_func)(void *userdata, ovm_value_t* params, ovm_value_t* result); + void *userdata; +}; + +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); +void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program); + // // Instruction encoding // @@ -93,9 +152,31 @@ void ovm_func_delete(ovm_func_t *func); #define OVM_TYPE_F64 0x06 #define OVM_TYPE_V128 0x07 +struct ovm_value_t { + ovm_valtype_t type; + union { + i8 i8; + i16 i16; + i32 i32; + i64 i64; + u8 u8; + u16 u16; + u32 u32; + u64 u64; + f32 f32; + f64 f64; + }; +}; + struct ovm_instr_t { - ovm_instr_kind_t instr : 24; - ovm_valtype_t type; + union { + struct { + ovm_valtype_t type : 8; + ovm_instr_kind_t instr : 24; + }; + + u32 full_instr; + }; // Destination value number. ovm_valnum_t r; @@ -115,10 +196,45 @@ struct ovm_instr_t { }; #define OVMI_NOP 0x00 -#define OVMI_ADD 0x01 -#define OVMI_SUB 0x02 -#define OVMI_MUL 0x03 -#define OVMI_DIV 0x04 +#define OVMI_ADD 0x01 // r = a + b +#define OVMI_SUB 0x02 // r = a - b +#define OVMI_MUL 0x03 // r = a * b +#define OVMI_DIV 0x04 // r = a / b +#define OVMI_REM 0x05 // r = a % b + +#define OVMI_AND 0x06 // r = a & b +#define OVMI_OR 0x07 // r = a | b +#define OVMI_XOR 0x08 // r = a ^ b +#define OVMI_NOT 0x09 // r = ~a +#define OVMI_SHL 0x0A // r = a << b +#define OVMI_SHR 0x0B // r = a >> b +#define OVMI_SAR 0x0C // r = a >>> b + +#define OVMI_IMM 0x0D // r = imm a + +#define OVMI_LT 0x20 // r = a < b +#define OVMI_LT_S 0x21 // r = a < b +#define OVMI_LE 0x22 // r = a <= b +#define OVMI_LE_S 0x23 // r = a <= b +#define OVMI_EQ 0x24 // r = a == b +#define OVMI_GE 0x25 // r = a >= b +#define OVMI_GE_S 0x26 // r = a >= b +#define OVMI_GT 0x27 // r = a > b +#define OVMI_GT_S 0x28 // r = a > b +#define OVMI_NE 0x29 // r = a != b + +#define OVMI_PARAM 0x30 // push a +#define OVMI_RETURN 0x31 // return a +#define OVMI_CALL 0x32 // r = a(...) +#define OVMI_NATIVE_CALL 0x33 // r = a(...) + +#define OVMI_BR 0x40 // br a +#define OVMI_BR_NZ 0x41 // br a if b != 0 + +#define OVMI_CLZ 0x50 // r = clz(a) +#define OVMI_CTZ 0x51 // r = ctr(a) +#define OVMI_POPCNT 0x52 // r = popcnt(a) + // // OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I32) == instruction for adding i32s diff --git a/src/cli.c b/src/cli.c index 3324024..0fe3882 100644 --- a/src/cli.c +++ b/src/cli.c @@ -1,7 +1,53 @@ +#include "vm.h" + +void print_result(void *data, ovm_value_t *params, ovm_value_t *result) { + printf("Result: %f\n", params[0].f32); + + result->type = OVM_TYPE_F32; + result->f32 = 123; +} int main(int argc, char *argv[]) { - int embed_test(); - embed_test(); + + ovm_store_t *store = ovm_store_new(); + + ovm_program_t *prog = ovm_program_new(store); + + static ovm_instr_t instrs[] = { + // { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 0, 10 }, + // { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 1, 20 }, + // { .full_instr = OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F32), 2, 0, 1 }, + { .full_instr = OVMI_PARAM, 0, 0 }, + { .full_instr = OVMI_PARAM, 0, 1 }, + { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 5, 1 }, + { .full_instr = OVMI_CALL, 2, 5 }, + { .full_instr = OVMI_PARAM, 0, 2 }, + { .full_instr = OVM_TYPED_INSTR(OVMI_IMM, OVM_TYPE_I32), 3, 0 }, + { .full_instr = OVM_TYPED_INSTR(OVMI_NATIVE_CALL, OVM_TYPE_F32), 4, 3 }, + { .full_instr = OVMI_RETURN, 0, 4 }, + + + { .full_instr = OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_F32), 2, 0, 1 }, + { .full_instr = OVMI_RETURN, 0, 2 }, + }; + + ovm_program_add_instructions(prog, sizeof(instrs) / sizeof(*instrs), instrs); + ovm_program_register_func(prog, 0, 2, 5); + ovm_program_register_func(prog, 8, 2, 3); + ovm_program_register_native_func(prog, print_result, NULL, 1); + + ovm_program_print_instructions(prog, 0, sizeof(instrs) / sizeof(*instrs)); + + ovm_engine_t *engine = ovm_engine_new(store); + ovm_state_t *state = ovm_state_new(engine); + + state->pc = 0; + ovm_value_t values[] = { + { .type = OVM_TYPE_F32, .f32 = 20 }, + { .type = OVM_TYPE_F32, .f32 = 40 }, + }; + ovm_value_t result = ovm_func_call(engine, state, prog, 0, 2, values); + printf("%d %f\n", result.type, result.f32); return 0; } diff --git a/src/vm/vm.c b/src/vm/vm.c index a199988..9465cb5 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -1 +1,535 @@ #include "vm.h" + +#include + +// +// 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; + } + } +} + diff --git a/src/wasm.c b/src/wasm.c index 5478453..8ad5cf9 100644 --- a/src/wasm.c +++ b/src/wasm.c @@ -6,8 +6,3 @@ #include "stb_ds.h" #include "onyx_wasm.h" - -i32 embed_test() { - printf("Build works.\n"); - return 0; -}