Skip to content

Commit

Permalink
Merge pull request #25 from rdunnington/dev
Browse files Browse the repository at this point in the history
C ffi support
  • Loading branch information
rdunnington authored Aug 15, 2023
2 parents 1bd1b79 + c8f4b8a commit 95b13f1
Show file tree
Hide file tree
Showing 11 changed files with 1,973 additions and 929 deletions.
3 changes: 2 additions & 1 deletion bench/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ fn run(allocator: std.mem.Allocator, benchmark: Benchmark) !void {
defer module_instance.deinit();
try module_instance.instantiate(.{});

const handle = try module_instance.getFunctionHandle("run");
var input = [1]Val{.{ .I32 = benchmark.param }};
var output = [1]Val{.{ .I32 = 0 }};
try module_instance.invoke("run", &input, &output);
try module_instance.invoke(handle, &input, &output, .{});

const ms_elapsed: f64 = elapsedMilliseconds(&timer);
std.log.info("{s} decode+instantiate+run took {d}ms\n", .{ benchmark.name, ms_elapsed });
Expand Down
20 changes: 19 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const std = @import("std");
const CrossTarget = std.zig.CrossTarget;
const Builder = std.build.Builder;
const LibExeObjStep = std.build.LibExeObjStep;
const InstallFileStep = std.build.InstallFileStep;

const ExeOpts = struct {
exe_name: []const u8,
Expand All @@ -14,7 +15,7 @@ const ExeOpts = struct {
};

pub fn build(b: *Builder) void {
const should_emit_asm = b.option(bool, "asm", "Emit asm for the bytebox .exe") orelse false;
const should_emit_asm = b.option(bool, "asm", "Emit asm for the bytebox binaries") orelse false;

const target = b.standardTargetOptions(.{});

Expand Down Expand Up @@ -46,6 +47,23 @@ pub fn build(b: *Builder) void {
&bench_mandelbrot_step.step,
},
});

var c_header: *InstallFileStep = b.addInstallFileWithDir(std.build.FileSource{ .path = "src/bytebox.h" }, .header, "bytebox.h");

const lib_bytebox: *LibExeObjStep = b.addStaticLibrary("bytebox", "src/cffi.zig");
lib_bytebox.setTarget(target);
lib_bytebox.setBuildMode(b.standardReleaseOptions());
lib_bytebox.step.dependOn(&c_header.step);
lib_bytebox.emit_asm = if (should_emit_asm) .emit else .default;
// const lib_bytebox = b.addStaticLibrary(.{
// .name = "bytebox",
// .root_source_file = .{ .path = "src/cffi.zig" },
// .target = target,
// .optimize = optimize,
// });
// lib_bytebox.installHeader("src/bytebox.h", "bytebox.h");

lib_bytebox.install();
}

fn buildExeWithStep(b: *Builder, target: CrossTarget, opts: ExeOpts) void {
Expand Down
38 changes: 20 additions & 18 deletions run/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,15 @@ pub fn main() !void {
var module_instance = bytebox.ModuleInstance.init(&module_def, allocator);
defer module_instance.deinit();

var imports_wasi: bytebox.ModuleImports = try wasi.initImports(.{
var imports_wasi: bytebox.ModuleImportPackage = try wasi.initImports(.{
.argv = opts.wasm_argv,
.env = opts.wasm_env,
.dirs = opts.wasm_dirs,
}, allocator);
defer wasi.deinitImports(&imports_wasi);

var instantiate_opts = bytebox.ModuleInstantiateOpts{
.imports = &[_]bytebox.ModuleImports{imports_wasi},
.imports = &[_]bytebox.ModuleImportPackage{imports_wasi},
};

module_instance.instantiate(instantiate_opts) catch |e| {
Expand All @@ -275,23 +275,24 @@ pub fn main() !void {
const invoke_funcname: []const u8 = if (opts.invoke) |invoke| invoke.funcname else "_start";
const invoke_args: [][]const u8 = if (opts.invoke) |invoke| invoke.args else &[_][]u8{};

const func_info: ?bytebox.FunctionExportInfo = module_def.getFuncExportInfo(invoke_funcname);
if (func_info == null) {
const func_handle: bytebox.FunctionHandle = module_instance.getFunctionHandle(invoke_funcname) catch {
// don't log an error if the user didn't explicitly try to invoke a function
if (opts.invoke != null) {
std.log.err("Failed to find function '{s}' - either it doesn't exist or is not a public export.", .{invoke_funcname});
}
return RunErrors.MissingFunction;
}
};

const func_export: bytebox.FunctionExport = module_def.getFunctionExport(func_handle);

const num_params: usize = invoke_args.len;
if (func_info.?.params.len != num_params) {
if (func_export.params.len != num_params) {
var strbuf = std.ArrayList(u8).init(allocator);
defer strbuf.deinit();
try writeSignature(&strbuf, &func_info.?);
try writeSignature(&strbuf, &func_export);
std.log.err("Specified {} params but expected {}. The signature of '{s}' is:\n{s}", .{
num_params,
func_info.?.params.len,
func_export.params.len,
invoke_funcname,
strbuf.items,
});
Expand All @@ -303,7 +304,7 @@ pub fn main() !void {
var params = std.ArrayList(bytebox.Val).init(allocator);
defer params.deinit();
try params.resize(invoke_args.len);
for (func_info.?.params) |valtype, i| {
for (func_export.params) |valtype, i| {
const arg: []const u8 = invoke_args[i];
switch (valtype) {
.I32 => {
Expand Down Expand Up @@ -350,9 +351,9 @@ pub fn main() !void {
}

var returns = std.ArrayList(bytebox.Val).init(allocator);
try returns.resize(func_info.?.returns.len);
try returns.resize(func_export.returns.len);

module_instance.invoke(invoke_funcname, params.items, returns.items) catch |e| {
module_instance.invoke(func_handle, params.items, returns.items, .{}) catch |e| {
var backtrace = module_instance.formatBacktrace(1, allocator) catch unreachable;
std.log.err("Caught {} during function invoke. Backtrace:\n{s}\n", .{ e, backtrace.items });
backtrace.deinit();
Expand All @@ -365,13 +366,14 @@ pub fn main() !void {
var writer = strbuf.writer();

if (returns.items.len > 0) {
const return_types = func_export.returns;
try std.fmt.format(writer, ":\n", .{});
for (returns.items) |val| {
switch (val) {
.I32 => |v| try std.fmt.format(writer, " {} (i32)\n", .{v}),
.I64 => |v| try std.fmt.format(writer, " {} (i64)\n", .{v}),
.F32 => |v| try std.fmt.format(writer, " {} (f32)\n", .{v}),
.F64 => |v| try std.fmt.format(writer, " {} (f64)\n", .{v}),
for (returns.items) |_, i| {
switch (return_types[i]) {
.I32 => try std.fmt.format(writer, " {} (i32)\n", .{returns.items[i]}),
.I64 => try std.fmt.format(writer, " {} (i64)\n", .{returns.items[i]}),
.F32 => try std.fmt.format(writer, " {} (f32)\n", .{returns.items[i]}),
.F64 => try std.fmt.format(writer, " {} (f64)\n", .{returns.items[i]}),
.V128 => unreachable, // TODO support
.FuncRef => try std.fmt.format(writer, " (funcref)\n", .{}),
.ExternRef => try std.fmt.format(writer, " (externref)\n", .{}),
Expand All @@ -385,7 +387,7 @@ pub fn main() !void {
}
}

fn writeSignature(strbuf: *std.ArrayList(u8), info: *const bytebox.FunctionExportInfo) !void {
fn writeSignature(strbuf: *std.ArrayList(u8), info: *const bytebox.FunctionExport) !void {
var writer = strbuf.writer();
if (info.params.len == 0) {
try std.fmt.format(writer, " params: none\n", .{});
Expand Down
169 changes: 169 additions & 0 deletions src/bytebox.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// C interface for bytebox wasm runtime.

#include <stdint.h>
#include <stdbool.h>

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,
};
typedef enum bb_error bb_error;

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;
};
typedef union bb_val bb_val;

struct bb_module_definition_init_opts
{
const char* debug_name;
};
typedef struct bb_module_definition_init_opts bb_module_definition_init_opts;

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);

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;
};
typedef struct bb_module_instance_instantiate_opts bb_module_instance_instantiate_opts;

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;
};
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;
};
typedef struct bb_func_info bb_func_info;

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;
};
typedef struct bb_global bb_global;

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,
};
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);

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);

bool bb_func_handle_isvalid(bb_func_handle handle);
Loading

0 comments on commit 95b13f1

Please sign in to comment.