From 5418e09712a8ba53aac1d822d639d16f20147e9f Mon Sep 17 00:00:00 2001 From: Xiaokang Qin <69961735+qinxk-inter@users.noreply.github.com> Date: Tue, 8 Sep 2020 13:03:35 +0800 Subject: [PATCH] Add two apis for wasm function call (#375) Add below two apis: bool wasm_runtime_call_wasm_a(WASMExecEnv *exec_env, WASMFunctionInstanceCommon *function, uint32 num_results, wasm_val_t results[], uint32 num_args, wasm_val_t args[]) bool wasm_runtime_call_wasm_v(WASMExecEnv *exec_env, WASMFunctionInstanceCommon *function, uint32 num_results, wasm_val_t results[], uint32 num_args, ...) Signed-off-by: Xiaokang Qin --- core/iwasm/common/wasm_runtime_common.c | 221 ++++++++++++++++++++++++ core/iwasm/common/wasm_runtime_common.h | 12 ++ core/iwasm/include/wasm_c_api.h | 6 + core/iwasm/include/wasm_export.h | 73 ++++++++ doc/embed_wamr.md | 50 +++++- 5 files changed, 359 insertions(+), 3 deletions(-) diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 2cba0d5b99..b729751b84 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -863,6 +863,227 @@ wasm_runtime_call_wasm(WASMExecEnv *exec_env, return false; } +static uint32 +parse_args_to_uint32_array(WASMType *type, + uint32 num_args, wasm_val_t *args, + uint32 *out_argv) +{ + int i, p; + + for (i = 0, p = 0; i < num_args; i++) { + switch (args[i].kind) { + case WASM_I32: + out_argv[p++] = args[i].of.i32; + break; + case WASM_I64: + { + union { uint64 val; uint32 parts[2]; } u; + u.val = args[i].of.i64; + out_argv[p++] = u.parts[0]; + out_argv[p++] = u.parts[1]; + break; + } + case WASM_F32: + { + union { float32 val; uint32 part; } u; + u.val = args[i].of.f32; + out_argv[p++] = u.part; + break; + } + case WASM_F64: + { + union { float64 val; uint32 parts[2]; } u; + u.val = args[i].of.f64; + out_argv[p++] = u.parts[0]; + out_argv[p++] = u.parts[1]; + break; + } + default: + bh_assert(0); + break; + } + } + return p; +} + +static uint32 +parse_uint32_array_to_results(WASMType *type, + uint32 argc, uint32 *argv, + wasm_val_t *out_results) +{ + int i, p; + + for (i = 0, p = 0; i < type->result_count; i++) { + switch (type->types[type->param_count + i]) { + case VALUE_TYPE_I32: + out_results[i].kind = WASM_I32; + out_results[i].of.i32 = *(int32 *)argv[p++]; + break; + case VALUE_TYPE_I64: + { + union { uint64 val; uint32 parts[2]; } u; + u.parts[0] = argv[p++]; + u.parts[1] = argv[p++]; + out_results[i].kind = WASM_I64; + out_results[i].of.i64 = u.val; + break; + } + case VALUE_TYPE_F32: + { + union { float32 val; uint32 part; } u; + u.part = argv[p++]; + out_results[i].kind = WASM_F32; + out_results[i].of.f32 = u.val; + break; + } + case VALUE_TYPE_F64: + { + union { float64 val; uint32 parts[2]; } u; + u.parts[0] = argv[p++]; + u.parts[1] = argv[p++]; + out_results[i].kind = WASM_F64; + out_results[i].of.f64 = u.val; + break; + } + default: + bh_assert(0); + break; + } + } + bh_assert(argc == p); + return type->result_count; +} + +bool +wasm_runtime_call_wasm_a(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t results[], + uint32 num_args, wasm_val_t args[]) +{ + uint32 argc, *argv, ret_num, cell_num, total_size; + bool ret = false; + WASMType *type = NULL; + +#if WASM_ENABLE_INTERP != 0 + if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) { + WASMFunctionInstance *wasm_func = (WASMFunctionInstance*)function; + type = wasm_func->u.func->func_type; + argc = wasm_func->param_cell_num; + cell_num = argc > wasm_func->ret_cell_num ? + argc : wasm_func->ret_cell_num; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (exec_env->module_inst->module_type == Wasm_Module_AoT) { + type = ((AOTFunctionInstance*)function)->u.func.func_type; + argc = type->param_cell_num; + cell_num = argc > type->ret_cell_num ? + argc : type->ret_cell_num; + } +#endif + if (!type) { + LOG_ERROR("Function type get failed, WAMR Interpreter and AOT must be enabled at least one."); + goto fail1; + } + + if (num_results != type->result_count) { + LOG_ERROR("The result value number does not match the function declaration."); + goto fail1; + } + + if (num_args != type->param_count) { + LOG_ERROR("The argument value number does not match the function declaration."); + goto fail1; + } + + total_size = sizeof(uint32) * (uint64)(cell_num > 2 ? cell_num : 2); + if (!(argv = runtime_malloc((uint32)total_size, exec_env->module_inst, NULL, 0))) { + wasm_runtime_set_exception(exec_env->module_inst, "allocate memory failed"); + goto fail1; + } + + argc = parse_args_to_uint32_array(type, num_args, args, argv); + if (!(ret = wasm_runtime_call_wasm(exec_env, function, argc, argv))) + goto fail2; + + ret_num = parse_uint32_array_to_results(type, type->ret_cell_num, argv, results); + bh_assert(ret_num == num_results); + +fail2: + wasm_runtime_free(argv); +fail1: + return ret; +} + +bool +wasm_runtime_call_wasm_v(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t results[], + uint32 num_args, ...) +{ + wasm_val_t *args = NULL; + WASMType *type = NULL; + bool ret = false; + int i = 0; + va_list vargs; + +#if WASM_ENABLE_INTERP != 0 + if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) { + WASMFunctionInstance *wasm_func = (WASMFunctionInstance*)function; + type = wasm_func->u.func->func_type; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (exec_env->module_inst->module_type == Wasm_Module_AoT) { + type = ((AOTFunctionInstance*)function)->u.func.func_type; + } +#endif + if (!type) { + LOG_ERROR("Function type get failed, WAMR Interpreter and AOT must be enabled at least one."); + goto fail1; + } + + if (num_args != type->param_count) { + LOG_ERROR("The argument value number does not match the function declaration."); + goto fail1; + } + if (!(args = runtime_malloc(sizeof(wasm_val_t) * num_args, NULL, NULL, 0))) { + wasm_runtime_set_exception(exec_env->module_inst, "allocate memory failed"); + goto fail1; + } + + va_start(vargs, num_args); + for (i = 0; i < num_args; i++) { + switch (type->types[i]) { + case VALUE_TYPE_I32: + args[i].kind = WASM_I32; + args[i].of.i32 = va_arg(vargs, uint32); + break; + case VALUE_TYPE_I64: + args[i].kind = WASM_I64; + args[i].of.i64 = va_arg(vargs, uint64); + break; + case VALUE_TYPE_F32: + args[i].kind = WASM_F32; + args[i].of.f32 = (float32)va_arg(vargs, float64); + break; + case VALUE_TYPE_F64: + args[i].kind = WASM_F64; + args[i].of.f64 = va_arg(vargs, float64);; + break; + default: + bh_assert(0); + break; + } + } + va_end(vargs); + ret = wasm_runtime_call_wasm_a(exec_env, function, num_results, results, num_args, args); + wasm_runtime_free(args); + +fail1: + return ret; +} + bool wasm_runtime_create_exec_env_and_call_wasm(WASMModuleInstanceCommon *module_inst, WASMFunctionInstanceCommon *function, diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index ba03e59aaf..51ef6c7777 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -167,6 +167,18 @@ wasm_runtime_call_wasm(WASMExecEnv *exec_env, WASMFunctionInstanceCommon *function, uint32 argc, uint32 argv[]); +bool +wasm_runtime_call_wasm_a(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t *results, + uint32 num_args, wasm_val_t *args); + +bool +wasm_runtime_call_wasm_v(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t *results, + uint32 num_args, ...); + /** * Call a function reference of a given WASM runtime instance with * arguments. diff --git a/core/iwasm/include/wasm_c_api.h b/core/iwasm/include/wasm_c_api.h index 5b5bd79e49..fdc072327a 100644 --- a/core/iwasm/include/wasm_c_api.h +++ b/core/iwasm/include/wasm_c_api.h @@ -165,6 +165,8 @@ static const uint32_t wasm_limits_max_default = 0xffffffff; WASM_DECLARE_TYPE(valtype) +#ifndef WASM_VALKIND_T_DEFINED +#define WASM_VALKIND_T_DEFINED typedef uint8_t wasm_valkind_t; enum wasm_valkind_enum { WASM_I32, @@ -174,6 +176,7 @@ enum wasm_valkind_enum { WASM_ANYREF = 128, WASM_FUNCREF, }; +#endif WASM_API_EXTERN own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t); @@ -299,6 +302,8 @@ WASM_API_EXTERN const wasm_externtype_t* wasm_exporttype_type(const wasm_exportt // Values +#ifndef WASM_VAL_T_DEFINED +#define WASM_VAL_T_DEFINED struct wasm_ref_t; typedef struct wasm_val_t { @@ -311,6 +316,7 @@ typedef struct wasm_val_t { struct wasm_ref_t* ref; } of; } wasm_val_t; +#endif WASM_API_EXTERN void wasm_val_delete(own wasm_val_t* v); WASM_API_EXTERN void wasm_val_copy(own wasm_val_t* out, const wasm_val_t*); diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index 79ec96f630..0f392e2797 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -120,6 +120,35 @@ typedef struct RuntimeInitArgs { uint32_t max_thread_num; } RuntimeInitArgs; +#ifndef WASM_VALKIND_T_DEFINED +#define WASM_VALKIND_T_DEFINED +typedef uint8_t wasm_valkind_t; +enum wasm_valkind_enum { + WASM_I32, + WASM_I64, + WASM_F32, + WASM_F64, + WASM_ANYREF = 128, + WASM_FUNCREF, +}; +#endif + +#ifndef WASM_VAL_T_DEFINED +#define WASM_VAL_T_DEFINED +struct wasm_ref_t; + +typedef struct wasm_val_t { + wasm_valkind_t kind; + union { + int32_t i32; + int64_t i64; + float f32; + double f64; + struct wasm_ref_t* ref; + } of; +} wasm_val_t; +#endif + /** * Initialize the WASM runtime environment, and also initialize * the memory allocator with system allocator, which calls os_malloc @@ -385,6 +414,50 @@ wasm_runtime_call_wasm(wasm_exec_env_t exec_env, wasm_function_inst_t function, uint32_t argc, uint32_t argv[]); +/** + * Call the given WASM function of a WASM module instance with + * provided results space and arguments (bytecode and AoT). + * + * @param exec_env the execution environment to call the function, + * which must be created from wasm_create_exec_env() + * @param function the function to call + * @param num_results the number of results + * @param results the pre-alloced pointer to get the results + * @param num_args the number of arguments + * @param args the arguments + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call wasm_runtime_get_exception to get the exception + * info. + */ +bool +wasm_runtime_call_wasm_a(wasm_exec_env_t exec_env, + wasm_function_inst_t function, + uint32_t num_results, wasm_val_t results[], + uint32_t num_args, wasm_val_t *args); + +/** + * Call the given WASM function of a WASM module instance with + * provided results space and variant arguments (bytecode and AoT). + * + * @param exec_env the execution environment to call the function, + * which must be created from wasm_create_exec_env() + * @param function the function to call + * @param num_results the number of results + * @param results the pre-alloced pointer to get the results + * @param num_args the number of arguments + * @param ... the variant arguments + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call wasm_runtime_get_exception to get the exception + * info. + */ +bool +wasm_runtime_call_wasm_v(wasm_exec_env_t exec_env, + wasm_function_inst_t function, + uint32_t num_results, wasm_val_t results[], + uint32_t num_args, ...); + /** * Find the unique main function from a WASM module instance * and execute that function. diff --git a/doc/embed_wamr.md b/doc/embed_wamr.md index 4503e84c57..d0c8e3612c 100644 --- a/doc/embed_wamr.md +++ b/doc/embed_wamr.md @@ -71,17 +71,23 @@ if (!wasm_runtime_full_init(&init_args)) { ## Native calls WASM functions and passes parameters -After a module is instantiated, the runtime native can lookup WASM functions by the names and call them. +After a module is instantiated, the runtime embedder can lookup the target WASM function by name, and create execution environment to call the function. ```c - unit32 argv[2]; - /* lookup a WASM function by its name The function signature can NULL here */ func = wasm_runtime_lookup_function(module_inst, "fib", NULL); /* creat an execution environment to execute the WASM functions */ exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); +``` + +There are several ways to call WASM function: + +1. Function call with parameters in an array of 32 bits elements and size: + +```c + unit32 argv[2]; /* arguments are always transferred in 32-bit element */ argv[0] = 8; @@ -129,6 +135,44 @@ The parameters are transferred in an array of 32 bits elements. For parameters t memcpy(&ret, &argv[0], sizeof(ret)); ``` +2. Function call with results and arguments both in `wasm_val_t` struct and size: + +```c + unit32 num_args = 1, num_results = 1; + wasm_val_t args[1], results[1]; + + /* set the argument type and value */ + args[0].kind = WASM_I32; + args[0].of.i32 = 8; + + /* call the WASM function */ + if (wasm_runtime_call_wasm_a(exec_env, func, num_results, results, num_args, args)) { + /* the return value is stored in results */ + printf("fib function return: %d\n", results[0].of.i32); + } + else { + /* exception is thrown if call fails */ + printf("%s\n", wasm_runtime_get_exception(module_inst)); + } +``` + +3. Function call with variant argument support: + +```c + unit32 num_args = 1, num_results = 1; + wasm_val_t results[1]; + + /* call the WASM function */ + if (wasm_runtime_call_wasm_v(exec_env, func, 1, results, 1, 8)) { + /* the return value is stored in results */ + printf("fib function return: %d\n", results[0].of.i32); + } + else { + /* exception is thrown if call fails */ + printf("%s\n", wasm_runtime_get_exception(module_inst)); + } +``` + ## Pass buffer to WASM function If we need to transfer a buffer to WASM function, we can pass the buffer address through a parameter. **Attention**: The sandbox will forbid the WASM code to access outside memory, we must **allocate the buffer from WASM instance's own memory space and pass the buffer address in instance's space (not the runtime native address)**.