getting close to a full test
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 27 Jun 2022 03:38:26 +0000 (22:38 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 27 Jun 2022 03:38:26 +0000 (22:38 -0500)
include/onyx_wasm.h
src/vm/vm.c
src/wasm/extern.c
src/wasm/func.c
src/wasm/global.c
src/wasm/instance.c
src/wasm/memory.c
src/wasm/module.c
src/wasm/module_parsing.c.incl
src/wasm/store.c
src/wasm/table.c

index eae8c3d922a93bca230cffb8c2d8b7620f0c4787..d2e8bab1b1c700deede9ca83354ea86b1d750cc2 100644 (file)
@@ -45,6 +45,8 @@ struct wasm_globaltype_inner_t {
 struct wasm_tabletype_inner_t {
     wasm_valtype_t *element;
     wasm_limits_t   limits;
+
+    i32 static_arr;
 };
 
 struct wasm_memorytype_inner_t {
@@ -126,29 +128,41 @@ struct wasm_module_t {
 };
 
 struct wasm_func_inner_t {
-    wasm_instance_t *instance;
-    i32 func_idx;
+    bool env_present;
+    void *env;
+    void (*func_ptr)();
+    void (*finalizer)(void *);
 
-    wasm_functype_t *type;
+    const wasm_functype_t *type;
 };
 
 struct wasm_global_inner_t {
-    wasm_instance_t *instance;
     int register_index;
+    ovm_state_t  *state;
+    ovm_engine_t *engine;
+
+    wasm_val_t initial_value;
 
-    wasm_globaltype_t *type;
+    const wasm_globaltype_t *type;
 };
 
 struct wasm_table_inner_t {
-    wasm_tabletype_t *type;
+    ovm_program_t *program;
+    ovm_engine_t  *engine;
+
+    i32 static_arr;
+
+    const wasm_tabletype_t *type;
 };
 
 struct wasm_memory_inner_t {
-    wasm_memorytype_t *type;
+    ovm_engine_t* engine;
+
+    const wasm_memorytype_t *type;
 };
 
 struct wasm_extern_t {
-    wasm_externtype_t *type;
+    const wasm_externtype_t *type;
     union {
         struct wasm_func_inner_t   func;
         struct wasm_global_inner_t global;
@@ -163,7 +177,16 @@ struct wasm_table_t { wasm_extern_t inner; };
 struct wasm_memory_t { wasm_extern_t inner; };
 
 struct wasm_instance_t {
-    wasm_module_t *module;
+    const wasm_module_t *module;
+    wasm_store_t  *store;
+
+    bh_arr(wasm_func_t *)   funcs;
+    bh_arr(wasm_memory_t *) memories;
+    bh_arr(wasm_table_t *)  tables;
+    bh_arr(wasm_global_t *) globals;
+
+    wasm_extern_vec_t exports;
+
     ovm_state_t *state;
 };
 
index 7084ac312915e834b4c6715396c577131adfe86c..5b058e9a8f8e77544efceeebb2062e9874f5112f 100644 (file)
@@ -495,22 +495,37 @@ static inline void ovm__func_setup_stack_frame(ovm_engine_t *engine, ovm_state_t
 }
 
 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;
+    ovm_func_t func = program->funcs[func_idx];
 
-    state->pc = 0x7fffffff;
-    ovm__func_setup_stack_frame(engine, state, program, func_idx, 0);
+    switch (func.kind) {
+        case OVM_FUNC_INTERNAL: {
+            bh_arr_insert_end(state->numbered_values, 1);
+            state->value_number_offset += 1;
 
-    fori (i, 0, param_count) {
-        state->numbered_values[i + state->value_number_offset] = params[i];
-    }
+            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];
+            }
 
-    assert(program->funcs[func_idx].kind == OVM_FUNC_INTERNAL);
-    state->pc = program->funcs[func_idx].start_instr;
-    ovm_run_code(engine, state, program);
+            state->pc = func.start_instr;
+            ovm_run_code(engine, state, program);
+
+            state->value_number_offset -= 1;
+            return bh_arr_pop(state->numbered_values);
+        }
 
-    state->value_number_offset -= 1;
-    return bh_arr_pop(state->numbered_values);
+        case OVM_FUNC_EXTERNAL: {
+            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_set_length(state->params, 0);
+            return result;
+        }
+
+        default: return (ovm_value_t) {};
+    }
 }
 
 static inline double __ovm_abs(double f) {
index 0a1b5d472202e35d194798cab8ececee72460181..22bc6e90a599e9f985d37bb150d35d0469529c12 100644 (file)
@@ -10,7 +10,7 @@ wasm_externkind_t wasm_extern_kind(const wasm_extern_t* ext) {
 }
 
 wasm_externtype_t* wasm_extern_type(const wasm_extern_t* ext) {
-    return ext->type;
+    return (wasm_externtype_t *) ext->type;
 }
 
 wasm_extern_t* wasm_func_as_extern(wasm_func_t* ext)     { return (wasm_extern_t *) ext; }
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bd5f6b1ba1e0ad594add66309d4bae10be16250e 100644 (file)
@@ -0,0 +1,61 @@
+
+#include "onyx_wasm.h"
+#include "vm.h"
+
+wasm_func_t *wasm_func_new(wasm_store_t *store, const wasm_functype_t *type, wasm_func_callback_t callback) {
+    wasm_func_t *func = bh_alloc(store->engine->store->arena_allocator, sizeof(*func));
+    func->inner.type = wasm_functype_as_externtype_const(type);
+    func->inner.func.type = type;
+    func->inner.func.env_present = false;
+    func->inner.func.env = NULL;
+    func->inner.func.func_ptr = (void (*)()) callback;
+    func->inner.func.finalizer = NULL;
+
+    return func;
+}
+
+wasm_func_t *wasm_func_new_with_env(wasm_store_t *store, const wasm_functype_t *type,
+    wasm_func_callback_with_env_t callback, void *env, void (*finalizer)(void *)) {
+    
+    wasm_func_t *func = bh_alloc(store->engine->store->arena_allocator, sizeof(*func));
+    func->inner.type = wasm_functype_as_externtype_const(type);
+    func->inner.func.type = type;
+    func->inner.func.env_present = true;
+    func->inner.func.env = env;
+    func->inner.func.func_ptr = (void (*)()) callback;
+    func->inner.func.finalizer = finalizer;
+
+    return func;
+}
+
+wasm_functype_t *wasm_func_type(const wasm_func_t *func) {
+    return (wasm_functype_t *) func->inner.func.type;
+}
+
+size_t wasm_func_param_arity(const wasm_func_t *func) {
+    // Wow this is gross...
+    return func->inner.func.type->type.func.params.size;
+}
+
+size_t wasm_func_result_arity(const wasm_func_t *func) {
+    // Wow this is gross...
+    return func->inner.func.type->type.func.results.size;
+}
+
+wasm_trap_t *wasm_func_call(const wasm_func_t *func, const wasm_val_vec_t *args, wasm_val_vec_t *results) {
+    if (func->inner.func.env_present) {
+        wasm_func_callback_with_env_t cb = (wasm_func_callback_with_env_t) func->inner.func.func_ptr;
+        wasm_trap_t *trap = cb(func->inner.func.env, args, results);
+
+        if (func->inner.func.finalizer) {
+            func->inner.func.finalizer(func->inner.func.env);
+        }
+
+        return trap;
+
+    } else {
+        wasm_func_callback_t cb = (wasm_func_callback_t) func->inner.func.func_ptr;
+        wasm_trap_t *trap = cb(args, results);
+        return trap;
+    }
+}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1dee022d7bff9ec75a7305b7fd14d34667ebf322 100644 (file)
@@ -0,0 +1,29 @@
+
+#include "onyx_wasm.h"
+#include "vm.h"
+
+wasm_global_t *wasm_global_new(wasm_store_t *store, const wasm_globaltype_t *type, const wasm_val_t *initial) {
+    wasm_global_t *global = bh_alloc(store->engine->store->arena_allocator, sizeof(*global));
+    global->inner.type = wasm_globaltype_as_externtype_const(type);
+    global->inner.global.register_index = -1;
+    global->inner.global.engine = NULL;
+
+    if (initial) {
+        global->inner.global.initial_value = *initial;
+    }
+
+    return global;
+}
+
+wasm_globaltype_t *wasm_global_type(const wasm_global_t *global) {
+    return (wasm_globaltype_t *) global->inner.global.type;
+}
+
+void wasm_global_get(const wasm_global_t *global, wasm_val_t *value) {
+    assert(("unimplemented", 0));
+}
+
+void wasm_global_set(wasm_global_t *global, const wasm_val_t *value) {
+    assert(("unimplemented", 0));
+}
+
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f9d249dc622117e139b38a20ba12945ebeddcdcf 100644 (file)
@@ -0,0 +1,192 @@
+
+
+#include "onyx_wasm.h"
+#include "vm.h"
+#include <alloca.h>
+
+typedef struct wasm_ovm_binding wasm_ovm_binding;
+struct wasm_ovm_binding {
+    int func_idx;
+    ovm_engine_t  *engine;
+    ovm_state_t   *state;
+    ovm_program_t *program;
+};
+
+static wasm_trap_t *wasm_to_ovm_func_call_binding(void *vbinding, const wasm_val_vec_t *args, wasm_val_vec_t *res) {
+    wasm_ovm_binding *binding = (wasm_ovm_binding *) vbinding;
+
+    ovm_value_t *vals = alloca(sizeof(*vals) * args->size);
+    fori (i, 0, (int) args->size) {
+        switch (args->data[i].kind) {
+            case WASM_I32:
+                vals[i].type = OVM_TYPE_I32;
+                vals[i].i32  = args->data[i].of.i32;
+                break;
+
+            case WASM_I64:
+                vals[i].type = OVM_TYPE_I64;
+                vals[i].i64  = args->data[i].of.i64;
+                break;
+
+            case WASM_F32:
+                vals[i].type = OVM_TYPE_F32;
+                vals[i].f32  = args->data[i].of.f32;
+                break;
+
+            case WASM_F64:
+                vals[i].type = OVM_TYPE_F64;
+                vals[i].f64  = args->data[i].of.f64;
+                break;
+
+            default: assert(("invalid wasm value type for conversion", 0));
+        }
+    }
+
+    ovm_value_t ovm_res = ovm_func_call(binding->engine, binding->state, binding->program, binding->func_idx, args->size, vals);
+    if (!res || res->size == 0) return NULL;
+
+    switch (ovm_res.type) {
+        case OVM_TYPE_I8:
+            res->data[0].kind = WASM_I32;
+            res->data[0].of.i32 = (i32) ovm_res.i8;
+            break;
+
+        case OVM_TYPE_I16:
+            res->data[0].kind = WASM_I32;
+            res->data[0].of.i32 = (i32) ovm_res.i16;
+            break;
+
+        case OVM_TYPE_I32:
+            res->data[0].kind = WASM_I32;
+            res->data[0].of.i32 = (i32) ovm_res.i32;
+            break;
+
+        case OVM_TYPE_I64:
+            res->data[0].kind = WASM_I64;
+            res->data[0].of.i64 = ovm_res.i64;
+            break;
+
+        case OVM_TYPE_F32:
+            res->data[0].kind = WASM_F32;
+            res->data[0].of.f32 = ovm_res.f32;
+            break;
+
+        case OVM_TYPE_F64:
+            res->data[0].kind = WASM_F64;
+            res->data[0].of.f64 = ovm_res.f64;
+            break;
+    }
+
+    return NULL;
+}
+
+static void prepare_instance(wasm_instance_t *instance, const wasm_extern_vec_t *imports) {
+    //
+    // Place imports in their corresponding "bucket"
+    fori (i, 0, (int) imports->size) {
+        switch (wasm_extern_kind(imports->data[i])) {
+            case WASM_EXTERN_FUNC: 
+                bh_arr_push(instance->funcs, wasm_extern_as_func(imports->data[i]));
+                break;
+
+            case WASM_EXTERN_MEMORY:
+                bh_arr_push(instance->memories, wasm_extern_as_memory(imports->data[i]));
+                break;
+
+            case WASM_EXTERN_GLOBAL:
+                bh_arr_push(instance->globals, wasm_extern_as_global(imports->data[i]));
+                break;
+
+            case WASM_EXTERN_TABLE:
+                bh_arr_push(instance->tables, wasm_extern_as_table(imports->data[i]));
+                break;
+        }
+    }
+
+    //
+    // Create function objects
+    fori (i, 0, (int) instance->module->functypes.size) {
+        wasm_ovm_binding *binding = bh_alloc(instance->store->engine->store->arena_allocator, sizeof(*binding));
+        binding->engine = instance->store->engine->engine;
+        binding->func_idx = bh_arr_length(instance->funcs);
+        binding->program = instance->module->program;
+        binding->state = instance->state;
+        
+        wasm_func_t *func = wasm_func_new_with_env(instance->store, instance->module->functypes.data[i], 
+            wasm_to_ovm_func_call_binding, binding, NULL);
+
+        bh_arr_push(instance->funcs, func);
+    }
+
+    //
+    // Create memory objects
+    fori (i, 0, (int) instance->module->memorytypes.size) {
+        wasm_memory_t *memory = wasm_memory_new(instance->store, instance->module->memorytypes.data[i]);
+        memory->inner.memory.engine = instance->store->engine->engine;
+
+        bh_arr_push(instance->memories, memory);
+    }
+
+    //
+    // Create table objects
+    fori (i, 0, (int) instance->module->tabletypes.size) {
+        wasm_table_t *table = wasm_table_new(instance->store, instance->module->tabletypes.data[i], NULL);
+        table->inner.table.engine = instance->store->engine->engine;
+        table->inner.table.program = instance->module->program;
+        table->inner.table.static_arr = instance->module->tabletypes.data[i]->type.table.static_arr;
+
+        bh_arr_push(instance->tables, table);
+    }
+
+    //
+    // Create global objects
+    fori (i, 0, (int) instance->module->globaltypes.size) {
+        wasm_global_t *global = wasm_global_new(instance->store, instance->module->globaltypes.data[i],
+            &instance->module->globaltypes.data[i]->type.global.initial_value);
+        global->inner.global.engine = instance->store->engine->engine;
+        global->inner.global.state = instance->state;
+        global->inner.global.register_index = bh_arr_length(instance->globals);
+
+        bh_arr_push(instance->globals, global);
+    }
+
+
+    //
+    // Initialize all non-passive data segments
+    fori (i, 0, (int) instance->module->data_count) {
+        struct wasm_data_t *datum = &instance->module->data_entries[i];
+        if (datum->passive) continue;
+
+        ovm_engine_memory_copy(instance->store->engine->engine, datum->offset, datum->data, datum->length);
+    }
+}
+
+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));
+    instance->store = store;
+    instance->module = module;
+
+    instance->funcs = NULL;
+    instance->memories = NULL;
+    instance->tables = NULL;
+    instance->globals = NULL;
+    bh_arr_new(store->engine->store->heap_allocator, instance->funcs, module->functypes.size);
+    bh_arr_new(store->engine->store->heap_allocator, instance->memories, 1);
+    bh_arr_new(store->engine->store->heap_allocator, instance->tables, 1);
+    bh_arr_new(store->engine->store->heap_allocator, instance->globals, module->globaltypes.size);
+
+    instance->state = ovm_state_new(store->engine->engine, module->program);
+    
+    // TODO
+    instance->exports.size = 0;
+    instance->exports.data = NULL;
+
+    prepare_instance(instance, imports);
+
+    if (trap) *trap = NULL;
+
+    return instance;
+}
+
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..04722e67da3bdf02bfad81c4461db8052addf4f1 100644 (file)
@@ -0,0 +1,42 @@
+
+#include "onyx_wasm.h"
+#include "vm.h"
+
+wasm_memory_t *wasm_memory_new(wasm_store_t *store, const wasm_memorytype_t *type) {
+    wasm_memory_t *memory = bh_alloc(store->engine->store->arena_allocator, sizeof(*store));
+    memory->inner.type = wasm_memorytype_as_externtype_const(type);
+    memory->inner.memory.type = type;
+    memory->inner.memory.engine = NULL;
+
+    return memory;
+}
+
+wasm_memorytype_t *wasm_memory_type(const wasm_memory_t *memory) {
+    return (wasm_memorytype_t *) memory->inner.memory.type;
+}
+
+byte_t *wasm_memory_data(wasm_memory_t *memory) {
+    assert(memory && memory->inner.memory.engine);
+    return memory->inner.memory.engine->memory;
+}
+
+size_t wasm_memory_data_size(const wasm_memory_t *memory) {
+    assert(memory && memory->inner.memory.engine);
+    return memory->inner.memory.engine->memory_size;
+}
+
+wasm_memory_pages_t wasm_memory_size(const wasm_memory_t *memory) {
+    assert(memory && memory->inner.memory.engine);
+    return memory->inner.memory.engine->memory_size / MEMORY_PAGE_SIZE;
+}
+
+bool wasm_memory_grow(wasm_memory_t *memory, wasm_memory_pages_t pages) {
+    //
+    // This will always fail, as initially the VM is created with
+    // a 4GiB mmap, so growing it will not be an option. If that
+    // changes and a dynamically allocated solution is used, then
+    // this can change. I don't see that changing however, as I will
+    // never need to use this on 32-bit systems, and that would be the
+    // only case that I would not like to try to mmap 4 gigs.
+    return false;
+}
index e62bd322487caa0b84fa8359da752cb400586d6b..c4305703e17783d7dbe8581a7249da233ae7355b 100644 (file)
@@ -20,6 +20,8 @@ static bool module_build(wasm_module_t *module, const wasm_byte_vec_t *binary) {
         parse_section(&ctx);
     }
 
+    // TODO: This is not correct when the module imports a global.
+    // But Onyx does not do this, so I don't care at the moment.
     module->program->register_count = module->globaltypes.size;
     
     return true;
@@ -58,7 +60,7 @@ WASM_MODULE_INDEX(global, WASM_EXTERN_GLOBAL)
 
 
 wasm_module_t *wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary) {
-    wasm_module_t *module = malloc(sizeof(*module));
+    wasm_module_t *module = bh_alloc(store->engine->store->arena_allocator, sizeof(*module));
     module->store = store;
 
     bool success = module_build(module, binary); 
index 1b4e7dd185bd6a9f92a5b35e250c6e3fef67ea2f..d37d6e2fb6be04274b032ca3d690f958efcae1c1 100644 (file)
@@ -305,6 +305,9 @@ static void parse_elem_section(build_context *ctx) {
     }
 
     ctx->func_table_arr_idx = ovm_program_register_static_ints(ctx->program, entry_count, ctx->module->elem_entries);
+
+    assert(ctx->module->tabletypes.size == 1);
+    ctx->module->tabletypes.data[0]->type.table.static_arr = ctx->func_table_arr_idx;
 }
 
 static void parse_data_section(build_context *ctx) {
@@ -905,7 +908,7 @@ static void parse_code_section(build_context *ctx) {
     unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
     unsigned int code_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
     assert(ctx->module->functypes.size == code_count);
-    
+
     fori (i, 0, (int) code_count) {
         unsigned int code_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
         unsigned int local_sections_count = uleb128_to_uint(ctx->binary.data, &ctx->offset);
index 0f30cf61f9212be2e0287115c8057a6dfc3b17a2..07a3bc19d7c8f2dd6f83f3b890a7accbde9be8f3 100644 (file)
@@ -3,7 +3,7 @@
 #include "vm.h"
 
 wasm_store_t *wasm_store_new(wasm_engine_t *engine) {
-    wasm_store_t *store = malloc(sizeof(wasm_store_t));
+    wasm_store_t *store = bh_alloc(engine->store->arena_allocator, sizeof(wasm_store_t));
     store->engine = engine;
     return store;
 }
index 0ade447ecfc2027a78dd2352e36079425e0fd4ec..6110df21f94e1573c0c0e7769a211c5a310ea791 100644 (file)
@@ -1,2 +1,16 @@
 
-// TODO
+#include "onyx_wasm.h"
+#include "vm.h"
+
+wasm_table_t *wasm_table_new(wasm_store_t *store, const wasm_tabletype_t *type, wasm_ref_t *init) {
+    wasm_table_t *table = bh_alloc(store->engine->store->arena_allocator, sizeof(*table));
+    table->inner.type = wasm_tabletype_as_externtype_const(type);
+    table->inner.table.type = type;
+    
+    return table;
+}
+
+wasm_tabletype_t *wasm_table_type(const wasm_table_t *table) {
+    return (wasm_tabletype_t *) table->inner.table.type;
+}
+