From 6b1d81650d8b66edf743ba98f0b627347c6e04f6 Mon Sep 17 00:00:00 2001 From: Enrico Loparco Date: Fri, 17 May 2024 03:00:08 +0200 Subject: [PATCH] Allow not copying the wasm binary in wasm-c-api and not referring to the binary in wasm/aot loader (#3389) Add flag `LoadArgs.clone_wasm_binary` to control whether to clone the wasm/aot binary in wasm-c-api module. If false, API `wasm_module_new_ex` won't clone the binary, which may reduce the footprint. Add flag `LoadArgs.wasm_binary_freeable` to control whether the wasm/aot binary may be freed after instantiation for wamr API `wasm_runtime_load_ex`, if yes, then for some running modes, the wasm/aot module doesn't refer to the input binary again so developer can free it after instantiation to reduce the footprint. And add API `wasm_module_is_underlying_binary_freeable` and `wasm_runtime_is_underlying_binary_freeable` to check whether the input binary can be freed after instantiation for wasm-c-api and wamr api. And add sample to illustrate it. --- core/iwasm/aot/aot_loader.c | 12 ++- core/iwasm/aot/aot_runtime.h | 3 + core/iwasm/common/wasm_c_api.c | 46 +++++--- core/iwasm/common/wasm_runtime_common.c | 57 ++++++++++ core/iwasm/common/wasm_runtime_common.h | 4 + core/iwasm/include/wasm_c_api.h | 10 +- core/iwasm/include/wasm_export.h | 17 +++ core/iwasm/interpreter/wasm.h | 3 + core/iwasm/interpreter/wasm_loader.c | 12 ++- core/iwasm/interpreter/wasm_mini_loader.c | 12 ++- doc/memory_tune.md | 2 + samples/basic/CMakeLists.txt | 4 + samples/basic/build.sh | 1 + samples/basic/src/free_buffer_early.c | 121 ++++++++++++++++++++++ 14 files changed, 276 insertions(+), 28 deletions(-) create mode 100644 samples/basic/src/free_buffer_early.c diff --git a/core/iwasm/aot/aot_loader.c b/core/iwasm/aot/aot_loader.c index d1719659..33e74acc 100644 --- a/core/iwasm/aot/aot_loader.c +++ b/core/iwasm/aot/aot_loader.c @@ -3852,6 +3852,7 @@ create_module(char *name, char *error_buf, uint32 error_buf_size) module->module_type = Wasm_Module_AoT; module->name = name; + module->is_binary_freeable = false; #if WASM_ENABLE_MULTI_MODULE != 0 module->import_module_list = &module->import_module_list_head; @@ -4082,8 +4083,8 @@ fail: } static bool -load(const uint8 *buf, uint32 size, AOTModule *module, char *error_buf, - uint32 error_buf_size) +load(const uint8 *buf, uint32 size, AOTModule *module, + bool wasm_binary_freeable, char *error_buf, uint32 error_buf_size) { const uint8 *buf_end = buf + size; const uint8 *p = buf, *p_end = buf_end; @@ -4107,8 +4108,8 @@ load(const uint8 *buf, uint32 size, AOTModule *module, char *error_buf, error_buf_size)) return false; - ret = load_from_sections(module, section_list, true, error_buf, - error_buf_size); + ret = load_from_sections(module, section_list, !wasm_binary_freeable, + error_buf, error_buf_size); if (!ret) { /* If load_from_sections() fails, then aot text is destroyed in destroy_sections() */ @@ -4152,7 +4153,8 @@ aot_load_from_aot_file(const uint8 *buf, uint32 size, const LoadArgs *args, return NULL; os_thread_jit_write_protect_np(false); /* Make memory writable */ - if (!load(buf, size, module, error_buf, error_buf_size)) { + if (!load(buf, size, module, args->wasm_binary_freeable, error_buf, + error_buf_size)) { aot_unload(module); return NULL; } diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index 79bdb1df..b46f676a 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -310,6 +310,9 @@ typedef struct AOTModule { /* user defined name */ char *name; + + /* Whether the underlying wasm binary buffer can be freed */ + bool is_binary_freeable; } AOTModule; #define AOTMemoryInstance WASMMemoryInstance diff --git a/core/iwasm/common/wasm_c_api.c b/core/iwasm/common/wasm_c_api.c index 7c582bbc..2c7b4bc3 100644 --- a/core/iwasm/common/wasm_c_api.c +++ b/core/iwasm/common/wasm_c_api.c @@ -42,6 +42,8 @@ typedef struct wasm_module_ex_t { struct WASMModuleCommon *module_comm_rt; wasm_byte_vec_t *binary; + /* If true, binary in wasm_module_ex_t contains a copy of the WASM binary */ + bool is_binary_cloned; korp_mutex lock; uint32 ref_count; #if WASM_ENABLE_WASM_CACHE != 0 @@ -2242,8 +2244,7 @@ quit: #endif /* WASM_ENABLE_WASM_CACHE != 0 */ wasm_module_t * -wasm_module_new_ex(wasm_store_t *store, const wasm_byte_vec_t *binary, - const LoadArgs *args) +wasm_module_new_ex(wasm_store_t *store, wasm_byte_vec_t *binary, LoadArgs *args) { char error_buf[128] = { 0 }; wasm_module_ex_t *module_ex = NULL; @@ -2291,14 +2292,21 @@ wasm_module_new_ex(wasm_store_t *store, const wasm_byte_vec_t *binary, if (!module_ex) goto quit; - module_ex->binary = malloc_internal(sizeof(wasm_byte_vec_t)); - if (!module_ex->binary) - goto free_module; + module_ex->is_binary_cloned = args->clone_wasm_binary; + if (args->clone_wasm_binary) { + module_ex->binary = malloc_internal(sizeof(wasm_byte_vec_t)); + if (!module_ex->binary) + goto free_module; - wasm_byte_vec_copy(module_ex->binary, binary); - if (!module_ex->binary->data) - goto free_binary; + wasm_byte_vec_copy(module_ex->binary, binary); + if (!module_ex->binary->data) + goto free_binary; + } + else { + module_ex->binary = binary; + } + args->wasm_binary_freeable = !args->clone_wasm_binary; module_ex->module_comm_rt = wasm_runtime_load_ex( (uint8 *)module_ex->binary->data, (uint32)module_ex->binary->size, args, error_buf, (uint32)sizeof(error_buf)); @@ -2336,9 +2344,11 @@ remove_last: unload: wasm_runtime_unload(module_ex->module_comm_rt); free_vec: - wasm_byte_vec_delete(module_ex->binary); + if (args->clone_wasm_binary) + wasm_byte_vec_delete(module_ex->binary); free_binary: - wasm_runtime_free(module_ex->binary); + if (args->clone_wasm_binary) + wasm_runtime_free(module_ex->binary); free_module: wasm_runtime_free(module_ex); quit: @@ -2351,7 +2361,8 @@ wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary) { LoadArgs args = { 0 }; args.name = ""; - return wasm_module_new_ex(store, binary, &args); + args.clone_wasm_binary = true; + return wasm_module_new_ex(store, (wasm_byte_vec_t *)binary, &args); } bool @@ -2410,7 +2421,8 @@ wasm_module_delete_internal(wasm_module_t *module) return; } - DEINIT_VEC(module_ex->binary, wasm_byte_vec_delete); + if (module_ex->is_binary_cloned) + DEINIT_VEC(module_ex->binary, wasm_byte_vec_delete); if (module_ex->module_comm_rt) { wasm_runtime_unload(module_ex->module_comm_rt); @@ -2994,6 +3006,16 @@ wasm_module_get_name(wasm_module_t *module) return wasm_runtime_get_module_name(module_ex->module_comm_rt); } +bool +wasm_module_is_underlying_binary_freeable( + const wasm_module_t *module, const struct wasm_instance_t *instance) +{ + if (((wasm_module_ex_t *)module)->is_binary_cloned) + return true; + + return wasm_runtime_is_underlying_binary_freeable(instance->inst_comm_rt); +} + static wasm_func_t * wasm_func_new_basic(wasm_store_t *store, const wasm_functype_t *type, wasm_func_callback_t func_callback) diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 830cf605..6c4620fe 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -1350,12 +1350,18 @@ wasm_runtime_load_ex(uint8 *buf, uint32 size, const LoadArgs *args, true, #endif args, error_buf, error_buf_size); + if (module_common) + ((WASMModule *)module_common)->is_binary_freeable = + args->wasm_binary_freeable; #endif } else if (get_package_type(buf, size) == Wasm_Module_AoT) { #if WASM_ENABLE_AOT != 0 module_common = (WASMModuleCommon *)aot_load_from_aot_file( buf, size, args, error_buf, error_buf_size); + if (module_common) + ((AOTModule *)module_common)->is_binary_freeable = + args->wasm_binary_freeable; #endif } else { @@ -1383,6 +1389,7 @@ wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf, { LoadArgs args = { 0 }; args.name = ""; + args.wasm_binary_freeable = false; return wasm_runtime_load_ex(buf, size, &args, error_buf, error_buf_size); } @@ -1400,6 +1407,7 @@ wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot, LOG_DEBUG("WASM module load failed from sections"); return NULL; } + ((WASMModule *)module_common)->is_binary_freeable = true; return register_module_with_null_name(module_common, error_buf, error_buf_size); #endif @@ -1412,6 +1420,7 @@ wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot, LOG_DEBUG("WASM module load failed from sections"); return NULL; } + ((AOTModule *)module_common)->is_binary_freeable = true; return register_module_with_null_name(module_common, error_buf, error_buf_size); #endif @@ -7247,3 +7256,51 @@ wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env, } return true; } + +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_underlying_binary_freeable(const wasm_module_inst_t module_inst) +{ + uint32 i; + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { +#if (WASM_ENABLE_JIT != 0 || WASM_ENABLE_FAST_JIT != 0) \ + && (WASM_ENABLE_LAZY_JIT != 0) + return false; +#elif WASM_ENABLE_FAST_INTERP == 0 + return false; +#else + /* Fast interpreter mode */ + WASMModule *module = ((WASMModuleInstance *)module_inst)->module; + if (!module->is_binary_freeable) + return false; +#if WASM_ENABLE_GC != 0 && WASM_ENABLE_STRINGREF != 0 + if (module->string_literal_ptrs) + return false; +#endif +#if WASM_ENABLE_BULK_MEMORY != 0 + for (i = 0; i < module->data_seg_count; i++) + if (!bh_bitmap_get_bit( + ((WASMModuleInstance *)module_inst)->e->common.data_dropped, + i)) + return false; +#endif +#endif + } +#endif /* WASM_ENABLE_INTERP != 0 */ +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModule *module = + (AOTModule *)((AOTModuleInstance *)module_inst)->module; + if (!module->is_binary_freeable) + return false; +#if WASM_ENABLE_GC != 0 && WASM_ENABLE_STRINGREF != 0 + if (module->string_literal_ptrs) + return false; +#endif + } +#endif /* WASM_ENABLE_AOT != 0 */ + + (void)i; + return true; +} diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index f38eb717..52b8cab9 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -1199,6 +1199,10 @@ WASM_RUNTIME_API_EXTERN bool wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env, uint32 requested_size); +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_underlying_binary_freeable( + const wasm_module_inst_t module_inst); + #if WASM_ENABLE_LINUX_PERF != 0 bool wasm_runtime_get_linux_perf(void); diff --git a/core/iwasm/include/wasm_c_api.h b/core/iwasm/include/wasm_c_api.h index 498f4389..f542b0fc 100644 --- a/core/iwasm/include/wasm_c_api.h +++ b/core/iwasm/include/wasm_c_api.h @@ -528,6 +528,12 @@ typedef struct WASMModuleCommon *wasm_module_t; #define LOAD_ARGS_OPTION_DEFINED typedef struct LoadArgs { char *name; + /* True by default, used by wasm-c-api only. + If false, the wasm input buffer (wasm_byte_vec_t) is referenced by the + module instead of being cloned. Hence, it can be freed after module loading. */ + bool clone_wasm_binary; + /* This option is only used by the AOT/wasm loader (see wasm_export.h) */ + bool wasm_binary_freeable; /* TODO: more fields? */ } LoadArgs; #endif /* LOAD_ARGS_OPTION_DEFINED */ @@ -537,7 +543,7 @@ WASM_API_EXTERN own wasm_module_t* wasm_module_new( // please refer to wasm_runtime_load_ex(...) in core/iwasm/include/wasm_export.h WASM_API_EXTERN own wasm_module_t* wasm_module_new_ex( - wasm_store_t*, const wasm_byte_vec_t* binary, const LoadArgs *args); + wasm_store_t*, wasm_byte_vec_t* binary, LoadArgs *args); WASM_API_EXTERN void wasm_module_delete(own wasm_module_t*); @@ -557,6 +563,8 @@ WASM_API_EXTERN void wasm_shared_module_delete(own wasm_shared_module_t*); WASM_API_EXTERN bool wasm_module_set_name(wasm_module_t*, const char* name); WASM_API_EXTERN const char *wasm_module_get_name(wasm_module_t*); +WASM_API_EXTERN bool wasm_module_is_underlying_binary_freeable(const wasm_module_t *module, const struct wasm_instance_t* instance); + // Function Instances diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index cb5b7530..a5eadd68 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -227,6 +227,13 @@ typedef struct RuntimeInitArgs { #define LOAD_ARGS_OPTION_DEFINED typedef struct LoadArgs { char *name; + /* This option is only used by the Wasm C API (see wasm_c_api.h) */ + bool clone_wasm_binary; + /* False by default, used by AOT/wasm loader only. + If true, the AOT/wasm loader creates a copy of some module fields (e.g. + const strings), making it possible to free the wasm binary buffer after + loading. */ + bool wasm_binary_freeable; /* TODO: more fields? */ } LoadArgs; #endif /* LOAD_ARGS_OPTION_DEFINED */ @@ -1886,6 +1893,16 @@ WASM_RUNTIME_API_EXTERN bool wasm_runtime_detect_native_stack_overflow_size(wasm_exec_env_t exec_env, uint32_t required_size); +/** + * Query whether the wasm binary buffer used to create the module can be freed + * + * @param module_inst the target module instance + * @return true if the wasm binary buffer can be freed + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_underlying_binary_freeable( + const wasm_module_inst_t module_inst); + #ifdef __cplusplus } #endif diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index b60649e0..bc8ff4c7 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -1077,6 +1077,9 @@ struct WASMModule { /* user defined name */ char *name; + + /* Whether the underlying wasm binary buffer can be freed */ + bool is_binary_freeable; }; typedef struct BlockType { diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index d7687a1f..ba9f45a5 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -6120,6 +6120,7 @@ create_module(char *name, char *error_buf, uint32 error_buf_size) module->start_function = (uint32)-1; module->name = name; + module->is_binary_freeable = false; #if WASM_ENABLE_FAST_INTERP == 0 module->br_table_cache_list = &module->br_table_cache_list_head; @@ -6347,8 +6348,8 @@ static union { #define is_little_endian() (__ue.b == 1) static bool -load(const uint8 *buf, uint32 size, WASMModule *module, char *error_buf, - uint32 error_buf_size) +load(const uint8 *buf, uint32 size, WASMModule *module, + bool wasm_binary_freeable, char *error_buf, uint32 error_buf_size) { const uint8 *buf_end = buf + size; const uint8 *p = buf, *p_end = buf_end; @@ -6376,8 +6377,8 @@ load(const uint8 *buf, uint32 size, WASMModule *module, char *error_buf, } if (!create_sections(buf, size, §ion_list, error_buf, error_buf_size) - || !load_from_sections(module, section_list, true, error_buf, - error_buf_size)) { + || !load_from_sections(module, section_list, !wasm_binary_freeable, + error_buf, error_buf_size)) { destroy_sections(section_list); return false; } @@ -6553,7 +6554,8 @@ wasm_loader_load(uint8 *buf, uint32 size, module->load_size = size; #endif - if (!load(buf, size, module, error_buf, error_buf_size)) { + if (!load(buf, size, module, args->wasm_binary_freeable, error_buf, + error_buf_size)) { goto fail; } diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c index ed1cb2e6..6db4d5ec 100644 --- a/core/iwasm/interpreter/wasm_mini_loader.c +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -3008,6 +3008,7 @@ create_module(char *name, char *error_buf, uint32 error_buf_size) module->start_function = (uint32)-1; module->name = name; + module->is_binary_freeable = false; #if WASM_ENABLE_FAST_INTERP == 0 module->br_table_cache_list = &module->br_table_cache_list_head; @@ -3166,8 +3167,8 @@ static union { #define is_little_endian() (__ue.b == 1) static bool -load(const uint8 *buf, uint32 size, WASMModule *module, char *error_buf, - uint32 error_buf_size) +load(const uint8 *buf, uint32 size, WASMModule *module, + bool wasm_binary_freeable, char *error_buf, uint32 error_buf_size) { const uint8 *buf_end = buf + size; const uint8 *p = buf, *p_end = buf_end; @@ -3192,8 +3193,8 @@ load(const uint8 *buf, uint32 size, WASMModule *module, char *error_buf, } if (!create_sections(buf, size, §ion_list, error_buf, error_buf_size) - || !load_from_sections(module, section_list, true, error_buf, - error_buf_size)) { + || !load_from_sections(module, section_list, !wasm_binary_freeable, + error_buf, error_buf_size)) { destroy_sections(section_list); return false; } @@ -3218,7 +3219,8 @@ wasm_loader_load(uint8 *buf, uint32 size, const LoadArgs *args, char *error_buf, module->load_size = size; #endif - if (!load(buf, size, module, error_buf, error_buf_size)) { + if (!load(buf, size, module, args->wasm_binary_freeable, error_buf, + error_buf_size)) { goto fail; } diff --git a/doc/memory_tune.md b/doc/memory_tune.md index 05a1a633..9bb4f6a1 100644 --- a/doc/memory_tune.md +++ b/doc/memory_tune.md @@ -30,3 +30,5 @@ Normally there are some methods to tune the memory usage: - set the app heap size with `wasm_runtime_instantiate` - use `nostdlib` mode, add `-Wl,--strip-all`: refer to [How to reduce the footprint](./build_wasm_app.md#2-how-to-reduce-the-footprint) of building wasm app for more details - use XIP mode, refer to [WAMR XIP (Execution In Place) feature introduction](./xip.md) for more details +- when using the Wasm C API in fast interpreter or AOT mode, set `clone_wasm_binary=false` in `LoadArgs` and free the wasm binary buffer (with `wasm_byte_vec_delete`) after module loading; `wasm_module_is_underlying_binary_freeable` can be queried to check if the wasm binary buffer can be safely freed (see [the example](../samples/basic/src/free_buffer_early.c)); after the buffer is freed, `wasm_runtime_get_custom_section` cannot be called anymore +- when using the wasm/AOT loader in fast interpreter or AOT mode, set `wasm_binary_freeable=true` in `LoadArgs` and free the wasm binary buffer (with `wasm_byte_vec_delete`) after module loading; `wasm_runtime_is_underlying_binary_freeable` can be queried to check if the wasm binary buffer can be safely freed; after the buffer is freed, `wasm_runtime_get_custom_section` cannot be called anymore diff --git a/samples/basic/CMakeLists.txt b/samples/basic/CMakeLists.txt index 62edf08f..3be19df8 100644 --- a/samples/basic/CMakeLists.txt +++ b/samples/basic/CMakeLists.txt @@ -79,12 +79,16 @@ include_directories(${CMAKE_CURRENT_LIST_DIR}/src) include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) add_executable (basic src/main.c src/native_impl.c ${UNCOMMON_SHARED_SOURCE}) +add_executable (free_buffer_early src/free_buffer_early.c ${UNCOMMON_SHARED_SOURCE}) check_pie_supported() set_target_properties (basic PROPERTIES POSITION_INDEPENDENT_CODE ON) +set_target_properties (free_buffer_early PROPERTIES POSITION_INDEPENDENT_CODE ON) if (APPLE) target_link_libraries (basic vmlib -lm -ldl -lpthread) + target_link_libraries (free_buffer_early vmlib -lm -ldl -lpthread) else () target_link_libraries (basic vmlib -lm -ldl -lpthread -lrt) + target_link_libraries (free_buffer_early vmlib -lm -ldl -lpthread -lrt) endif () diff --git a/samples/basic/build.sh b/samples/basic/build.sh index c1d598a8..4ea54c67 100755 --- a/samples/basic/build.sh +++ b/samples/basic/build.sh @@ -50,6 +50,7 @@ OUT_FILE=${i%.*}.wasm -Wl,--export=generate_float \ -Wl,--export=float_to_string \ -Wl,--export=calculate\ + -Wl,--export=mul7\ -Wl,--allow-undefined \ -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC} diff --git a/samples/basic/src/free_buffer_early.c b/samples/basic/src/free_buffer_early.c new file mode 100644 index 00000000..8bdcc27f --- /dev/null +++ b/samples/basic/src/free_buffer_early.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" + +void +my_log(uint32 log_level, const char *file, int line, const char *fmt, ...) +{ + char buf[200]; + snprintf(buf, sizeof(buf), "[WamrLogger] %s\n", fmt); + + va_list ap; + va_start(ap, fmt); + vprintf(buf, ap); + va_end(ap); +} + +int +my_vprintf(const char *format, va_list ap) +{ + return vprintf(format, ap); +} + +void +print_usage(void) +{ + fprintf(stdout, "Options:\r\n"); + fprintf(stdout, " -f [path of wasm file] \n"); +} + +int +main(int argc, char *argv_main[]) +{ + static char global_heap_buf[512 * 1024]; + char *buffer = NULL, error_buf[128]; + int opt; + char *wasm_path = NULL; + bool success; + + wasm_module_t module = NULL; + wasm_module_inst_t module_inst = NULL; + uint32 buf_size, stack_size = 8092, heap_size = 8092; + LoadArgs load_args = { 0 }; + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + while ((opt = getopt(argc, argv_main, "hf:")) != -1) { + switch (opt) { + case 'f': + wasm_path = optarg; + break; + case 'h': + print_usage(); + return 0; + case '?': + print_usage(); + return 0; + } + } + if (optind == 1) { + print_usage(); + return 0; + } + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + if (!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + load_args.wasm_binary_freeable = true; + module = wasm_runtime_load_ex((uint8 *)buffer, buf_size, &load_args, + error_buf, sizeof(error_buf)); + if (!module) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + if (!module_inst) { + printf("Instantiate wasm module failed. error: %s.\n", error_buf); + goto fail; + } + + if (wasm_runtime_is_underlying_binary_freeable(module_inst)) { + printf("Able to free wasm binary buffer.\n"); + wasm_runtime_free(buffer); + buffer = NULL; + } + + char *args[1] = { "3" }; + success = wasm_application_execute_func(module_inst, "mul7", 1, args); + if (!success) { + printf("Unable to execute function.\n"); + goto fail; + } + +fail: + if (module_inst) + wasm_runtime_deinstantiate(module_inst); + if (module) + wasm_runtime_unload(module); + if (buffer) + wasm_runtime_free(buffer); + wasm_runtime_destroy(); + return 0; +} \ No newline at end of file