mirror of
				https://github.com/bytecodealliance/wasm-micro-runtime.git
				synced 2025-10-31 05:11:19 +00:00 
			
		
		
		
	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 <xiaokang.qxk@antgroup.com>
			
			
This commit is contained in:
		
							parent
							
								
									2135badc54
								
							
						
					
					
						commit
						5418e09712
					
				|  | @ -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, | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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*); | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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)**. | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Xiaokang Qin
						Xiaokang Qin