Przeglądaj źródła

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>
Xiaokang Qin 5 lat temu
rodzic
commit
5418e09712

+ 221 - 0
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,

+ 12 - 0
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.

+ 6 - 0
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*);

+ 73 - 0
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.

+ 47 - 3
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)**.