From b680a321cb2f072096216f6b71bf5b90cbff1be4 Mon Sep 17 00:00:00 2001 From: Reuben Dunnington Date: Wed, 20 Mar 2024 22:40:17 -0700 Subject: [PATCH] Move module def/inst to be allocated * Allocated memory guarantees stable pointers * Removed dependency on stack vm in instance.zig --- bench/main.zig | 8 +- run/main.zig | 8 +- src/bytebox.h | 242 ++++++++++++++++++++++++--------------------- src/cffi.zig | 44 +++------ src/core.zig | 23 +++++ src/definition.zig | 16 ++- src/instance.zig | 17 ++-- test/main.zig | 16 +-- 8 files changed, 202 insertions(+), 172 deletions(-) diff --git a/bench/main.zig b/bench/main.zig index 9150a7a..533f354 100644 --- a/bench/main.zig +++ b/bench/main.zig @@ -21,12 +21,12 @@ fn run(allocator: std.mem.Allocator, benchmark: Benchmark) !void { var timer = try Timer.start(); - var module_def = bytebox.ModuleDefinition.init(allocator, .{}); - defer module_def.deinit(); + var module_def = try bytebox.createModuleDefinition(allocator, .{}); + defer module_def.destroy(); try module_def.decode(wasm_data); - var module_instance = try bytebox.ModuleInstance.init(&module_def, allocator); - defer module_instance.deinit(); + var module_instance = try bytebox.createModuleInstance(.Stack, module_def, allocator); + defer module_instance.destroy(); try module_instance.instantiate(.{}); const handle = try module_instance.getFunctionHandle("run"); diff --git a/run/main.zig b/run/main.zig index e18fc97..7d9159e 100644 --- a/run/main.zig +++ b/run/main.zig @@ -237,8 +237,8 @@ pub fn main() !void { const module_def_opts = bytebox.ModuleDefinitionOpts{ .debug_name = std.fs.path.basename(opts.filename.?), }; - var module_def = bytebox.ModuleDefinition.init(allocator, module_def_opts); - defer module_def.deinit(); + var module_def = try bytebox.createModuleDefinition(allocator, module_def_opts); + defer module_def.destroy(); module_def.decode(wasm_data) catch |e| { std.log.err("Caught error decoding module: {}", .{e}); @@ -253,8 +253,8 @@ pub fn main() !void { return; } - var module_instance = try bytebox.ModuleInstance.init(&module_def, allocator); - defer module_instance.deinit(); + var module_instance = try bytebox.createModuleInstance(.Stack, module_def, allocator); + defer module_instance.destroy(); var imports_wasi: bytebox.ModuleImportPackage = try wasi.initImports(.{ .argv = opts.wasm_argv, diff --git a/src/bytebox.h b/src/bytebox.h index db9dd8d..5240502 100644 --- a/src/bytebox.h +++ b/src/bytebox.h @@ -1,63 +1,58 @@ // C interface for bytebox wasm runtime. +#include #include #include -#include -struct bb_slice -{ - char* data; - size_t length; +struct bb_slice { + char *data; + size_t length; }; typedef struct bb_slice bb_slice; -enum bb_error -{ - BB_ERROR_OK, - BB_ERROR_FAILED, - BB_ERROR_OUTOFMEMORY, - BB_ERROR_INVALIDPARAM, - BB_ERROR_UNKNOWNEXPORT, - BB_ERROR_UNKNOWNIMPORT, - BB_ERROR_INCOMPATIBLEIMPORT, - BB_ERROR_TRAP_DEBUG, - BB_ERROR_TRAP_UNREACHABLE, - BB_ERROR_TRAP_INTEGERDIVISIONBYZERO, - BB_ERROR_TRAP_INTEGEROVERFLOW, - BB_ERROR_TRAP_INDIRECTCALLTYPEMISMATCH, - BB_ERROR_TRAP_INVALIDINTEGERCONVERSION, - BB_ERROR_TRAP_OUTOFBOUNDSMEMORYACCESS, - BB_ERROR_TRAP_UNDEFINEDELEMENT, - BB_ERROR_TRAP_UNINITIALIZEDELEMENT, - BB_ERROR_TRAP_OUTOFBOUNDSTABLEACCESS, - BB_ERROR_TRAP_STACKEXHAUSTED, +enum bb_error { + BB_ERROR_OK, + BB_ERROR_FAILED, + BB_ERROR_OUTOFMEMORY, + BB_ERROR_INVALIDPARAM, + BB_ERROR_UNKNOWNEXPORT, + BB_ERROR_UNKNOWNIMPORT, + BB_ERROR_INCOMPATIBLEIMPORT, + BB_ERROR_TRAP_DEBUG, + BB_ERROR_TRAP_UNREACHABLE, + BB_ERROR_TRAP_INTEGERDIVISIONBYZERO, + BB_ERROR_TRAP_INTEGEROVERFLOW, + BB_ERROR_TRAP_INDIRECTCALLTYPEMISMATCH, + BB_ERROR_TRAP_INVALIDINTEGERCONVERSION, + BB_ERROR_TRAP_OUTOFBOUNDSMEMORYACCESS, + BB_ERROR_TRAP_UNDEFINEDELEMENT, + BB_ERROR_TRAP_UNINITIALIZEDELEMENT, + BB_ERROR_TRAP_OUTOFBOUNDSTABLEACCESS, + BB_ERROR_TRAP_STACKEXHAUSTED, }; typedef enum bb_error bb_error; -enum bb_valtype -{ - BB_VALTYPE_I32, - BB_VALTYPE_I64, - BB_VALTYPE_F32, - BB_VALTYPE_F64, +enum bb_valtype { + BB_VALTYPE_I32, + BB_VALTYPE_I64, + BB_VALTYPE_F32, + BB_VALTYPE_F64, }; typedef enum bb_valtype bb_valtype; typedef float bb_v128[4]; -union bb_val -{ - int32_t i32_val; - int64_t i64_val; - float f32_val; - double f64_val; - bb_v128 v128_val; - uint32_t externref_val; +union bb_val { + int32_t i32_val; + int64_t i64_val; + float f32_val; + double f64_val; + bb_v128 v128_val; + uint32_t externref_val; }; typedef union bb_val bb_val; -struct bb_module_definition_init_opts -{ - const char* debug_name; +struct bb_module_definition_init_opts { + const char *debug_name; }; typedef struct bb_module_definition_init_opts bb_module_definition_init_opts; @@ -65,106 +60,127 @@ typedef struct bb_module_definition bb_module_definition; typedef struct bb_module_instance bb_module_instance; typedef struct bb_import_package bb_import_package; -typedef void bb_host_function(void* userdata, bb_module_instance* module, const bb_val* params, bb_val* returns); -typedef void* bb_wasm_memory_resize(void* mem, size_t new_size_bytes, size_t old_size_bytes, void* userdata); -typedef void bb_wasm_memory_free(void* mem, size_t size_bytes, void* userdata); +typedef void bb_host_function(void *userdata, bb_module_instance *module, + const bb_val *params, bb_val *returns); +typedef void *bb_wasm_memory_resize(void *mem, size_t new_size_bytes, + size_t old_size_bytes, void *userdata); +typedef void bb_wasm_memory_free(void *mem, size_t size_bytes, void *userdata); -struct bb_wasm_memory_config -{ - bb_wasm_memory_resize* resize_callback; - bb_wasm_memory_free* free_callback; - void* userdata; +struct bb_wasm_memory_config { + bb_wasm_memory_resize *resize_callback; + bb_wasm_memory_free *free_callback; + void *userdata; }; typedef struct bb_wasm_memory_config bb_wasm_memory_config; -struct bb_module_instance_instantiate_opts -{ - bb_import_package** packages; - size_t num_packages; - bb_wasm_memory_config wasm_memory_config; - size_t stack_size; - bool enable_debug; +struct bb_module_instance_instantiate_opts { + bb_import_package **packages; + size_t num_packages; + bb_wasm_memory_config wasm_memory_config; + size_t stack_size; + bool enable_debug; }; -typedef struct bb_module_instance_instantiate_opts bb_module_instance_instantiate_opts; +typedef struct bb_module_instance_instantiate_opts + bb_module_instance_instantiate_opts; -struct bb_module_instance_invoke_opts -{ - bool trap_on_start; +struct bb_module_instance_invoke_opts { + bool trap_on_start; }; typedef struct bb_module_instance_invoke_opts bb_module_instance_invoke_opts; -struct bb_func_handle -{ - uint32_t index; - uint32_t type; +struct bb_func_handle { + uint32_t index; + uint32_t type; }; typedef struct bb_func_handle bb_func_handle; -struct bb_func_info -{ - bb_valtype* params; - size_t num_params; - bb_valtype* returns; - size_t num_returns; +struct bb_func_info { + bb_valtype *params; + size_t num_params; + bb_valtype *returns; + size_t num_returns; }; typedef struct bb_func_info bb_func_info; -enum bb_global_mut -{ - BB_GLOBAL_MUT_IMMUTABLE, - BB_GLOBAL_MUT_MUTABLE, +enum bb_global_mut { + BB_GLOBAL_MUT_IMMUTABLE, + BB_GLOBAL_MUT_MUTABLE, }; typedef enum bb_global_mut bb_global_mut; -struct bb_global -{ - bb_val* value; - bb_valtype type; - bb_global_mut mut; +struct bb_global { + bb_val *value; + bb_valtype type; + bb_global_mut mut; }; typedef struct bb_global bb_global; -enum bb_debug_trace_mode -{ - BB_DEBUG_TRACE_NONE, - BB_DEBUG_TRACE_FUNCTION, - BB_DEBUG_TRACE_INSTRUCTION, +enum bb_debug_trace_mode { + BB_DEBUG_TRACE_NONE, + BB_DEBUG_TRACE_FUNCTION, + BB_DEBUG_TRACE_INSTRUCTION, }; typedef enum bb_debug_trace_mode bb_debug_trace_mode; -enum bb_debug_trap_mode -{ - BB_DEBUG_TRAP_MODE_DISABLED, - BB_DEBUG_TRAP_MODE_ENABLED, +enum bb_debug_trap_mode { + BB_DEBUG_TRAP_MODE_DISABLED, + BB_DEBUG_TRAP_MODE_ENABLED, }; typedef enum bb_debug_trap_mode bb_debug_trap_mode; -const char* bb_error_str(bb_error err); - -bb_module_definition* bb_module_definition_init(bb_module_definition_init_opts opts); -void bb_module_definition_deinit(bb_module_definition* definition); -bb_error bb_module_definition_decode(bb_module_definition* definition, const char* data, size_t length); -bb_slice bb_module_definition_get_custom_section(const bb_module_definition* definition, const char* name); - -bb_import_package* bb_import_package_init(const char* name); -void bb_import_package_deinit(bb_import_package* package); // only deinit when all module_instances using the package have been deinited -bb_error bb_import_package_add_function(bb_import_package* package, bb_host_function* func, const char* export_name, const bb_valtype* params, size_t num_params, const bb_valtype* returns, size_t num_returns, void* userdata); -bb_error bb_import_package_add_memory(bb_import_package* package, const bb_wasm_memory_config* config, const char* export_name, uint32_t min_pages, uint32_t max_pages); +const char *bb_error_str(bb_error err); + +bb_module_definition * +bb_module_definition_create(bb_module_definition_init_opts opts); +void bb_module_definition_destroy(bb_module_definition *definition); +bb_error bb_module_definition_decode(bb_module_definition *definition, + const char *data, size_t length); +bb_slice +bb_module_definition_get_custom_section(const bb_module_definition *definition, + const char *name); + +bb_import_package *bb_import_package_init(const char *name); +void bb_import_package_deinit( + bb_import_package *package); // only deinit when all module_instances using + // the package have been deinited +bb_error bb_import_package_add_function( + bb_import_package *package, bb_host_function *func, const char *export_name, + const bb_valtype *params, size_t num_params, const bb_valtype *returns, + size_t num_returns, void *userdata); +bb_error bb_import_package_add_memory(bb_import_package *package, + const bb_wasm_memory_config *config, + const char *export_name, + uint32_t min_pages, uint32_t max_pages); void bb_set_debug_trace_mode(bb_debug_trace_mode mode); -bb_module_instance* bb_module_instance_init(bb_module_definition* definition); -void bb_module_instance_deinit(bb_module_instance* instance); -bb_error bb_module_instance_instantiate(bb_module_instance* instance, bb_module_instance_instantiate_opts opts); -bb_error bb_module_instance_find_func(bb_module_instance* instance, const char* func_name, bb_func_handle* out_handle); -bb_func_info bb_module_instance_func_info(bb_module_instance* instance, bb_func_handle handle); -bb_error bb_module_instance_invoke(bb_module_instance* instance, bb_func_handle, const bb_val* params, size_t num_params, bb_val* returns, size_t num_returns, bb_module_instance_invoke_opts opts); -bb_error bb_module_instance_resume(bb_module_instance* instance, bb_val* returns, size_t num_returns); -bb_error bb_module_instance_step(bb_module_instance* instance, bb_val* returns, size_t num_returns); -bb_error bb_module_instance_debug_set_trap(bb_module_instance* instance, uint32_t address, bb_debug_trap_mode trap_mode); -void* bb_module_instance_mem(bb_module_instance* instance, size_t offset, size_t length); -bb_slice bb_module_instance_mem_all(bb_module_instance* instance); -bb_error bb_module_instance_mem_grow(bb_module_instance* instance, size_t num_pages); -bb_global bb_module_instance_find_global(bb_module_instance* instance, const char* global_name); +bb_module_instance *bb_module_instance_create(bb_module_definition *definition); +void bb_module_instance_destroy(bb_module_instance *instance); +bb_error +bb_module_instance_instantiate(bb_module_instance *instance, + bb_module_instance_instantiate_opts opts); +bb_error bb_module_instance_find_func(bb_module_instance *instance, + const char *func_name, + bb_func_handle *out_handle); +bb_func_info bb_module_instance_func_info(bb_module_instance *instance, + bb_func_handle handle); +bb_error bb_module_instance_invoke(bb_module_instance *instance, bb_func_handle, + const bb_val *params, size_t num_params, + bb_val *returns, size_t num_returns, + bb_module_instance_invoke_opts opts); +bb_error bb_module_instance_resume(bb_module_instance *instance, + bb_val *returns, size_t num_returns); +bb_error bb_module_instance_step(bb_module_instance *instance, bb_val *returns, + size_t num_returns); +bb_error bb_module_instance_debug_set_trap(bb_module_instance *instance, + uint32_t address, + bb_debug_trap_mode trap_mode); +void *bb_module_instance_mem(bb_module_instance *instance, size_t offset, + size_t length); +bb_slice bb_module_instance_mem_all(bb_module_instance *instance); +bb_error bb_module_instance_mem_grow(bb_module_instance *instance, + size_t num_pages); +bb_global bb_module_instance_find_global(bb_module_instance *instance, + const char *global_name); bool bb_func_handle_isvalid(bb_func_handle handle); diff --git a/src/cffi.zig b/src/cffi.zig index d039c8a..434723e 100644 --- a/src/cffi.zig +++ b/src/cffi.zig @@ -182,27 +182,19 @@ export fn bb_error_str(c_error: CError) [*:0]const u8 { }; } -export fn bb_module_definition_init(c_opts: CModuleDefinitionInitOpts) ?*core.ModuleDefinition { +export fn bb_module_definition_create(c_opts: CModuleDefinitionInitOpts) ?*core.ModuleDefinition { var allocator = cffi_gpa.allocator(); - var module: ?*core.ModuleDefinition = allocator.create(core.ModuleDefinition) catch null; - if (module) |m| { - const debug_name: []const u8 = if (c_opts.debug_name == null) "" else std.mem.sliceTo(c_opts.debug_name.?, 0); - const opts_translated = core.ModuleDefinitionOpts{ - .debug_name = debug_name, - }; - m.* = core.ModuleDefinition.init(allocator, opts_translated); - } - - return module; + const debug_name: []const u8 = if (c_opts.debug_name == null) "" else std.mem.sliceTo(c_opts.debug_name.?, 0); + const opts_translated = core.ModuleDefinitionOpts{ + .debug_name = debug_name, + }; + return core.createModuleDefinition(allocator, opts_translated) catch null; } -export fn bb_module_definition_deinit(module: ?*core.ModuleDefinition) void { +export fn bb_module_definition_destroy(module: ?*core.ModuleDefinition) void { if (module) |m| { - m.deinit(); - - var allocator = cffi_gpa.allocator(); - allocator.destroy(m); + m.destroy(); } } @@ -341,31 +333,21 @@ export fn bb_set_debug_trace_mode(c_mode: CDebugTraceMode) void { _ = core.DebugTrace.setMode(mode); } -export fn bb_module_instance_init(module_definition: ?*ModuleDefinition) ?*ModuleInstance { +export fn bb_module_instance_create(module_definition: ?*ModuleDefinition) ?*ModuleInstance { var allocator = cffi_gpa.allocator(); var module: ?*core.ModuleInstance = null; - if (module_definition != null) { - module = allocator.create(core.ModuleInstance) catch null; - - if (module) |m| { - m.* = core.ModuleInstance.init(module_definition.?, allocator) catch { - // TODO log out of memory? - return null; - }; - } + if (module_definition) |def| { + module = core.createModuleInstance(.Stack, def, allocator) catch null; } return module; } -export fn bb_module_instance_deinit(module: ?*ModuleInstance) void { - var allocator = cffi_gpa.allocator(); - +export fn bb_module_instance_destroy(module: ?*ModuleInstance) void { if (module) |m| { - m.deinit(); - allocator.destroy(m); + m.destroy(); } } diff --git a/src/core.zig b/src/core.zig index aabc762..8030869 100644 --- a/src/core.zig +++ b/src/core.zig @@ -1,5 +1,8 @@ +const std = @import("std"); const def = @import("definition.zig"); const inst = @import("instance.zig"); +const vm_stack = @import("vm_stack.zig"); +const vm_register = @import("vm_register.zig"); pub const wasi = @import("wasi.zig"); pub const i8x16 = def.i8x16; @@ -48,3 +51,23 @@ pub const WasmMemoryExternal = inst.WasmMemoryExternal; pub const WasmMemoryFreeFunction = inst.WasmMemoryFreeFunction; pub const WasmMemoryResizeFunction = inst.WasmMemoryResizeFunction; pub const InvokeOpts = inst.InvokeOpts; + +const AllocError = std.mem.Allocator.Error; + +pub fn createModuleDefinition(allocator: std.mem.Allocator, opts: ModuleDefinitionOpts) AllocError!*ModuleDefinition { + return try ModuleDefinition.create(allocator, opts); +} + +pub const VmKind = enum { + Stack, + Register, +}; + +pub fn createModuleInstance(comptime vm_kind: VmKind, module_def: *const ModuleDefinition, allocator: std.mem.Allocator) AllocError!*ModuleInstance { + const vm_type = switch (vm_kind) { + .Stack => vm_stack.StackVM, + .Register => vm_stack.RegisterVM, + }; + var vm: *inst.VM = try inst.VM.create(vm_type, allocator); + return try ModuleInstance.create(module_def, vm, allocator); +} diff --git a/src/definition.zig b/src/definition.zig index b39e44c..f80ed0e 100644 --- a/src/definition.zig +++ b/src/definition.zig @@ -1,6 +1,7 @@ // This file contains types and code shared between both the ModuleDefinition and VMs const std = @import("std"); +const AllocError = std.mem.Allocator.Error; const common = @import("common.zig"); const StableArray = common.StableArray; @@ -2593,8 +2594,9 @@ pub const ModuleDefinition = struct { is_decoded: bool = false, - pub fn init(allocator: std.mem.Allocator, opts: ModuleDefinitionOpts) ModuleDefinition { - return ModuleDefinition{ + pub fn create(allocator: std.mem.Allocator, opts: ModuleDefinitionOpts) AllocError!*ModuleDefinition { + var def = try allocator.create(ModuleDefinition); + def.* = ModuleDefinition{ .allocator = allocator, .code = Code{ .instructions = std.ArrayList(Instruction).init(allocator), @@ -2622,8 +2624,9 @@ pub const ModuleDefinition = struct { .datas = std.ArrayList(DataDefinition).init(allocator), .custom_sections = std.ArrayList(CustomSection).init(allocator), .name_section = NameCustomSection.init(allocator), - .debug_name = opts.debug_name, + .debug_name = try allocator.dupe(u8, opts.debug_name), }; + return def; } pub fn decode(self: *ModuleDefinition, wasm: []const u8) anyerror!void { @@ -3295,7 +3298,7 @@ pub const ModuleDefinition = struct { } } - pub fn deinit(self: *ModuleDefinition) void { + pub fn destroy(self: *ModuleDefinition) void { self.code.instructions.deinit(); self.code.wasm_address_to_instruction_index.deinit(); for (self.code.branch_table.items) |*item| { @@ -3366,6 +3369,11 @@ pub const ModuleDefinition = struct { item.data.deinit(); } self.custom_sections.deinit(); + + self.allocator.free(self.debug_name); + + var allocator = self.allocator; + allocator.destroy(self); } pub fn getCustomSection(self: *const ModuleDefinition, name: []const u8) ?[]u8 { diff --git a/src/instance.zig b/src/instance.zig index 339e927..52da09d 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -28,9 +28,6 @@ const Val = def.Val; const ValType = def.ValType; const GlobalExport = def.GlobalExport; -// HACK remove this dependency by pushing the create stuff up one level -const StackVM = @import("vm_stack.zig").StackVM; - pub const UnlinkableError = error{ UnlinkableUnknownImport, UnlinkableIncompatibleImportType, @@ -708,19 +705,23 @@ pub const ModuleInstance = struct { is_instantiated: bool = false, vm: *VM, - // TODO move this to a "create" function that allocates memory for the instance so it has a stable pointer - pub fn init(module_def: *const ModuleDefinition, allocator: std.mem.Allocator) AllocError!ModuleInstance { - return ModuleInstance{ + pub fn create(module_def: *const ModuleDefinition, vm: *VM, allocator: std.mem.Allocator) AllocError!*ModuleInstance { + var inst = try allocator.create(ModuleInstance); + inst.* = ModuleInstance{ .allocator = allocator, .store = Store.init(allocator), .module_def = module_def, - .vm = try VM.create(StackVM, allocator), // TODO make this a parameter so this file doesn't have to know about VM implementations, core can know about it and wrap it all up. + .vm = vm, }; + return inst; } - pub fn deinit(self: *ModuleInstance) void { + pub fn destroy(self: *ModuleInstance) void { self.vm.destroy(); self.store.deinit(); + + var allocator = self.allocator; + allocator.destroy(self); } pub fn instantiate(self: *ModuleInstance, opts: ModuleInstantiateOpts) !void { diff --git a/test/main.zig b/test/main.zig index f90f470..37846d4 100644 --- a/test/main.zig +++ b/test/main.zig @@ -614,8 +614,8 @@ fn parseCommands(json_path: []const u8, allocator: std.mem.Allocator) !std.Array const Module = struct { filename: []const u8 = "", - def: ?bytebox.ModuleDefinition = null, - inst: ?bytebox.ModuleInstance = null, + def: ?*bytebox.ModuleDefinition = null, + inst: ?*bytebox.ModuleInstance = null, }; const TestOpts = struct { @@ -758,11 +758,11 @@ fn run(allocator: std.mem.Allocator, suite_path: []const u8, opts: *const TestOp // key memory is owned by commands list, so no need to free allocator.free(kv.value_ptr.filename); // ^^^ - if (kv.value_ptr.def) |*def| { - def.deinit(); + if (kv.value_ptr.def) |def| { + def.destroy(); } - if (kv.value_ptr.inst) |*inst| { - inst.deinit(); + if (kv.value_ptr.inst) |inst| { + inst.destroy(); } } name_to_module.deinit(); @@ -866,7 +866,7 @@ fn run(allocator: std.mem.Allocator, suite_path: []const u8, opts: *const TestOp module.filename = try allocator.dupe(u8, module_filename); - module.def = bytebox.ModuleDefinition.init(allocator, .{ .debug_name = std.fs.path.basename(module_filename) }); + module.def = try bytebox.createModuleDefinition(allocator, .{ .debug_name = std.fs.path.basename(module_filename) }); (module.def.?).decode(module_data) catch |e| { var expected_str_or_null: ?[]const u8 = null; if (decode_expected_error) |unwrapped_expected| { @@ -928,7 +928,7 @@ fn run(allocator: std.mem.Allocator, suite_path: []const u8, opts: *const TestOp else => {}, } - module.inst = try bytebox.ModuleInstance.init(&module.def.?, allocator); + module.inst = try bytebox.createModuleInstance(.Stack, module.def.?, allocator); (module.inst.?).instantiate(.{ .imports = imports.items }) catch |e| { if (instantiate_expected_error) |expected_str| { if (isSameError(e, expected_str)) {