From a0a0482dee106070d7177efd1e91dd204393c187 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Wed, 6 Jul 2022 10:03:58 -0500 Subject: [PATCH] progress towards a completely running test suite --- build.sh | 2 +- include/bh.h | 112 ++++++++++++++++++++++++++++++++- include/onyx_wasm.h | 1 + include/vm.h | 8 +-- src/vm/vm.c | 88 +++++++++++++------------- src/wasm/instance.c | 27 ++++++-- src/wasm/module_parsing.c.incl | 2 +- src/wasm/type.c | 15 +++++ 8 files changed, 197 insertions(+), 58 deletions(-) diff --git a/build.sh b/build.sh index e4095d8..160e456 100755 --- a/build.sh +++ b/build.sh @@ -2,7 +2,7 @@ CC="gcc" WARNINGS='-Wimplicit -Wmisleading-indentation -Wparentheses -Wsequence-point -Wreturn-type -Wshift-negative-value -Wunused-but-set-parameter -Wunused-but-set-variable -Wunused-function -Wunused-label -Wmaybe-uninitialized -Wsign-compare -Wstrict-overflow -Wduplicated-branches -Wduplicated-cond -Wtrigraphs -Waddress -Wlogical-op' -FLAGS="-O3" +FLAGS="-g3" LIBS="-pthread" INCLUDES="-I include" TARGET="libonyx_embedder.so" diff --git a/include/bh.h b/include/bh.h index 498ef08..55c9408 100644 --- a/include/bh.h +++ b/include/bh.h @@ -27,6 +27,7 @@ #include #include #include + #include #endif #include @@ -276,6 +277,26 @@ BH_DEF BH_ALLOCATOR_PROC(bh_arena_allocator_proc); +// ATOMIC ARENA ALLOCATOR +typedef struct bh_atomic_arena { + bh_allocator backing; + ptr first_arena, current_arena; + isize size, arena_size; // in bytes + + pthread_mutex_t mutex; +} bh_atomic_arena; + +typedef struct bh__atomic_arena_internal { + ptr next_arena; + void* data; // Not actually a pointer, just used for the offset +} bh__atomic_arena_internal; + +BH_DEF void bh_atomic_arena_init(bh_atomic_arena* alloc, bh_allocator backing, isize arena_size); +BH_DEF void bh_atomic_arena_free(bh_atomic_arena* alloc); +BH_DEF bh_allocator bh_atomic_arena_allocator(bh_atomic_arena* alloc); +BH_DEF BH_ALLOCATOR_PROC(bh_atomic_arena_allocator_proc); + + // SCRATCH ALLOCATOR @@ -1101,6 +1122,95 @@ BH_DEF BH_ALLOCATOR_PROC(bh_arena_allocator_proc) { } +// ATOMIC ARENA ALLOCATOR IMPLEMENTATION +BH_DEF void bh_atomic_arena_init(bh_atomic_arena* alloc, bh_allocator backing, isize arena_size) { + arena_size = bh_max(arena_size, size_of(ptr)); + ptr data = bh_alloc(backing, arena_size); + + alloc->backing = backing; + alloc->arena_size = arena_size; + alloc->size = sizeof(ptr); + alloc->first_arena = data; + alloc->current_arena = data; + pthread_mutex_init(&alloc->mutex, NULL); + + ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL; +} + +BH_DEF void bh_atomic_arena_free(bh_atomic_arena* alloc) { + bh__atomic_arena_internal *walker = (bh__atomic_arena_internal *) alloc->first_arena; + bh__atomic_arena_internal *trailer = walker; + while (walker != NULL) { + walker = walker->next_arena; + bh_free(alloc->backing, trailer); + trailer = walker; + } + + alloc->first_arena = NULL; + alloc->current_arena = NULL; + alloc->arena_size = 0; + alloc->size = 0; + pthread_mutex_destroy(&alloc->mutex); +} + +BH_DEF bh_allocator bh_atomic_arena_allocator(bh_atomic_arena* alloc) { + return (bh_allocator) { + .proc = bh_atomic_arena_allocator_proc, + .data = alloc, + }; +} + +BH_DEF BH_ALLOCATOR_PROC(bh_atomic_arena_allocator_proc) { + bh_atomic_arena* alloc_arena = (bh_atomic_arena*) data; + pthread_mutex_lock(&alloc_arena->mutex); + + ptr retval = NULL; + + switch (action) { + case bh_allocator_action_alloc: { + bh_align(size, alignment); + bh_align(alloc_arena->size, alignment); + + if (size > alloc_arena->arena_size - size_of(ptr)) { + // Size too large for the arena + break; + } + + if (alloc_arena->size + size >= alloc_arena->arena_size) { + bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size); + + if (new_arena == NULL) { + bh_printf_err("Arena Allocator: couldn't allocate new arena"); + break; + } + + new_arena->next_arena = NULL; + ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena; + alloc_arena->current_arena = new_arena; + alloc_arena->size = sizeof(ptr); + } + + retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size); + alloc_arena->size += size; + } break; + + case bh_allocator_action_resize: { + // Do nothing since this is a fixed allocator + } break; + + case bh_allocator_action_free: { + // Do nothing since this allocator isn't made for freeing memory + } break; + } + + pthread_mutex_unlock(&alloc_arena->mutex); + return retval; +} + + + + + // SCRATCH ALLOCATOR IMPLEMENTATION @@ -1294,7 +1404,7 @@ BH_DEF i64 leb128_to_int(u8* bytes, i32 *byte_count) { } if ((shift < size) && (byte & 0x40) != 0) { - return res | ((~0) << shift); + return res | ((~(u64) 0x0) << shift); } return res; diff --git a/include/onyx_wasm.h b/include/onyx_wasm.h index f5258b4..a775dc7 100644 --- a/include/onyx_wasm.h +++ b/include/onyx_wasm.h @@ -202,6 +202,7 @@ struct wasm_instance_t { }; +bool wasm_functype_equals(wasm_functype_t *a, wasm_functype_t *b); wasm_functype_t *wasm_module_index_functype(wasm_module_t *module, int index); wasm_tabletype_t *wasm_module_index_tabletype(wasm_module_t *module, int index); diff --git a/include/vm.h b/include/vm.h index b692601..7583735 100644 --- a/include/vm.h +++ b/include/vm.h @@ -27,9 +27,9 @@ typedef struct ovm_static_integer_array_t ovm_static_integer_array_t; // // Contains storage. struct ovm_store_t { - bh_allocator heap_allocator; - bh_arena arena; - bh_allocator arena_allocator; + bh_allocator heap_allocator; + bh_atomic_arena arena; + bh_allocator arena_allocator; }; ovm_store_t *ovm_store_new(); @@ -180,7 +180,7 @@ void ovm_func_delete(ovm_func_t *func); ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program, i32 func_idx, i32 param_count, ovm_value_t *params); -void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program); +ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program); // // Instruction encoding diff --git a/src/vm/vm.c b/src/vm/vm.c index 353e2eb..30fde47 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -23,14 +23,14 @@ ovm_store_t *ovm_store_new() { store->heap_allocator = bh_heap_allocator(); - bh_arena_init(&store->arena, store->heap_allocator, 1 << 20); - store->arena_allocator = bh_arena_allocator(&store->arena); + bh_atomic_arena_init(&store->arena, store->heap_allocator, 1 << 20); + store->arena_allocator = bh_atomic_arena_allocator(&store->arena); return store; } void ovm_store_delete(ovm_store_t *store) { - bh_arena_free(&store->arena); + bh_atomic_arena_free(&store->arena); free(store); } @@ -466,7 +466,7 @@ void ovm_engine_memory_copy(ovm_engine_t *engine, i64 target, void *data, i64 si // Should there be another mechanism for this? or is this the most concise way? ovm_state_t *ovm_state_new(ovm_engine_t *engine, ovm_program_t *program) { ovm_store_t *store = engine->store; - ovm_state_t *state = bh_alloc_item(store->heap_allocator, ovm_state_t); + ovm_state_t *state = bh_alloc_item(store->arena_allocator, ovm_state_t); state->store = store; state->pc = 0; @@ -496,8 +496,6 @@ void ovm_state_delete(ovm_state_t *state) { bh_arr_free(state->stack_frames); bh_arr_free(state->registers); bh_arr_free(state->external_funcs); - - bh_free(store->heap_allocator, state); } void ovm_state_register_external_func(ovm_state_t *state, i32 idx, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data) { @@ -549,7 +547,12 @@ static inline ovm_stack_frame_t ovm__func_teardown_stack_frame(ovm_engine_t *eng ovm_stack_frame_t frame = bh_arr_pop(state->stack_frames); bh_arr_fastdeleten(state->numbered_values, frame.value_number_count); - state->value_number_offset = bh_arr_last(state->stack_frames).value_number_base; + if (bh_arr_length(state->stack_frames) == 0) { + state->value_number_offset = 0; + } else { + state->value_number_offset = bh_arr_last(state->stack_frames).value_number_base; + } + return frame; } @@ -559,9 +562,6 @@ ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_ switch (func.kind) { case OVM_FUNC_INTERNAL: { - bh_arr_insert_end(state->numbered_values, 1); - state->value_number_offset += 1; - ovm__func_setup_stack_frame(engine, state, program, func_idx, 0); fori (i, 0, param_count) { @@ -569,10 +569,9 @@ ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_ } state->pc = func.start_instr; - ovm_run_code(engine, state, program); + ovm_value_t result = ovm_run_code(engine, state, program); - state->value_number_offset -= 1; - return bh_arr_pop(state->numbered_values); + return result; } case OVM_FUNC_EXTERNAL: { @@ -580,8 +579,7 @@ ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_ ovm_value_t result = {0}; ovm_external_func_t external_func = state->external_funcs[func.external_func_idx]; - external_func.native_func(external_func.userdata, state->params, &result); - bh_arr_fastdeleten(state->params, func.param_count); + external_func.native_func(external_func.userdata, params, &result); ovm__func_teardown_stack_frame(engine, state, program); return result; @@ -634,7 +632,7 @@ static inline double __ovm_copysign(a, b) double a, b; { } -void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program) { +ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program) { assert(engine); assert(state); assert(program); @@ -722,25 +720,25 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr OVM_OP(OVMI_REM_S, OVM_TYPE_I32, %, i32) OVM_OP(OVMI_REM_S, OVM_TYPE_I64, %, i64) - OVM_OP(OVMI_AND, OVM_TYPE_I8 , &, 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_AND, OVM_TYPE_I8 , &, u8) + OVM_OP(OVMI_AND, OVM_TYPE_I16, &, u16) + OVM_OP(OVMI_AND, OVM_TYPE_I32, &, u32) + OVM_OP(OVMI_AND, OVM_TYPE_I64, &, u64) - OVM_OP(OVMI_OR, OVM_TYPE_I8 , |, 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_OR, OVM_TYPE_I8 , |, u8) + OVM_OP(OVMI_OR, OVM_TYPE_I16, |, u16) + OVM_OP(OVMI_OR, OVM_TYPE_I32, |, u32) + OVM_OP(OVMI_OR, OVM_TYPE_I64, |, u64) - OVM_OP(OVMI_XOR, OVM_TYPE_I8 , ^, i8) - OVM_OP(OVMI_XOR, OVM_TYPE_I16, ^, i16) - OVM_OP(OVMI_XOR, OVM_TYPE_I32, ^, i32) - OVM_OP(OVMI_XOR, OVM_TYPE_I64, ^, i64) + OVM_OP(OVMI_XOR, OVM_TYPE_I8 , ^, u8) + OVM_OP(OVMI_XOR, OVM_TYPE_I16, ^, u16) + OVM_OP(OVMI_XOR, OVM_TYPE_I32, ^, u32) + OVM_OP(OVMI_XOR, OVM_TYPE_I64, ^, u64) - OVM_OP(OVMI_SHL, OVM_TYPE_I8 , <<, 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_SHL, OVM_TYPE_I8 , <<, u8) + OVM_OP(OVMI_SHL, OVM_TYPE_I16, <<, u16) + OVM_OP(OVMI_SHL, OVM_TYPE_I32, <<, u32) + OVM_OP(OVMI_SHL, OVM_TYPE_I64, <<, u64) OVM_OP(OVMI_SHR, OVM_TYPE_I8 , >>, u8) OVM_OP(OVMI_SHR, OVM_TYPE_I16, >>, u16) @@ -975,12 +973,12 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr } case OVMI_REG_GET: { - VAL(instr.r) = ovm_state_register_get(state, instr.a); + VAL(instr.r) = state->registers[instr.a]; break; } case OVMI_REG_SET: { - ovm_state_register_set(state, instr.r, VAL(instr.a)); + state->registers[instr.r] = VAL(instr.a); break; } @@ -999,17 +997,17 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr ovm_value_t val = VAL(instr.a); ovm_stack_frame_t frame = ovm__func_teardown_stack_frame(engine, state, program); - if (frame.return_number_value >= 0) { - VAL(frame.return_number_value) = val; - } - if (bh_arr_length(state->stack_frames) == 0) { - return; + return val; } ovm_func_t *new_func = bh_arr_last(state->stack_frames).func; if (new_func->kind == OVM_FUNC_EXTERNAL) { - return; + return val; + } + + if (frame.return_number_value >= 0) { + VAL(frame.return_number_value) = val; } // printf("Returning from %s to %s: ", frame.func->name, bh_arr_last(state->stack_frames).func->name); @@ -1023,10 +1021,10 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr #define OVM_CALL_CODE(func_idx) \ i32 fidx = func_idx; \ ovm_func_t *func = &program->funcs[fidx]; \ + i32 extra_params = bh_arr_length(state->params) - func->param_count; \ if (func->kind == OVM_FUNC_INTERNAL) { \ ovm__func_setup_stack_frame(engine, state, program, fidx, instr.r); \ \ - i32 extra_params = bh_arr_length(state->params) - func->param_count; \ fori (i, 0, func->param_count) { \ VAL(i) = state->params[i + extra_params]; \ } \ @@ -1034,13 +1032,11 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr \ state->pc = func->start_instr; \ } else { \ - ovm__func_setup_stack_frame(engine, state, program, fidx, 0); \ - \ - i32 extra_params = bh_arr_length(state->params) - func->param_count; \ + ovm__func_setup_stack_frame(engine, state, program, fidx, instr.r); \ \ ovm_value_t result = {0}; \ ovm_external_func_t external_func = state->external_funcs[func->external_func_idx]; \ - external_func.native_func(external_func.userdata, state->params + extra_params, &result); \ + external_func.native_func(external_func.userdata, &state->params[extra_params], &result); \ bh_arr_fastdeleten(state->params, func->param_count); \ \ ovm__func_teardown_stack_frame(engine, state, program); \ @@ -1178,5 +1174,7 @@ void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *progr release_mutex_at_end = false; } } + + return ((ovm_value_t) {0}); } diff --git a/src/wasm/instance.c b/src/wasm/instance.c index 0f5ebcd..3300a7f 100644 --- a/src/wasm/instance.c +++ b/src/wasm/instance.c @@ -77,6 +77,10 @@ struct ovm_wasm_binding { (w).kind = WASM_F64; \ (w).of.f64 = (o).f64; \ break; \ + \ + default: \ + printf("INVALID: %d\n", (o).type); \ + assert(("invalid ovm value type for conversion", 0)); \ } } static wasm_trap_t *wasm_to_ovm_func_call_binding(void *vbinding, const wasm_val_vec_t *args, wasm_val_vec_t *res) { @@ -106,14 +110,18 @@ static void ovm_to_wasm_func_call_binding(void *env, ovm_value_t* params, ovm_va OVM_TO_WASM(params[i], wasm_params.data[i]); } + wasm_val_t return_value; wasm_val_vec_t wasm_results; - wasm_results.data = alloca(sizeof(wasm_val_t) * binding->result_count); + wasm_results.data = &return_value; wasm_results.size = binding->result_count; wasm_trap_t *trap = wasm_func_call(binding->func, &wasm_params, &wasm_results); assert(!trap); - if (binding->result_count > 0) WASM_TO_OVM(wasm_results.data[0], *res); + if (binding->result_count > 0) { + assert(wasm_results.data[0].kind == binding->func->inner.type->func.results.data[0]->kind); + WASM_TO_OVM(return_value, *res); + } } static void wasm_memory_init(void *env, ovm_value_t* params, ovm_value_t *res) { @@ -136,14 +144,20 @@ static void prepare_instance(wasm_instance_t *instance, const wasm_extern_vec_t // // Place imports in their corresponding "bucket" fori (i, 0, (int) imports->size) { + assert(instance->module->imports.data[i]->type->kind == imports->data[i]->type->kind); + switch (wasm_extern_kind(imports->data[i])) { case WASM_EXTERN_FUNC: { - wasm_func_t *func = wasm_extern_as_func(imports->data[i]); - bh_arr_push(instance->funcs, func); - wasm_importtype_t *importtype = instance->module->imports.data[i]; struct wasm_functype_inner_t *functype = &importtype->type->func; + if (!wasm_functype_equals(wasm_externtype_as_functype(importtype->type), wasm_externtype_as_functype(imports->data[i]->type))) { + assert(("MISMATCHED FUNCTION TYPE", 0)); + } + + wasm_func_t *func = wasm_extern_as_func(imports->data[i]); + bh_arr_push(instance->funcs, func); + ovm_wasm_binding *binding = bh_alloc(ovm_store->arena_allocator, sizeof(*binding)); binding->param_count = functype->params.size; binding->result_count = functype->results.size; @@ -287,7 +301,7 @@ static void prepare_instance(wasm_instance_t *instance, const wasm_extern_vec_t wasm_instance_t *wasm_instance_new(wasm_store_t *store, const wasm_module_t *module, const wasm_extern_vec_t *imports, wasm_trap_t **trap) { - wasm_instance_t *instance = bh_alloc(store->engine->store->arena_allocator, sizeof(*instance)); + wasm_instance_t *instance = bh_alloc(store->engine->store->heap_allocator, sizeof(*instance)); instance->store = store; instance->module = module; @@ -317,6 +331,7 @@ void wasm_instance_delete(wasm_instance_t *instance) { wasm_extern_vec_delete(&instance->exports); ovm_state_delete(instance->state); + bh_free(instance->store->engine->store->heap_allocator, instance); } void wasm_instance_exports(const wasm_instance_t *instance, wasm_extern_vec_t *out) { diff --git a/src/wasm/module_parsing.c.incl b/src/wasm/module_parsing.c.incl index 48af61b..2b09548 100644 --- a/src/wasm/module_parsing.c.incl +++ b/src/wasm/module_parsing.c.incl @@ -143,7 +143,7 @@ static void parse_import_section(build_context *ctx) { wasm_byte_vec_new_uninitialized(&import_name, import_name_size); fori (n, 0, import_name_size) import_name.data[n] = CONSUME_BYTE(ctx); - wasm_externtype_t *import_type; + wasm_externtype_t *import_type = NULL; switch (CONSUME_BYTE(ctx)) { case 0x00: { unsigned int type_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset); diff --git a/src/wasm/type.c b/src/wasm/type.c index 37fa859..ba34497 100644 --- a/src/wasm/type.c +++ b/src/wasm/type.c @@ -66,6 +66,21 @@ const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t *functype) return &functype->type.func.results; } +bool wasm_functype_equals(wasm_functype_t *a, wasm_functype_t *b) { + if (a->type.func.params.size != b->type.func.params.size) return false; + if (a->type.func.results.size != b->type.func.results.size) return false; + + fori (i, 0, (int) a->type.func.params.size) { + if (a->type.func.params.data[i]->kind != b->type.func.params.data[i]->kind) return false; + } + + fori (i, 0, (int) a->type.func.results.size) { + if (a->type.func.results.data[i]->kind != b->type.func.results.data[i]->kind) return false; + } + + return true; +} + WASM_DECLARE_VEC_IMPL(functype, *) -- 2.25.1