From: Brendan Hansen Date: Mon, 23 Oct 2023 13:42:35 +0000 (-0500) Subject: added: callback functionality to `cbindgen` X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=59ed339c68c498b8dd973e4840300fa75363a68a;p=onyx.git added: callback functionality to `cbindgen` --- diff --git a/compiler/build.sh b/compiler/build.sh index 715a7baa..eff0031d 100755 --- a/compiler/build.sh +++ b/compiler/build.sh @@ -30,7 +30,7 @@ fi FLAGS="$FLAGS -DENABLE_RUN_WITH_WASMER" -if [ "$USE_DYNCALL" = "1" ]; then +if [ "$USE_DYNCALL" = "1" ] && [ "$RUNTIME_LIBRARY" = "ovmwasm" ]; then LIBS="$LIBS ../shared/lib/linux_$ARCH/lib/libdyncall_s.a ../shared/lib/linux_$ARCH/lib/libdyncallback_s.a" FLAGS="$FLAGS -DUSE_DYNCALL" fi diff --git a/compiler/src/wasm_runtime.c b/compiler/src/wasm_runtime.c index 58c3cdeb..9e4b221f 100644 --- a/compiler/src/wasm_runtime.c +++ b/compiler/src/wasm_runtime.c @@ -202,6 +202,11 @@ static void lookup_and_load_custom_libraries(LinkLibraryContext *ctx, bh_arr(Was #ifdef USE_DYNCALL +// +// This code implements the ability to use `dyncall:some_library.so` as the +// module import name to dynamically load a shared library at runtime. +// + typedef struct DynCallContext { void (*func)(); char types[64]; @@ -290,6 +295,84 @@ static wasm_func_t *link_and_prepare_dyncall_function( return wasm_func; } + +// +// This code implements the ability to wrap a wasm function in a callback +// that can be handed to a library that expects a C function. +// + +typedef struct DynCallbackContext { + wasm_func_t *wasm_func; + char *sig; +} DynCallbackContext; + +static DCsigchar __wasm_dyncallback(DCCallback *cb, DCArgs *args, DCValue *result, void *userdata) { + DynCallbackContext *ctx = userdata; + int arg_count = bh_str_last_index_of(ctx->sig, ')'); + + wasm_val_vec_t wasm_args; + wasm_val_vec_new_uninitialized(&wasm_args, arg_count); + + for (int i = 0; i < arg_count; i++) { + switch (ctx->sig[i]) { + case 'B': wasm_args.data[i] = WASM_I32_VAL(dcbArgBool(args)); break; + case 'c': wasm_args.data[i] = WASM_I32_VAL(dcbArgChar(args)); break; + case 'C': wasm_args.data[i] = WASM_I32_VAL(dcbArgUChar(args)); break; + case 's': wasm_args.data[i] = WASM_I32_VAL(dcbArgShort(args)); break; + case 'S': wasm_args.data[i] = WASM_I32_VAL(dcbArgUShort(args)); break; + case 'i': wasm_args.data[i] = WASM_I32_VAL(dcbArgInt(args)); break; + case 'I': wasm_args.data[i] = WASM_I32_VAL(dcbArgUInt(args)); break; + case 'j': wasm_args.data[i] = WASM_I64_VAL(dcbArgLong(args)); break; + case 'J': wasm_args.data[i] = WASM_I64_VAL(dcbArgULong(args)); break; + case 'l': wasm_args.data[i] = WASM_I64_VAL(dcbArgLongLong(args)); break; + case 'L': wasm_args.data[i] = WASM_I64_VAL(dcbArgULongLong(args)); break; + case 'f': wasm_args.data[i] = WASM_F32_VAL(dcbArgFloat(args)); break; + case 'd': wasm_args.data[i] = WASM_F64_VAL(dcbArgDouble(args)); break; + case 'p': wasm_args.data[i] = WASM_I64_VAL((uintptr_t) dcbArgPointer(args)); break; + } + } + + wasm_val_vec_t wasm_results; + wasm_val_vec_new_uninitialized(&wasm_results, 1); + + wasm_func_call(ctx->wasm_func, &wasm_args, &wasm_results); + + switch (ctx->sig[arg_count + 1]) { + case 'B': result->B = wasm_results.data[0].of.i32; break; + case 'c': result->c = wasm_results.data[0].of.i32; break; + case 'C': result->C = wasm_results.data[0].of.i32; break; + case 's': result->s = wasm_results.data[0].of.i32; break; + case 'S': result->S = wasm_results.data[0].of.i32; break; + case 'i': result->i = wasm_results.data[0].of.i32; break; + case 'I': result->I = wasm_results.data[0].of.i32; break; + case 'j': result->j = wasm_results.data[0].of.i64; break; + case 'J': result->J = wasm_results.data[0].of.i64; break; + case 'l': result->l = wasm_results.data[0].of.i64; break; + case 'L': result->L = wasm_results.data[0].of.i64; break; + case 'f': result->f = wasm_results.data[0].of.f32; break; + case 'd': result->d = wasm_results.data[0].of.f64; break; + case 'p': result->p = (void *) wasm_results.data[0].of.i64; break; + } + + wasm_val_vec_delete(&wasm_args); + wasm_val_vec_delete(&wasm_results); + + return ctx->sig[arg_count + 1]; +} + +static void (* wasm_func_from_idx(wasm_table_t *func_table, unsigned int index, char *signature))(void) { + wasm_ref_t *func_ref = wasm_table_get(func_table, index); + assert(func_ref); // TODO: Switch to trap + + wasm_func_t *func = wasm_ref_as_func(func_ref); + + DynCallbackContext *dcc = bh_alloc_item(bh_heap_allocator(), DynCallbackContext); + dcc->wasm_func = func; + dcc->sig = signature; + + return (void (*)()) dcbNewCallback(signature, &__wasm_dyncallback, dcc); +} + #endif // USE_DYNCALL static void onyx_print_trap(wasm_trap_t* trap) { @@ -560,6 +643,13 @@ b32 onyx_run_wasm(bh_buffer wasm_bytes, int argc, char *argv[]) { wasm_runtime.wasm_imports = wasm_imports; wasm_runtime.wasm_memory = wasm_memory; wasm_runtime.wasm_instance = wasm_instance; + wasm_runtime.wasm_func_table = wasm_extern_as_table( + wasm_extern_lookup_by_name(wasm_module, wasm_instance, "__indirect_function_table") + ); + +#ifdef USE_DYNCALL + wasm_runtime.wasm_func_from_idx = wasm_func_from_idx; +#endif wasm_runtime.argc = argc; wasm_runtime.argv = argv; diff --git a/core/onyx/cbindgen.onyx b/core/onyx/cbindgen.onyx index b2678a88..d381ecfe 100644 --- a/core/onyx/cbindgen.onyx +++ b/core/onyx/cbindgen.onyx @@ -42,6 +42,10 @@ package cbindgen use runtime +customize :: struct { + symbol_name: str; +} + #if #defined (runtime.Generated_Foreign_Info) { use core {package, *} @@ -56,10 +60,6 @@ use simd {*} } -customize :: struct { - symbol_name: str; -} - Cast_Mapping :: struct { type: type_expr; name: str; @@ -246,12 +246,59 @@ compile_c_file :: ( param_num := 0; for method_info.parameter_types { - if get_type_info(it).kind == .Slice { + it_info := it->info(); + + if it_info.kind == .Slice { io.write_format(&callw, "ONYX_PTR(P({}, i32)), P({}, i32)", param_num, param_num + 1); param_num += 1; } elseif is_pointer(it) { io.write_format(&callw, "ONYX_PTR(P({}, i32))", param_num); + + } elseif it_info.kind == .Function { + call_signature := make(dyn_str); + defer delete(&call_signature); + + func_info := it_info->as_function(); + for p: func_info.parameter_types { + map_to_dyncall(p, &call_signature); + } + + string.append(&call_signature, ")"); + map_to_dyncall(func_info.return_type, &call_signature); + + io.write_format(&callw, "(void *) runtime->wasm_func_from_idx(runtime->wasm_func_table, P({}, i32), \"{}\")", param_num, cast(str) call_signature); + + map_to_dyncall :: (t: type_expr, call_signature: &dyn_str) { + p := t; + switch i := t->info(); i.kind { + case .Distinct { + p = i->as_distinct().base_type; + } + } + + switch p { + case void do string.append(call_signature, "v"); + case bool do string.append(call_signature, "B"); + case i8 do string.append(call_signature, "c"); + case u8 do string.append(call_signature, "C"); + case i16 do string.append(call_signature, "s"); + case u16 do string.append(call_signature, "S"); + case i32 do string.append(call_signature, "i"); + case u32 do string.append(call_signature, "I"); + case i64 do string.append(call_signature, "l"); + case u64 do string.append(call_signature, "L"); + case f32 do string.append(call_signature, "f"); + case f64 do string.append(call_signature, "d"); + case #default { + if is_pointer(p) { + string.append(call_signature, "p"); + } else { + assert(false, tprintf("Unsupported type in function pointer: {}", p)); + } + } + } + } } else { matched := false; @@ -318,7 +365,7 @@ compile_c_file :: ( case .Multi_Pointer do return "ptr"; case .Array do return "ptr"; - case .Function do assert(false, "Passing functions between wasm and c is not yet supported."); + case .Function do return "i32"; // assert(false, "Passing functions between wasm and c is not yet supported."); case .Slice do assert(false, "Passing a slice from c to wasm is not yet supported."); case .Enum do return type_to_wasm_type((cast(&Type_Info_Enum) param_info).backing_type); case .Distinct do return type_to_wasm_type((cast(&Type_Info_Distinct) param_info).base_type); @@ -373,7 +420,7 @@ compile_c_file :: ( case .Pointer do return "WASM_I32"; // This will also have to depend on the pointer size... case .Multi_Pointer do return "WASM_I32"; // This will also have to depend on the pointer size... - case .Function do assert(false, "Passing functions between wasm and c is not yet supported."); + case .Function do return "WASM_I32, WASM_I32, WASM_I32"; // assert(false, "Passing functions between wasm and c is not yet supported."); case .Array do return "WASM_I32"; case .Slice do return "WASM_I32,WASM_I32"; case .Enum do return type_encoding((cast(&Type_Info_Enum) param_info).backing_type); diff --git a/interpreter/include/ovm_wasm.h b/interpreter/include/ovm_wasm.h index 0dac195e..16ee7b37 100644 --- a/interpreter/include/ovm_wasm.h +++ b/interpreter/include/ovm_wasm.h @@ -94,7 +94,17 @@ struct wasm_exporttype_t { // Runtime Objects +enum wasm_ref_stored_obj_t { + wasm_ref_stored_obj_unknown, + wasm_ref_stored_obj_func, +}; + struct wasm_ref_t { + enum wasm_ref_stored_obj_t stored_obj; + + union { + struct wasm_func_t *func; + }; }; struct wasm_frame_t { @@ -180,6 +190,7 @@ struct wasm_global_inner_t { struct wasm_table_inner_t { ovm_program_t *program; ovm_engine_t *engine; + struct wasm_instance_t *instance; int static_arr; diff --git a/interpreter/src/wasm/extern.c b/interpreter/src/wasm/extern.c index 9cda46c8..c0dc3d71 100644 --- a/interpreter/src/wasm/extern.c +++ b/interpreter/src/wasm/extern.c @@ -18,10 +18,10 @@ wasm_extern_t* wasm_global_as_extern(wasm_global_t* ext) { return (wasm_extern_t wasm_extern_t* wasm_table_as_extern(wasm_table_t* ext) { return (wasm_extern_t *) ext; } wasm_extern_t* wasm_memory_as_extern(wasm_memory_t* ext) { return (wasm_extern_t *) ext; } -wasm_func_t* wasm_extern_as_func(wasm_extern_t* ext) { return ext->type->kind == WASM_EXTERN_FUNC ? (wasm_func_t *) ext : NULL; } -wasm_global_t* wasm_extern_as_global(wasm_extern_t* ext) { return ext->type->kind == WASM_EXTERN_GLOBAL ? (wasm_global_t *) ext : NULL; } -wasm_table_t* wasm_extern_as_table(wasm_extern_t* ext) { return ext->type->kind == WASM_EXTERN_TABLE ? (wasm_table_t *) ext : NULL; } -wasm_memory_t* wasm_extern_as_memory(wasm_extern_t* ext) { return ext->type->kind == WASM_EXTERN_MEMORY ? (wasm_memory_t *) ext : NULL; } +wasm_func_t* wasm_extern_as_func(wasm_extern_t* ext) { return (ext && ext->type->kind == WASM_EXTERN_FUNC) ? (wasm_func_t *) ext : NULL; } +wasm_global_t* wasm_extern_as_global(wasm_extern_t* ext) { return (ext && ext->type->kind == WASM_EXTERN_GLOBAL) ? (wasm_global_t *) ext : NULL; } +wasm_table_t* wasm_extern_as_table(wasm_extern_t* ext) { return (ext && ext->type->kind == WASM_EXTERN_TABLE) ? (wasm_table_t *) ext : NULL; } +wasm_memory_t* wasm_extern_as_memory(wasm_extern_t* ext) { return (ext && ext->type->kind == WASM_EXTERN_MEMORY) ? (wasm_memory_t *) ext : NULL; } const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t* ext) { return (const wasm_extern_t *) ext; } const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t* ext) { return (const wasm_extern_t *) ext; } diff --git a/interpreter/src/wasm/instance.c b/interpreter/src/wasm/instance.c index 2b04f91b..d2a9a397 100644 --- a/interpreter/src/wasm/instance.c +++ b/interpreter/src/wasm/instance.c @@ -242,6 +242,7 @@ static void prepare_instance(wasm_instance_t *instance, const wasm_extern_vec_t wasm_table_t *table = wasm_table_new(instance->store, instance->module->tabletypes.data[i], NULL); table->inner.table.engine = ovm_engine; table->inner.table.program = ovm_program; + table->inner.table.instance = instance; table->inner.table.static_arr = instance->module->tabletypes.data[i]->type.table.static_arr; bh_arr_push(instance->tables, table); diff --git a/interpreter/src/wasm/ref.c b/interpreter/src/wasm/ref.c index 0ade447e..1777d197 100644 --- a/interpreter/src/wasm/ref.c +++ b/interpreter/src/wasm/ref.c @@ -1,2 +1,20 @@ -// TODO +#include "ovm_wasm.h" +#include "vm.h" + +wasm_func_t *wasm_ref_as_func(wasm_ref_t *ref) { + if (ref->stored_obj == wasm_ref_stored_obj_func) { + return ref->func; + } + + return NULL; +} + +const wasm_func_t *wasm_ref_as_func_const(const wasm_ref_t *ref) { + if (ref->stored_obj == wasm_ref_stored_obj_func) { + return (const wasm_func_t *) ref->func; + } + + return NULL; +} + diff --git a/interpreter/src/wasm/table.c b/interpreter/src/wasm/table.c index b878eba8..0f66a4f7 100644 --- a/interpreter/src/wasm/table.c +++ b/interpreter/src/wasm/table.c @@ -14,3 +14,19 @@ wasm_tabletype_t *wasm_table_type(const wasm_table_t *table) { return (wasm_tabletype_t *) table->inner.table.type; } +wasm_ref_t *wasm_table_get(const wasm_table_t *table, unsigned int index) { + const struct wasm_table_inner_t *tab = &table->inner.table; + ovm_program_t *program = tab->program; + ovm_engine_t *engine = tab->engine; + + // TODO bounds checking + wasm_func_t *func = tab->instance->funcs[ + program->static_integers[program->static_data[tab->static_arr].start_idx + index] + ]; + + wasm_ref_t *new_ref = bh_alloc(engine->store->arena_allocator, sizeof(*new_ref)); + new_ref->stored_obj = wasm_ref_stored_obj_func; + new_ref->func = func; + + return new_ref; +} \ No newline at end of file diff --git a/shared/include/onyx_library.h b/shared/include/onyx_library.h index ef4db7c8..8b0d26ad 100644 --- a/shared/include/onyx_library.h +++ b/shared/include/onyx_library.h @@ -19,12 +19,12 @@ typedef struct OnyxRuntime { wasm_memory_t* wasm_memory; wasm_engine_t *wasm_engine; wasm_extern_vec_t wasm_imports; + wasm_table_t *wasm_func_table; int argc; char **argv; - // HACK HACK HACK - // There should need to be this much stuff in here, but because Wasmer doesn't ship a "wasmerdll.lib" + // There shouldn't need to be this much stuff in here, but because Wasmer doesn't ship a "wasmerdll.lib" // file for windows, it is impossible for it to link successfully against the function provided in onyx.exe. // Therefore, we must serve as the linker and do this manually. Hopefully that library file will be // shipped soon so this can go away... @@ -36,6 +36,10 @@ typedef struct OnyxRuntime { void (*onyx_print_trap)(wasm_trap_t* trap); wasm_store_t *(*wasm_store_new)(wasm_engine_t *engine); void (*wasm_store_delete)(wasm_store_t *store); + + // This is only set when using the OVMwasm runtime, as Wasmer's C-api does not allow + // for this function to exist, yet. + void (*(*wasm_func_from_idx)(wasm_table_t *table, unsigned int index, char *signature))(void); } OnyxRuntime; OnyxRuntime* runtime;